popeye-cli 1.0.1 → 1.2.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/.env.example +24 -1
- package/CONTRIBUTING.md +275 -0
- package/OPEN_SOURCE_MANIFESTO.md +172 -0
- package/README.md +832 -123
- package/dist/adapters/claude.d.ts +19 -4
- package/dist/adapters/claude.d.ts.map +1 -1
- package/dist/adapters/claude.js +908 -42
- 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/grok.d.ts +73 -0
- package/dist/adapters/grok.d.ts.map +1 -0
- package/dist/adapters/grok.js +430 -0
- package/dist/adapters/grok.js.map +1 -0
- package/dist/adapters/openai.d.ts +1 -1
- package/dist/adapters/openai.d.ts.map +1 -1
- package/dist/adapters/openai.js +47 -8
- 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/grok.d.ts +73 -0
- package/dist/auth/grok.d.ts.map +1 -0
- package/dist/auth/grok.js +211 -0
- package/dist/auth/grok.js.map +1 -0
- package/dist/auth/index.d.ts +14 -7
- package/dist/auth/index.d.ts.map +1 -1
- package/dist/auth/index.js +41 -6
- 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/commands/auth.d.ts +1 -1
- package/dist/cli/commands/auth.d.ts.map +1 -1
- package/dist/cli/commands/auth.js +79 -8
- package/dist/cli/commands/auth.js.map +1 -1
- package/dist/cli/commands/create.d.ts.map +1 -1
- package/dist/cli/commands/create.js +15 -4
- package/dist/cli/commands/create.js.map +1 -1
- package/dist/cli/interactive.d.ts.map +1 -1
- package/dist/cli/interactive.js +1494 -114
- package/dist/cli/interactive.js.map +1 -1
- package/dist/config/defaults.d.ts +9 -1
- package/dist/config/defaults.d.ts.map +1 -1
- package/dist/config/defaults.js +19 -2
- package/dist/config/defaults.js.map +1 -1
- package/dist/config/index.d.ts +19 -0
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js +33 -1
- package/dist/config/index.js.map +1 -1
- package/dist/config/schema.d.ts +47 -0
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/config/schema.js +29 -1
- package/dist/config/schema.js.map +1 -1
- package/dist/generators/fullstack.d.ts +32 -0
- package/dist/generators/fullstack.d.ts.map +1 -0
- package/dist/generators/fullstack.js +497 -0
- package/dist/generators/fullstack.js.map +1 -0
- package/dist/generators/index.d.ts +4 -3
- package/dist/generators/index.d.ts.map +1 -1
- package/dist/generators/index.js +15 -1
- package/dist/generators/index.js.map +1 -1
- package/dist/generators/python.d.ts +17 -1
- package/dist/generators/python.d.ts.map +1 -1
- package/dist/generators/python.js +34 -20
- package/dist/generators/python.js.map +1 -1
- package/dist/generators/templates/fullstack.d.ts +113 -0
- package/dist/generators/templates/fullstack.d.ts.map +1 -0
- package/dist/generators/templates/fullstack.js +1004 -0
- package/dist/generators/templates/fullstack.js.map +1 -0
- package/dist/generators/typescript.d.ts +19 -1
- package/dist/generators/typescript.d.ts.map +1 -1
- package/dist/generators/typescript.js +37 -20
- 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 +8 -0
- package/dist/types/cli.d.ts.map +1 -1
- package/dist/types/cli.js.map +1 -1
- package/dist/types/consensus.d.ts +186 -4
- package/dist/types/consensus.d.ts.map +1 -1
- package/dist/types/consensus.js +35 -3
- package/dist/types/consensus.js.map +1 -1
- package/dist/types/project.d.ts +76 -0
- package/dist/types/project.d.ts.map +1 -1
- package/dist/types/project.js +1 -1
- package/dist/types/project.js.map +1 -1
- package/dist/types/workflow.d.ts +217 -16
- package/dist/types/workflow.d.ts.map +1 -1
- package/dist/types/workflow.js +40 -1
- 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 +70 -2
- package/dist/workflow/consensus.d.ts.map +1 -1
- package/dist/workflow/consensus.js +872 -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 +80 -3
- package/dist/workflow/plan-mode.d.ts.map +1 -1
- package/dist/workflow/plan-mode.js +767 -49
- package/dist/workflow/plan-mode.js.map +1 -1
- package/dist/workflow/plan-storage.d.ts +386 -0
- package/dist/workflow/plan-storage.d.ts.map +1 -0
- package/dist/workflow/plan-storage.js +878 -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 +386 -0
- package/dist/workflow/task-workflow.js.map +1 -0
- package/dist/workflow/test-runner.d.ts +9 -0
- package/dist/workflow/test-runner.d.ts.map +1 -1
- package/dist/workflow/test-runner.js +101 -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/dist/workflow/workspace-manager.d.ts +342 -0
- package/dist/workflow/workspace-manager.d.ts.map +1 -0
- package/dist/workflow/workspace-manager.js +733 -0
- package/dist/workflow/workspace-manager.js.map +1 -0
- package/package.json +2 -2
- package/src/adapters/claude.ts +1067 -47
- package/src/adapters/gemini.ts +373 -0
- package/src/adapters/grok.ts +492 -0
- package/src/adapters/openai.ts +48 -9
- package/src/auth/claude.ts +120 -78
- package/src/auth/gemini.ts +207 -0
- package/src/auth/grok.ts +255 -0
- package/src/auth/index.ts +47 -9
- package/src/auth/keychain.ts +95 -28
- package/src/auth/openai.ts +29 -36
- package/src/cli/commands/auth.ts +89 -10
- package/src/cli/commands/create.ts +13 -4
- package/src/cli/interactive.ts +1774 -142
- package/src/config/defaults.ts +19 -2
- package/src/config/index.ts +36 -1
- package/src/config/schema.ts +30 -1
- package/src/generators/fullstack.ts +551 -0
- package/src/generators/index.ts +25 -1
- package/src/generators/python.ts +65 -20
- package/src/generators/templates/fullstack.ts +1047 -0
- package/src/generators/typescript.ts +69 -20
- package/src/state/index.ts +713 -4
- package/src/state/registry.ts +278 -0
- package/src/types/cli.ts +8 -0
- package/src/types/consensus.ts +197 -6
- package/src/types/project.ts +82 -1
- package/src/types/workflow.ts +90 -1
- package/src/workflow/auto-fix.ts +340 -0
- package/src/workflow/consensus.ts +1180 -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 +924 -50
- package/src/workflow/plan-storage.ts +1282 -0
- package/src/workflow/project-verification.ts +471 -0
- package/src/workflow/task-workflow.ts +528 -0
- package/src/workflow/test-runner.ts +120 -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/src/workflow/workspace-manager.ts +912 -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,14 +1,180 @@
|
|
|
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
|
*/
|
|
6
|
+
import { promises as fs } from 'node:fs';
|
|
7
|
+
import path from 'node:path';
|
|
5
8
|
import { generateCode } from '../adapters/claude.js';
|
|
6
|
-
import { loadProject, updateState,
|
|
9
|
+
import { loadProject, updateState, setCurrentMilestone, completeProject, verifyProjectCompletion, comprehensiveProjectVerification, } from '../state/index.js';
|
|
7
10
|
import { runTests, testsExist, getTestSummary, } from './test-runner.js';
|
|
11
|
+
import { getWorkflowLogger } from './workflow-logger.js';
|
|
12
|
+
import { buildWithAutoFix } from './auto-fix.js';
|
|
13
|
+
import { runComprehensiveVerification, autoFixIssues } from './project-verification.js';
|
|
14
|
+
import { setupUI } from './ui-setup.js';
|
|
15
|
+
import { designUI, saveUISpecification, loadUISpecification } from './ui-designer.js';
|
|
8
16
|
/**
|
|
9
17
|
* Maximum number of retries for failed tests
|
|
10
18
|
*/
|
|
11
19
|
const DEFAULT_MAX_RETRIES = 3;
|
|
20
|
+
/**
|
|
21
|
+
* Generate a comprehensive README.md for the completed project
|
|
22
|
+
* This provides users with setup and run instructions
|
|
23
|
+
*
|
|
24
|
+
* @param projectDir - Project directory
|
|
25
|
+
* @param state - Project state
|
|
26
|
+
* @returns Path to generated README or error
|
|
27
|
+
*/
|
|
28
|
+
async function generateProjectReadme(projectDir, state) {
|
|
29
|
+
try {
|
|
30
|
+
const readmePath = path.join(projectDir, 'README.md');
|
|
31
|
+
// Extract features from completed milestones
|
|
32
|
+
const features = state.milestones
|
|
33
|
+
.filter(m => m.status === 'complete')
|
|
34
|
+
.map(m => ({
|
|
35
|
+
name: m.name,
|
|
36
|
+
description: m.description,
|
|
37
|
+
tasks: m.tasks.filter(t => t.status === 'complete').map(t => t.name),
|
|
38
|
+
}));
|
|
39
|
+
// Determine run commands based on language
|
|
40
|
+
const isTypeScript = state.language === 'typescript';
|
|
41
|
+
const installCmd = isTypeScript ? 'npm install' : 'pip install -r requirements.txt';
|
|
42
|
+
const devCmd = isTypeScript ? 'npm run dev' : 'python src/main.py';
|
|
43
|
+
const testCmd = isTypeScript ? 'npm test' : 'pytest tests/ -v';
|
|
44
|
+
const buildCmd = isTypeScript ? 'npm run build' : 'python -m py_compile src/**/*.py';
|
|
45
|
+
// Build README content
|
|
46
|
+
const readmeContent = `# ${state.name}
|
|
47
|
+
|
|
48
|
+
${state.specification ? extractDescriptionFromSpec(state.specification) : 'A project generated by Popeye CLI.'}
|
|
49
|
+
|
|
50
|
+
## Features
|
|
51
|
+
|
|
52
|
+
${features.map(f => `### ${f.name}
|
|
53
|
+
${f.description || ''}
|
|
54
|
+
${f.tasks.length > 0 ? f.tasks.map(t => `- ${t}`).join('\n') : ''}`).join('\n\n')}
|
|
55
|
+
|
|
56
|
+
## Prerequisites
|
|
57
|
+
|
|
58
|
+
${isTypeScript ? `- Node.js 18.0 or higher
|
|
59
|
+
- npm 8.0 or higher` : `- Python 3.9 or higher
|
|
60
|
+
- pip (Python package manager)`}
|
|
61
|
+
|
|
62
|
+
## Installation
|
|
63
|
+
|
|
64
|
+
\`\`\`bash
|
|
65
|
+
# Clone the repository (if applicable)
|
|
66
|
+
cd ${state.name}
|
|
67
|
+
|
|
68
|
+
# Install dependencies
|
|
69
|
+
${installCmd}
|
|
70
|
+
\`\`\`
|
|
71
|
+
|
|
72
|
+
## Environment Setup
|
|
73
|
+
|
|
74
|
+
1. Copy the example environment file:
|
|
75
|
+
\`\`\`bash
|
|
76
|
+
cp .env.example .env
|
|
77
|
+
\`\`\`
|
|
78
|
+
|
|
79
|
+
2. Edit \`.env\` and fill in the required values.
|
|
80
|
+
|
|
81
|
+
## Running the Application
|
|
82
|
+
|
|
83
|
+
### Development Mode
|
|
84
|
+
|
|
85
|
+
\`\`\`bash
|
|
86
|
+
${devCmd}
|
|
87
|
+
\`\`\`
|
|
88
|
+
|
|
89
|
+
### Running Tests
|
|
90
|
+
|
|
91
|
+
\`\`\`bash
|
|
92
|
+
${testCmd}
|
|
93
|
+
\`\`\`
|
|
94
|
+
|
|
95
|
+
### Build for Production
|
|
96
|
+
|
|
97
|
+
\`\`\`bash
|
|
98
|
+
${buildCmd}
|
|
99
|
+
\`\`\`
|
|
100
|
+
|
|
101
|
+
${isTypeScript ? `### Start Production Server
|
|
102
|
+
|
|
103
|
+
\`\`\`bash
|
|
104
|
+
npm start
|
|
105
|
+
\`\`\`
|
|
106
|
+
` : ''}
|
|
107
|
+
## Project Structure
|
|
108
|
+
|
|
109
|
+
\`\`\`
|
|
110
|
+
${state.name}/
|
|
111
|
+
├── src/ # Source code
|
|
112
|
+
${isTypeScript ? `│ └── index.ts # Main entry point` : `│ ├── __init__.py
|
|
113
|
+
│ └── main.py # Main entry point`}
|
|
114
|
+
├── tests/ # Test files
|
|
115
|
+
├── docs/ # Documentation
|
|
116
|
+
│ ├── PLAN.md # Development plan
|
|
117
|
+
│ └── WORKFLOW_LOG.md # Execution log
|
|
118
|
+
${isTypeScript ? `├── package.json # Dependencies and scripts
|
|
119
|
+
├── tsconfig.json # TypeScript configuration` : `├── requirements.txt # Python dependencies
|
|
120
|
+
├── pyproject.toml # Project configuration`}
|
|
121
|
+
├── .env.example # Environment template
|
|
122
|
+
├── .gitignore
|
|
123
|
+
├── Dockerfile
|
|
124
|
+
└── README.md
|
|
125
|
+
\`\`\`
|
|
126
|
+
|
|
127
|
+
## Development
|
|
128
|
+
|
|
129
|
+
This project was generated using [Popeye CLI](https://github.com/your-org/popeye-cli), an autonomous code generation tool.
|
|
130
|
+
|
|
131
|
+
### Development Plan
|
|
132
|
+
|
|
133
|
+
See [docs/PLAN.md](docs/PLAN.md) for the complete development plan used to build this project.
|
|
134
|
+
|
|
135
|
+
### Workflow Log
|
|
136
|
+
|
|
137
|
+
See [docs/WORKFLOW_LOG.md](docs/WORKFLOW_LOG.md) for detailed execution logs.
|
|
138
|
+
|
|
139
|
+
## License
|
|
140
|
+
|
|
141
|
+
MIT
|
|
142
|
+
`;
|
|
143
|
+
await fs.writeFile(readmePath, readmeContent, 'utf-8');
|
|
144
|
+
return { success: true, path: readmePath };
|
|
145
|
+
}
|
|
146
|
+
catch (error) {
|
|
147
|
+
return {
|
|
148
|
+
success: false,
|
|
149
|
+
error: error instanceof Error ? error.message : 'Failed to generate README',
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Extract a brief description from the specification
|
|
155
|
+
*
|
|
156
|
+
* @param spec - Full specification text
|
|
157
|
+
* @returns Brief description (first paragraph or summary)
|
|
158
|
+
*/
|
|
159
|
+
function extractDescriptionFromSpec(spec) {
|
|
160
|
+
// Try to find a summary section
|
|
161
|
+
const summaryMatch = spec.match(/(?:##?\s*(?:Summary|Overview|Description)[\s\S]*?\n)([\s\S]*?)(?:\n##|\n\n##|$)/i);
|
|
162
|
+
if (summaryMatch) {
|
|
163
|
+
const summary = summaryMatch[1].trim();
|
|
164
|
+
if (summary.length > 0 && summary.length < 500) {
|
|
165
|
+
return summary;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
// Fall back to first paragraph
|
|
169
|
+
const lines = spec.split('\n').filter(line => line.trim() && !line.startsWith('#'));
|
|
170
|
+
const firstParagraph = lines.slice(0, 3).join(' ').trim();
|
|
171
|
+
if (firstParagraph.length > 500) {
|
|
172
|
+
return firstParagraph.slice(0, 497) + '...';
|
|
173
|
+
}
|
|
174
|
+
return firstParagraph || 'A project generated by Popeye CLI.';
|
|
175
|
+
}
|
|
176
|
+
// Note: runCommand and runFinalBuildVerification replaced by buildWithAutoFix from auto-fix.ts
|
|
177
|
+
// which provides automatic error fixing capabilities
|
|
12
178
|
/**
|
|
13
179
|
* Build the execution context for a task
|
|
14
180
|
*
|
|
@@ -17,7 +183,7 @@ const DEFAULT_MAX_RETRIES = 3;
|
|
|
17
183
|
* @param task - Current task
|
|
18
184
|
* @returns Context string for code generation
|
|
19
185
|
*/
|
|
20
|
-
function buildTaskContext(state, milestone, _task) {
|
|
186
|
+
function buildTaskContext(state, milestone, _task, uiDesignContext) {
|
|
21
187
|
const lines = [];
|
|
22
188
|
lines.push(`## Project: ${state.name}`);
|
|
23
189
|
lines.push(`Language: ${state.language}`);
|
|
@@ -32,6 +198,11 @@ function buildTaskContext(state, milestone, _task) {
|
|
|
32
198
|
lines.push(state.plan.slice(0, 2000));
|
|
33
199
|
lines.push('');
|
|
34
200
|
}
|
|
201
|
+
// Include UI design context if available
|
|
202
|
+
if (uiDesignContext) {
|
|
203
|
+
lines.push(uiDesignContext);
|
|
204
|
+
lines.push('');
|
|
205
|
+
}
|
|
35
206
|
lines.push('## Current Milestone');
|
|
36
207
|
lines.push(`Name: ${milestone.name}`);
|
|
37
208
|
lines.push(`Description: ${milestone.description}`);
|
|
@@ -53,9 +224,10 @@ function buildTaskContext(state, milestone, _task) {
|
|
|
53
224
|
* @param task - The task to execute
|
|
54
225
|
* @param context - Execution context
|
|
55
226
|
* @param projectDir - Project directory
|
|
227
|
+
* @param onProgress - Progress callback for Claude activity
|
|
56
228
|
* @returns Execution result
|
|
57
229
|
*/
|
|
58
|
-
export async function executeTask(task, context, projectDir) {
|
|
230
|
+
export async function executeTask(task, context, projectDir, onProgress) {
|
|
59
231
|
const prompt = `
|
|
60
232
|
## Task
|
|
61
233
|
${task.name}
|
|
@@ -70,7 +242,7 @@ Please implement this task completely. After implementing:
|
|
|
70
242
|
2. Ensure code follows best practices
|
|
71
243
|
3. Document any complex logic
|
|
72
244
|
`.trim();
|
|
73
|
-
return generateCode(prompt, context, { cwd: projectDir });
|
|
245
|
+
return generateCode(prompt, context, { cwd: projectDir, onProgress });
|
|
74
246
|
}
|
|
75
247
|
/**
|
|
76
248
|
* Handle a test failure by attempting to fix the code
|
|
@@ -79,9 +251,10 @@ Please implement this task completely. After implementing:
|
|
|
79
251
|
* @param testResult - The test result
|
|
80
252
|
* @param context - Execution context
|
|
81
253
|
* @param projectDir - Project directory
|
|
254
|
+
* @param onProgress - Progress callback for Claude activity
|
|
82
255
|
* @returns Fix attempt result
|
|
83
256
|
*/
|
|
84
|
-
export async function handleTestFailure(task, testResult, context, projectDir) {
|
|
257
|
+
export async function handleTestFailure(task, testResult, context, projectDir, onProgress) {
|
|
85
258
|
const prompt = `
|
|
86
259
|
## Test Failure Fix Required
|
|
87
260
|
|
|
@@ -103,7 +276,7 @@ Please:
|
|
|
103
276
|
2. Fix the code to make all tests pass
|
|
104
277
|
3. Do NOT modify the tests unless they are incorrect
|
|
105
278
|
`.trim();
|
|
106
|
-
return generateCode(prompt, context, { cwd: projectDir });
|
|
279
|
+
return generateCode(prompt, context, { cwd: projectDir, onProgress });
|
|
107
280
|
}
|
|
108
281
|
/**
|
|
109
282
|
* Execute a task with retry logic for test failures
|
|
@@ -115,11 +288,27 @@ Please:
|
|
|
115
288
|
* @returns Task execution result
|
|
116
289
|
*/
|
|
117
290
|
async function executeTaskWithRetry(milestone, task, state, options) {
|
|
118
|
-
const { projectDir, maxRetries = DEFAULT_MAX_RETRIES, onTestResult, } = options;
|
|
119
|
-
|
|
291
|
+
const { projectDir, maxRetries = DEFAULT_MAX_RETRIES, onTestResult, onProgress, } = options;
|
|
292
|
+
// Load UI design context if available
|
|
293
|
+
let uiDesignContext;
|
|
294
|
+
try {
|
|
295
|
+
const uiSpec = await loadUISpecification(projectDir);
|
|
296
|
+
if (uiSpec) {
|
|
297
|
+
const { generateDesignSystemPrompt } = await import('./ui-designer.js');
|
|
298
|
+
uiDesignContext = generateDesignSystemPrompt(uiSpec);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
catch {
|
|
302
|
+
// UI spec not available, continue without it
|
|
303
|
+
}
|
|
304
|
+
const context = buildTaskContext(state, milestone, task, uiDesignContext);
|
|
120
305
|
let retries = 0;
|
|
306
|
+
// Create a progress handler that prefixes messages with [claude]
|
|
307
|
+
const claudeProgress = onProgress
|
|
308
|
+
? (msg) => onProgress('claude', msg)
|
|
309
|
+
: undefined;
|
|
121
310
|
// Execute the task
|
|
122
|
-
const execResult = await executeTask(task, context, projectDir);
|
|
311
|
+
const execResult = await executeTask(task, context, projectDir, claudeProgress);
|
|
123
312
|
if (!execResult.success) {
|
|
124
313
|
return {
|
|
125
314
|
success: false,
|
|
@@ -187,17 +376,27 @@ async function executeTaskWithRetry(milestone, task, state, options) {
|
|
|
187
376
|
};
|
|
188
377
|
}
|
|
189
378
|
/**
|
|
190
|
-
* Run execution mode for a project
|
|
379
|
+
* Run execution mode for a project using hierarchical consensus workflow
|
|
380
|
+
* Each milestone and task goes through: Plan → Consensus → Implement → Test → Review
|
|
191
381
|
*
|
|
192
382
|
* @param options - Execution options
|
|
193
383
|
* @returns Execution mode result
|
|
194
384
|
*/
|
|
195
385
|
export async function runExecutionMode(options) {
|
|
196
386
|
const { projectDir, onProgress, onTaskStart, onTaskComplete, } = options;
|
|
387
|
+
// Initialize workflow logger
|
|
388
|
+
const logger = getWorkflowLogger(projectDir);
|
|
197
389
|
let completedTasks = 0;
|
|
198
390
|
let failedTasks = 0;
|
|
391
|
+
let completedMilestones = 0;
|
|
199
392
|
try {
|
|
200
393
|
let state = await loadProject(projectDir);
|
|
394
|
+
await logger.stageStart('execution', 'Execution Mode started', {
|
|
395
|
+
projectName: state.name,
|
|
396
|
+
phase: state.phase,
|
|
397
|
+
totalMilestones: state.milestones.length,
|
|
398
|
+
totalTasks: state.milestones.reduce((sum, m) => sum + m.tasks.length, 0),
|
|
399
|
+
});
|
|
201
400
|
// Ensure we're in execution phase
|
|
202
401
|
if (state.phase !== 'execution') {
|
|
203
402
|
return {
|
|
@@ -210,58 +409,308 @@ export async function runExecutionMode(options) {
|
|
|
210
409
|
}
|
|
211
410
|
// Update status
|
|
212
411
|
state = await updateState(projectDir, { status: 'in-progress' });
|
|
213
|
-
onProgress?.('execution-start', 'Starting execution mode...');
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
412
|
+
onProgress?.('execution-start', 'Starting hierarchical execution mode...');
|
|
413
|
+
onProgress?.('execution-start', `Processing ${state.milestones.length} milestones with per-task consensus`);
|
|
414
|
+
// Import milestone workflow dynamically to avoid circular dependencies
|
|
415
|
+
const { runMilestoneWorkflow } = await import('./milestone-workflow.js');
|
|
416
|
+
// Process each milestone
|
|
417
|
+
for (const milestone of state.milestones) {
|
|
418
|
+
// Skip completed milestones
|
|
419
|
+
if (milestone.status === 'complete' && milestone.completionApproved) {
|
|
420
|
+
onProgress?.('milestone-skip', `Skipping completed milestone: ${milestone.name}`);
|
|
421
|
+
completedMilestones++;
|
|
422
|
+
completedTasks += milestone.tasks.filter(t => t.status === 'complete').length;
|
|
423
|
+
continue;
|
|
424
|
+
}
|
|
425
|
+
// Set current milestone
|
|
219
426
|
state = await setCurrentMilestone(projectDir, milestone.id);
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
427
|
+
onProgress?.('milestone-start', `Starting milestone: ${milestone.name} (${milestone.tasks.length} tasks)`);
|
|
428
|
+
await logger.stageStart('milestone', `Starting milestone: ${milestone.name}`, {
|
|
429
|
+
milestoneId: milestone.id,
|
|
430
|
+
milestoneName: milestone.name,
|
|
431
|
+
taskCount: milestone.tasks.length,
|
|
432
|
+
taskNames: milestone.tasks.map(t => t.name),
|
|
433
|
+
});
|
|
434
|
+
// Run the complete milestone workflow
|
|
435
|
+
const milestoneResult = await runMilestoneWorkflow(milestone, {
|
|
436
|
+
projectDir,
|
|
437
|
+
consensusConfig: options.consensusConfig,
|
|
438
|
+
onProgress,
|
|
439
|
+
onTaskStart: (task) => {
|
|
440
|
+
if (onTaskStart) {
|
|
441
|
+
onTaskStart(milestone, task);
|
|
442
|
+
}
|
|
443
|
+
},
|
|
444
|
+
onTaskComplete: (task, success) => {
|
|
445
|
+
if (success) {
|
|
446
|
+
completedTasks++;
|
|
447
|
+
}
|
|
448
|
+
else {
|
|
449
|
+
failedTasks++;
|
|
450
|
+
}
|
|
451
|
+
if (onTaskComplete) {
|
|
452
|
+
onTaskComplete(milestone, task, success);
|
|
453
|
+
}
|
|
454
|
+
},
|
|
455
|
+
});
|
|
456
|
+
if (milestoneResult.success) {
|
|
457
|
+
completedMilestones++;
|
|
458
|
+
onProgress?.('milestone-complete', `Milestone complete: ${milestone.name} (Score: ${milestoneResult.completionConsensus?.finalScore}%)`);
|
|
459
|
+
await logger.stageComplete('milestone', `Milestone completed: ${milestone.name}`, {
|
|
460
|
+
milestoneId: milestone.id,
|
|
461
|
+
milestoneName: milestone.name,
|
|
462
|
+
consensusScore: milestoneResult.completionConsensus?.finalScore,
|
|
463
|
+
tasksCompleted: milestone.tasks.filter(t => t.status === 'complete').length,
|
|
464
|
+
});
|
|
226
465
|
}
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
466
|
+
else {
|
|
467
|
+
onProgress?.('milestone-failed', `Milestone failed: ${milestone.name} - ${milestoneResult.error}`);
|
|
468
|
+
await logger.stageFailed('milestone', `Milestone failed: ${milestone.name}`, milestoneResult.error || 'Unknown error', {
|
|
469
|
+
milestoneId: milestone.id,
|
|
470
|
+
milestoneName: milestone.name,
|
|
471
|
+
});
|
|
472
|
+
// Reload state to get latest
|
|
473
|
+
state = await loadProject(projectDir);
|
|
474
|
+
return {
|
|
475
|
+
success: false,
|
|
476
|
+
state,
|
|
477
|
+
completedTasks,
|
|
478
|
+
failedTasks,
|
|
479
|
+
error: `Milestone "${milestone.name}" failed: ${milestoneResult.error}`,
|
|
480
|
+
};
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
// ============================================
|
|
484
|
+
// UI SETUP PHASE
|
|
485
|
+
// ============================================
|
|
486
|
+
onProgress?.('ui-setup', 'Running UI setup and design system configuration...');
|
|
487
|
+
await logger.stageStart('ui-setup', 'Starting UI setup');
|
|
488
|
+
// Check if this is a frontend project
|
|
489
|
+
const frontendDir = path.join(projectDir, 'packages', 'frontend');
|
|
490
|
+
const hasFrontend = await fs.access(frontendDir).then(() => true).catch(() => false);
|
|
491
|
+
if (hasFrontend) {
|
|
492
|
+
try {
|
|
493
|
+
// Load or generate UI specification
|
|
494
|
+
let uiSpec = await loadUISpecification(projectDir);
|
|
495
|
+
if (!uiSpec && state.idea) {
|
|
496
|
+
onProgress?.('ui-setup', 'Designing UI from project idea...');
|
|
497
|
+
uiSpec = await designUI(state.idea, (msg) => onProgress?.('ui-setup', msg));
|
|
498
|
+
await saveUISpecification(projectDir, uiSpec);
|
|
499
|
+
onProgress?.('ui-setup', `UI design complete: ${uiSpec.themeName} theme selected`);
|
|
500
|
+
}
|
|
501
|
+
// Run UI setup to install components and configure styling
|
|
502
|
+
onProgress?.('ui-setup', 'Setting up component library and styling...');
|
|
503
|
+
const uiResult = await setupUI(projectDir, {
|
|
504
|
+
theme: uiSpec?.themeName || 'modern',
|
|
505
|
+
idea: state.idea,
|
|
506
|
+
}, (msg) => onProgress?.('ui-setup', msg));
|
|
507
|
+
if (uiResult.success) {
|
|
508
|
+
onProgress?.('ui-setup', `UI setup complete: ${uiResult.componentsInstalled.length} components installed`);
|
|
509
|
+
await logger.success('ui-setup', 'ui_setup_complete', 'UI setup completed successfully', {
|
|
510
|
+
theme: uiResult.theme,
|
|
511
|
+
components: uiResult.componentsInstalled,
|
|
512
|
+
});
|
|
513
|
+
}
|
|
514
|
+
else {
|
|
515
|
+
onProgress?.('ui-setup', `UI setup warning: ${uiResult.error}`);
|
|
516
|
+
await logger.warn('ui-setup', 'ui_setup_warning', 'UI setup had issues', {
|
|
517
|
+
error: uiResult.error,
|
|
518
|
+
});
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
catch (uiError) {
|
|
522
|
+
// Non-blocking - UI setup failures shouldn't stop the project
|
|
523
|
+
onProgress?.('ui-setup', `UI setup skipped: ${uiError instanceof Error ? uiError.message : 'Unknown error'}`);
|
|
524
|
+
await logger.warn('ui-setup', 'ui_setup_skipped', 'UI setup was skipped', {
|
|
525
|
+
error: uiError instanceof Error ? uiError.message : 'Unknown error',
|
|
526
|
+
});
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
else {
|
|
530
|
+
onProgress?.('ui-setup', 'No frontend detected, skipping UI setup');
|
|
531
|
+
}
|
|
532
|
+
// ============================================
|
|
533
|
+
// FINAL VERIFICATION PHASE
|
|
534
|
+
// ============================================
|
|
535
|
+
onProgress?.('verification', 'Running final verification...');
|
|
536
|
+
await logger.stageStart('verification', 'Starting final verification');
|
|
537
|
+
// Reload state to get latest
|
|
538
|
+
state = await loadProject(projectDir);
|
|
539
|
+
// Verify all milestones are complete
|
|
540
|
+
const incompleteMilestones = state.milestones.filter(m => m.status !== 'complete');
|
|
541
|
+
if (incompleteMilestones.length > 0) {
|
|
542
|
+
const incompleteNames = incompleteMilestones.map(m => m.name).join(', ');
|
|
543
|
+
onProgress?.('verification-warning', `Warning: ${incompleteMilestones.length} milestone(s) not marked complete: ${incompleteNames}`);
|
|
544
|
+
}
|
|
545
|
+
// Verify all tasks are complete
|
|
546
|
+
const allTasks = state.milestones.flatMap(m => m.tasks);
|
|
547
|
+
const incompleteTasks = allTasks.filter(t => t.status !== 'complete');
|
|
548
|
+
if (incompleteTasks.length > 0) {
|
|
549
|
+
const incompleteTaskNames = incompleteTasks.slice(0, 5).map(t => t.name).join(', ');
|
|
550
|
+
onProgress?.('verification-warning', `Warning: ${incompleteTasks.length} task(s) not complete: ${incompleteTaskNames}${incompleteTasks.length > 5 ? '...' : ''}`);
|
|
551
|
+
}
|
|
552
|
+
// Run final build verification with auto-fix
|
|
553
|
+
onProgress?.('verification', 'Running final build verification...');
|
|
554
|
+
const buildResult = await buildWithAutoFix(projectDir, state.language, 3, // max fix attempts
|
|
555
|
+
(msg) => onProgress?.('verification', `[auto-fix] ${msg}`));
|
|
556
|
+
if (!buildResult.success) {
|
|
557
|
+
onProgress?.('verification-error', `Build verification failed${buildResult.autoFixed ? ' (after auto-fix attempts)' : ''}: Build errors remain`);
|
|
558
|
+
await logger.error('verification', 'build_failed', 'Build verification failed', {
|
|
559
|
+
output: buildResult.output.slice(0, 2000),
|
|
560
|
+
autoFixed: buildResult.autoFixed,
|
|
561
|
+
});
|
|
562
|
+
// Block completion if build fails
|
|
563
|
+
onProgress?.('verification-error', 'BLOCKING: Cannot complete project with build errors. Please fix manually and resume.');
|
|
564
|
+
return {
|
|
565
|
+
success: false,
|
|
566
|
+
state,
|
|
567
|
+
completedTasks,
|
|
568
|
+
failedTasks,
|
|
569
|
+
error: 'Build verification failed - project not complete',
|
|
570
|
+
};
|
|
571
|
+
}
|
|
572
|
+
else {
|
|
573
|
+
onProgress?.('verification', `Build verification passed${buildResult.autoFixed ? ' (after auto-fix)' : ''}`);
|
|
574
|
+
await logger.success('verification', 'build_passed', 'Build verification passed', {
|
|
575
|
+
autoFixed: buildResult.autoFixed,
|
|
576
|
+
});
|
|
577
|
+
}
|
|
578
|
+
// Run final test verification
|
|
579
|
+
const hasTests = await testsExist(projectDir, state.language);
|
|
580
|
+
if (hasTests) {
|
|
581
|
+
onProgress?.('verification', 'Running final test verification...');
|
|
582
|
+
const testResult = await runTests(projectDir, state.language);
|
|
583
|
+
if (!testResult.success) {
|
|
584
|
+
onProgress?.('verification-warning', `Final test verification failed: ${testResult.failed} test(s) failed`);
|
|
585
|
+
await logger.warn('verification', 'tests_failed', 'Final test verification failed', {
|
|
586
|
+
passed: testResult.passed,
|
|
587
|
+
failed: testResult.failed,
|
|
588
|
+
total: testResult.total,
|
|
589
|
+
failedTests: testResult.failedTests,
|
|
232
590
|
});
|
|
233
|
-
completedTasks++;
|
|
234
|
-
onProgress?.('task-complete', `Completed: ${task.name}${result.testResult ? ` (${getTestSummary(result.testResult)})` : ''}`);
|
|
235
591
|
}
|
|
236
592
|
else {
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
593
|
+
onProgress?.('verification', `All tests passed: ${testResult.passed}/${testResult.total}`);
|
|
594
|
+
await logger.success('verification', 'tests_passed', 'All tests passed', {
|
|
595
|
+
passed: testResult.passed,
|
|
596
|
+
total: testResult.total,
|
|
240
597
|
});
|
|
241
|
-
failedTasks++;
|
|
242
|
-
onProgress?.('task-failed', `Failed: ${task.name} - ${result.error}`);
|
|
243
598
|
}
|
|
244
|
-
|
|
245
|
-
|
|
599
|
+
}
|
|
600
|
+
// Run comprehensive code quality verification
|
|
601
|
+
onProgress?.('verification', 'Running code quality verification...');
|
|
602
|
+
const codeVerification = await comprehensiveProjectVerification(projectDir);
|
|
603
|
+
// Log comprehensive verification results
|
|
604
|
+
await logger.info('verification', 'code_quality_check', 'Code quality verification results', {
|
|
605
|
+
totalSourceFiles: codeVerification.codeVerification.totalSourceFiles,
|
|
606
|
+
totalLinesOfCode: codeVerification.codeVerification.totalLinesOfCode,
|
|
607
|
+
hasMainEntryPoint: codeVerification.codeVerification.hasMainEntryPoint,
|
|
608
|
+
hasTests: codeVerification.codeVerification.hasTests,
|
|
609
|
+
hasSubstantiveCode: codeVerification.codeVerification.hasSubstantiveCode,
|
|
610
|
+
issues: codeVerification.codeVerification.issues,
|
|
611
|
+
warnings: codeVerification.codeVerification.warnings,
|
|
612
|
+
});
|
|
613
|
+
// Display code quality results
|
|
614
|
+
onProgress?.('verification', `Code: ${codeVerification.codeVerification.totalSourceFiles} files, ` +
|
|
615
|
+
`${codeVerification.codeVerification.totalLinesOfCode} lines of code`);
|
|
616
|
+
if (codeVerification.codeVerification.issues.length > 0) {
|
|
617
|
+
for (const issue of codeVerification.codeVerification.issues) {
|
|
618
|
+
onProgress?.('verification-warning', `Code Issue: ${issue}`);
|
|
246
619
|
}
|
|
247
|
-
// Get next task
|
|
248
|
-
nextTask = await getNextTask(projectDir);
|
|
249
620
|
}
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
621
|
+
if (codeVerification.codeVerification.warnings.length > 0) {
|
|
622
|
+
for (const warning of codeVerification.codeVerification.warnings.slice(0, 3)) {
|
|
623
|
+
onProgress?.('verification-info', `Code Warning: ${warning}`);
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
// Summary
|
|
627
|
+
onProgress?.('verification-summary', `Verification complete: ${completedMilestones}/${state.milestones.length} milestones, ` +
|
|
628
|
+
`${completedTasks}/${allTasks.length} tasks, ` +
|
|
629
|
+
`${codeVerification.codeVerification.totalLinesOfCode} lines of code`);
|
|
630
|
+
// Check if genuinely complete
|
|
631
|
+
if (!codeVerification.isGenuinelyComplete) {
|
|
632
|
+
onProgress?.('verification-warning', 'WARNING: Project may not be genuinely complete. Code verification found issues.');
|
|
633
|
+
await logger.warn('verification', 'incomplete_code', 'Project may not be genuinely complete', {
|
|
634
|
+
taskComplete: codeVerification.taskVerification.isComplete,
|
|
635
|
+
codeQualityPassed: codeVerification.codeVerification.passed,
|
|
636
|
+
summary: codeVerification.summary,
|
|
637
|
+
});
|
|
638
|
+
}
|
|
639
|
+
// ============================================
|
|
640
|
+
// COMPREHENSIVE PROJECT VERIFICATION
|
|
641
|
+
// ============================================
|
|
642
|
+
onProgress?.('verification', 'Running comprehensive project verification...');
|
|
643
|
+
const comprehensiveReport = await runComprehensiveVerification(projectDir, (msg) => onProgress?.('verification', msg));
|
|
644
|
+
// Log verification results
|
|
645
|
+
for (const result of comprehensiveReport.results) {
|
|
646
|
+
if (!result.passed) {
|
|
647
|
+
const level = result.severity === 'error' ? 'verification-error' : 'verification-warning';
|
|
648
|
+
onProgress?.(level, `[${result.category}] ${result.message}`);
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
// Auto-fix fixable issues
|
|
652
|
+
if (comprehensiveReport.failedChecks > 0) {
|
|
653
|
+
const fixableCount = comprehensiveReport.results.filter(r => !r.passed && r.autoFixable).length;
|
|
654
|
+
if (fixableCount > 0) {
|
|
655
|
+
onProgress?.('verification', `Attempting to auto-fix ${fixableCount} issue(s)...`);
|
|
656
|
+
const fixed = await autoFixIssues(comprehensiveReport, (msg) => onProgress?.('verification', msg));
|
|
657
|
+
onProgress?.('verification', `Auto-fixed ${fixed} issue(s)`);
|
|
658
|
+
// Re-run verification after fixes
|
|
659
|
+
if (fixed > 0) {
|
|
660
|
+
onProgress?.('verification', 'Re-running verification after fixes...');
|
|
661
|
+
const reVerifyReport = await runComprehensiveVerification(projectDir);
|
|
662
|
+
if (reVerifyReport.failedChecks > 0) {
|
|
663
|
+
onProgress?.('verification-error', `${reVerifyReport.failedChecks} critical issue(s) remain after auto-fix`);
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
// Block completion if critical issues remain
|
|
669
|
+
if (comprehensiveReport.criticalIssues.length > 0) {
|
|
670
|
+
onProgress?.('verification-error', `BLOCKING: ${comprehensiveReport.criticalIssues.length} critical issue(s) found`);
|
|
671
|
+
for (const issue of comprehensiveReport.criticalIssues) {
|
|
672
|
+
onProgress?.('verification-error', ` - ${issue}`);
|
|
673
|
+
}
|
|
674
|
+
await logger.error('verification', 'critical_issues', 'Critical verification issues found', {
|
|
675
|
+
issues: comprehensiveReport.criticalIssues,
|
|
676
|
+
});
|
|
254
677
|
return {
|
|
255
678
|
success: false,
|
|
256
679
|
state,
|
|
257
680
|
completedTasks,
|
|
258
681
|
failedTasks,
|
|
259
|
-
error: `
|
|
682
|
+
error: `Project verification failed: ${comprehensiveReport.criticalIssues.length} critical issues`,
|
|
260
683
|
};
|
|
261
684
|
}
|
|
262
|
-
|
|
685
|
+
onProgress?.('verification', `Verification complete: ${comprehensiveReport.passedChecks}/${comprehensiveReport.totalChecks} checks passed`);
|
|
686
|
+
// ============================================
|
|
687
|
+
// GENERATE PROJECT README
|
|
688
|
+
// ============================================
|
|
689
|
+
onProgress?.('readme', 'Generating project README with setup and run instructions...');
|
|
690
|
+
const readmeResult = await generateProjectReadme(projectDir, state);
|
|
691
|
+
if (readmeResult.success) {
|
|
692
|
+
onProgress?.('readme', `README.md generated successfully`);
|
|
693
|
+
await logger.success('completion', 'readme_generated', 'Project README generated', {
|
|
694
|
+
path: readmeResult.path,
|
|
695
|
+
});
|
|
696
|
+
}
|
|
697
|
+
else {
|
|
698
|
+
onProgress?.('readme-warning', `Failed to generate README: ${readmeResult.error}`);
|
|
699
|
+
await logger.warn('completion', 'readme_failed', 'Failed to generate README', {
|
|
700
|
+
error: readmeResult.error,
|
|
701
|
+
});
|
|
702
|
+
}
|
|
703
|
+
// All milestones complete
|
|
263
704
|
state = await completeProject(projectDir);
|
|
264
|
-
onProgress?.('execution-complete', `Project complete! ${completedTasks} tasks executed successfully.`);
|
|
705
|
+
onProgress?.('execution-complete', `Project complete! ${completedMilestones} milestones, ${completedTasks} tasks executed successfully.`);
|
|
706
|
+
await logger.stageComplete('completion', 'Project execution completed successfully', {
|
|
707
|
+
completedMilestones: completedMilestones,
|
|
708
|
+
totalMilestones: state.milestones.length,
|
|
709
|
+
completedTasks: completedTasks,
|
|
710
|
+
totalTasks: state.milestones.reduce((sum, m) => sum + m.tasks.length, 0),
|
|
711
|
+
buildPassed: buildResult.success,
|
|
712
|
+
testsPassed: hasTests ? (await runTests(projectDir, state.language)).success : true,
|
|
713
|
+
});
|
|
265
714
|
return {
|
|
266
715
|
success: true,
|
|
267
716
|
state,
|
|
@@ -272,6 +721,12 @@ export async function runExecutionMode(options) {
|
|
|
272
721
|
catch (error) {
|
|
273
722
|
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
274
723
|
onProgress?.('error', errorMessage);
|
|
724
|
+
await logger.stageFailed('execution', 'Execution Mode failed', errorMessage, {
|
|
725
|
+
errorType: error instanceof Error ? error.constructor.name : typeof error,
|
|
726
|
+
completedTasks: completedTasks,
|
|
727
|
+
failedTasks: failedTasks,
|
|
728
|
+
completedMilestones: completedMilestones,
|
|
729
|
+
});
|
|
275
730
|
return {
|
|
276
731
|
success: false,
|
|
277
732
|
state: await loadProject(projectDir).catch(() => ({})),
|
|
@@ -288,27 +743,61 @@ export async function runExecutionMode(options) {
|
|
|
288
743
|
* @returns Execution mode result
|
|
289
744
|
*/
|
|
290
745
|
export async function resumeExecutionMode(options) {
|
|
291
|
-
const
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
746
|
+
const { projectDir, onProgress } = options;
|
|
747
|
+
let state = await loadProject(projectDir);
|
|
748
|
+
// Verify actual completion - don't trust status alone
|
|
749
|
+
const verification = await verifyProjectCompletion(projectDir);
|
|
750
|
+
const progress = verification.progress;
|
|
751
|
+
// Log current progress
|
|
752
|
+
onProgress?.('resume-analysis', `Current progress: ${progress.progressSummary}`);
|
|
753
|
+
// Check if actually complete
|
|
754
|
+
if (state.status === 'complete' || state.phase === 'complete') {
|
|
755
|
+
if (verification.isComplete) {
|
|
756
|
+
onProgress?.('resume-complete', 'Project is genuinely complete');
|
|
757
|
+
return {
|
|
758
|
+
success: true,
|
|
759
|
+
state,
|
|
760
|
+
completedTasks: progress.completedTasks,
|
|
761
|
+
failedTasks: 0,
|
|
762
|
+
};
|
|
763
|
+
}
|
|
764
|
+
// Status says complete but work is incomplete
|
|
765
|
+
onProgress?.('resume-mismatch', `Status mismatch detected: status='complete' but ${progress.completedTasks}/${progress.totalTasks} tasks done`);
|
|
766
|
+
// Reset incorrect status
|
|
767
|
+
state = await updateState(projectDir, {
|
|
768
|
+
status: 'in-progress',
|
|
769
|
+
phase: 'execution',
|
|
770
|
+
error: undefined,
|
|
771
|
+
});
|
|
772
|
+
onProgress?.('resume-reset', `Reset project status. Will continue with ${progress.pendingTasks + progress.failedTasks} remaining tasks`);
|
|
300
773
|
}
|
|
301
774
|
// Reset failed tasks to pending so they can be retried
|
|
302
|
-
if (state.status === 'failed') {
|
|
775
|
+
if (state.status === 'failed' || progress.failedTasks > 0) {
|
|
303
776
|
const updatedMilestones = state.milestones.map((m) => ({
|
|
304
777
|
...m,
|
|
778
|
+
// Also reset milestone status if needed
|
|
779
|
+
status: m.tasks.every(t => t.status === 'complete')
|
|
780
|
+
? 'complete'
|
|
781
|
+
: m.tasks.some(t => t.status === 'complete' || t.status === 'in-progress')
|
|
782
|
+
? 'in-progress'
|
|
783
|
+
: 'pending',
|
|
305
784
|
tasks: m.tasks.map((t) => t.status === 'failed' ? { ...t, status: 'pending', error: undefined } : t),
|
|
306
785
|
}));
|
|
307
|
-
await updateState(
|
|
786
|
+
await updateState(projectDir, {
|
|
308
787
|
milestones: updatedMilestones,
|
|
309
|
-
status: '
|
|
788
|
+
status: 'in-progress',
|
|
310
789
|
error: undefined,
|
|
311
790
|
});
|
|
791
|
+
if (progress.failedTasks > 0) {
|
|
792
|
+
onProgress?.('resume-retry', `Reset ${progress.failedTasks} failed task(s) to pending for retry`);
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
// Show what will be worked on
|
|
796
|
+
if (progress.nextMilestone) {
|
|
797
|
+
onProgress?.('resume-next', `Next milestone: ${progress.nextMilestone.name}`);
|
|
798
|
+
}
|
|
799
|
+
if (progress.nextTask) {
|
|
800
|
+
onProgress?.('resume-next', `Next task: ${progress.nextTask.name} (in ${progress.nextTask.milestone})`);
|
|
312
801
|
}
|
|
313
802
|
return runExecutionMode(options);
|
|
314
803
|
}
|