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.
Files changed (209) hide show
  1. package/.env.example +25 -0
  2. package/.prettierrc +8 -0
  3. package/README.md +320 -0
  4. package/dist/adapters/claude.d.ts +82 -0
  5. package/dist/adapters/claude.d.ts.map +1 -0
  6. package/dist/adapters/claude.js +230 -0
  7. package/dist/adapters/claude.js.map +1 -0
  8. package/dist/adapters/openai.d.ts +48 -0
  9. package/dist/adapters/openai.d.ts.map +1 -0
  10. package/dist/adapters/openai.js +257 -0
  11. package/dist/adapters/openai.js.map +1 -0
  12. package/dist/auth/claude.d.ts +44 -0
  13. package/dist/auth/claude.d.ts.map +1 -0
  14. package/dist/auth/claude.js +139 -0
  15. package/dist/auth/claude.js.map +1 -0
  16. package/dist/auth/index.d.ts +61 -0
  17. package/dist/auth/index.d.ts.map +1 -0
  18. package/dist/auth/index.js +141 -0
  19. package/dist/auth/index.js.map +1 -0
  20. package/dist/auth/keychain.d.ts +66 -0
  21. package/dist/auth/keychain.d.ts.map +1 -0
  22. package/dist/auth/keychain.js +125 -0
  23. package/dist/auth/keychain.js.map +1 -0
  24. package/dist/auth/openai-entry.d.ts +9 -0
  25. package/dist/auth/openai-entry.d.ts.map +1 -0
  26. package/dist/auth/openai-entry.js +410 -0
  27. package/dist/auth/openai-entry.js.map +1 -0
  28. package/dist/auth/openai.d.ts +71 -0
  29. package/dist/auth/openai.d.ts.map +1 -0
  30. package/dist/auth/openai.js +212 -0
  31. package/dist/auth/openai.js.map +1 -0
  32. package/dist/auth/server.d.ts +32 -0
  33. package/dist/auth/server.d.ts.map +1 -0
  34. package/dist/auth/server.js +213 -0
  35. package/dist/auth/server.js.map +1 -0
  36. package/dist/cli/commands/auth.d.ts +10 -0
  37. package/dist/cli/commands/auth.d.ts.map +1 -0
  38. package/dist/cli/commands/auth.js +162 -0
  39. package/dist/cli/commands/auth.js.map +1 -0
  40. package/dist/cli/commands/config.d.ts +10 -0
  41. package/dist/cli/commands/config.d.ts.map +1 -0
  42. package/dist/cli/commands/config.js +215 -0
  43. package/dist/cli/commands/config.js.map +1 -0
  44. package/dist/cli/commands/create.d.ts +10 -0
  45. package/dist/cli/commands/create.d.ts.map +1 -0
  46. package/dist/cli/commands/create.js +240 -0
  47. package/dist/cli/commands/create.js.map +1 -0
  48. package/dist/cli/commands/index.d.ts +10 -0
  49. package/dist/cli/commands/index.d.ts.map +1 -0
  50. package/dist/cli/commands/index.js +10 -0
  51. package/dist/cli/commands/index.js.map +1 -0
  52. package/dist/cli/commands/resume.d.ts +18 -0
  53. package/dist/cli/commands/resume.d.ts.map +1 -0
  54. package/dist/cli/commands/resume.js +241 -0
  55. package/dist/cli/commands/resume.js.map +1 -0
  56. package/dist/cli/commands/status.d.ts +18 -0
  57. package/dist/cli/commands/status.d.ts.map +1 -0
  58. package/dist/cli/commands/status.js +154 -0
  59. package/dist/cli/commands/status.js.map +1 -0
  60. package/dist/cli/index.d.ts +17 -0
  61. package/dist/cli/index.d.ts.map +1 -0
  62. package/dist/cli/index.js +71 -0
  63. package/dist/cli/index.js.map +1 -0
  64. package/dist/cli/interactive.d.ts +9 -0
  65. package/dist/cli/interactive.d.ts.map +1 -0
  66. package/dist/cli/interactive.js +330 -0
  67. package/dist/cli/interactive.js.map +1 -0
  68. package/dist/cli/output.d.ts +182 -0
  69. package/dist/cli/output.d.ts.map +1 -0
  70. package/dist/cli/output.js +355 -0
  71. package/dist/cli/output.js.map +1 -0
  72. package/dist/config/defaults.d.ts +57 -0
  73. package/dist/config/defaults.d.ts.map +1 -0
  74. package/dist/config/defaults.js +103 -0
  75. package/dist/config/defaults.js.map +1 -0
  76. package/dist/config/index.d.ts +138 -0
  77. package/dist/config/index.d.ts.map +1 -0
  78. package/dist/config/index.js +244 -0
  79. package/dist/config/index.js.map +1 -0
  80. package/dist/config/schema.d.ts +220 -0
  81. package/dist/config/schema.d.ts.map +1 -0
  82. package/dist/config/schema.js +141 -0
  83. package/dist/config/schema.js.map +1 -0
  84. package/dist/generators/index.d.ts +101 -0
  85. package/dist/generators/index.d.ts.map +1 -0
  86. package/dist/generators/index.js +200 -0
  87. package/dist/generators/index.js.map +1 -0
  88. package/dist/generators/python.d.ts +48 -0
  89. package/dist/generators/python.d.ts.map +1 -0
  90. package/dist/generators/python.js +262 -0
  91. package/dist/generators/python.js.map +1 -0
  92. package/dist/generators/templates/index.d.ts +6 -0
  93. package/dist/generators/templates/index.d.ts.map +1 -0
  94. package/dist/generators/templates/index.js +6 -0
  95. package/dist/generators/templates/index.js.map +1 -0
  96. package/dist/generators/templates/python.d.ts +53 -0
  97. package/dist/generators/templates/python.d.ts.map +1 -0
  98. package/dist/generators/templates/python.js +454 -0
  99. package/dist/generators/templates/python.js.map +1 -0
  100. package/dist/generators/templates/typescript.d.ts +53 -0
  101. package/dist/generators/templates/typescript.d.ts.map +1 -0
  102. package/dist/generators/templates/typescript.js +394 -0
  103. package/dist/generators/templates/typescript.js.map +1 -0
  104. package/dist/generators/typescript.d.ts +64 -0
  105. package/dist/generators/typescript.d.ts.map +1 -0
  106. package/dist/generators/typescript.js +271 -0
  107. package/dist/generators/typescript.js.map +1 -0
  108. package/dist/index.d.ts +7 -0
  109. package/dist/index.d.ts.map +1 -0
  110. package/dist/index.js +12 -0
  111. package/dist/index.js.map +1 -0
  112. package/dist/state/index.d.ts +168 -0
  113. package/dist/state/index.d.ts.map +1 -0
  114. package/dist/state/index.js +338 -0
  115. package/dist/state/index.js.map +1 -0
  116. package/dist/state/persistence.d.ts +91 -0
  117. package/dist/state/persistence.d.ts.map +1 -0
  118. package/dist/state/persistence.js +201 -0
  119. package/dist/state/persistence.js.map +1 -0
  120. package/dist/types/cli.d.ts +132 -0
  121. package/dist/types/cli.d.ts.map +1 -0
  122. package/dist/types/cli.js +17 -0
  123. package/dist/types/cli.js.map +1 -0
  124. package/dist/types/consensus.d.ts +111 -0
  125. package/dist/types/consensus.d.ts.map +1 -0
  126. package/dist/types/consensus.js +29 -0
  127. package/dist/types/consensus.js.map +1 -0
  128. package/dist/types/index.d.ts +9 -0
  129. package/dist/types/index.d.ts.map +1 -0
  130. package/dist/types/index.js +13 -0
  131. package/dist/types/index.js.map +1 -0
  132. package/dist/types/project.d.ts +73 -0
  133. package/dist/types/project.d.ts.map +1 -0
  134. package/dist/types/project.js +55 -0
  135. package/dist/types/project.js.map +1 -0
  136. package/dist/types/workflow.d.ts +236 -0
  137. package/dist/types/workflow.d.ts.map +1 -0
  138. package/dist/types/workflow.js +74 -0
  139. package/dist/types/workflow.js.map +1 -0
  140. package/dist/workflow/consensus.d.ts +89 -0
  141. package/dist/workflow/consensus.d.ts.map +1 -0
  142. package/dist/workflow/consensus.js +220 -0
  143. package/dist/workflow/consensus.js.map +1 -0
  144. package/dist/workflow/execution-mode.d.ts +82 -0
  145. package/dist/workflow/execution-mode.d.ts.map +1 -0
  146. package/dist/workflow/execution-mode.js +346 -0
  147. package/dist/workflow/execution-mode.js.map +1 -0
  148. package/dist/workflow/index.d.ts +110 -0
  149. package/dist/workflow/index.d.ts.map +1 -0
  150. package/dist/workflow/index.js +283 -0
  151. package/dist/workflow/index.js.map +1 -0
  152. package/dist/workflow/plan-mode.d.ts +83 -0
  153. package/dist/workflow/plan-mode.d.ts.map +1 -0
  154. package/dist/workflow/plan-mode.js +241 -0
  155. package/dist/workflow/plan-mode.js.map +1 -0
  156. package/dist/workflow/test-runner.d.ts +87 -0
  157. package/dist/workflow/test-runner.d.ts.map +1 -0
  158. package/dist/workflow/test-runner.js +273 -0
  159. package/dist/workflow/test-runner.js.map +1 -0
  160. package/eslint.config.js +25 -0
  161. package/package.json +66 -0
  162. package/src/adapters/claude.ts +298 -0
  163. package/src/adapters/openai.ts +300 -0
  164. package/src/auth/claude.ts +166 -0
  165. package/src/auth/index.ts +171 -0
  166. package/src/auth/keychain.ts +138 -0
  167. package/src/auth/openai-entry.ts +410 -0
  168. package/src/auth/openai.ts +260 -0
  169. package/src/auth/server.ts +252 -0
  170. package/src/cli/commands/auth.ts +194 -0
  171. package/src/cli/commands/config.ts +241 -0
  172. package/src/cli/commands/create.ts +308 -0
  173. package/src/cli/commands/index.ts +10 -0
  174. package/src/cli/commands/resume.ts +304 -0
  175. package/src/cli/commands/status.ts +189 -0
  176. package/src/cli/index.ts +90 -0
  177. package/src/cli/interactive.ts +418 -0
  178. package/src/cli/output.ts +410 -0
  179. package/src/config/defaults.ts +114 -0
  180. package/src/config/index.ts +315 -0
  181. package/src/config/schema.ts +164 -0
  182. package/src/generators/index.ts +251 -0
  183. package/src/generators/python.ts +318 -0
  184. package/src/generators/templates/index.ts +6 -0
  185. package/src/generators/templates/python.ts +465 -0
  186. package/src/generators/templates/typescript.ts +417 -0
  187. package/src/generators/typescript.ts +340 -0
  188. package/src/index.ts +13 -0
  189. package/src/state/index.ts +454 -0
  190. package/src/state/persistence.ts +230 -0
  191. package/src/types/cli.ts +146 -0
  192. package/src/types/consensus.ts +116 -0
  193. package/src/types/index.ts +64 -0
  194. package/src/types/project.ts +85 -0
  195. package/src/types/workflow.ts +149 -0
  196. package/src/workflow/consensus.ts +299 -0
  197. package/src/workflow/execution-mode.ts +517 -0
  198. package/src/workflow/index.ts +396 -0
  199. package/src/workflow/plan-mode.ts +356 -0
  200. package/src/workflow/test-runner.ts +345 -0
  201. package/tests/adapters/openai.test.ts +145 -0
  202. package/tests/config/config.test.ts +208 -0
  203. package/tests/generators/generators.test.ts +185 -0
  204. package/tests/types/consensus.test.ts +152 -0
  205. package/tests/types/project.test.ts +134 -0
  206. package/tests/workflow/consensus.test.ts +221 -0
  207. package/tests/workflow/test-runner.test.ts +214 -0
  208. package/tsconfig.json +25 -0
  209. 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
+ }