@agile-vibe-coding/avc 0.1.1 → 0.3.1

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 (239) hide show
  1. package/cli/agent-loader.js +21 -0
  2. package/cli/agents/agent-selector.md +152 -0
  3. package/cli/agents/architecture-recommender.md +418 -0
  4. package/cli/agents/code-implementer.md +117 -0
  5. package/cli/agents/code-validator.md +80 -0
  6. package/cli/agents/context-reviewer-epic.md +101 -0
  7. package/cli/agents/context-reviewer-story.md +92 -0
  8. package/cli/agents/context-writer-epic.md +145 -0
  9. package/cli/agents/context-writer-story.md +111 -0
  10. package/cli/agents/database-deep-dive.md +470 -0
  11. package/cli/agents/database-recommender.md +634 -0
  12. package/cli/agents/doc-distributor.md +176 -0
  13. package/cli/agents/doc-writer-epic.md +42 -0
  14. package/cli/agents/doc-writer-story.md +43 -0
  15. package/cli/agents/documentation-updater.md +203 -0
  16. package/cli/agents/duplicate-detector.md +110 -0
  17. package/cli/agents/epic-story-decomposer.md +559 -0
  18. package/cli/agents/feature-context-generator.md +91 -0
  19. package/cli/agents/gap-checker-epic.md +52 -0
  20. package/cli/agents/impact-checker-story.md +51 -0
  21. package/cli/agents/migration-guide-generator.md +305 -0
  22. package/cli/agents/mission-scope-generator.md +143 -0
  23. package/cli/agents/mission-scope-validator.md +146 -0
  24. package/cli/agents/project-context-extractor.md +122 -0
  25. package/cli/agents/project-documentation-creator.json +226 -0
  26. package/cli/agents/project-documentation-creator.md +595 -0
  27. package/cli/agents/question-prefiller.md +269 -0
  28. package/cli/agents/refiner-epic.md +39 -0
  29. package/cli/agents/refiner-story.md +42 -0
  30. package/cli/agents/scaffolding-generator.md +99 -0
  31. package/cli/agents/seed-validator.md +71 -0
  32. package/cli/agents/story-doc-enricher.md +133 -0
  33. package/cli/agents/story-scope-reviewer.md +147 -0
  34. package/cli/agents/story-splitter.md +83 -0
  35. package/cli/agents/suggestion-business-analyst.md +88 -0
  36. package/cli/agents/suggestion-deployment-architect.md +263 -0
  37. package/cli/agents/suggestion-product-manager.md +129 -0
  38. package/cli/agents/suggestion-security-specialist.md +156 -0
  39. package/cli/agents/suggestion-technical-architect.md +269 -0
  40. package/cli/agents/suggestion-ux-researcher.md +93 -0
  41. package/cli/agents/task-subtask-decomposer.md +188 -0
  42. package/cli/agents/validator-documentation.json +183 -0
  43. package/cli/agents/validator-documentation.md +455 -0
  44. package/cli/agents/validator-selector.md +211 -0
  45. package/cli/ansi-colors.js +21 -0
  46. package/cli/api-reference-tool.js +368 -0
  47. package/cli/build-docs.js +29 -8
  48. package/cli/ceremony-history.js +369 -0
  49. package/cli/checks/catalog.json +76 -0
  50. package/cli/checks/code/quality.json +26 -0
  51. package/cli/checks/code/testing.json +14 -0
  52. package/cli/checks/code/traceability.json +26 -0
  53. package/cli/checks/cross-refs/epic.json +171 -0
  54. package/cli/checks/cross-refs/story.json +149 -0
  55. package/cli/checks/epic/api.json +114 -0
  56. package/cli/checks/epic/backend.json +126 -0
  57. package/cli/checks/epic/cloud.json +126 -0
  58. package/cli/checks/epic/data.json +102 -0
  59. package/cli/checks/epic/database.json +114 -0
  60. package/cli/checks/epic/developer.json +182 -0
  61. package/cli/checks/epic/devops.json +174 -0
  62. package/cli/checks/epic/frontend.json +162 -0
  63. package/cli/checks/epic/mobile.json +102 -0
  64. package/cli/checks/epic/qa.json +90 -0
  65. package/cli/checks/epic/security.json +184 -0
  66. package/cli/checks/epic/solution-architect.json +192 -0
  67. package/cli/checks/epic/test-architect.json +90 -0
  68. package/cli/checks/epic/ui.json +102 -0
  69. package/cli/checks/epic/ux.json +90 -0
  70. package/cli/checks/fixes/epic-fix-template.md +10 -0
  71. package/cli/checks/fixes/story-fix-template.md +10 -0
  72. package/cli/checks/story/api.json +186 -0
  73. package/cli/checks/story/backend.json +102 -0
  74. package/cli/checks/story/cloud.json +102 -0
  75. package/cli/checks/story/data.json +210 -0
  76. package/cli/checks/story/database.json +102 -0
  77. package/cli/checks/story/developer.json +168 -0
  78. package/cli/checks/story/devops.json +102 -0
  79. package/cli/checks/story/frontend.json +174 -0
  80. package/cli/checks/story/mobile.json +102 -0
  81. package/cli/checks/story/qa.json +210 -0
  82. package/cli/checks/story/security.json +198 -0
  83. package/cli/checks/story/solution-architect.json +230 -0
  84. package/cli/checks/story/test-architect.json +210 -0
  85. package/cli/checks/story/ui.json +102 -0
  86. package/cli/checks/story/ux.json +102 -0
  87. package/cli/coding-order.js +401 -0
  88. package/cli/command-logger.js +49 -12
  89. package/cli/components/static-output.js +63 -0
  90. package/cli/console-output-manager.js +94 -0
  91. package/cli/dependency-checker.js +72 -0
  92. package/cli/docs-sync.js +306 -0
  93. package/cli/epic-story-validator.js +659 -0
  94. package/cli/evaluation-prompts.js +1008 -0
  95. package/cli/execution-context.js +195 -0
  96. package/cli/generate-summary-table.js +340 -0
  97. package/cli/init-model-config.js +704 -0
  98. package/cli/init.js +1737 -278
  99. package/cli/kanban-server-manager.js +227 -0
  100. package/cli/llm-claude.js +150 -1
  101. package/cli/llm-gemini.js +109 -0
  102. package/cli/llm-local.js +493 -0
  103. package/cli/llm-mock.js +233 -0
  104. package/cli/llm-openai.js +454 -0
  105. package/cli/llm-provider.js +379 -3
  106. package/cli/llm-token-limits.js +211 -0
  107. package/cli/llm-verifier.js +662 -0
  108. package/cli/llm-xiaomi.js +143 -0
  109. package/cli/message-constants.js +49 -0
  110. package/cli/message-manager.js +334 -0
  111. package/cli/message-types.js +96 -0
  112. package/cli/messaging-api.js +291 -0
  113. package/cli/micro-check-fixer.js +335 -0
  114. package/cli/micro-check-runner.js +449 -0
  115. package/cli/micro-check-scorer.js +148 -0
  116. package/cli/micro-check-validator.js +538 -0
  117. package/cli/model-pricing.js +192 -0
  118. package/cli/model-query-engine.js +468 -0
  119. package/cli/model-recommendation-analyzer.js +495 -0
  120. package/cli/model-selector.js +270 -0
  121. package/cli/output-buffer.js +107 -0
  122. package/cli/process-manager.js +73 -2
  123. package/cli/prompt-logger.js +57 -0
  124. package/cli/repl-ink.js +4625 -1094
  125. package/cli/repl-old.js +3 -4
  126. package/cli/seed-processor.js +962 -0
  127. package/cli/sprint-planning-processor.js +4162 -0
  128. package/cli/template-processor.js +2149 -105
  129. package/cli/templates/project.md +25 -8
  130. package/cli/templates/vitepress-config.mts.template +5 -4
  131. package/cli/token-tracker.js +547 -0
  132. package/cli/tools/generate-story-validators.js +317 -0
  133. package/cli/tools/generate-validators.js +669 -0
  134. package/cli/update-checker.js +19 -17
  135. package/cli/update-notifier.js +4 -4
  136. package/cli/validation-router.js +667 -0
  137. package/cli/verification-tracker.js +563 -0
  138. package/cli/worktree-runner.js +654 -0
  139. package/kanban/README.md +386 -0
  140. package/kanban/client/README.md +205 -0
  141. package/kanban/client/components.json +20 -0
  142. package/kanban/client/dist/assets/index-D_KC5EQT.css +1 -0
  143. package/kanban/client/dist/assets/index-DjY5zqW7.js +351 -0
  144. package/kanban/client/dist/index.html +16 -0
  145. package/kanban/client/dist/vite.svg +1 -0
  146. package/kanban/client/index.html +15 -0
  147. package/kanban/client/package-lock.json +9442 -0
  148. package/kanban/client/package.json +44 -0
  149. package/kanban/client/postcss.config.js +6 -0
  150. package/kanban/client/public/vite.svg +1 -0
  151. package/kanban/client/src/App.jsx +651 -0
  152. package/kanban/client/src/components/ProjectFileEditorPopup.jsx +117 -0
  153. package/kanban/client/src/components/ceremony/AskArchPopup.jsx +420 -0
  154. package/kanban/client/src/components/ceremony/AskModelPopup.jsx +629 -0
  155. package/kanban/client/src/components/ceremony/CeremonyWorkflowModal.jsx +1133 -0
  156. package/kanban/client/src/components/ceremony/EpicStorySelectionModal.jsx +254 -0
  157. package/kanban/client/src/components/ceremony/ProviderSwitcherButton.jsx +290 -0
  158. package/kanban/client/src/components/ceremony/SponsorCallModal.jsx +686 -0
  159. package/kanban/client/src/components/ceremony/SprintPlanningModal.jsx +838 -0
  160. package/kanban/client/src/components/ceremony/steps/ArchitectureStep.jsx +150 -0
  161. package/kanban/client/src/components/ceremony/steps/CompleteStep.jsx +136 -0
  162. package/kanban/client/src/components/ceremony/steps/DatabaseStep.jsx +202 -0
  163. package/kanban/client/src/components/ceremony/steps/DeploymentStep.jsx +123 -0
  164. package/kanban/client/src/components/ceremony/steps/MissionStep.jsx +106 -0
  165. package/kanban/client/src/components/ceremony/steps/ReviewAnswersStep.jsx +329 -0
  166. package/kanban/client/src/components/ceremony/steps/RunningStep.jsx +249 -0
  167. package/kanban/client/src/components/kanban/CardDetailModal.jsx +646 -0
  168. package/kanban/client/src/components/kanban/EpicSection.jsx +146 -0
  169. package/kanban/client/src/components/kanban/FilterToolbar.jsx +222 -0
  170. package/kanban/client/src/components/kanban/GroupingSelector.jsx +63 -0
  171. package/kanban/client/src/components/kanban/KanbanBoard.jsx +211 -0
  172. package/kanban/client/src/components/kanban/KanbanCard.jsx +147 -0
  173. package/kanban/client/src/components/kanban/KanbanColumn.jsx +90 -0
  174. package/kanban/client/src/components/kanban/RefineWorkItemPopup.jsx +784 -0
  175. package/kanban/client/src/components/kanban/RunButton.jsx +162 -0
  176. package/kanban/client/src/components/kanban/SeedButton.jsx +176 -0
  177. package/kanban/client/src/components/layout/LoadingScreen.jsx +82 -0
  178. package/kanban/client/src/components/process/ProcessMonitorBar.jsx +80 -0
  179. package/kanban/client/src/components/settings/AgentEditorPopup.jsx +171 -0
  180. package/kanban/client/src/components/settings/AgentsTab.jsx +381 -0
  181. package/kanban/client/src/components/settings/ApiKeysTab.jsx +142 -0
  182. package/kanban/client/src/components/settings/CeremonyModelsTab.jsx +105 -0
  183. package/kanban/client/src/components/settings/CheckEditorPopup.jsx +507 -0
  184. package/kanban/client/src/components/settings/CostThresholdsTab.jsx +95 -0
  185. package/kanban/client/src/components/settings/ModelPricingTab.jsx +269 -0
  186. package/kanban/client/src/components/settings/OpenAIAuthSection.jsx +412 -0
  187. package/kanban/client/src/components/settings/ServersTab.jsx +121 -0
  188. package/kanban/client/src/components/settings/SettingsModal.jsx +84 -0
  189. package/kanban/client/src/components/stats/CostModal.jsx +384 -0
  190. package/kanban/client/src/components/ui/badge.jsx +27 -0
  191. package/kanban/client/src/components/ui/dialog.jsx +121 -0
  192. package/kanban/client/src/components/ui/tabs.jsx +85 -0
  193. package/kanban/client/src/hooks/__tests__/useGrouping.test.js +232 -0
  194. package/kanban/client/src/hooks/useGrouping.js +177 -0
  195. package/kanban/client/src/hooks/useWebSocket.js +120 -0
  196. package/kanban/client/src/lib/__tests__/api.test.js +196 -0
  197. package/kanban/client/src/lib/__tests__/status-grouping.test.js +94 -0
  198. package/kanban/client/src/lib/api.js +515 -0
  199. package/kanban/client/src/lib/status-grouping.js +154 -0
  200. package/kanban/client/src/lib/utils.js +11 -0
  201. package/kanban/client/src/main.jsx +10 -0
  202. package/kanban/client/src/store/__tests__/kanbanStore.test.js +164 -0
  203. package/kanban/client/src/store/ceremonyStore.js +172 -0
  204. package/kanban/client/src/store/filterStore.js +201 -0
  205. package/kanban/client/src/store/kanbanStore.js +123 -0
  206. package/kanban/client/src/store/processStore.js +65 -0
  207. package/kanban/client/src/store/sprintPlanningStore.js +33 -0
  208. package/kanban/client/src/styles/globals.css +59 -0
  209. package/kanban/client/tailwind.config.js +77 -0
  210. package/kanban/client/vite.config.js +28 -0
  211. package/kanban/client/vitest.config.js +28 -0
  212. package/kanban/dev-start.sh +47 -0
  213. package/kanban/package.json +12 -0
  214. package/kanban/server/index.js +537 -0
  215. package/kanban/server/routes/ceremony.js +454 -0
  216. package/kanban/server/routes/costs.js +163 -0
  217. package/kanban/server/routes/openai-oauth.js +366 -0
  218. package/kanban/server/routes/processes.js +50 -0
  219. package/kanban/server/routes/settings.js +736 -0
  220. package/kanban/server/routes/websocket.js +281 -0
  221. package/kanban/server/routes/work-items.js +487 -0
  222. package/kanban/server/services/CeremonyService.js +1441 -0
  223. package/kanban/server/services/FileSystemScanner.js +95 -0
  224. package/kanban/server/services/FileWatcher.js +144 -0
  225. package/kanban/server/services/HierarchyBuilder.js +196 -0
  226. package/kanban/server/services/ProcessRegistry.js +122 -0
  227. package/kanban/server/services/TaskRunnerService.js +261 -0
  228. package/kanban/server/services/WorkItemReader.js +123 -0
  229. package/kanban/server/services/WorkItemRefineService.js +510 -0
  230. package/kanban/server/start.js +49 -0
  231. package/kanban/server/utils/kanban-logger.js +132 -0
  232. package/kanban/server/utils/markdown.js +91 -0
  233. package/kanban/server/utils/status-grouping.js +107 -0
  234. package/kanban/server/workers/run-task-worker.js +121 -0
  235. package/kanban/server/workers/seed-worker.js +94 -0
  236. package/kanban/server/workers/sponsor-call-worker.js +92 -0
  237. package/kanban/server/workers/sprint-planning-worker.js +212 -0
  238. package/package.json +19 -7
  239. package/cli/agents/documentation.md +0 -302
@@ -0,0 +1,291 @@
1
+ /**
2
+ * Messaging API - Simple interface for command message handling
3
+ *
4
+ * Provides convenient methods for commands to send messages through
5
+ * the MessageManager without directly importing it.
6
+ */
7
+
8
+ import { messageManager } from './message-manager.js';
9
+ import { MessageType } from './message-types.js';
10
+ import { boldCyan, gray, cyan } from './ansi-colors.js';
11
+
12
+ /**
13
+ * Track sent ceremony headers to prevent duplication
14
+ * Cleared on each startCommand() call
15
+ */
16
+ let ceremonySent = new Set();
17
+
18
+ /**
19
+ * Start a new command execution context
20
+ * Should be called at the beginning of every command
21
+ *
22
+ * @param {string} commandName - Name of the command (e.g., 'sponsor-call')
23
+ * @returns {ExecutionContext} New execution context
24
+ *
25
+ * @example
26
+ * import { startCommand, endCommand } from './messaging-api.js';
27
+ *
28
+ * async function sponsorCallCommand() {
29
+ * startCommand('sponsor-call');
30
+ * try {
31
+ * // ... command logic
32
+ * } finally {
33
+ * endCommand();
34
+ * }
35
+ * }
36
+ */
37
+ export function startCommand(commandName) {
38
+ // Clear ceremony header tracking for new command
39
+ ceremonySent.clear();
40
+ return messageManager.startExecution(commandName);
41
+ }
42
+
43
+ /**
44
+ * End the current command execution context
45
+ * Should be called in finally block to ensure cleanup
46
+ *
47
+ * @example
48
+ * try {
49
+ * // ... command logic
50
+ * } finally {
51
+ * endCommand();
52
+ * }
53
+ */
54
+ export function endCommand() {
55
+ messageManager.endExecution();
56
+ }
57
+
58
+ /**
59
+ * Cancel the current command execution context
60
+ * Used when user interrupts or restarts command
61
+ */
62
+ export function cancelCommand() {
63
+ messageManager.cancelExecution();
64
+ }
65
+
66
+ /**
67
+ * Send a ceremony header (title + documentation URL)
68
+ * Automatically prevents duplicate headers within same command execution
69
+ *
70
+ * @param {string} title - Ceremony title
71
+ */
72
+ export function sendCeremonyHeader(title) {
73
+ // Prevent duplicate ceremony headers within the same command execution
74
+ if (ceremonySent.has(title)) {
75
+ return;
76
+ }
77
+
78
+ ceremonySent.add(title);
79
+ const content = boldCyan(title);
80
+ messageManager.send(MessageType.CEREMONY_HEADER, content);
81
+ }
82
+
83
+ /**
84
+ * Send a progress message (shows in executing state)
85
+ *
86
+ * @param {string} message - Progress message
87
+ *
88
+ * @example
89
+ * sendProgress('Analyzing database requirements...');
90
+ */
91
+ export function sendProgress(message) {
92
+ messageManager.send(MessageType.PROGRESS, message);
93
+ }
94
+
95
+ /**
96
+ * Send a substep message (detailed progress)
97
+ *
98
+ * @param {string} message - Substep message
99
+ *
100
+ * @example
101
+ * sendSubstep('Calling Claude API...');
102
+ */
103
+ export function sendSubstep(message) {
104
+ messageManager.send(MessageType.SUBSTEP, message);
105
+ }
106
+
107
+ /**
108
+ * Send user output (main content)
109
+ *
110
+ * @param {string} content - Content to display
111
+ *
112
+ * @example
113
+ * sendOutput('Mission statement: Build a task manager\n');
114
+ */
115
+ export function sendOutput(content) {
116
+ messageManager.send(MessageType.USER_OUTPUT, content);
117
+ }
118
+
119
+ /**
120
+ * Send an error message
121
+ *
122
+ * @param {string} message - Error message
123
+ *
124
+ * @example
125
+ * sendError('Failed to load configuration file');
126
+ */
127
+ export function sendError(message) {
128
+ messageManager.send(MessageType.ERROR, message);
129
+ }
130
+
131
+ /**
132
+ * Send a warning message
133
+ *
134
+ * @param {string} message - Warning message
135
+ *
136
+ * @example
137
+ * sendWarning('API rate limit approaching');
138
+ */
139
+ export function sendWarning(message) {
140
+ messageManager.send(MessageType.WARNING, message);
141
+ }
142
+
143
+ /**
144
+ * Send a success message
145
+ *
146
+ * @param {string} message - Success message
147
+ *
148
+ * @example
149
+ * sendSuccess('Documentation generated successfully');
150
+ */
151
+ export function sendSuccess(message) {
152
+ messageManager.send(MessageType.SUCCESS, message);
153
+ }
154
+
155
+ /**
156
+ * Send an info message
157
+ *
158
+ * @param {string} message - Info message
159
+ *
160
+ * @example
161
+ * sendInfo('Using Claude provider for LLM operations');
162
+ */
163
+ export function sendInfo(message) {
164
+ messageManager.send(MessageType.INFO, message);
165
+ }
166
+
167
+ /**
168
+ * Send a debug message (only to console.log)
169
+ *
170
+ * @param {string} message - Debug message
171
+ * @param {Object} data - Optional data to log
172
+ *
173
+ * @example
174
+ * sendDebug('Processing questionnaire', { currentIndex: 2, total: 6 });
175
+ */
176
+ export function sendDebug(message, data = null) {
177
+ messageManager.send(MessageType.DEBUG, message, { data });
178
+ }
179
+
180
+ /**
181
+ * Clear progress indicators
182
+ *
183
+ * @example
184
+ * clearProgress();
185
+ */
186
+ export function clearProgress() {
187
+ messageManager.clearProgress();
188
+ }
189
+
190
+ /**
191
+ * Get current execution context
192
+ *
193
+ * @returns {ExecutionContext|null} Current context or null
194
+ */
195
+ export function getCurrentContext() {
196
+ return messageManager.getCurrentContext();
197
+ }
198
+
199
+ /**
200
+ * Check if current context is active
201
+ *
202
+ * @returns {boolean} True if active context exists
203
+ */
204
+ export function isContextActive() {
205
+ const context = messageManager.getCurrentContext();
206
+ return context ? context.isActive() : false;
207
+ }
208
+
209
+ /**
210
+ * Register callbacks from React state
211
+ * Called once during REPL initialization
212
+ *
213
+ * @param {Object} callbacks - Callback functions
214
+ * @param {Function} callbacks.setExecutingMessage - setExecutingMessage React state setter
215
+ * @param {Function} callbacks.setExecutingSubstep - setExecutingSubstep React state setter
216
+ *
217
+ * @example
218
+ * // In repl-ink.js
219
+ * useEffect(() => {
220
+ * registerCallbacks({
221
+ * setExecutingMessage,
222
+ * setExecutingSubstep
223
+ * });
224
+ * }, []);
225
+ */
226
+ export function registerCallbacks(callbacks) {
227
+ if (callbacks.setExecutingMessage) {
228
+ messageManager.setExecutingMessageCallback(callbacks.setExecutingMessage);
229
+ }
230
+ if (callbacks.setExecutingSubstep) {
231
+ messageManager.setExecutingSubstepCallback(callbacks.setExecutingSubstep);
232
+ }
233
+ }
234
+
235
+ /**
236
+ * Helper: Send formatted section header (cyan bold)
237
+ *
238
+ * @param {string} title - Section title
239
+ *
240
+ * @example
241
+ * sendSectionHeader('Database Options Comparison');
242
+ */
243
+ export function sendSectionHeader(title) {
244
+ sendOutput(`\n${title}\n\n`);
245
+ }
246
+
247
+ /**
248
+ * Helper: Send formatted list item
249
+ *
250
+ * @param {string} item - List item text
251
+ * @param {number} indent - Indentation level (default: 0)
252
+ *
253
+ * @example
254
+ * sendListItem('PostgreSQL - ACID compliant');
255
+ * sendListItem('Supports complex queries', 1);
256
+ */
257
+ export function sendListItem(item, indent = 0) {
258
+ const spacing = ' '.repeat(indent);
259
+ sendOutput(`${spacing}- ${item}\n`);
260
+ }
261
+
262
+ /**
263
+ * Helper: Send indented content
264
+ *
265
+ * @param {string} content - Content to indent
266
+ * @param {number} level - Indentation level (0-3, each level = 2 spaces)
267
+ *
268
+ * @example
269
+ * sendIndented('Details about the option', 1);
270
+ * sendIndented('Nested information', 2);
271
+ */
272
+ export function sendIndented(content, level = 0) {
273
+ const spacing = ' '.repeat(level);
274
+ sendOutput(`${spacing}${content}`);
275
+ }
276
+
277
+ /**
278
+ * Helper: Send newline
279
+ */
280
+ export function sendNewline() {
281
+ sendOutput('\n');
282
+ }
283
+
284
+ /**
285
+ * Helper: Send multiple newlines
286
+ *
287
+ * @param {number} count - Number of newlines (default: 2)
288
+ */
289
+ export function sendNewlines(count = 2) {
290
+ sendOutput('\n'.repeat(count));
291
+ }
@@ -0,0 +1,335 @@
1
+ /**
2
+ * micro-check-fixer.js
3
+ *
4
+ * Handles atomic targeted fixes for failed micro-checks with regression revert.
5
+ * Each failed check is fixed individually, verified, and reverted if it doesn't pass.
6
+ * After all fixes, a regression scan ensures no previously-passing checks broke.
7
+ */
8
+
9
+ import { runTier1Check, runTier2Check } from './micro-check-runner.js';
10
+
11
+ // ---------------------------------------------------------------------------
12
+ // Constants
13
+ // ---------------------------------------------------------------------------
14
+
15
+ const FIX_SYSTEM_INSTRUCTIONS =
16
+ 'You are a precise work item editor. You fix exactly one issue at a time. ' +
17
+ 'Make the minimum change needed to address the failed check. Never remove existing content. ' +
18
+ 'Never add unrelated improvements.';
19
+
20
+ const SEVERITY_PRIORITY = { critical: 0, major: 1, minor: 2 };
21
+
22
+ // ---------------------------------------------------------------------------
23
+ // Prompt builder
24
+ // ---------------------------------------------------------------------------
25
+
26
+ function buildFixPrompt(workItemType, workItemText, check) {
27
+ const fieldsHint =
28
+ workItemType === 'epic'
29
+ ? '- features: string[] (for epics)'
30
+ : '- acceptanceCriteria: string[] (for stories)';
31
+
32
+ return (
33
+ '# Fix Required\n\n' +
34
+ `## Work Item (${workItemType})\n${workItemText}\n\n` +
35
+ '## Failed Check\n' +
36
+ `- **Check:** ${check.failDescription || check.id}\n` +
37
+ `- **Suggestion:** ${check.failSuggestion || 'N/A'}\n` +
38
+ `- **Evidence:** ${check.evidence || 'N/A'}\n\n` +
39
+ '## Instructions\n' +
40
+ 'Modify the work item to address ONLY this specific issue. Do not change anything else.\n' +
41
+ 'Return the complete updated work item as JSON with these fields:\n' +
42
+ '- description: string (the full updated description)\n' +
43
+ `${fieldsHint}\n` +
44
+ '- dependencies: string[] (keep unchanged unless the fix requires it)\n\n' +
45
+ 'CRITICAL: Make the MINIMUM change necessary to pass this check. Do not add unrelated improvements.'
46
+ );
47
+ }
48
+
49
+ // ---------------------------------------------------------------------------
50
+ // Helper: deep clone a work item (plain object)
51
+ // ---------------------------------------------------------------------------
52
+
53
+ function cloneWorkItem(workItem) {
54
+ return JSON.parse(JSON.stringify(workItem));
55
+ }
56
+
57
+ // ---------------------------------------------------------------------------
58
+ // Helper: merge work item fields from a fix into the original
59
+ // ---------------------------------------------------------------------------
60
+
61
+ /**
62
+ * Merge fix fields into the original work item without removing existing content.
63
+ * @param {Object} original - The current work item
64
+ * @param {Object} fixed - The fix result from LLM
65
+ * @param {string} type - "epic" or "story"
66
+ * @returns {Object} Merged work item
67
+ */
68
+ export function mergeWorkItemFields(original, fixed, type) {
69
+ const merged = cloneWorkItem(original);
70
+
71
+ // Update description from fix
72
+ if (fixed.description) {
73
+ merged.description = fixed.description;
74
+ }
75
+
76
+ if (type === 'epic') {
77
+ const existingFeatures = Array.isArray(merged.features) ? merged.features : [];
78
+ const fixedFeatures = Array.isArray(fixed.features) ? fixed.features : [];
79
+ const featureSet = new Set(existingFeatures);
80
+ for (const f of fixedFeatures) {
81
+ featureSet.add(f);
82
+ }
83
+ merged.features = [...featureSet];
84
+ } else {
85
+ const existingCriteria = Array.isArray(merged.acceptanceCriteria) ? merged.acceptanceCriteria : [];
86
+ const fixedCriteria = Array.isArray(fixed.acceptanceCriteria) ? fixed.acceptanceCriteria : [];
87
+ const criteriaSet = new Set(existingCriteria);
88
+ for (const c of fixedCriteria) {
89
+ criteriaSet.add(c);
90
+ }
91
+ merged.acceptanceCriteria = [...criteriaSet];
92
+ }
93
+
94
+ // Keep original dependencies, add new ones from fix if any
95
+ const existingDeps = Array.isArray(merged.dependencies) ? merged.dependencies : [];
96
+ const fixedDeps = Array.isArray(fixed.dependencies) ? fixed.dependencies : [];
97
+ const depSet = new Set(existingDeps);
98
+ for (const d of fixedDeps) {
99
+ depSet.add(d);
100
+ }
101
+ merged.dependencies = [...depSet];
102
+
103
+ return merged;
104
+ }
105
+
106
+ // ---------------------------------------------------------------------------
107
+ // Helper: re-run a single check
108
+ // ---------------------------------------------------------------------------
109
+
110
+ async function rerunCheck(checkDef, workItemText, llmProvider, tier1Results = null) {
111
+ if (checkDef.tier === 2) {
112
+ // Tier 2 checks need tier1Results map for template variable resolution
113
+ return runTier2Check(checkDef, workItemText, tier1Results || new Map(), llmProvider);
114
+ }
115
+ return runTier1Check(checkDef, workItemText, llmProvider);
116
+ }
117
+
118
+ // ---------------------------------------------------------------------------
119
+ // Helper: generate basic context text from a work item
120
+ // ---------------------------------------------------------------------------
121
+
122
+ function generateBasicContext(workItem, workItemType) {
123
+ const lines = [`# ${workItemType === 'epic' ? 'Epic' : 'Story'}: ${workItem.title || workItem.id || 'Untitled'}`];
124
+ if (workItem.description) {
125
+ lines.push('', workItem.description);
126
+ }
127
+ if (workItemType === 'epic' && Array.isArray(workItem.features)) {
128
+ lines.push('', '## Features');
129
+ for (const f of workItem.features) {
130
+ lines.push(`- ${f}`);
131
+ }
132
+ }
133
+ if (workItemType === 'story' && Array.isArray(workItem.acceptanceCriteria)) {
134
+ lines.push('', '## Acceptance Criteria');
135
+ for (const c of workItem.acceptanceCriteria) {
136
+ lines.push(`- ${c}`);
137
+ }
138
+ }
139
+ if (Array.isArray(workItem.dependencies) && workItem.dependencies.length > 0) {
140
+ lines.push('', '## Dependencies');
141
+ for (const d of workItem.dependencies) {
142
+ lines.push(`- ${d}`);
143
+ }
144
+ }
145
+ return lines.join('\n');
146
+ }
147
+
148
+ // ---------------------------------------------------------------------------
149
+ // Main export
150
+ // ---------------------------------------------------------------------------
151
+
152
+ /**
153
+ * Fix failed checks one at a time with regression detection.
154
+ * @param {Object} params - Named parameters
155
+ * @param {Object[]} params.failedChecks - Array of failed check results (from scorer), sorted by severity
156
+ * @param {Object[]} params.allCheckDefinitions - All check definitions (for re-running)
157
+ * @param {Object[]} params.allCheckResults - All check results from initial run (for regression comparison)
158
+ * @param {Object} params.workItem - The work item object (epic or story) to fix
159
+ * @param {string} params.workItemText - The work item context markdown
160
+ * @param {string} params.workItemType - "epic" or "story"
161
+ * @param {Object} params.llmProvider - LLM provider instance
162
+ * @param {number} [params.maxFixAttempts=3] - Max fix iterations
163
+ * @param {Function} [params.generateContextFn] - Regenerate context markdown from work item
164
+ * @param {Function} [params.progressCallback] - Progress reporting callback
165
+ * @param {number} [params.concurrency] - Concurrency limit (unused for now, fixes are sequential)
166
+ * @param {Map} [params.tier1Results] - Tier 1 results map for Tier 2 re-runs
167
+ * @returns {Object} { fixedWorkItem, fixedWorkItemText, fixResults[], rerunResults[], regressionDetected }
168
+ */
169
+ export async function fixFailedChecks({
170
+ failedChecks,
171
+ allCheckDefinitions,
172
+ allCheckResults = [],
173
+ workItem,
174
+ workItemText,
175
+ workItemType,
176
+ llmProvider,
177
+ maxFixAttempts = 3,
178
+ generateContextFn = null,
179
+ progressCallback = null,
180
+ concurrency = 5,
181
+ tier1Results = null,
182
+ } = {}) {
183
+
184
+ // Sort by severity: critical first, then major. Skip minor.
185
+ const fixable = [...failedChecks]
186
+ .filter((c) => c.severity === 'critical' || c.severity === 'major')
187
+ .sort((a, b) => (SEVERITY_PRIORITY[a.severity] || 99) - (SEVERITY_PRIORITY[b.severity] || 99));
188
+
189
+ // Build a lookup map for check definitions by id
190
+ const checkDefMap = new Map();
191
+ for (const def of allCheckDefinitions) {
192
+ checkDefMap.set(def.id, def);
193
+ }
194
+
195
+ // Working copies that accumulate fixes
196
+ let currentWorkItem = cloneWorkItem(workItem);
197
+ let currentText = workItemText;
198
+ const originalWorkItem = cloneWorkItem(workItem);
199
+ const originalText = workItemText;
200
+
201
+ const fixResults = [];
202
+ let fixCount = 0;
203
+
204
+ // 2. Fix each failed check individually
205
+ for (const failedCheck of fixable) {
206
+ if (fixCount >= maxFixAttempts) {
207
+ break;
208
+ }
209
+
210
+ const snapshotWorkItem = cloneWorkItem(currentWorkItem);
211
+ const snapshotText = currentText;
212
+
213
+ // 2a. Build prompt and call LLM for fix
214
+ const prompt = buildFixPrompt(workItemType, currentText, failedCheck);
215
+ let fixResponse;
216
+ try {
217
+ fixResponse = await llmProvider.generateJSON(prompt, FIX_SYSTEM_INSTRUCTIONS);
218
+ } catch (err) {
219
+ console.error(`[DEBUG] Fix LLM call failed for check ${failedCheck.id}:`, err.message);
220
+ fixResults.push({ checkId: failedCheck.id, attempted: true, fixed: false, reverted: false });
221
+ continue;
222
+ }
223
+
224
+ // Parse fix response (may already be an object from generateJSON)
225
+ let fixData;
226
+ if (fixResponse && typeof fixResponse === 'object') {
227
+ fixData = fixResponse;
228
+ } else {
229
+ try {
230
+ fixData = JSON.parse(String(fixResponse));
231
+ } catch {
232
+ console.error(`[DEBUG] Failed to parse fix response for check ${failedCheck.id}`);
233
+ fixResults.push({ checkId: failedCheck.id, attempted: true, fixed: false, reverted: false });
234
+ continue;
235
+ }
236
+ }
237
+
238
+ // 2b. Apply fix to working copy
239
+ currentWorkItem = mergeWorkItemFields(currentWorkItem, fixData, workItemType);
240
+ if (generateContextFn) {
241
+ currentText = await generateContextFn(currentWorkItem);
242
+ } else {
243
+ currentText = generateBasicContext(currentWorkItem, workItemType);
244
+ }
245
+
246
+ // 2c. Re-run the specific check that was fixed
247
+ const checkDef = checkDefMap.get(failedCheck.id);
248
+ if (!checkDef) {
249
+ // Cannot verify without definition — accept optimistically
250
+ fixResults.push({ checkId: failedCheck.id, attempted: true, fixed: true, reverted: false });
251
+ fixCount++;
252
+ continue;
253
+ }
254
+
255
+ let rerunResult;
256
+ try {
257
+ rerunResult = await rerunCheck(checkDef, currentText, llmProvider, tier1Results);
258
+ } catch (err) {
259
+ console.error(`[DEBUG] Re-run check failed for ${failedCheck.id}:`, err.message);
260
+ // Revert on verification failure
261
+ currentWorkItem = snapshotWorkItem;
262
+ currentText = snapshotText;
263
+ fixResults.push({ checkId: failedCheck.id, attempted: true, fixed: false, reverted: true });
264
+ continue;
265
+ }
266
+
267
+ if (rerunResult.passed) {
268
+ // Fix accepted
269
+ fixResults.push({ checkId: failedCheck.id, attempted: true, fixed: true, reverted: false });
270
+ fixCount++;
271
+ } else {
272
+ // Fix didn't work — revert
273
+ currentWorkItem = snapshotWorkItem;
274
+ currentText = snapshotText;
275
+ fixResults.push({ checkId: failedCheck.id, attempted: true, fixed: false, reverted: true });
276
+ }
277
+ }
278
+
279
+ // 3. Post-fix regression scan
280
+ // Tolerate minor noise from local models — only revert if regression rate > 20%
281
+ // of previously-passing checks (single flipped result on a noisy model ≠ true regression)
282
+ const previouslyPassing = allCheckResults.filter(
283
+ (r) => r.applicable !== false && r.passed === true
284
+ );
285
+
286
+ const rerunResults = [];
287
+ let regressionCount = 0;
288
+
289
+ for (const passingResult of previouslyPassing) {
290
+ const checkDef = checkDefMap.get(passingResult.id);
291
+ if (!checkDef) {
292
+ continue;
293
+ }
294
+
295
+ let rerunResult;
296
+ try {
297
+ rerunResult = await rerunCheck(checkDef, currentText, llmProvider, tier1Results);
298
+ } catch {
299
+ // If we can't verify, treat as potential regression
300
+ rerunResult = { id: passingResult.id, passed: false, evidence: 'Re-run failed' };
301
+ }
302
+ rerunResults.push(rerunResult);
303
+
304
+ if (rerunResult.passed === false) {
305
+ regressionCount++;
306
+ }
307
+ }
308
+
309
+ // Only revert if >20% of previously-passing checks now fail (accounts for LLM noise)
310
+ const regressionRate = previouslyPassing.length > 0 ? regressionCount / previouslyPassing.length : 0;
311
+ const regressionDetected = regressionRate > 0.2;
312
+
313
+ if (regressionDetected) {
314
+ console.warn(`[DEBUG] Regression detected during post-fix scan. ${regressionCount}/${previouslyPassing.length} (${(regressionRate * 100).toFixed(0)}%) checks flipped. Reverting all fixes.`);
315
+ return {
316
+ fixedWorkItem: originalWorkItem,
317
+ fixedWorkItemText: originalText,
318
+ fixResults,
319
+ rerunResults,
320
+ regressionDetected: true,
321
+ };
322
+ }
323
+
324
+ if (regressionCount > 0) {
325
+ console.log(`[DEBUG] Post-fix scan: ${regressionCount}/${previouslyPassing.length} checks flipped (${(regressionRate * 100).toFixed(0)}%) — within noise tolerance, keeping fixes.`);
326
+ }
327
+
328
+ return {
329
+ fixedWorkItem: currentWorkItem,
330
+ fixedWorkItemText: currentText,
331
+ fixResults,
332
+ rerunResults,
333
+ regressionDetected: false,
334
+ };
335
+ }