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.
- package/README.md +521 -125
- package/dist/adapters/claude.d.ts +16 -4
- package/dist/adapters/claude.d.ts.map +1 -1
- package/dist/adapters/claude.js +679 -33
- package/dist/adapters/claude.js.map +1 -1
- package/dist/adapters/gemini.d.ts +55 -0
- package/dist/adapters/gemini.d.ts.map +1 -0
- package/dist/adapters/gemini.js +318 -0
- package/dist/adapters/gemini.js.map +1 -0
- package/dist/adapters/openai.d.ts.map +1 -1
- package/dist/adapters/openai.js +41 -7
- package/dist/adapters/openai.js.map +1 -1
- package/dist/auth/claude.d.ts +11 -9
- package/dist/auth/claude.d.ts.map +1 -1
- package/dist/auth/claude.js +107 -71
- package/dist/auth/claude.js.map +1 -1
- package/dist/auth/gemini.d.ts +58 -0
- package/dist/auth/gemini.d.ts.map +1 -0
- package/dist/auth/gemini.js +172 -0
- package/dist/auth/gemini.js.map +1 -0
- package/dist/auth/index.d.ts +11 -7
- package/dist/auth/index.d.ts.map +1 -1
- package/dist/auth/index.js +23 -5
- package/dist/auth/index.js.map +1 -1
- package/dist/auth/keychain.d.ts +20 -7
- package/dist/auth/keychain.d.ts.map +1 -1
- package/dist/auth/keychain.js +85 -29
- package/dist/auth/keychain.js.map +1 -1
- package/dist/auth/openai.d.ts +2 -2
- package/dist/auth/openai.d.ts.map +1 -1
- package/dist/auth/openai.js +30 -32
- package/dist/auth/openai.js.map +1 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +4 -7
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/interactive.d.ts +2 -2
- package/dist/cli/interactive.d.ts.map +1 -1
- package/dist/cli/interactive.js +1380 -183
- package/dist/cli/interactive.js.map +1 -1
- package/dist/config/defaults.d.ts +6 -1
- package/dist/config/defaults.d.ts.map +1 -1
- package/dist/config/defaults.js +10 -2
- package/dist/config/defaults.js.map +1 -1
- package/dist/config/index.d.ts +10 -0
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js +19 -0
- package/dist/config/index.js.map +1 -1
- package/dist/config/schema.d.ts +20 -0
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/config/schema.js +7 -0
- package/dist/config/schema.js.map +1 -1
- package/dist/generators/python.d.ts.map +1 -1
- package/dist/generators/python.js +1 -0
- package/dist/generators/python.js.map +1 -1
- package/dist/generators/typescript.d.ts.map +1 -1
- package/dist/generators/typescript.js +1 -0
- package/dist/generators/typescript.js.map +1 -1
- package/dist/state/index.d.ts +108 -0
- package/dist/state/index.d.ts.map +1 -1
- package/dist/state/index.js +551 -4
- package/dist/state/index.js.map +1 -1
- package/dist/state/registry.d.ts +52 -0
- package/dist/state/registry.d.ts.map +1 -0
- package/dist/state/registry.js +215 -0
- package/dist/state/registry.js.map +1 -0
- package/dist/types/cli.d.ts +4 -0
- package/dist/types/cli.d.ts.map +1 -1
- package/dist/types/cli.js.map +1 -1
- package/dist/types/consensus.d.ts +69 -4
- package/dist/types/consensus.d.ts.map +1 -1
- package/dist/types/consensus.js +24 -3
- package/dist/types/consensus.js.map +1 -1
- package/dist/types/workflow.d.ts +55 -0
- package/dist/types/workflow.d.ts.map +1 -1
- package/dist/types/workflow.js +16 -0
- package/dist/types/workflow.js.map +1 -1
- package/dist/workflow/auto-fix.d.ts +45 -0
- package/dist/workflow/auto-fix.d.ts.map +1 -0
- package/dist/workflow/auto-fix.js +274 -0
- package/dist/workflow/auto-fix.js.map +1 -0
- package/dist/workflow/consensus.d.ts +44 -2
- package/dist/workflow/consensus.d.ts.map +1 -1
- package/dist/workflow/consensus.js +565 -17
- package/dist/workflow/consensus.js.map +1 -1
- package/dist/workflow/execution-mode.d.ts +10 -4
- package/dist/workflow/execution-mode.d.ts.map +1 -1
- package/dist/workflow/execution-mode.js +547 -58
- package/dist/workflow/execution-mode.js.map +1 -1
- package/dist/workflow/index.d.ts +14 -2
- package/dist/workflow/index.d.ts.map +1 -1
- package/dist/workflow/index.js +69 -6
- package/dist/workflow/index.js.map +1 -1
- package/dist/workflow/milestone-workflow.d.ts +34 -0
- package/dist/workflow/milestone-workflow.d.ts.map +1 -0
- package/dist/workflow/milestone-workflow.js +414 -0
- package/dist/workflow/milestone-workflow.js.map +1 -0
- package/dist/workflow/plan-mode.d.ts +14 -1
- package/dist/workflow/plan-mode.d.ts.map +1 -1
- package/dist/workflow/plan-mode.js +589 -47
- package/dist/workflow/plan-mode.js.map +1 -1
- package/dist/workflow/plan-storage.d.ts +142 -0
- package/dist/workflow/plan-storage.d.ts.map +1 -0
- package/dist/workflow/plan-storage.js +331 -0
- package/dist/workflow/plan-storage.js.map +1 -0
- package/dist/workflow/project-verification.d.ts +37 -0
- package/dist/workflow/project-verification.d.ts.map +1 -0
- package/dist/workflow/project-verification.js +381 -0
- package/dist/workflow/project-verification.js.map +1 -0
- package/dist/workflow/task-workflow.d.ts +37 -0
- package/dist/workflow/task-workflow.d.ts.map +1 -0
- package/dist/workflow/task-workflow.js +383 -0
- package/dist/workflow/task-workflow.js.map +1 -0
- package/dist/workflow/test-runner.d.ts +1 -0
- package/dist/workflow/test-runner.d.ts.map +1 -1
- package/dist/workflow/test-runner.js +9 -5
- package/dist/workflow/test-runner.js.map +1 -1
- package/dist/workflow/ui-designer.d.ts +82 -0
- package/dist/workflow/ui-designer.d.ts.map +1 -0
- package/dist/workflow/ui-designer.js +234 -0
- package/dist/workflow/ui-designer.js.map +1 -0
- package/dist/workflow/ui-setup.d.ts +58 -0
- package/dist/workflow/ui-setup.d.ts.map +1 -0
- package/dist/workflow/ui-setup.js +685 -0
- package/dist/workflow/ui-setup.js.map +1 -0
- package/dist/workflow/ui-verification.d.ts +114 -0
- package/dist/workflow/ui-verification.d.ts.map +1 -0
- package/dist/workflow/ui-verification.js +258 -0
- package/dist/workflow/ui-verification.js.map +1 -0
- package/dist/workflow/workflow-logger.d.ts +110 -0
- package/dist/workflow/workflow-logger.d.ts.map +1 -0
- package/dist/workflow/workflow-logger.js +267 -0
- package/dist/workflow/workflow-logger.js.map +1 -0
- package/package.json +2 -2
- package/src/adapters/claude.ts +815 -34
- package/src/adapters/gemini.ts +373 -0
- package/src/adapters/openai.ts +40 -7
- package/src/auth/claude.ts +120 -78
- package/src/auth/gemini.ts +207 -0
- package/src/auth/index.ts +28 -8
- package/src/auth/keychain.ts +95 -28
- package/src/auth/openai.ts +29 -36
- package/src/cli/index.ts +4 -7
- package/src/cli/interactive.ts +1641 -216
- package/src/config/defaults.ts +10 -2
- package/src/config/index.ts +21 -0
- package/src/config/schema.ts +7 -0
- package/src/generators/python.ts +1 -0
- package/src/generators/typescript.ts +1 -0
- package/src/state/index.ts +713 -4
- package/src/state/registry.ts +278 -0
- package/src/types/cli.ts +4 -0
- package/src/types/consensus.ts +65 -6
- package/src/types/workflow.ts +35 -0
- package/src/workflow/auto-fix.ts +340 -0
- package/src/workflow/consensus.ts +750 -16
- package/src/workflow/execution-mode.ts +673 -74
- package/src/workflow/index.ts +95 -6
- package/src/workflow/milestone-workflow.ts +576 -0
- package/src/workflow/plan-mode.ts +696 -50
- package/src/workflow/plan-storage.ts +482 -0
- package/src/workflow/project-verification.ts +471 -0
- package/src/workflow/task-workflow.ts +525 -0
- package/src/workflow/test-runner.ts +10 -5
- package/src/workflow/ui-designer.ts +337 -0
- package/src/workflow/ui-setup.ts +797 -0
- package/src/workflow/ui-verification.ts +357 -0
- package/src/workflow/workflow-logger.ts +353 -0
- package/tests/config/config.test.ts +1 -1
- package/tests/types/consensus.test.ts +3 -3
- package/tests/workflow/plan-mode.test.ts +213 -0
- 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
|
+
}
|