popeye-cli 1.0.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 +25 -0
- package/.prettierrc +8 -0
- package/README.md +320 -0
- package/dist/adapters/claude.d.ts +82 -0
- package/dist/adapters/claude.d.ts.map +1 -0
- package/dist/adapters/claude.js +230 -0
- package/dist/adapters/claude.js.map +1 -0
- package/dist/adapters/openai.d.ts +48 -0
- package/dist/adapters/openai.d.ts.map +1 -0
- package/dist/adapters/openai.js +257 -0
- package/dist/adapters/openai.js.map +1 -0
- package/dist/auth/claude.d.ts +44 -0
- package/dist/auth/claude.d.ts.map +1 -0
- package/dist/auth/claude.js +139 -0
- package/dist/auth/claude.js.map +1 -0
- package/dist/auth/index.d.ts +61 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +141 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/auth/keychain.d.ts +66 -0
- package/dist/auth/keychain.d.ts.map +1 -0
- package/dist/auth/keychain.js +125 -0
- package/dist/auth/keychain.js.map +1 -0
- package/dist/auth/openai-entry.d.ts +9 -0
- package/dist/auth/openai-entry.d.ts.map +1 -0
- package/dist/auth/openai-entry.js +410 -0
- package/dist/auth/openai-entry.js.map +1 -0
- package/dist/auth/openai.d.ts +71 -0
- package/dist/auth/openai.d.ts.map +1 -0
- package/dist/auth/openai.js +212 -0
- package/dist/auth/openai.js.map +1 -0
- package/dist/auth/server.d.ts +32 -0
- package/dist/auth/server.d.ts.map +1 -0
- package/dist/auth/server.js +213 -0
- package/dist/auth/server.js.map +1 -0
- package/dist/cli/commands/auth.d.ts +10 -0
- package/dist/cli/commands/auth.d.ts.map +1 -0
- package/dist/cli/commands/auth.js +162 -0
- package/dist/cli/commands/auth.js.map +1 -0
- package/dist/cli/commands/config.d.ts +10 -0
- package/dist/cli/commands/config.d.ts.map +1 -0
- package/dist/cli/commands/config.js +215 -0
- package/dist/cli/commands/config.js.map +1 -0
- package/dist/cli/commands/create.d.ts +10 -0
- package/dist/cli/commands/create.d.ts.map +1 -0
- package/dist/cli/commands/create.js +240 -0
- package/dist/cli/commands/create.js.map +1 -0
- package/dist/cli/commands/index.d.ts +10 -0
- package/dist/cli/commands/index.d.ts.map +1 -0
- package/dist/cli/commands/index.js +10 -0
- package/dist/cli/commands/index.js.map +1 -0
- package/dist/cli/commands/resume.d.ts +18 -0
- package/dist/cli/commands/resume.d.ts.map +1 -0
- package/dist/cli/commands/resume.js +241 -0
- package/dist/cli/commands/resume.js.map +1 -0
- package/dist/cli/commands/status.d.ts +18 -0
- package/dist/cli/commands/status.d.ts.map +1 -0
- package/dist/cli/commands/status.js +154 -0
- package/dist/cli/commands/status.js.map +1 -0
- package/dist/cli/index.d.ts +17 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +71 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/interactive.d.ts +9 -0
- package/dist/cli/interactive.d.ts.map +1 -0
- package/dist/cli/interactive.js +330 -0
- package/dist/cli/interactive.js.map +1 -0
- package/dist/cli/output.d.ts +182 -0
- package/dist/cli/output.d.ts.map +1 -0
- package/dist/cli/output.js +355 -0
- package/dist/cli/output.js.map +1 -0
- package/dist/config/defaults.d.ts +57 -0
- package/dist/config/defaults.d.ts.map +1 -0
- package/dist/config/defaults.js +103 -0
- package/dist/config/defaults.js.map +1 -0
- package/dist/config/index.d.ts +138 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +244 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/schema.d.ts +220 -0
- package/dist/config/schema.d.ts.map +1 -0
- package/dist/config/schema.js +141 -0
- package/dist/config/schema.js.map +1 -0
- package/dist/generators/index.d.ts +101 -0
- package/dist/generators/index.d.ts.map +1 -0
- package/dist/generators/index.js +200 -0
- package/dist/generators/index.js.map +1 -0
- package/dist/generators/python.d.ts +48 -0
- package/dist/generators/python.d.ts.map +1 -0
- package/dist/generators/python.js +262 -0
- package/dist/generators/python.js.map +1 -0
- package/dist/generators/templates/index.d.ts +6 -0
- package/dist/generators/templates/index.d.ts.map +1 -0
- package/dist/generators/templates/index.js +6 -0
- package/dist/generators/templates/index.js.map +1 -0
- package/dist/generators/templates/python.d.ts +53 -0
- package/dist/generators/templates/python.d.ts.map +1 -0
- package/dist/generators/templates/python.js +454 -0
- package/dist/generators/templates/python.js.map +1 -0
- package/dist/generators/templates/typescript.d.ts +53 -0
- package/dist/generators/templates/typescript.d.ts.map +1 -0
- package/dist/generators/templates/typescript.js +394 -0
- package/dist/generators/templates/typescript.js.map +1 -0
- package/dist/generators/typescript.d.ts +64 -0
- package/dist/generators/typescript.d.ts.map +1 -0
- package/dist/generators/typescript.js +271 -0
- package/dist/generators/typescript.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -0
- package/dist/state/index.d.ts +168 -0
- package/dist/state/index.d.ts.map +1 -0
- package/dist/state/index.js +338 -0
- package/dist/state/index.js.map +1 -0
- package/dist/state/persistence.d.ts +91 -0
- package/dist/state/persistence.d.ts.map +1 -0
- package/dist/state/persistence.js +201 -0
- package/dist/state/persistence.js.map +1 -0
- package/dist/types/cli.d.ts +132 -0
- package/dist/types/cli.d.ts.map +1 -0
- package/dist/types/cli.js +17 -0
- package/dist/types/cli.js.map +1 -0
- package/dist/types/consensus.d.ts +111 -0
- package/dist/types/consensus.d.ts.map +1 -0
- package/dist/types/consensus.js +29 -0
- package/dist/types/consensus.js.map +1 -0
- package/dist/types/index.d.ts +9 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +13 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/project.d.ts +73 -0
- package/dist/types/project.d.ts.map +1 -0
- package/dist/types/project.js +55 -0
- package/dist/types/project.js.map +1 -0
- package/dist/types/workflow.d.ts +236 -0
- package/dist/types/workflow.d.ts.map +1 -0
- package/dist/types/workflow.js +74 -0
- package/dist/types/workflow.js.map +1 -0
- package/dist/workflow/consensus.d.ts +89 -0
- package/dist/workflow/consensus.d.ts.map +1 -0
- package/dist/workflow/consensus.js +220 -0
- package/dist/workflow/consensus.js.map +1 -0
- package/dist/workflow/execution-mode.d.ts +82 -0
- package/dist/workflow/execution-mode.d.ts.map +1 -0
- package/dist/workflow/execution-mode.js +346 -0
- package/dist/workflow/execution-mode.js.map +1 -0
- package/dist/workflow/index.d.ts +110 -0
- package/dist/workflow/index.d.ts.map +1 -0
- package/dist/workflow/index.js +283 -0
- package/dist/workflow/index.js.map +1 -0
- package/dist/workflow/plan-mode.d.ts +83 -0
- package/dist/workflow/plan-mode.d.ts.map +1 -0
- package/dist/workflow/plan-mode.js +241 -0
- package/dist/workflow/plan-mode.js.map +1 -0
- package/dist/workflow/test-runner.d.ts +87 -0
- package/dist/workflow/test-runner.d.ts.map +1 -0
- package/dist/workflow/test-runner.js +273 -0
- package/dist/workflow/test-runner.js.map +1 -0
- package/eslint.config.js +25 -0
- package/package.json +66 -0
- package/src/adapters/claude.ts +298 -0
- package/src/adapters/openai.ts +300 -0
- package/src/auth/claude.ts +166 -0
- package/src/auth/index.ts +171 -0
- package/src/auth/keychain.ts +138 -0
- package/src/auth/openai-entry.ts +410 -0
- package/src/auth/openai.ts +260 -0
- package/src/auth/server.ts +252 -0
- package/src/cli/commands/auth.ts +194 -0
- package/src/cli/commands/config.ts +241 -0
- package/src/cli/commands/create.ts +308 -0
- package/src/cli/commands/index.ts +10 -0
- package/src/cli/commands/resume.ts +304 -0
- package/src/cli/commands/status.ts +189 -0
- package/src/cli/index.ts +90 -0
- package/src/cli/interactive.ts +418 -0
- package/src/cli/output.ts +410 -0
- package/src/config/defaults.ts +114 -0
- package/src/config/index.ts +315 -0
- package/src/config/schema.ts +164 -0
- package/src/generators/index.ts +251 -0
- package/src/generators/python.ts +318 -0
- package/src/generators/templates/index.ts +6 -0
- package/src/generators/templates/python.ts +465 -0
- package/src/generators/templates/typescript.ts +417 -0
- package/src/generators/typescript.ts +340 -0
- package/src/index.ts +13 -0
- package/src/state/index.ts +454 -0
- package/src/state/persistence.ts +230 -0
- package/src/types/cli.ts +146 -0
- package/src/types/consensus.ts +116 -0
- package/src/types/index.ts +64 -0
- package/src/types/project.ts +85 -0
- package/src/types/workflow.ts +149 -0
- package/src/workflow/consensus.ts +299 -0
- package/src/workflow/execution-mode.ts +517 -0
- package/src/workflow/index.ts +396 -0
- package/src/workflow/plan-mode.ts +356 -0
- package/src/workflow/test-runner.ts +345 -0
- package/tests/adapters/openai.test.ts +145 -0
- package/tests/config/config.test.ts +208 -0
- package/tests/generators/generators.test.ts +185 -0
- package/tests/types/consensus.test.ts +152 -0
- package/tests/types/project.test.ts +134 -0
- package/tests/workflow/consensus.test.ts +221 -0
- package/tests/workflow/test-runner.test.ts +214 -0
- package/tsconfig.json +25 -0
- package/vitest.config.ts +22 -0
|
@@ -0,0 +1,396 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workflow orchestration module
|
|
3
|
+
* Main entry point for managing the complete project workflow
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { ProjectSpec } from '../types/project.js';
|
|
7
|
+
import type { ProjectState, WorkflowPhase } from '../types/workflow.js';
|
|
8
|
+
import type { ConsensusConfig } from '../types/consensus.js';
|
|
9
|
+
import {
|
|
10
|
+
loadProject,
|
|
11
|
+
projectExists,
|
|
12
|
+
getProgress,
|
|
13
|
+
resetToPhase,
|
|
14
|
+
deleteProject,
|
|
15
|
+
} from '../state/index.js';
|
|
16
|
+
import {
|
|
17
|
+
runPlanMode,
|
|
18
|
+
resumePlanMode,
|
|
19
|
+
type PlanModeResult,
|
|
20
|
+
} from './plan-mode.js';
|
|
21
|
+
import {
|
|
22
|
+
runExecutionMode,
|
|
23
|
+
resumeExecutionMode,
|
|
24
|
+
executeSingleTask,
|
|
25
|
+
type ExecutionModeResult,
|
|
26
|
+
type TaskExecutionResult,
|
|
27
|
+
} from './execution-mode.js';
|
|
28
|
+
// Types are re-exported via export * from statements below
|
|
29
|
+
|
|
30
|
+
// Re-export submodules
|
|
31
|
+
export * from './consensus.js';
|
|
32
|
+
export * from './plan-mode.js';
|
|
33
|
+
export * from './execution-mode.js';
|
|
34
|
+
export * from './test-runner.js';
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Workflow options
|
|
38
|
+
*/
|
|
39
|
+
export interface WorkflowOptions {
|
|
40
|
+
projectDir: string;
|
|
41
|
+
consensusConfig?: Partial<ConsensusConfig>;
|
|
42
|
+
maxRetries?: number;
|
|
43
|
+
onProgress?: (phase: string, message: string) => void;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Complete workflow result
|
|
48
|
+
*/
|
|
49
|
+
export interface WorkflowResult {
|
|
50
|
+
success: boolean;
|
|
51
|
+
state: ProjectState;
|
|
52
|
+
planResult?: PlanModeResult;
|
|
53
|
+
executionResult?: ExecutionModeResult;
|
|
54
|
+
error?: string;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Run the complete workflow from idea to deployed code
|
|
59
|
+
*
|
|
60
|
+
* @param spec - Project specification
|
|
61
|
+
* @param options - Workflow options
|
|
62
|
+
* @returns Workflow result
|
|
63
|
+
*/
|
|
64
|
+
export async function runWorkflow(
|
|
65
|
+
spec: ProjectSpec,
|
|
66
|
+
options: WorkflowOptions
|
|
67
|
+
): Promise<WorkflowResult> {
|
|
68
|
+
const { projectDir, consensusConfig, maxRetries, onProgress } = options;
|
|
69
|
+
|
|
70
|
+
try {
|
|
71
|
+
// Phase 1: Plan Mode
|
|
72
|
+
onProgress?.('workflow', 'Starting Plan Mode...');
|
|
73
|
+
|
|
74
|
+
const planResult = await runPlanMode(spec, {
|
|
75
|
+
projectDir,
|
|
76
|
+
consensusConfig,
|
|
77
|
+
onProgress,
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
if (!planResult.success) {
|
|
81
|
+
return {
|
|
82
|
+
success: false,
|
|
83
|
+
state: planResult.state,
|
|
84
|
+
planResult,
|
|
85
|
+
error: planResult.error || 'Plan mode failed to reach consensus',
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Phase 2: Execution Mode
|
|
90
|
+
onProgress?.('workflow', 'Starting Execution Mode...');
|
|
91
|
+
|
|
92
|
+
const executionResult = await runExecutionMode({
|
|
93
|
+
projectDir,
|
|
94
|
+
maxRetries,
|
|
95
|
+
onProgress,
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
return {
|
|
99
|
+
success: executionResult.success,
|
|
100
|
+
state: executionResult.state,
|
|
101
|
+
planResult,
|
|
102
|
+
executionResult,
|
|
103
|
+
error: executionResult.error,
|
|
104
|
+
};
|
|
105
|
+
} catch (error) {
|
|
106
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
107
|
+
|
|
108
|
+
return {
|
|
109
|
+
success: false,
|
|
110
|
+
state: await loadProject(projectDir).catch(() => ({} as ProjectState)),
|
|
111
|
+
error: errorMessage,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Resume an existing workflow from where it left off
|
|
118
|
+
*
|
|
119
|
+
* @param projectDir - Project directory
|
|
120
|
+
* @param options - Workflow options
|
|
121
|
+
* @returns Workflow result
|
|
122
|
+
*/
|
|
123
|
+
export async function resumeWorkflow(
|
|
124
|
+
projectDir: string,
|
|
125
|
+
options: Omit<WorkflowOptions, 'projectDir'>
|
|
126
|
+
): Promise<WorkflowResult> {
|
|
127
|
+
const { consensusConfig, maxRetries, onProgress } = options;
|
|
128
|
+
|
|
129
|
+
try {
|
|
130
|
+
// Check if project exists
|
|
131
|
+
if (!(await projectExists(projectDir))) {
|
|
132
|
+
throw new Error(`No project found at ${projectDir}`);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const state = await loadProject(projectDir);
|
|
136
|
+
|
|
137
|
+
// Determine which phase to resume
|
|
138
|
+
switch (state.phase) {
|
|
139
|
+
case 'plan': {
|
|
140
|
+
onProgress?.('workflow', 'Resuming Plan Mode...');
|
|
141
|
+
|
|
142
|
+
const planResult = await resumePlanMode(projectDir, {
|
|
143
|
+
consensusConfig,
|
|
144
|
+
onProgress,
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
if (!planResult.success) {
|
|
148
|
+
return {
|
|
149
|
+
success: false,
|
|
150
|
+
state: planResult.state,
|
|
151
|
+
planResult,
|
|
152
|
+
error: planResult.error || 'Plan mode failed to reach consensus',
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Continue to execution
|
|
157
|
+
onProgress?.('workflow', 'Starting Execution Mode...');
|
|
158
|
+
|
|
159
|
+
const executionResult = await runExecutionMode({
|
|
160
|
+
projectDir,
|
|
161
|
+
maxRetries,
|
|
162
|
+
onProgress,
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
return {
|
|
166
|
+
success: executionResult.success,
|
|
167
|
+
state: executionResult.state,
|
|
168
|
+
planResult,
|
|
169
|
+
executionResult,
|
|
170
|
+
error: executionResult.error,
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
case 'execution': {
|
|
175
|
+
onProgress?.('workflow', 'Resuming Execution Mode...');
|
|
176
|
+
|
|
177
|
+
const executionResult = await resumeExecutionMode({
|
|
178
|
+
projectDir,
|
|
179
|
+
maxRetries,
|
|
180
|
+
onProgress,
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
return {
|
|
184
|
+
success: executionResult.success,
|
|
185
|
+
state: executionResult.state,
|
|
186
|
+
executionResult,
|
|
187
|
+
error: executionResult.error,
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
case 'complete': {
|
|
192
|
+
onProgress?.('workflow', 'Project already complete');
|
|
193
|
+
return {
|
|
194
|
+
success: true,
|
|
195
|
+
state,
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
default:
|
|
200
|
+
throw new Error(`Unknown phase: ${state.phase}`);
|
|
201
|
+
}
|
|
202
|
+
} catch (error) {
|
|
203
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
204
|
+
|
|
205
|
+
return {
|
|
206
|
+
success: false,
|
|
207
|
+
state: await loadProject(projectDir).catch(() => ({} as ProjectState)),
|
|
208
|
+
error: errorMessage,
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Get workflow status and progress
|
|
215
|
+
*
|
|
216
|
+
* @param projectDir - Project directory
|
|
217
|
+
* @returns Status information
|
|
218
|
+
*/
|
|
219
|
+
export async function getWorkflowStatus(projectDir: string): Promise<{
|
|
220
|
+
exists: boolean;
|
|
221
|
+
state?: ProjectState;
|
|
222
|
+
progress?: {
|
|
223
|
+
totalMilestones: number;
|
|
224
|
+
completedMilestones: number;
|
|
225
|
+
totalTasks: number;
|
|
226
|
+
completedTasks: number;
|
|
227
|
+
percentComplete: number;
|
|
228
|
+
};
|
|
229
|
+
}> {
|
|
230
|
+
if (!(await projectExists(projectDir))) {
|
|
231
|
+
return { exists: false };
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
const state = await loadProject(projectDir);
|
|
235
|
+
const progress = await getProgress(projectDir);
|
|
236
|
+
|
|
237
|
+
return {
|
|
238
|
+
exists: true,
|
|
239
|
+
state,
|
|
240
|
+
progress,
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Reset workflow to a specific phase
|
|
246
|
+
*
|
|
247
|
+
* @param projectDir - Project directory
|
|
248
|
+
* @param phase - Phase to reset to
|
|
249
|
+
* @returns Updated state
|
|
250
|
+
*/
|
|
251
|
+
export async function resetWorkflow(
|
|
252
|
+
projectDir: string,
|
|
253
|
+
phase: WorkflowPhase
|
|
254
|
+
): Promise<ProjectState> {
|
|
255
|
+
return resetToPhase(projectDir, phase);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Cancel and delete a workflow
|
|
260
|
+
*
|
|
261
|
+
* @param projectDir - Project directory
|
|
262
|
+
* @returns True if deleted
|
|
263
|
+
*/
|
|
264
|
+
export async function cancelWorkflow(projectDir: string): Promise<boolean> {
|
|
265
|
+
return deleteProject(projectDir);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Get a human-readable summary of the workflow status
|
|
270
|
+
*
|
|
271
|
+
* @param projectDir - Project directory
|
|
272
|
+
* @returns Summary string
|
|
273
|
+
*/
|
|
274
|
+
export async function getWorkflowSummary(projectDir: string): Promise<string> {
|
|
275
|
+
const status = await getWorkflowStatus(projectDir);
|
|
276
|
+
|
|
277
|
+
if (!status.exists || !status.state) {
|
|
278
|
+
return 'No project found';
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
const { state, progress } = status;
|
|
282
|
+
const lines: string[] = [];
|
|
283
|
+
|
|
284
|
+
lines.push(`# Project: ${state.name}`);
|
|
285
|
+
lines.push('');
|
|
286
|
+
lines.push(`**Phase:** ${state.phase}`);
|
|
287
|
+
lines.push(`**Status:** ${state.status}`);
|
|
288
|
+
lines.push(`**Language:** ${state.language}`);
|
|
289
|
+
lines.push('');
|
|
290
|
+
|
|
291
|
+
if (progress) {
|
|
292
|
+
lines.push(`## Progress`);
|
|
293
|
+
lines.push(`- Milestones: ${progress.completedMilestones}/${progress.totalMilestones}`);
|
|
294
|
+
lines.push(`- Tasks: ${progress.completedTasks}/${progress.totalTasks}`);
|
|
295
|
+
lines.push(`- Complete: ${progress.percentComplete}%`);
|
|
296
|
+
lines.push('');
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
if (state.consensusHistory && state.consensusHistory.length > 0) {
|
|
300
|
+
const lastConsensus = state.consensusHistory[state.consensusHistory.length - 1];
|
|
301
|
+
lines.push(`## Last Consensus`);
|
|
302
|
+
lines.push(`- Score: ${lastConsensus.result.score}%`);
|
|
303
|
+
lines.push(`- Iteration: ${lastConsensus.iteration}`);
|
|
304
|
+
lines.push('');
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
if (state.error) {
|
|
308
|
+
lines.push(`## Error`);
|
|
309
|
+
lines.push(state.error);
|
|
310
|
+
lines.push('');
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
if (state.currentMilestone) {
|
|
314
|
+
const milestone = state.milestones.find((m) => m.id === state.currentMilestone);
|
|
315
|
+
if (milestone) {
|
|
316
|
+
lines.push(`## Current Milestone`);
|
|
317
|
+
lines.push(`**${milestone.name}**`);
|
|
318
|
+
|
|
319
|
+
if (state.currentTask) {
|
|
320
|
+
const task = milestone.tasks.find((t) => t.id === state.currentTask);
|
|
321
|
+
if (task) {
|
|
322
|
+
lines.push(`- Current Task: ${task.name}`);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
lines.push('');
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
return lines.join('\n');
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Execute a single task manually
|
|
334
|
+
*
|
|
335
|
+
* @param projectDir - Project directory
|
|
336
|
+
* @param taskId - Task ID
|
|
337
|
+
* @param options - Execution options
|
|
338
|
+
* @returns Task result
|
|
339
|
+
*/
|
|
340
|
+
export async function executeTask(
|
|
341
|
+
projectDir: string,
|
|
342
|
+
taskId: string,
|
|
343
|
+
options?: { maxRetries?: number; onProgress?: (message: string) => void }
|
|
344
|
+
): Promise<TaskExecutionResult> {
|
|
345
|
+
return executeSingleTask(projectDir, taskId, {
|
|
346
|
+
projectDir,
|
|
347
|
+
maxRetries: options?.maxRetries,
|
|
348
|
+
onProgress: options?.onProgress ? (_, msg) => options.onProgress!(msg) : undefined,
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* Validate that a project is ready for execution
|
|
354
|
+
*
|
|
355
|
+
* @param projectDir - Project directory
|
|
356
|
+
* @returns Validation result
|
|
357
|
+
*/
|
|
358
|
+
export async function validateReadyForExecution(projectDir: string): Promise<{
|
|
359
|
+
ready: boolean;
|
|
360
|
+
issues: string[];
|
|
361
|
+
}> {
|
|
362
|
+
const issues: string[] = [];
|
|
363
|
+
|
|
364
|
+
if (!(await projectExists(projectDir))) {
|
|
365
|
+
return { ready: false, issues: ['No project found'] };
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
const state = await loadProject(projectDir);
|
|
369
|
+
|
|
370
|
+
if (!state.plan) {
|
|
371
|
+
issues.push('No approved plan');
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
if (state.milestones.length === 0) {
|
|
375
|
+
issues.push('No milestones defined');
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
const allTasks = state.milestones.flatMap((m) => m.tasks);
|
|
379
|
+
if (allTasks.length === 0) {
|
|
380
|
+
issues.push('No tasks defined');
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
if (state.phase === 'plan' && state.consensusHistory.length === 0) {
|
|
384
|
+
issues.push('Plan has not been through consensus review');
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
const lastConsensus = state.consensusHistory[state.consensusHistory.length - 1];
|
|
388
|
+
if (lastConsensus && lastConsensus.result.score < 95) {
|
|
389
|
+
issues.push(`Consensus score (${lastConsensus.result.score}%) below 95% threshold`);
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
return {
|
|
393
|
+
ready: issues.length === 0,
|
|
394
|
+
issues,
|
|
395
|
+
};
|
|
396
|
+
}
|