popeye-cli 1.0.1 → 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/interactive.d.ts.map +1 -1
- package/dist/cli/interactive.js +1151 -110
- 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/interactive.ts +1357 -115
- 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
|
@@ -1,20 +1,21 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Execution Mode workflow
|
|
3
|
-
* Handles task execution
|
|
3
|
+
* Handles task execution with hierarchical consensus:
|
|
4
|
+
* Milestone Plan → Consensus → (Task Plan → Consensus → Implement → Test) → Milestone Review
|
|
4
5
|
*/
|
|
5
6
|
|
|
7
|
+
import { promises as fs } from 'node:fs';
|
|
8
|
+
import path from 'node:path';
|
|
6
9
|
import type { ProjectState, Task, Milestone } from '../types/workflow.js';
|
|
10
|
+
import type { ConsensusConfig } from '../types/consensus.js';
|
|
7
11
|
import { generateCode, type ClaudeExecuteResult } from '../adapters/claude.js';
|
|
8
12
|
import {
|
|
9
13
|
loadProject,
|
|
10
14
|
updateState,
|
|
11
|
-
updateTaskStatus,
|
|
12
15
|
setCurrentMilestone,
|
|
13
|
-
setCurrentTask,
|
|
14
|
-
getNextTask,
|
|
15
|
-
getProgress,
|
|
16
16
|
completeProject,
|
|
17
|
-
|
|
17
|
+
verifyProjectCompletion,
|
|
18
|
+
comprehensiveProjectVerification,
|
|
18
19
|
} from '../state/index.js';
|
|
19
20
|
import {
|
|
20
21
|
runTests,
|
|
@@ -22,6 +23,11 @@ import {
|
|
|
22
23
|
getTestSummary,
|
|
23
24
|
type TestResult,
|
|
24
25
|
} from './test-runner.js';
|
|
26
|
+
import { getWorkflowLogger } from './workflow-logger.js';
|
|
27
|
+
import { buildWithAutoFix } from './auto-fix.js';
|
|
28
|
+
import { runComprehensiveVerification, autoFixIssues } from './project-verification.js';
|
|
29
|
+
import { setupUI } from './ui-setup.js';
|
|
30
|
+
import { designUI, saveUISpecification, loadUISpecification } from './ui-designer.js';
|
|
25
31
|
|
|
26
32
|
/**
|
|
27
33
|
* Options for execution mode
|
|
@@ -29,6 +35,7 @@ import {
|
|
|
29
35
|
export interface ExecutionModeOptions {
|
|
30
36
|
projectDir: string;
|
|
31
37
|
maxRetries?: number;
|
|
38
|
+
consensusConfig?: Partial<ConsensusConfig>; // For per-task and per-milestone consensus
|
|
32
39
|
onProgress?: (phase: string, message: string) => void;
|
|
33
40
|
onTaskStart?: (milestone: Milestone, task: Task) => void;
|
|
34
41
|
onTaskComplete?: (milestone: Milestone, task: Task, success: boolean) => void;
|
|
@@ -63,6 +70,177 @@ export interface ExecutionModeResult {
|
|
|
63
70
|
*/
|
|
64
71
|
const DEFAULT_MAX_RETRIES = 3;
|
|
65
72
|
|
|
73
|
+
/**
|
|
74
|
+
* Generate a comprehensive README.md for the completed project
|
|
75
|
+
* This provides users with setup and run instructions
|
|
76
|
+
*
|
|
77
|
+
* @param projectDir - Project directory
|
|
78
|
+
* @param state - Project state
|
|
79
|
+
* @returns Path to generated README or error
|
|
80
|
+
*/
|
|
81
|
+
async function generateProjectReadme(
|
|
82
|
+
projectDir: string,
|
|
83
|
+
state: ProjectState
|
|
84
|
+
): Promise<{ success: boolean; path?: string; error?: string }> {
|
|
85
|
+
try {
|
|
86
|
+
const readmePath = path.join(projectDir, 'README.md');
|
|
87
|
+
|
|
88
|
+
// Extract features from completed milestones
|
|
89
|
+
const features = state.milestones
|
|
90
|
+
.filter(m => m.status === 'complete')
|
|
91
|
+
.map(m => ({
|
|
92
|
+
name: m.name,
|
|
93
|
+
description: m.description,
|
|
94
|
+
tasks: m.tasks.filter(t => t.status === 'complete').map(t => t.name),
|
|
95
|
+
}));
|
|
96
|
+
|
|
97
|
+
// Determine run commands based on language
|
|
98
|
+
const isTypeScript = state.language === 'typescript';
|
|
99
|
+
const installCmd = isTypeScript ? 'npm install' : 'pip install -r requirements.txt';
|
|
100
|
+
const devCmd = isTypeScript ? 'npm run dev' : 'python src/main.py';
|
|
101
|
+
const testCmd = isTypeScript ? 'npm test' : 'pytest tests/ -v';
|
|
102
|
+
const buildCmd = isTypeScript ? 'npm run build' : 'python -m py_compile src/**/*.py';
|
|
103
|
+
|
|
104
|
+
// Build README content
|
|
105
|
+
const readmeContent = `# ${state.name}
|
|
106
|
+
|
|
107
|
+
${state.specification ? extractDescriptionFromSpec(state.specification) : 'A project generated by Popeye CLI.'}
|
|
108
|
+
|
|
109
|
+
## Features
|
|
110
|
+
|
|
111
|
+
${features.map(f => `### ${f.name}
|
|
112
|
+
${f.description || ''}
|
|
113
|
+
${f.tasks.length > 0 ? f.tasks.map(t => `- ${t}`).join('\n') : ''}`).join('\n\n')}
|
|
114
|
+
|
|
115
|
+
## Prerequisites
|
|
116
|
+
|
|
117
|
+
${isTypeScript ? `- Node.js 18.0 or higher
|
|
118
|
+
- npm 8.0 or higher` : `- Python 3.9 or higher
|
|
119
|
+
- pip (Python package manager)`}
|
|
120
|
+
|
|
121
|
+
## Installation
|
|
122
|
+
|
|
123
|
+
\`\`\`bash
|
|
124
|
+
# Clone the repository (if applicable)
|
|
125
|
+
cd ${state.name}
|
|
126
|
+
|
|
127
|
+
# Install dependencies
|
|
128
|
+
${installCmd}
|
|
129
|
+
\`\`\`
|
|
130
|
+
|
|
131
|
+
## Environment Setup
|
|
132
|
+
|
|
133
|
+
1. Copy the example environment file:
|
|
134
|
+
\`\`\`bash
|
|
135
|
+
cp .env.example .env
|
|
136
|
+
\`\`\`
|
|
137
|
+
|
|
138
|
+
2. Edit \`.env\` and fill in the required values.
|
|
139
|
+
|
|
140
|
+
## Running the Application
|
|
141
|
+
|
|
142
|
+
### Development Mode
|
|
143
|
+
|
|
144
|
+
\`\`\`bash
|
|
145
|
+
${devCmd}
|
|
146
|
+
\`\`\`
|
|
147
|
+
|
|
148
|
+
### Running Tests
|
|
149
|
+
|
|
150
|
+
\`\`\`bash
|
|
151
|
+
${testCmd}
|
|
152
|
+
\`\`\`
|
|
153
|
+
|
|
154
|
+
### Build for Production
|
|
155
|
+
|
|
156
|
+
\`\`\`bash
|
|
157
|
+
${buildCmd}
|
|
158
|
+
\`\`\`
|
|
159
|
+
|
|
160
|
+
${isTypeScript ? `### Start Production Server
|
|
161
|
+
|
|
162
|
+
\`\`\`bash
|
|
163
|
+
npm start
|
|
164
|
+
\`\`\`
|
|
165
|
+
` : ''}
|
|
166
|
+
## Project Structure
|
|
167
|
+
|
|
168
|
+
\`\`\`
|
|
169
|
+
${state.name}/
|
|
170
|
+
├── src/ # Source code
|
|
171
|
+
${isTypeScript ? `│ └── index.ts # Main entry point` : `│ ├── __init__.py
|
|
172
|
+
│ └── main.py # Main entry point`}
|
|
173
|
+
├── tests/ # Test files
|
|
174
|
+
├── docs/ # Documentation
|
|
175
|
+
│ ├── PLAN.md # Development plan
|
|
176
|
+
│ └── WORKFLOW_LOG.md # Execution log
|
|
177
|
+
${isTypeScript ? `├── package.json # Dependencies and scripts
|
|
178
|
+
├── tsconfig.json # TypeScript configuration` : `├── requirements.txt # Python dependencies
|
|
179
|
+
├── pyproject.toml # Project configuration`}
|
|
180
|
+
├── .env.example # Environment template
|
|
181
|
+
├── .gitignore
|
|
182
|
+
├── Dockerfile
|
|
183
|
+
└── README.md
|
|
184
|
+
\`\`\`
|
|
185
|
+
|
|
186
|
+
## Development
|
|
187
|
+
|
|
188
|
+
This project was generated using [Popeye CLI](https://github.com/your-org/popeye-cli), an autonomous code generation tool.
|
|
189
|
+
|
|
190
|
+
### Development Plan
|
|
191
|
+
|
|
192
|
+
See [docs/PLAN.md](docs/PLAN.md) for the complete development plan used to build this project.
|
|
193
|
+
|
|
194
|
+
### Workflow Log
|
|
195
|
+
|
|
196
|
+
See [docs/WORKFLOW_LOG.md](docs/WORKFLOW_LOG.md) for detailed execution logs.
|
|
197
|
+
|
|
198
|
+
## License
|
|
199
|
+
|
|
200
|
+
MIT
|
|
201
|
+
`;
|
|
202
|
+
|
|
203
|
+
await fs.writeFile(readmePath, readmeContent, 'utf-8');
|
|
204
|
+
|
|
205
|
+
return { success: true, path: readmePath };
|
|
206
|
+
} catch (error) {
|
|
207
|
+
return {
|
|
208
|
+
success: false,
|
|
209
|
+
error: error instanceof Error ? error.message : 'Failed to generate README',
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Extract a brief description from the specification
|
|
216
|
+
*
|
|
217
|
+
* @param spec - Full specification text
|
|
218
|
+
* @returns Brief description (first paragraph or summary)
|
|
219
|
+
*/
|
|
220
|
+
function extractDescriptionFromSpec(spec: string): string {
|
|
221
|
+
// Try to find a summary section
|
|
222
|
+
const summaryMatch = spec.match(/(?:##?\s*(?:Summary|Overview|Description)[\s\S]*?\n)([\s\S]*?)(?:\n##|\n\n##|$)/i);
|
|
223
|
+
if (summaryMatch) {
|
|
224
|
+
const summary = summaryMatch[1].trim();
|
|
225
|
+
if (summary.length > 0 && summary.length < 500) {
|
|
226
|
+
return summary;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// Fall back to first paragraph
|
|
231
|
+
const lines = spec.split('\n').filter(line => line.trim() && !line.startsWith('#'));
|
|
232
|
+
const firstParagraph = lines.slice(0, 3).join(' ').trim();
|
|
233
|
+
|
|
234
|
+
if (firstParagraph.length > 500) {
|
|
235
|
+
return firstParagraph.slice(0, 497) + '...';
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
return firstParagraph || 'A project generated by Popeye CLI.';
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// Note: runCommand and runFinalBuildVerification replaced by buildWithAutoFix from auto-fix.ts
|
|
242
|
+
// which provides automatic error fixing capabilities
|
|
243
|
+
|
|
66
244
|
/**
|
|
67
245
|
* Build the execution context for a task
|
|
68
246
|
*
|
|
@@ -74,7 +252,8 @@ const DEFAULT_MAX_RETRIES = 3;
|
|
|
74
252
|
function buildTaskContext(
|
|
75
253
|
state: ProjectState,
|
|
76
254
|
milestone: Milestone,
|
|
77
|
-
_task: Task
|
|
255
|
+
_task: Task,
|
|
256
|
+
uiDesignContext?: string
|
|
78
257
|
): string {
|
|
79
258
|
const lines: string[] = [];
|
|
80
259
|
|
|
@@ -94,6 +273,12 @@ function buildTaskContext(
|
|
|
94
273
|
lines.push('');
|
|
95
274
|
}
|
|
96
275
|
|
|
276
|
+
// Include UI design context if available
|
|
277
|
+
if (uiDesignContext) {
|
|
278
|
+
lines.push(uiDesignContext);
|
|
279
|
+
lines.push('');
|
|
280
|
+
}
|
|
281
|
+
|
|
97
282
|
lines.push('## Current Milestone');
|
|
98
283
|
lines.push(`Name: ${milestone.name}`);
|
|
99
284
|
lines.push(`Description: ${milestone.description}`);
|
|
@@ -118,12 +303,14 @@ function buildTaskContext(
|
|
|
118
303
|
* @param task - The task to execute
|
|
119
304
|
* @param context - Execution context
|
|
120
305
|
* @param projectDir - Project directory
|
|
306
|
+
* @param onProgress - Progress callback for Claude activity
|
|
121
307
|
* @returns Execution result
|
|
122
308
|
*/
|
|
123
309
|
export async function executeTask(
|
|
124
310
|
task: Task,
|
|
125
311
|
context: string,
|
|
126
|
-
projectDir: string
|
|
312
|
+
projectDir: string,
|
|
313
|
+
onProgress?: (message: string) => void
|
|
127
314
|
): Promise<ClaudeExecuteResult> {
|
|
128
315
|
const prompt = `
|
|
129
316
|
## Task
|
|
@@ -140,7 +327,7 @@ Please implement this task completely. After implementing:
|
|
|
140
327
|
3. Document any complex logic
|
|
141
328
|
`.trim();
|
|
142
329
|
|
|
143
|
-
return generateCode(prompt, context, { cwd: projectDir });
|
|
330
|
+
return generateCode(prompt, context, { cwd: projectDir, onProgress });
|
|
144
331
|
}
|
|
145
332
|
|
|
146
333
|
/**
|
|
@@ -150,13 +337,15 @@ Please implement this task completely. After implementing:
|
|
|
150
337
|
* @param testResult - The test result
|
|
151
338
|
* @param context - Execution context
|
|
152
339
|
* @param projectDir - Project directory
|
|
340
|
+
* @param onProgress - Progress callback for Claude activity
|
|
153
341
|
* @returns Fix attempt result
|
|
154
342
|
*/
|
|
155
343
|
export async function handleTestFailure(
|
|
156
344
|
task: Task,
|
|
157
345
|
testResult: TestResult,
|
|
158
346
|
context: string,
|
|
159
|
-
projectDir: string
|
|
347
|
+
projectDir: string,
|
|
348
|
+
onProgress?: (message: string) => void
|
|
160
349
|
): Promise<ClaudeExecuteResult> {
|
|
161
350
|
const prompt = `
|
|
162
351
|
## Test Failure Fix Required
|
|
@@ -180,7 +369,7 @@ Please:
|
|
|
180
369
|
3. Do NOT modify the tests unless they are incorrect
|
|
181
370
|
`.trim();
|
|
182
371
|
|
|
183
|
-
return generateCode(prompt, context, { cwd: projectDir });
|
|
372
|
+
return generateCode(prompt, context, { cwd: projectDir, onProgress });
|
|
184
373
|
}
|
|
185
374
|
|
|
186
375
|
/**
|
|
@@ -202,13 +391,31 @@ async function executeTaskWithRetry(
|
|
|
202
391
|
projectDir,
|
|
203
392
|
maxRetries = DEFAULT_MAX_RETRIES,
|
|
204
393
|
onTestResult,
|
|
394
|
+
onProgress,
|
|
205
395
|
} = options;
|
|
206
396
|
|
|
207
|
-
|
|
397
|
+
// Load UI design context if available
|
|
398
|
+
let uiDesignContext: string | undefined;
|
|
399
|
+
try {
|
|
400
|
+
const uiSpec = await loadUISpecification(projectDir);
|
|
401
|
+
if (uiSpec) {
|
|
402
|
+
const { generateDesignSystemPrompt } = await import('./ui-designer.js');
|
|
403
|
+
uiDesignContext = generateDesignSystemPrompt(uiSpec);
|
|
404
|
+
}
|
|
405
|
+
} catch {
|
|
406
|
+
// UI spec not available, continue without it
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
const context = buildTaskContext(state, milestone, task, uiDesignContext);
|
|
208
410
|
let retries = 0;
|
|
209
411
|
|
|
412
|
+
// Create a progress handler that prefixes messages with [claude]
|
|
413
|
+
const claudeProgress = onProgress
|
|
414
|
+
? (msg: string) => onProgress('claude', msg)
|
|
415
|
+
: undefined;
|
|
416
|
+
|
|
210
417
|
// Execute the task
|
|
211
|
-
const execResult = await executeTask(task, context, projectDir);
|
|
418
|
+
const execResult = await executeTask(task, context, projectDir, claudeProgress);
|
|
212
419
|
|
|
213
420
|
if (!execResult.success) {
|
|
214
421
|
return {
|
|
@@ -287,7 +494,8 @@ async function executeTaskWithRetry(
|
|
|
287
494
|
}
|
|
288
495
|
|
|
289
496
|
/**
|
|
290
|
-
* Run execution mode for a project
|
|
497
|
+
* Run execution mode for a project using hierarchical consensus workflow
|
|
498
|
+
* Each milestone and task goes through: Plan → Consensus → Implement → Test → Review
|
|
291
499
|
*
|
|
292
500
|
* @param options - Execution options
|
|
293
501
|
* @returns Execution mode result
|
|
@@ -302,12 +510,23 @@ export async function runExecutionMode(
|
|
|
302
510
|
onTaskComplete,
|
|
303
511
|
} = options;
|
|
304
512
|
|
|
513
|
+
// Initialize workflow logger
|
|
514
|
+
const logger = getWorkflowLogger(projectDir);
|
|
515
|
+
|
|
305
516
|
let completedTasks = 0;
|
|
306
517
|
let failedTasks = 0;
|
|
518
|
+
let completedMilestones = 0;
|
|
307
519
|
|
|
308
520
|
try {
|
|
309
521
|
let state = await loadProject(projectDir);
|
|
310
522
|
|
|
523
|
+
await logger.stageStart('execution', 'Execution Mode started', {
|
|
524
|
+
projectName: state.name,
|
|
525
|
+
phase: state.phase,
|
|
526
|
+
totalMilestones: state.milestones.length,
|
|
527
|
+
totalTasks: state.milestones.reduce((sum, m) => sum + m.tasks.length, 0),
|
|
528
|
+
});
|
|
529
|
+
|
|
311
530
|
// Ensure we're in execution phase
|
|
312
531
|
if (state.phase !== 'execution') {
|
|
313
532
|
return {
|
|
@@ -322,95 +541,409 @@ export async function runExecutionMode(
|
|
|
322
541
|
// Update status
|
|
323
542
|
state = await updateState(projectDir, { status: 'in-progress' });
|
|
324
543
|
|
|
325
|
-
onProgress?.('execution-start', 'Starting execution mode...');
|
|
544
|
+
onProgress?.('execution-start', 'Starting hierarchical execution mode...');
|
|
545
|
+
onProgress?.('execution-start', `Processing ${state.milestones.length} milestones with per-task consensus`);
|
|
326
546
|
|
|
327
|
-
//
|
|
328
|
-
|
|
547
|
+
// Import milestone workflow dynamically to avoid circular dependencies
|
|
548
|
+
const { runMilestoneWorkflow } = await import('./milestone-workflow.js');
|
|
329
549
|
|
|
330
|
-
|
|
331
|
-
|
|
550
|
+
// Process each milestone
|
|
551
|
+
for (const milestone of state.milestones) {
|
|
552
|
+
// Skip completed milestones
|
|
553
|
+
if (milestone.status === 'complete' && milestone.completionApproved) {
|
|
554
|
+
onProgress?.('milestone-skip', `Skipping completed milestone: ${milestone.name}`);
|
|
555
|
+
completedMilestones++;
|
|
556
|
+
completedTasks += milestone.tasks.filter(t => t.status === 'complete').length;
|
|
557
|
+
continue;
|
|
558
|
+
}
|
|
332
559
|
|
|
333
|
-
// Set current milestone
|
|
560
|
+
// Set current milestone
|
|
334
561
|
state = await setCurrentMilestone(projectDir, milestone.id);
|
|
335
|
-
state = await setCurrentTask(projectDir, task.id);
|
|
336
|
-
|
|
337
|
-
// Update task to in-progress
|
|
338
|
-
state = await updateTaskStatus(projectDir, task.id, 'in-progress');
|
|
339
562
|
|
|
340
563
|
onProgress?.(
|
|
341
|
-
'
|
|
342
|
-
`
|
|
564
|
+
'milestone-start',
|
|
565
|
+
`Starting milestone: ${milestone.name} (${milestone.tasks.length} tasks)`
|
|
343
566
|
);
|
|
344
567
|
|
|
345
|
-
|
|
346
|
-
|
|
568
|
+
await logger.stageStart('milestone', `Starting milestone: ${milestone.name}`, {
|
|
569
|
+
milestoneId: milestone.id,
|
|
570
|
+
milestoneName: milestone.name,
|
|
571
|
+
taskCount: milestone.tasks.length,
|
|
572
|
+
taskNames: milestone.tasks.map(t => t.name),
|
|
573
|
+
});
|
|
574
|
+
|
|
575
|
+
// Run the complete milestone workflow
|
|
576
|
+
const milestoneResult = await runMilestoneWorkflow(milestone, {
|
|
577
|
+
projectDir,
|
|
578
|
+
consensusConfig: options.consensusConfig,
|
|
579
|
+
onProgress,
|
|
580
|
+
onTaskStart: (task) => {
|
|
581
|
+
if (onTaskStart) {
|
|
582
|
+
onTaskStart(milestone, task);
|
|
583
|
+
}
|
|
584
|
+
},
|
|
585
|
+
onTaskComplete: (task, success) => {
|
|
586
|
+
if (success) {
|
|
587
|
+
completedTasks++;
|
|
588
|
+
} else {
|
|
589
|
+
failedTasks++;
|
|
590
|
+
}
|
|
591
|
+
if (onTaskComplete) {
|
|
592
|
+
onTaskComplete(milestone, task, success);
|
|
593
|
+
}
|
|
594
|
+
},
|
|
595
|
+
});
|
|
596
|
+
|
|
597
|
+
if (milestoneResult.success) {
|
|
598
|
+
completedMilestones++;
|
|
599
|
+
onProgress?.(
|
|
600
|
+
'milestone-complete',
|
|
601
|
+
`Milestone complete: ${milestone.name} (Score: ${milestoneResult.completionConsensus?.finalScore}%)`
|
|
602
|
+
);
|
|
603
|
+
|
|
604
|
+
await logger.stageComplete('milestone', `Milestone completed: ${milestone.name}`, {
|
|
605
|
+
milestoneId: milestone.id,
|
|
606
|
+
milestoneName: milestone.name,
|
|
607
|
+
consensusScore: milestoneResult.completionConsensus?.finalScore,
|
|
608
|
+
tasksCompleted: milestone.tasks.filter(t => t.status === 'complete').length,
|
|
609
|
+
});
|
|
610
|
+
} else {
|
|
611
|
+
onProgress?.(
|
|
612
|
+
'milestone-failed',
|
|
613
|
+
`Milestone failed: ${milestone.name} - ${milestoneResult.error}`
|
|
614
|
+
);
|
|
615
|
+
|
|
616
|
+
await logger.stageFailed('milestone', `Milestone failed: ${milestone.name}`, milestoneResult.error || 'Unknown error', {
|
|
617
|
+
milestoneId: milestone.id,
|
|
618
|
+
milestoneName: milestone.name,
|
|
619
|
+
});
|
|
620
|
+
|
|
621
|
+
// Reload state to get latest
|
|
622
|
+
state = await loadProject(projectDir);
|
|
623
|
+
|
|
624
|
+
return {
|
|
625
|
+
success: false,
|
|
626
|
+
state,
|
|
627
|
+
completedTasks,
|
|
628
|
+
failedTasks,
|
|
629
|
+
error: `Milestone "${milestone.name}" failed: ${milestoneResult.error}`,
|
|
630
|
+
};
|
|
347
631
|
}
|
|
632
|
+
}
|
|
348
633
|
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
634
|
+
// ============================================
|
|
635
|
+
// UI SETUP PHASE
|
|
636
|
+
// ============================================
|
|
637
|
+
onProgress?.('ui-setup', 'Running UI setup and design system configuration...');
|
|
638
|
+
await logger.stageStart('ui-setup', 'Starting UI setup');
|
|
639
|
+
|
|
640
|
+
// Check if this is a frontend project
|
|
641
|
+
const frontendDir = path.join(projectDir, 'packages', 'frontend');
|
|
642
|
+
const hasFrontend = await fs.access(frontendDir).then(() => true).catch(() => false);
|
|
643
|
+
|
|
644
|
+
if (hasFrontend) {
|
|
645
|
+
try {
|
|
646
|
+
// Load or generate UI specification
|
|
647
|
+
let uiSpec = await loadUISpecification(projectDir);
|
|
648
|
+
|
|
649
|
+
if (!uiSpec && state.idea) {
|
|
650
|
+
onProgress?.('ui-setup', 'Designing UI from project idea...');
|
|
651
|
+
uiSpec = await designUI(state.idea, (msg) => onProgress?.('ui-setup', msg));
|
|
652
|
+
await saveUISpecification(projectDir, uiSpec);
|
|
653
|
+
onProgress?.('ui-setup', `UI design complete: ${uiSpec.themeName} theme selected`);
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
// Run UI setup to install components and configure styling
|
|
657
|
+
onProgress?.('ui-setup', 'Setting up component library and styling...');
|
|
658
|
+
const uiResult = await setupUI(
|
|
659
|
+
projectDir,
|
|
660
|
+
{
|
|
661
|
+
theme: uiSpec?.themeName || 'modern',
|
|
662
|
+
idea: state.idea,
|
|
663
|
+
},
|
|
664
|
+
(msg) => onProgress?.('ui-setup', msg)
|
|
665
|
+
);
|
|
356
666
|
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
667
|
+
if (uiResult.success) {
|
|
668
|
+
onProgress?.('ui-setup', `UI setup complete: ${uiResult.componentsInstalled.length} components installed`);
|
|
669
|
+
await logger.success('ui-setup', 'ui_setup_complete', 'UI setup completed successfully', {
|
|
670
|
+
theme: uiResult.theme,
|
|
671
|
+
components: uiResult.componentsInstalled,
|
|
672
|
+
});
|
|
673
|
+
} else {
|
|
674
|
+
onProgress?.('ui-setup', `UI setup warning: ${uiResult.error}`);
|
|
675
|
+
await logger.warn('ui-setup', 'ui_setup_warning', 'UI setup had issues', {
|
|
676
|
+
error: uiResult.error,
|
|
677
|
+
});
|
|
678
|
+
}
|
|
679
|
+
} catch (uiError) {
|
|
680
|
+
// Non-blocking - UI setup failures shouldn't stop the project
|
|
681
|
+
onProgress?.('ui-setup', `UI setup skipped: ${uiError instanceof Error ? uiError.message : 'Unknown error'}`);
|
|
682
|
+
await logger.warn('ui-setup', 'ui_setup_skipped', 'UI setup was skipped', {
|
|
683
|
+
error: uiError instanceof Error ? uiError.message : 'Unknown error',
|
|
360
684
|
});
|
|
361
|
-
|
|
685
|
+
}
|
|
686
|
+
} else {
|
|
687
|
+
onProgress?.('ui-setup', 'No frontend detected, skipping UI setup');
|
|
688
|
+
}
|
|
362
689
|
|
|
690
|
+
// ============================================
|
|
691
|
+
// FINAL VERIFICATION PHASE
|
|
692
|
+
// ============================================
|
|
693
|
+
onProgress?.('verification', 'Running final verification...');
|
|
694
|
+
await logger.stageStart('verification', 'Starting final verification');
|
|
695
|
+
|
|
696
|
+
// Reload state to get latest
|
|
697
|
+
state = await loadProject(projectDir);
|
|
698
|
+
|
|
699
|
+
// Verify all milestones are complete
|
|
700
|
+
const incompleteMilestones = state.milestones.filter(m => m.status !== 'complete');
|
|
701
|
+
if (incompleteMilestones.length > 0) {
|
|
702
|
+
const incompleteNames = incompleteMilestones.map(m => m.name).join(', ');
|
|
703
|
+
onProgress?.(
|
|
704
|
+
'verification-warning',
|
|
705
|
+
`Warning: ${incompleteMilestones.length} milestone(s) not marked complete: ${incompleteNames}`
|
|
706
|
+
);
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
// Verify all tasks are complete
|
|
710
|
+
const allTasks = state.milestones.flatMap(m => m.tasks);
|
|
711
|
+
const incompleteTasks = allTasks.filter(t => t.status !== 'complete');
|
|
712
|
+
if (incompleteTasks.length > 0) {
|
|
713
|
+
const incompleteTaskNames = incompleteTasks.slice(0, 5).map(t => t.name).join(', ');
|
|
714
|
+
onProgress?.(
|
|
715
|
+
'verification-warning',
|
|
716
|
+
`Warning: ${incompleteTasks.length} task(s) not complete: ${incompleteTaskNames}${incompleteTasks.length > 5 ? '...' : ''}`
|
|
717
|
+
);
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
// Run final build verification with auto-fix
|
|
721
|
+
onProgress?.('verification', 'Running final build verification...');
|
|
722
|
+
const buildResult = await buildWithAutoFix(
|
|
723
|
+
projectDir,
|
|
724
|
+
state.language,
|
|
725
|
+
3, // max fix attempts
|
|
726
|
+
(msg) => onProgress?.('verification', `[auto-fix] ${msg}`)
|
|
727
|
+
);
|
|
728
|
+
|
|
729
|
+
if (!buildResult.success) {
|
|
730
|
+
onProgress?.(
|
|
731
|
+
'verification-error',
|
|
732
|
+
`Build verification failed${buildResult.autoFixed ? ' (after auto-fix attempts)' : ''}: Build errors remain`
|
|
733
|
+
);
|
|
734
|
+
await logger.error('verification', 'build_failed', 'Build verification failed', {
|
|
735
|
+
output: buildResult.output.slice(0, 2000),
|
|
736
|
+
autoFixed: buildResult.autoFixed,
|
|
737
|
+
});
|
|
738
|
+
|
|
739
|
+
// Block completion if build fails
|
|
740
|
+
onProgress?.(
|
|
741
|
+
'verification-error',
|
|
742
|
+
'BLOCKING: Cannot complete project with build errors. Please fix manually and resume.'
|
|
743
|
+
);
|
|
744
|
+
|
|
745
|
+
return {
|
|
746
|
+
success: false,
|
|
747
|
+
state,
|
|
748
|
+
completedTasks,
|
|
749
|
+
failedTasks,
|
|
750
|
+
error: 'Build verification failed - project not complete',
|
|
751
|
+
};
|
|
752
|
+
} else {
|
|
753
|
+
onProgress?.('verification', `Build verification passed${buildResult.autoFixed ? ' (after auto-fix)' : ''}`);
|
|
754
|
+
await logger.success('verification', 'build_passed', 'Build verification passed', {
|
|
755
|
+
autoFixed: buildResult.autoFixed,
|
|
756
|
+
});
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
// Run final test verification
|
|
760
|
+
const hasTests = await testsExist(projectDir, state.language);
|
|
761
|
+
if (hasTests) {
|
|
762
|
+
onProgress?.('verification', 'Running final test verification...');
|
|
763
|
+
const testResult = await runTests(projectDir, state.language);
|
|
764
|
+
if (!testResult.success) {
|
|
363
765
|
onProgress?.(
|
|
364
|
-
'
|
|
365
|
-
`
|
|
766
|
+
'verification-warning',
|
|
767
|
+
`Final test verification failed: ${testResult.failed} test(s) failed`
|
|
366
768
|
);
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
769
|
+
await logger.warn('verification', 'tests_failed', 'Final test verification failed', {
|
|
770
|
+
passed: testResult.passed,
|
|
771
|
+
failed: testResult.failed,
|
|
772
|
+
total: testResult.total,
|
|
773
|
+
failedTests: testResult.failedTests,
|
|
371
774
|
});
|
|
372
|
-
|
|
373
|
-
|
|
775
|
+
} else {
|
|
374
776
|
onProgress?.(
|
|
375
|
-
'
|
|
376
|
-
`
|
|
777
|
+
'verification',
|
|
778
|
+
`All tests passed: ${testResult.passed}/${testResult.total}`
|
|
377
779
|
);
|
|
780
|
+
await logger.success('verification', 'tests_passed', 'All tests passed', {
|
|
781
|
+
passed: testResult.passed,
|
|
782
|
+
total: testResult.total,
|
|
783
|
+
});
|
|
378
784
|
}
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
// Run comprehensive code quality verification
|
|
788
|
+
onProgress?.('verification', 'Running code quality verification...');
|
|
789
|
+
const codeVerification = await comprehensiveProjectVerification(projectDir);
|
|
790
|
+
|
|
791
|
+
// Log comprehensive verification results
|
|
792
|
+
await logger.info('verification', 'code_quality_check', 'Code quality verification results', {
|
|
793
|
+
totalSourceFiles: codeVerification.codeVerification.totalSourceFiles,
|
|
794
|
+
totalLinesOfCode: codeVerification.codeVerification.totalLinesOfCode,
|
|
795
|
+
hasMainEntryPoint: codeVerification.codeVerification.hasMainEntryPoint,
|
|
796
|
+
hasTests: codeVerification.codeVerification.hasTests,
|
|
797
|
+
hasSubstantiveCode: codeVerification.codeVerification.hasSubstantiveCode,
|
|
798
|
+
issues: codeVerification.codeVerification.issues,
|
|
799
|
+
warnings: codeVerification.codeVerification.warnings,
|
|
800
|
+
});
|
|
379
801
|
|
|
380
|
-
|
|
381
|
-
|
|
802
|
+
// Display code quality results
|
|
803
|
+
onProgress?.(
|
|
804
|
+
'verification',
|
|
805
|
+
`Code: ${codeVerification.codeVerification.totalSourceFiles} files, ` +
|
|
806
|
+
`${codeVerification.codeVerification.totalLinesOfCode} lines of code`
|
|
807
|
+
);
|
|
808
|
+
|
|
809
|
+
if (codeVerification.codeVerification.issues.length > 0) {
|
|
810
|
+
for (const issue of codeVerification.codeVerification.issues) {
|
|
811
|
+
onProgress?.('verification-warning', `Code Issue: ${issue}`);
|
|
382
812
|
}
|
|
813
|
+
}
|
|
383
814
|
|
|
384
|
-
|
|
385
|
-
|
|
815
|
+
if (codeVerification.codeVerification.warnings.length > 0) {
|
|
816
|
+
for (const warning of codeVerification.codeVerification.warnings.slice(0, 3)) {
|
|
817
|
+
onProgress?.('verification-info', `Code Warning: ${warning}`);
|
|
818
|
+
}
|
|
386
819
|
}
|
|
387
820
|
|
|
388
|
-
//
|
|
389
|
-
|
|
821
|
+
// Summary
|
|
822
|
+
onProgress?.(
|
|
823
|
+
'verification-summary',
|
|
824
|
+
`Verification complete: ${completedMilestones}/${state.milestones.length} milestones, ` +
|
|
825
|
+
`${completedTasks}/${allTasks.length} tasks, ` +
|
|
826
|
+
`${codeVerification.codeVerification.totalLinesOfCode} lines of code`
|
|
827
|
+
);
|
|
390
828
|
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
829
|
+
// Check if genuinely complete
|
|
830
|
+
if (!codeVerification.isGenuinelyComplete) {
|
|
831
|
+
onProgress?.(
|
|
832
|
+
'verification-warning',
|
|
833
|
+
'WARNING: Project may not be genuinely complete. Code verification found issues.'
|
|
395
834
|
);
|
|
835
|
+
await logger.warn('verification', 'incomplete_code', 'Project may not be genuinely complete', {
|
|
836
|
+
taskComplete: codeVerification.taskVerification.isComplete,
|
|
837
|
+
codeQualityPassed: codeVerification.codeVerification.passed,
|
|
838
|
+
summary: codeVerification.summary,
|
|
839
|
+
});
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
// ============================================
|
|
843
|
+
// COMPREHENSIVE PROJECT VERIFICATION
|
|
844
|
+
// ============================================
|
|
845
|
+
onProgress?.('verification', 'Running comprehensive project verification...');
|
|
846
|
+
|
|
847
|
+
const comprehensiveReport = await runComprehensiveVerification(
|
|
848
|
+
projectDir,
|
|
849
|
+
(msg) => onProgress?.('verification', msg)
|
|
850
|
+
);
|
|
851
|
+
|
|
852
|
+
// Log verification results
|
|
853
|
+
for (const result of comprehensiveReport.results) {
|
|
854
|
+
if (!result.passed) {
|
|
855
|
+
const level = result.severity === 'error' ? 'verification-error' : 'verification-warning';
|
|
856
|
+
onProgress?.(level, `[${result.category}] ${result.message}`);
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
// Auto-fix fixable issues
|
|
861
|
+
if (comprehensiveReport.failedChecks > 0) {
|
|
862
|
+
const fixableCount = comprehensiveReport.results.filter(r => !r.passed && r.autoFixable).length;
|
|
863
|
+
if (fixableCount > 0) {
|
|
864
|
+
onProgress?.('verification', `Attempting to auto-fix ${fixableCount} issue(s)...`);
|
|
865
|
+
const fixed = await autoFixIssues(comprehensiveReport, (msg) => onProgress?.('verification', msg));
|
|
866
|
+
onProgress?.('verification', `Auto-fixed ${fixed} issue(s)`);
|
|
867
|
+
|
|
868
|
+
// Re-run verification after fixes
|
|
869
|
+
if (fixed > 0) {
|
|
870
|
+
onProgress?.('verification', 'Re-running verification after fixes...');
|
|
871
|
+
const reVerifyReport = await runComprehensiveVerification(projectDir);
|
|
872
|
+
|
|
873
|
+
if (reVerifyReport.failedChecks > 0) {
|
|
874
|
+
onProgress?.(
|
|
875
|
+
'verification-error',
|
|
876
|
+
`${reVerifyReport.failedChecks} critical issue(s) remain after auto-fix`
|
|
877
|
+
);
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
// Block completion if critical issues remain
|
|
884
|
+
if (comprehensiveReport.criticalIssues.length > 0) {
|
|
885
|
+
onProgress?.(
|
|
886
|
+
'verification-error',
|
|
887
|
+
`BLOCKING: ${comprehensiveReport.criticalIssues.length} critical issue(s) found`
|
|
888
|
+
);
|
|
889
|
+
|
|
890
|
+
for (const issue of comprehensiveReport.criticalIssues) {
|
|
891
|
+
onProgress?.('verification-error', ` - ${issue}`);
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
await logger.error('verification', 'critical_issues', 'Critical verification issues found', {
|
|
895
|
+
issues: comprehensiveReport.criticalIssues,
|
|
896
|
+
});
|
|
396
897
|
|
|
397
898
|
return {
|
|
398
899
|
success: false,
|
|
399
900
|
state,
|
|
400
901
|
completedTasks,
|
|
401
902
|
failedTasks,
|
|
402
|
-
error: `
|
|
903
|
+
error: `Project verification failed: ${comprehensiveReport.criticalIssues.length} critical issues`,
|
|
403
904
|
};
|
|
404
905
|
}
|
|
405
906
|
|
|
406
|
-
|
|
907
|
+
onProgress?.(
|
|
908
|
+
'verification',
|
|
909
|
+
`Verification complete: ${comprehensiveReport.passedChecks}/${comprehensiveReport.totalChecks} checks passed`
|
|
910
|
+
);
|
|
911
|
+
|
|
912
|
+
// ============================================
|
|
913
|
+
// GENERATE PROJECT README
|
|
914
|
+
// ============================================
|
|
915
|
+
onProgress?.('readme', 'Generating project README with setup and run instructions...');
|
|
916
|
+
const readmeResult = await generateProjectReadme(projectDir, state);
|
|
917
|
+
|
|
918
|
+
if (readmeResult.success) {
|
|
919
|
+
onProgress?.('readme', `README.md generated successfully`);
|
|
920
|
+
await logger.success('completion', 'readme_generated', 'Project README generated', {
|
|
921
|
+
path: readmeResult.path,
|
|
922
|
+
});
|
|
923
|
+
} else {
|
|
924
|
+
onProgress?.('readme-warning', `Failed to generate README: ${readmeResult.error}`);
|
|
925
|
+
await logger.warn('completion', 'readme_failed', 'Failed to generate README', {
|
|
926
|
+
error: readmeResult.error,
|
|
927
|
+
});
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
// All milestones complete
|
|
407
931
|
state = await completeProject(projectDir);
|
|
408
932
|
|
|
409
933
|
onProgress?.(
|
|
410
934
|
'execution-complete',
|
|
411
|
-
`Project complete! ${completedTasks} tasks executed successfully.`
|
|
935
|
+
`Project complete! ${completedMilestones} milestones, ${completedTasks} tasks executed successfully.`
|
|
412
936
|
);
|
|
413
937
|
|
|
938
|
+
await logger.stageComplete('completion', 'Project execution completed successfully', {
|
|
939
|
+
completedMilestones: completedMilestones,
|
|
940
|
+
totalMilestones: state.milestones.length,
|
|
941
|
+
completedTasks: completedTasks,
|
|
942
|
+
totalTasks: state.milestones.reduce((sum, m) => sum + m.tasks.length, 0),
|
|
943
|
+
buildPassed: buildResult.success,
|
|
944
|
+
testsPassed: hasTests ? (await runTests(projectDir, state.language)).success : true,
|
|
945
|
+
});
|
|
946
|
+
|
|
414
947
|
return {
|
|
415
948
|
success: true,
|
|
416
949
|
state,
|
|
@@ -421,6 +954,13 @@ export async function runExecutionMode(
|
|
|
421
954
|
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
422
955
|
onProgress?.('error', errorMessage);
|
|
423
956
|
|
|
957
|
+
await logger.stageFailed('execution', 'Execution Mode failed', errorMessage, {
|
|
958
|
+
errorType: error instanceof Error ? error.constructor.name : typeof error,
|
|
959
|
+
completedTasks: completedTasks,
|
|
960
|
+
failedTasks: failedTasks,
|
|
961
|
+
completedMilestones: completedMilestones,
|
|
962
|
+
});
|
|
963
|
+
|
|
424
964
|
return {
|
|
425
965
|
success: false,
|
|
426
966
|
state: await loadProject(projectDir).catch(() => ({} as ProjectState)),
|
|
@@ -440,32 +980,91 @@ export async function runExecutionMode(
|
|
|
440
980
|
export async function resumeExecutionMode(
|
|
441
981
|
options: ExecutionModeOptions
|
|
442
982
|
): Promise<ExecutionModeResult> {
|
|
443
|
-
const
|
|
983
|
+
const { projectDir, onProgress } = options;
|
|
984
|
+
let state = await loadProject(projectDir);
|
|
444
985
|
|
|
445
|
-
//
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
}
|
|
986
|
+
// Verify actual completion - don't trust status alone
|
|
987
|
+
const verification = await verifyProjectCompletion(projectDir);
|
|
988
|
+
const progress = verification.progress;
|
|
989
|
+
|
|
990
|
+
// Log current progress
|
|
991
|
+
onProgress?.(
|
|
992
|
+
'resume-analysis',
|
|
993
|
+
`Current progress: ${progress.progressSummary}`
|
|
994
|
+
);
|
|
995
|
+
|
|
996
|
+
// Check if actually complete
|
|
997
|
+
if (state.status === 'complete' || state.phase === 'complete') {
|
|
998
|
+
if (verification.isComplete) {
|
|
999
|
+
onProgress?.('resume-complete', 'Project is genuinely complete');
|
|
1000
|
+
return {
|
|
1001
|
+
success: true,
|
|
1002
|
+
state,
|
|
1003
|
+
completedTasks: progress.completedTasks,
|
|
1004
|
+
failedTasks: 0,
|
|
1005
|
+
};
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
// Status says complete but work is incomplete
|
|
1009
|
+
onProgress?.(
|
|
1010
|
+
'resume-mismatch',
|
|
1011
|
+
`Status mismatch detected: status='complete' but ${progress.completedTasks}/${progress.totalTasks} tasks done`
|
|
1012
|
+
);
|
|
1013
|
+
|
|
1014
|
+
// Reset incorrect status
|
|
1015
|
+
state = await updateState(projectDir, {
|
|
1016
|
+
status: 'in-progress',
|
|
1017
|
+
phase: 'execution',
|
|
1018
|
+
error: undefined,
|
|
1019
|
+
});
|
|
1020
|
+
|
|
1021
|
+
onProgress?.(
|
|
1022
|
+
'resume-reset',
|
|
1023
|
+
`Reset project status. Will continue with ${progress.pendingTasks + progress.failedTasks} remaining tasks`
|
|
1024
|
+
);
|
|
453
1025
|
}
|
|
454
1026
|
|
|
455
1027
|
// Reset failed tasks to pending so they can be retried
|
|
456
|
-
if (state.status === 'failed') {
|
|
1028
|
+
if (state.status === 'failed' || progress.failedTasks > 0) {
|
|
457
1029
|
const updatedMilestones = state.milestones.map((m) => ({
|
|
458
1030
|
...m,
|
|
1031
|
+
// Also reset milestone status if needed
|
|
1032
|
+
status: m.tasks.every(t => t.status === 'complete')
|
|
1033
|
+
? 'complete' as const
|
|
1034
|
+
: m.tasks.some(t => t.status === 'complete' || t.status === 'in-progress')
|
|
1035
|
+
? 'in-progress' as const
|
|
1036
|
+
: 'pending' as const,
|
|
459
1037
|
tasks: m.tasks.map((t) =>
|
|
460
1038
|
t.status === 'failed' ? { ...t, status: 'pending' as const, error: undefined } : t
|
|
461
1039
|
),
|
|
462
1040
|
}));
|
|
463
1041
|
|
|
464
|
-
await updateState(
|
|
1042
|
+
await updateState(projectDir, {
|
|
465
1043
|
milestones: updatedMilestones,
|
|
466
|
-
status: '
|
|
1044
|
+
status: 'in-progress',
|
|
467
1045
|
error: undefined,
|
|
468
1046
|
});
|
|
1047
|
+
|
|
1048
|
+
if (progress.failedTasks > 0) {
|
|
1049
|
+
onProgress?.(
|
|
1050
|
+
'resume-retry',
|
|
1051
|
+
`Reset ${progress.failedTasks} failed task(s) to pending for retry`
|
|
1052
|
+
);
|
|
1053
|
+
}
|
|
1054
|
+
}
|
|
1055
|
+
|
|
1056
|
+
// Show what will be worked on
|
|
1057
|
+
if (progress.nextMilestone) {
|
|
1058
|
+
onProgress?.(
|
|
1059
|
+
'resume-next',
|
|
1060
|
+
`Next milestone: ${progress.nextMilestone.name}`
|
|
1061
|
+
);
|
|
1062
|
+
}
|
|
1063
|
+
if (progress.nextTask) {
|
|
1064
|
+
onProgress?.(
|
|
1065
|
+
'resume-next',
|
|
1066
|
+
`Next task: ${progress.nextTask.name} (in ${progress.nextTask.milestone})`
|
|
1067
|
+
);
|
|
469
1068
|
}
|
|
470
1069
|
|
|
471
1070
|
return runExecutionMode(options);
|