popeye-cli 1.0.0 → 1.1.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 (171) hide show
  1. package/README.md +521 -125
  2. package/dist/adapters/claude.d.ts +16 -4
  3. package/dist/adapters/claude.d.ts.map +1 -1
  4. package/dist/adapters/claude.js +679 -33
  5. package/dist/adapters/claude.js.map +1 -1
  6. package/dist/adapters/gemini.d.ts +55 -0
  7. package/dist/adapters/gemini.d.ts.map +1 -0
  8. package/dist/adapters/gemini.js +318 -0
  9. package/dist/adapters/gemini.js.map +1 -0
  10. package/dist/adapters/openai.d.ts.map +1 -1
  11. package/dist/adapters/openai.js +41 -7
  12. package/dist/adapters/openai.js.map +1 -1
  13. package/dist/auth/claude.d.ts +11 -9
  14. package/dist/auth/claude.d.ts.map +1 -1
  15. package/dist/auth/claude.js +107 -71
  16. package/dist/auth/claude.js.map +1 -1
  17. package/dist/auth/gemini.d.ts +58 -0
  18. package/dist/auth/gemini.d.ts.map +1 -0
  19. package/dist/auth/gemini.js +172 -0
  20. package/dist/auth/gemini.js.map +1 -0
  21. package/dist/auth/index.d.ts +11 -7
  22. package/dist/auth/index.d.ts.map +1 -1
  23. package/dist/auth/index.js +23 -5
  24. package/dist/auth/index.js.map +1 -1
  25. package/dist/auth/keychain.d.ts +20 -7
  26. package/dist/auth/keychain.d.ts.map +1 -1
  27. package/dist/auth/keychain.js +85 -29
  28. package/dist/auth/keychain.js.map +1 -1
  29. package/dist/auth/openai.d.ts +2 -2
  30. package/dist/auth/openai.d.ts.map +1 -1
  31. package/dist/auth/openai.js +30 -32
  32. package/dist/auth/openai.js.map +1 -1
  33. package/dist/cli/index.d.ts.map +1 -1
  34. package/dist/cli/index.js +4 -7
  35. package/dist/cli/index.js.map +1 -1
  36. package/dist/cli/interactive.d.ts +2 -2
  37. package/dist/cli/interactive.d.ts.map +1 -1
  38. package/dist/cli/interactive.js +1380 -183
  39. package/dist/cli/interactive.js.map +1 -1
  40. package/dist/config/defaults.d.ts +6 -1
  41. package/dist/config/defaults.d.ts.map +1 -1
  42. package/dist/config/defaults.js +10 -2
  43. package/dist/config/defaults.js.map +1 -1
  44. package/dist/config/index.d.ts +10 -0
  45. package/dist/config/index.d.ts.map +1 -1
  46. package/dist/config/index.js +19 -0
  47. package/dist/config/index.js.map +1 -1
  48. package/dist/config/schema.d.ts +20 -0
  49. package/dist/config/schema.d.ts.map +1 -1
  50. package/dist/config/schema.js +7 -0
  51. package/dist/config/schema.js.map +1 -1
  52. package/dist/generators/python.d.ts.map +1 -1
  53. package/dist/generators/python.js +1 -0
  54. package/dist/generators/python.js.map +1 -1
  55. package/dist/generators/typescript.d.ts.map +1 -1
  56. package/dist/generators/typescript.js +1 -0
  57. package/dist/generators/typescript.js.map +1 -1
  58. package/dist/state/index.d.ts +108 -0
  59. package/dist/state/index.d.ts.map +1 -1
  60. package/dist/state/index.js +551 -4
  61. package/dist/state/index.js.map +1 -1
  62. package/dist/state/registry.d.ts +52 -0
  63. package/dist/state/registry.d.ts.map +1 -0
  64. package/dist/state/registry.js +215 -0
  65. package/dist/state/registry.js.map +1 -0
  66. package/dist/types/cli.d.ts +4 -0
  67. package/dist/types/cli.d.ts.map +1 -1
  68. package/dist/types/cli.js.map +1 -1
  69. package/dist/types/consensus.d.ts +69 -4
  70. package/dist/types/consensus.d.ts.map +1 -1
  71. package/dist/types/consensus.js +24 -3
  72. package/dist/types/consensus.js.map +1 -1
  73. package/dist/types/workflow.d.ts +55 -0
  74. package/dist/types/workflow.d.ts.map +1 -1
  75. package/dist/types/workflow.js +16 -0
  76. package/dist/types/workflow.js.map +1 -1
  77. package/dist/workflow/auto-fix.d.ts +45 -0
  78. package/dist/workflow/auto-fix.d.ts.map +1 -0
  79. package/dist/workflow/auto-fix.js +274 -0
  80. package/dist/workflow/auto-fix.js.map +1 -0
  81. package/dist/workflow/consensus.d.ts +44 -2
  82. package/dist/workflow/consensus.d.ts.map +1 -1
  83. package/dist/workflow/consensus.js +565 -17
  84. package/dist/workflow/consensus.js.map +1 -1
  85. package/dist/workflow/execution-mode.d.ts +10 -4
  86. package/dist/workflow/execution-mode.d.ts.map +1 -1
  87. package/dist/workflow/execution-mode.js +547 -58
  88. package/dist/workflow/execution-mode.js.map +1 -1
  89. package/dist/workflow/index.d.ts +14 -2
  90. package/dist/workflow/index.d.ts.map +1 -1
  91. package/dist/workflow/index.js +69 -6
  92. package/dist/workflow/index.js.map +1 -1
  93. package/dist/workflow/milestone-workflow.d.ts +34 -0
  94. package/dist/workflow/milestone-workflow.d.ts.map +1 -0
  95. package/dist/workflow/milestone-workflow.js +414 -0
  96. package/dist/workflow/milestone-workflow.js.map +1 -0
  97. package/dist/workflow/plan-mode.d.ts +14 -1
  98. package/dist/workflow/plan-mode.d.ts.map +1 -1
  99. package/dist/workflow/plan-mode.js +589 -47
  100. package/dist/workflow/plan-mode.js.map +1 -1
  101. package/dist/workflow/plan-storage.d.ts +142 -0
  102. package/dist/workflow/plan-storage.d.ts.map +1 -0
  103. package/dist/workflow/plan-storage.js +331 -0
  104. package/dist/workflow/plan-storage.js.map +1 -0
  105. package/dist/workflow/project-verification.d.ts +37 -0
  106. package/dist/workflow/project-verification.d.ts.map +1 -0
  107. package/dist/workflow/project-verification.js +381 -0
  108. package/dist/workflow/project-verification.js.map +1 -0
  109. package/dist/workflow/task-workflow.d.ts +37 -0
  110. package/dist/workflow/task-workflow.d.ts.map +1 -0
  111. package/dist/workflow/task-workflow.js +383 -0
  112. package/dist/workflow/task-workflow.js.map +1 -0
  113. package/dist/workflow/test-runner.d.ts +1 -0
  114. package/dist/workflow/test-runner.d.ts.map +1 -1
  115. package/dist/workflow/test-runner.js +9 -5
  116. package/dist/workflow/test-runner.js.map +1 -1
  117. package/dist/workflow/ui-designer.d.ts +82 -0
  118. package/dist/workflow/ui-designer.d.ts.map +1 -0
  119. package/dist/workflow/ui-designer.js +234 -0
  120. package/dist/workflow/ui-designer.js.map +1 -0
  121. package/dist/workflow/ui-setup.d.ts +58 -0
  122. package/dist/workflow/ui-setup.d.ts.map +1 -0
  123. package/dist/workflow/ui-setup.js +685 -0
  124. package/dist/workflow/ui-setup.js.map +1 -0
  125. package/dist/workflow/ui-verification.d.ts +114 -0
  126. package/dist/workflow/ui-verification.d.ts.map +1 -0
  127. package/dist/workflow/ui-verification.js +258 -0
  128. package/dist/workflow/ui-verification.js.map +1 -0
  129. package/dist/workflow/workflow-logger.d.ts +110 -0
  130. package/dist/workflow/workflow-logger.d.ts.map +1 -0
  131. package/dist/workflow/workflow-logger.js +267 -0
  132. package/dist/workflow/workflow-logger.js.map +1 -0
  133. package/package.json +2 -2
  134. package/src/adapters/claude.ts +815 -34
  135. package/src/adapters/gemini.ts +373 -0
  136. package/src/adapters/openai.ts +40 -7
  137. package/src/auth/claude.ts +120 -78
  138. package/src/auth/gemini.ts +207 -0
  139. package/src/auth/index.ts +28 -8
  140. package/src/auth/keychain.ts +95 -28
  141. package/src/auth/openai.ts +29 -36
  142. package/src/cli/index.ts +4 -7
  143. package/src/cli/interactive.ts +1641 -216
  144. package/src/config/defaults.ts +10 -2
  145. package/src/config/index.ts +21 -0
  146. package/src/config/schema.ts +7 -0
  147. package/src/generators/python.ts +1 -0
  148. package/src/generators/typescript.ts +1 -0
  149. package/src/state/index.ts +713 -4
  150. package/src/state/registry.ts +278 -0
  151. package/src/types/cli.ts +4 -0
  152. package/src/types/consensus.ts +65 -6
  153. package/src/types/workflow.ts +35 -0
  154. package/src/workflow/auto-fix.ts +340 -0
  155. package/src/workflow/consensus.ts +750 -16
  156. package/src/workflow/execution-mode.ts +673 -74
  157. package/src/workflow/index.ts +95 -6
  158. package/src/workflow/milestone-workflow.ts +576 -0
  159. package/src/workflow/plan-mode.ts +696 -50
  160. package/src/workflow/plan-storage.ts +482 -0
  161. package/src/workflow/project-verification.ts +471 -0
  162. package/src/workflow/task-workflow.ts +525 -0
  163. package/src/workflow/test-runner.ts +10 -5
  164. package/src/workflow/ui-designer.ts +337 -0
  165. package/src/workflow/ui-setup.ts +797 -0
  166. package/src/workflow/ui-verification.ts +357 -0
  167. package/src/workflow/workflow-logger.ts +353 -0
  168. package/tests/config/config.test.ts +1 -1
  169. package/tests/types/consensus.test.ts +3 -3
  170. package/tests/workflow/plan-mode.test.ts +213 -0
  171. package/tests/workflow/test-runner.test.ts +5 -3
@@ -0,0 +1,576 @@
1
+ /**
2
+ * Milestone-level workflow
3
+ * Handles: Milestone Plan → Consensus → Execute Tasks → Milestone Review → Consensus
4
+ */
5
+
6
+ import { promises as fs } from 'node:fs';
7
+ import path from 'node:path';
8
+ import type { ProjectState, Milestone, Task } from '../types/workflow.js';
9
+ import type { ConsensusConfig } from '../types/consensus.js';
10
+ import { createPlan as claudeCreatePlan, analyzeCodebase } from '../adapters/claude.js';
11
+ import {
12
+ loadProject,
13
+ updateState,
14
+ } from '../state/index.js';
15
+ import { iterateUntilConsensus, type ConsensusProcessResult } from './consensus.js';
16
+ import { runTaskWorkflow, type TaskWorkflowResult } from './task-workflow.js';
17
+ import { parsePlanMilestones } from './plan-mode.js';
18
+
19
+ /**
20
+ * Options for milestone workflow
21
+ */
22
+ export interface MilestoneWorkflowOptions {
23
+ projectDir: string;
24
+ consensusConfig?: Partial<ConsensusConfig>;
25
+ onProgress?: (phase: string, message: string) => void;
26
+ onTaskStart?: (task: Task) => void;
27
+ onTaskComplete?: (task: Task, success: boolean) => void;
28
+ }
29
+
30
+ /**
31
+ * Result of milestone workflow
32
+ */
33
+ export interface MilestoneWorkflowResult {
34
+ success: boolean;
35
+ milestone: Milestone;
36
+ planConsensus?: ConsensusProcessResult;
37
+ completionConsensus?: ConsensusProcessResult;
38
+ taskResults: TaskWorkflowResult[];
39
+ error?: string;
40
+ }
41
+
42
+ /**
43
+ * Update milestone in state
44
+ */
45
+ async function updateMilestoneInState(
46
+ projectDir: string,
47
+ milestoneId: string,
48
+ updates: Partial<Milestone>
49
+ ): Promise<ProjectState> {
50
+ const state = await loadProject(projectDir);
51
+
52
+ const updatedMilestones = state.milestones.map(m =>
53
+ m.id === milestoneId ? { ...m, ...updates } : m
54
+ );
55
+
56
+ return updateState(projectDir, { milestones: updatedMilestones });
57
+ }
58
+
59
+ /**
60
+ * Create a detailed plan for a milestone
61
+ */
62
+ async function createMilestonePlan(
63
+ milestone: Milestone,
64
+ state: ProjectState,
65
+ onProgress?: (message: string) => void
66
+ ): Promise<string> {
67
+ onProgress?.('Creating detailed milestone plan...');
68
+
69
+ const context = `
70
+ ## Project Context
71
+ Project: ${state.name}
72
+ Language: ${state.language}
73
+
74
+ ## Project Specification
75
+ ${state.specification?.slice(0, 2000) || 'No specification available'}
76
+
77
+ ## Overall Project Plan
78
+ ${state.plan?.slice(0, 2000) || 'No overall plan available'}
79
+
80
+ ## Completed Milestones
81
+ ${state.milestones
82
+ .filter(m => m.status === 'complete')
83
+ .map(m => `- ${m.name}`)
84
+ .join('\n') || 'None yet'}
85
+ `.trim();
86
+
87
+ const prompt = `
88
+ Create a detailed implementation plan for the following milestone:
89
+
90
+ ## Milestone: ${milestone.name}
91
+ ${milestone.description}
92
+
93
+ ## Tasks in This Milestone
94
+ ${milestone.tasks.map((t, i) => `${i + 1}. ${t.name}: ${t.description}`).join('\n')}
95
+
96
+ Please provide a comprehensive plan that includes:
97
+
98
+ 1. **Milestone Overview**: Summary of what will be accomplished
99
+ 2. **Prerequisites**: What must be in place before starting
100
+ 3. **Implementation Order**: Optimal sequence for tasks
101
+ 4. **Integration Points**: How tasks connect to each other
102
+ 5. **Risk Assessment**: Potential issues and mitigations
103
+ 6. **Success Criteria**: How to verify milestone completion
104
+ 7. **Test Strategy**: Overall testing approach for the milestone
105
+
106
+ For each task, provide:
107
+ - Detailed implementation steps
108
+ - Files to create/modify
109
+ - Dependencies
110
+ - Acceptance criteria
111
+
112
+ This plan will be reviewed for consensus before any implementation begins.
113
+ `.trim();
114
+
115
+ const result = await claudeCreatePlan(prompt, context, onProgress);
116
+
117
+ if (!result.success) {
118
+ throw new Error(`Failed to create milestone plan: ${result.error}`);
119
+ }
120
+
121
+ return result.response;
122
+ }
123
+
124
+ /**
125
+ * Document milestone plan
126
+ */
127
+ async function documentMilestonePlan(
128
+ projectDir: string,
129
+ milestone: Milestone,
130
+ plan: string,
131
+ consensusResult: ConsensusProcessResult
132
+ ): Promise<string> {
133
+ const docsDir = path.join(projectDir, 'docs');
134
+ await fs.mkdir(docsDir, { recursive: true });
135
+
136
+ const milestoneNum = milestone.id.replace('milestone-', '');
137
+ const filename = `milestone_${milestoneNum}_plan.md`;
138
+ const docPath = path.join(docsDir, filename);
139
+
140
+ const content = `# Milestone Plan: ${milestone.name}
141
+
142
+ ## Metadata
143
+ - **Milestone ID**: ${milestone.id}
144
+ - **Consensus Score**: ${consensusResult.finalScore}%
145
+ - **Iterations**: ${consensusResult.totalIterations}
146
+ - **Status**: ${consensusResult.approved ? 'APPROVED' : 'NOT APPROVED'}
147
+ ${consensusResult.arbitrated ? '- **Arbitrated**: Yes' : ''}
148
+ - **Generated**: ${new Date().toISOString()}
149
+
150
+ ## Milestone Description
151
+ ${milestone.description}
152
+
153
+ ## Tasks
154
+ ${milestone.tasks.map((t, i) => `${i + 1}. **${t.name}**: ${t.description}`).join('\n')}
155
+
156
+ ## Implementation Plan
157
+ ${plan}
158
+
159
+ ## Consensus History
160
+ | Iteration | Score | Key Feedback |
161
+ |-----------|-------|--------------|
162
+ ${consensusResult.iterations.map(it =>
163
+ `| ${it.iteration} | ${it.result.score}% | ${it.result.concerns?.slice(0, 2).join('; ') || 'None'} |`
164
+ ).join('\n')}
165
+
166
+ ${consensusResult.finalConcerns.length > 0 ? `
167
+ ## Remaining Notes
168
+ ${consensusResult.finalConcerns.map(c => `- ${c}`).join('\n')}
169
+ ` : ''}
170
+ `;
171
+
172
+ await fs.writeFile(docPath, content, 'utf-8');
173
+ return `docs/${filename}`;
174
+ }
175
+
176
+ /**
177
+ * Create milestone completion review
178
+ */
179
+ async function createMilestoneReview(
180
+ milestone: Milestone,
181
+ state: ProjectState,
182
+ _taskResults: TaskWorkflowResult[],
183
+ onProgress?: (message: string) => void
184
+ ): Promise<string> {
185
+ onProgress?.('Creating milestone completion review...');
186
+
187
+ // Analyze the current codebase
188
+ const codebaseAnalysis = await analyzeCodebase(
189
+ state.id, // projectDir stored in state context
190
+ onProgress
191
+ );
192
+
193
+ const context = `
194
+ ## Project Context
195
+ Project: ${state.name}
196
+ Language: ${state.language}
197
+
198
+ ## Milestone: ${milestone.name}
199
+ ${milestone.description}
200
+
201
+ ## Codebase Analysis
202
+ ${codebaseAnalysis.success ? codebaseAnalysis.response?.slice(0, 2000) : 'Analysis not available'}
203
+ `.trim();
204
+
205
+ const prompt = `
206
+ Please perform a completion review for the following milestone:
207
+
208
+ ## Milestone: ${milestone.name}
209
+
210
+ ## Completed Tasks
211
+ ${milestone.tasks.map((t, i) => {
212
+ return `${i + 1}. **${t.name}**
213
+ - Status: ${t.status}
214
+ - Tests: ${t.testsPassed ? 'Passed' : 'N/A'}
215
+ - Consensus Score: ${t.consensusScore || 'N/A'}%`;
216
+ }).join('\n')}
217
+
218
+ Please provide:
219
+
220
+ 1. **Summary**: What was accomplished in this milestone
221
+ 2. **Code Review**: Assessment of code quality and architecture
222
+ 3. **Test Coverage**: Evaluation of test coverage and quality
223
+ 4. **Integration Check**: How well components work together
224
+ 5. **Technical Debt**: Any shortcuts or areas needing future attention
225
+ 6. **Documentation**: Status of documentation
226
+ 7. **Completion Verification**:
227
+ - Are all acceptance criteria met?
228
+ - Is the milestone truly complete?
229
+ - Any remaining work needed?
230
+
231
+ Provide a CONSENSUS SCORE (0-100%) indicating confidence that this milestone is complete.
232
+ `.trim();
233
+
234
+ const result = await claudeCreatePlan(prompt, context, onProgress);
235
+
236
+ if (!result.success) {
237
+ throw new Error(`Failed to create milestone review: ${result.error}`);
238
+ }
239
+
240
+ return result.response;
241
+ }
242
+
243
+ /**
244
+ * Document milestone completion
245
+ */
246
+ async function documentMilestoneCompletion(
247
+ projectDir: string,
248
+ milestone: Milestone,
249
+ review: string,
250
+ consensusResult: ConsensusProcessResult,
251
+ _taskResults: TaskWorkflowResult[]
252
+ ): Promise<string> {
253
+ const docsDir = path.join(projectDir, 'docs');
254
+ await fs.mkdir(docsDir, { recursive: true });
255
+
256
+ const milestoneNum = milestone.id.replace('milestone-', '');
257
+ const filename = `milestone_${milestoneNum}_complete.md`;
258
+ const docPath = path.join(docsDir, filename);
259
+
260
+ const content = `# Milestone Completion: ${milestone.name}
261
+
262
+ ## Metadata
263
+ - **Milestone ID**: ${milestone.id}
264
+ - **Completion Score**: ${consensusResult.finalScore}%
265
+ - **Review Iterations**: ${consensusResult.totalIterations}
266
+ - **Status**: ${consensusResult.approved ? 'COMPLETE' : 'NEEDS REVIEW'}
267
+ - **Completed**: ${new Date().toISOString()}
268
+
269
+ ## Task Summary
270
+ | Task | Status | Tests | Consensus |
271
+ |------|--------|-------|-----------|
272
+ ${milestone.tasks.map(t => {
273
+ return `| ${t.name} | ${t.status} | ${t.testsPassed ? 'Passed' : 'N/A'} | ${t.consensusScore || 'N/A'}% |`;
274
+ }).join('\n')}
275
+
276
+ ## Completion Review
277
+ ${review}
278
+
279
+ ## Final Assessment
280
+ - **All Tasks Complete**: ${milestone.tasks.every(t => t.status === 'complete') ? 'Yes' : 'No'}
281
+ - **All Tests Passing**: ${milestone.tasks.every(t => t.testsPassed !== false) ? 'Yes' : 'No'}
282
+ - **Milestone Approved**: ${consensusResult.approved ? 'Yes' : 'No'}
283
+ `;
284
+
285
+ await fs.writeFile(docPath, content, 'utf-8');
286
+ return `docs/${filename}`;
287
+ }
288
+
289
+ /**
290
+ * Run the complete milestone workflow
291
+ */
292
+ export async function runMilestoneWorkflow(
293
+ milestone: Milestone,
294
+ options: MilestoneWorkflowOptions
295
+ ): Promise<MilestoneWorkflowResult> {
296
+ const {
297
+ projectDir,
298
+ consensusConfig,
299
+ onProgress,
300
+ onTaskStart,
301
+ onTaskComplete,
302
+ } = options;
303
+
304
+ const taskResults: TaskWorkflowResult[] = [];
305
+
306
+ try {
307
+ let state = await loadProject(projectDir);
308
+
309
+ // Reload milestone from state to get latest data (including any saved plan)
310
+ const currentMilestone = state.milestones.find(m => m.id === milestone.id);
311
+ if (currentMilestone) {
312
+ milestone = currentMilestone;
313
+ }
314
+
315
+ // Check if milestone plan was already approved (resuming scenario)
316
+ let planConsensus: ConsensusProcessResult | undefined;
317
+
318
+ if (milestone.consensusApproved && milestone.plan) {
319
+ // Milestone plan already approved, skip planning phase
320
+ onProgress?.('milestone-plan', `Using existing approved plan for: ${milestone.name} (Score: ${milestone.consensusScore}%)`);
321
+
322
+ // Create a mock consensus result from saved data
323
+ planConsensus = {
324
+ approved: true,
325
+ finalPlan: milestone.plan,
326
+ finalScore: milestone.consensusScore || 95,
327
+ bestPlan: milestone.plan,
328
+ bestScore: milestone.consensusScore || 95,
329
+ bestIteration: milestone.consensusIterations || 1,
330
+ totalIterations: milestone.consensusIterations || 1,
331
+ iterations: [],
332
+ finalConcerns: [],
333
+ finalRecommendations: [],
334
+ arbitrated: false,
335
+ };
336
+ } else {
337
+ // Mark milestone as in-progress
338
+ state = await updateMilestoneInState(projectDir, milestone.id, {
339
+ status: 'in-progress',
340
+ });
341
+
342
+ // ============================================
343
+ // PHASE 1: Create Milestone Plan
344
+ // ============================================
345
+ onProgress?.('milestone-plan', `Planning milestone: ${milestone.name}`);
346
+
347
+ const milestonePlan = await createMilestonePlan(
348
+ milestone,
349
+ state,
350
+ (msg) => onProgress?.('milestone-plan', msg)
351
+ );
352
+
353
+ // ============================================
354
+ // PHASE 2: Get Consensus on Milestone Plan
355
+ // ============================================
356
+ onProgress?.('milestone-consensus', `Getting consensus for milestone: ${milestone.name}`);
357
+
358
+ const context = `
359
+ Project: ${state.name}
360
+ Language: ${state.language}
361
+ Milestone: ${milestone.name}
362
+ Tasks: ${milestone.tasks.length}
363
+ `.trim();
364
+
365
+ planConsensus = await iterateUntilConsensus(
366
+ milestonePlan,
367
+ context,
368
+ {
369
+ projectDir,
370
+ config: consensusConfig,
371
+ onIteration: (iteration, result) => {
372
+ onProgress?.('milestone-consensus', `Iteration ${iteration}: ${result.score}%`);
373
+ },
374
+ onProgress,
375
+ }
376
+ );
377
+
378
+ // Document the milestone plan
379
+ const planDocPath = await documentMilestonePlan(
380
+ projectDir,
381
+ milestone,
382
+ planConsensus.bestPlan,
383
+ planConsensus
384
+ );
385
+
386
+ // Update milestone with plan consensus
387
+ state = await updateMilestoneInState(projectDir, milestone.id, {
388
+ plan: planConsensus.bestPlan,
389
+ consensusScore: planConsensus.finalScore,
390
+ consensusIterations: planConsensus.totalIterations,
391
+ consensusApproved: planConsensus.approved,
392
+ planDoc: planDocPath,
393
+ });
394
+
395
+ // Check if consensus was achieved
396
+ if (!planConsensus.approved) {
397
+ onProgress?.('milestone-consensus', `Milestone plan not approved: ${planConsensus.finalScore}%`);
398
+
399
+ state = await updateMilestoneInState(projectDir, milestone.id, {
400
+ status: 'failed',
401
+ });
402
+
403
+ return {
404
+ success: false,
405
+ milestone: { ...milestone, status: 'failed' },
406
+ planConsensus,
407
+ taskResults: [],
408
+ error: `Milestone plan not approved. Score: ${planConsensus.finalScore}%`,
409
+ };
410
+ }
411
+
412
+ onProgress?.('milestone-consensus', `Milestone plan approved with ${planConsensus.finalScore}%`);
413
+
414
+ // Parse tasks from the approved plan (may have refined tasks)
415
+ const parsedMilestones = parsePlanMilestones(planConsensus.bestPlan);
416
+ if (parsedMilestones.length > 0 && parsedMilestones[0].tasks.length > 0) {
417
+ // Update tasks with more details from the approved plan
418
+ // Keep original task IDs but update descriptions
419
+ const updatedTasks = milestone.tasks.map((origTask, idx) => {
420
+ const parsedTask = parsedMilestones[0].tasks[idx];
421
+ if (parsedTask) {
422
+ return {
423
+ ...origTask,
424
+ description: parsedTask.description || origTask.description,
425
+ testPlan: parsedTask.testPlan || origTask.testPlan,
426
+ };
427
+ }
428
+ return origTask;
429
+ });
430
+
431
+ state = await updateMilestoneInState(projectDir, milestone.id, {
432
+ tasks: updatedTasks,
433
+ });
434
+
435
+ // Reload milestone with updated tasks
436
+ state = await loadProject(projectDir);
437
+ milestone = state.milestones.find(m => m.id === milestone.id) || milestone;
438
+ }
439
+ }
440
+
441
+ // ============================================
442
+ // PHASE 3: Execute Each Task (with per-task consensus)
443
+ // ============================================
444
+ onProgress?.('milestone-tasks', `Executing ${milestone.tasks.length} tasks...`);
445
+
446
+ for (const task of milestone.tasks) {
447
+ if (task.status === 'complete') {
448
+ onProgress?.('milestone-tasks', `Skipping completed task: ${task.name}`);
449
+ continue;
450
+ }
451
+
452
+ onTaskStart?.(task);
453
+
454
+ onProgress?.('milestone-tasks', `Starting task: ${task.name}`);
455
+
456
+ const taskResult = await runTaskWorkflow(milestone, task, {
457
+ projectDir,
458
+ consensusConfig,
459
+ onProgress,
460
+ });
461
+
462
+ taskResults.push(taskResult);
463
+ onTaskComplete?.(task, taskResult.success);
464
+
465
+ if (!taskResult.success) {
466
+ onProgress?.('milestone-tasks', `Task failed: ${task.name}`);
467
+
468
+ // Update milestone status
469
+ state = await updateMilestoneInState(projectDir, milestone.id, {
470
+ status: 'failed',
471
+ });
472
+
473
+ return {
474
+ success: false,
475
+ milestone: { ...milestone, status: 'failed' },
476
+ planConsensus,
477
+ taskResults,
478
+ error: `Task "${task.name}" failed: ${taskResult.error}`,
479
+ };
480
+ }
481
+
482
+ onProgress?.('milestone-tasks', `Task complete: ${task.name}`);
483
+ }
484
+
485
+ // ============================================
486
+ // PHASE 4: Milestone Completion Review
487
+ // ============================================
488
+ onProgress?.('milestone-review', `Reviewing milestone completion: ${milestone.name}`);
489
+
490
+ // Reload state to get latest task statuses
491
+ state = await loadProject(projectDir);
492
+ milestone = state.milestones.find(m => m.id === milestone.id) || milestone;
493
+
494
+ const completionReview = await createMilestoneReview(
495
+ milestone,
496
+ state,
497
+ taskResults,
498
+ (msg) => onProgress?.('milestone-review', msg)
499
+ );
500
+
501
+ // ============================================
502
+ // PHASE 5: Get Consensus on Completion
503
+ // ============================================
504
+ onProgress?.('milestone-completion', `Getting completion consensus for: ${milestone.name}`);
505
+
506
+ const completionConsensus = await iterateUntilConsensus(
507
+ completionReview,
508
+ `Milestone completion review for: ${milestone.name}`,
509
+ {
510
+ projectDir,
511
+ config: consensusConfig,
512
+ onIteration: (iteration, result) => {
513
+ onProgress?.('milestone-completion', `Completion review iteration ${iteration}: ${result.score}%`);
514
+ },
515
+ onProgress,
516
+ }
517
+ );
518
+
519
+ // Document milestone completion
520
+ const completionDocPath = await documentMilestoneCompletion(
521
+ projectDir,
522
+ milestone,
523
+ completionConsensus.bestPlan,
524
+ completionConsensus,
525
+ taskResults
526
+ );
527
+
528
+ // Update milestone with completion consensus
529
+ const finalStatus = completionConsensus.approved ? 'complete' : 'in-progress';
530
+ state = await updateMilestoneInState(projectDir, milestone.id, {
531
+ status: finalStatus,
532
+ completionReview: completionConsensus.bestPlan,
533
+ completionScore: completionConsensus.finalScore,
534
+ completionApproved: completionConsensus.approved,
535
+ completionDoc: completionDocPath,
536
+ });
537
+
538
+ if (!completionConsensus.approved) {
539
+ onProgress?.('milestone-completion', `Milestone completion not approved: ${completionConsensus.finalScore}%`);
540
+
541
+ return {
542
+ success: false,
543
+ milestone: { ...milestone, status: 'in-progress' },
544
+ planConsensus,
545
+ completionConsensus,
546
+ taskResults,
547
+ error: `Milestone completion not approved. Score: ${completionConsensus.finalScore}%`,
548
+ };
549
+ }
550
+
551
+ onProgress?.('milestone-complete', `Milestone complete: ${milestone.name}`);
552
+
553
+ return {
554
+ success: true,
555
+ milestone: { ...milestone, status: 'complete' },
556
+ planConsensus,
557
+ completionConsensus,
558
+ taskResults,
559
+ };
560
+
561
+ } catch (error) {
562
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error';
563
+ onProgress?.('milestone-error', errorMessage);
564
+
565
+ await updateMilestoneInState(projectDir, milestone.id, {
566
+ status: 'failed',
567
+ });
568
+
569
+ return {
570
+ success: false,
571
+ milestone: { ...milestone, status: 'failed' },
572
+ taskResults,
573
+ error: errorMessage,
574
+ };
575
+ }
576
+ }