popeye-cli 1.1.0 → 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.
Files changed (137) hide show
  1. package/.env.example +24 -1
  2. package/CONTRIBUTING.md +275 -0
  3. package/OPEN_SOURCE_MANIFESTO.md +172 -0
  4. package/README.md +340 -27
  5. package/dist/adapters/claude.d.ts +5 -2
  6. package/dist/adapters/claude.d.ts.map +1 -1
  7. package/dist/adapters/claude.js +239 -19
  8. package/dist/adapters/claude.js.map +1 -1
  9. package/dist/adapters/grok.d.ts +73 -0
  10. package/dist/adapters/grok.d.ts.map +1 -0
  11. package/dist/adapters/grok.js +430 -0
  12. package/dist/adapters/grok.js.map +1 -0
  13. package/dist/adapters/openai.d.ts +1 -1
  14. package/dist/adapters/openai.d.ts.map +1 -1
  15. package/dist/adapters/openai.js +6 -1
  16. package/dist/adapters/openai.js.map +1 -1
  17. package/dist/auth/grok.d.ts +73 -0
  18. package/dist/auth/grok.d.ts.map +1 -0
  19. package/dist/auth/grok.js +211 -0
  20. package/dist/auth/grok.js.map +1 -0
  21. package/dist/auth/index.d.ts +9 -6
  22. package/dist/auth/index.d.ts.map +1 -1
  23. package/dist/auth/index.js +23 -6
  24. package/dist/auth/index.js.map +1 -1
  25. package/dist/cli/commands/auth.d.ts +1 -1
  26. package/dist/cli/commands/auth.d.ts.map +1 -1
  27. package/dist/cli/commands/auth.js +79 -8
  28. package/dist/cli/commands/auth.js.map +1 -1
  29. package/dist/cli/commands/create.d.ts.map +1 -1
  30. package/dist/cli/commands/create.js +15 -4
  31. package/dist/cli/commands/create.js.map +1 -1
  32. package/dist/cli/interactive.d.ts.map +1 -1
  33. package/dist/cli/interactive.js +374 -35
  34. package/dist/cli/interactive.js.map +1 -1
  35. package/dist/config/defaults.d.ts +3 -0
  36. package/dist/config/defaults.d.ts.map +1 -1
  37. package/dist/config/defaults.js +9 -0
  38. package/dist/config/defaults.js.map +1 -1
  39. package/dist/config/index.d.ts +9 -0
  40. package/dist/config/index.d.ts.map +1 -1
  41. package/dist/config/index.js +16 -3
  42. package/dist/config/index.js.map +1 -1
  43. package/dist/config/schema.d.ts +27 -0
  44. package/dist/config/schema.d.ts.map +1 -1
  45. package/dist/config/schema.js +24 -3
  46. package/dist/config/schema.js.map +1 -1
  47. package/dist/generators/fullstack.d.ts +32 -0
  48. package/dist/generators/fullstack.d.ts.map +1 -0
  49. package/dist/generators/fullstack.js +497 -0
  50. package/dist/generators/fullstack.js.map +1 -0
  51. package/dist/generators/index.d.ts +4 -3
  52. package/dist/generators/index.d.ts.map +1 -1
  53. package/dist/generators/index.js +15 -1
  54. package/dist/generators/index.js.map +1 -1
  55. package/dist/generators/python.d.ts +17 -1
  56. package/dist/generators/python.d.ts.map +1 -1
  57. package/dist/generators/python.js +34 -21
  58. package/dist/generators/python.js.map +1 -1
  59. package/dist/generators/templates/fullstack.d.ts +113 -0
  60. package/dist/generators/templates/fullstack.d.ts.map +1 -0
  61. package/dist/generators/templates/fullstack.js +1004 -0
  62. package/dist/generators/templates/fullstack.js.map +1 -0
  63. package/dist/generators/typescript.d.ts +19 -1
  64. package/dist/generators/typescript.d.ts.map +1 -1
  65. package/dist/generators/typescript.js +37 -21
  66. package/dist/generators/typescript.js.map +1 -1
  67. package/dist/types/cli.d.ts +4 -0
  68. package/dist/types/cli.d.ts.map +1 -1
  69. package/dist/types/cli.js.map +1 -1
  70. package/dist/types/consensus.d.ts +119 -2
  71. package/dist/types/consensus.d.ts.map +1 -1
  72. package/dist/types/consensus.js +12 -1
  73. package/dist/types/consensus.js.map +1 -1
  74. package/dist/types/project.d.ts +76 -0
  75. package/dist/types/project.d.ts.map +1 -1
  76. package/dist/types/project.js +1 -1
  77. package/dist/types/project.js.map +1 -1
  78. package/dist/types/workflow.d.ts +162 -16
  79. package/dist/types/workflow.d.ts.map +1 -1
  80. package/dist/types/workflow.js +24 -1
  81. package/dist/types/workflow.js.map +1 -1
  82. package/dist/workflow/consensus.d.ts +29 -3
  83. package/dist/workflow/consensus.d.ts.map +1 -1
  84. package/dist/workflow/consensus.js +334 -27
  85. package/dist/workflow/consensus.js.map +1 -1
  86. package/dist/workflow/milestone-workflow.js +2 -2
  87. package/dist/workflow/milestone-workflow.js.map +1 -1
  88. package/dist/workflow/plan-mode.d.ts +66 -2
  89. package/dist/workflow/plan-mode.d.ts.map +1 -1
  90. package/dist/workflow/plan-mode.js +187 -11
  91. package/dist/workflow/plan-mode.js.map +1 -1
  92. package/dist/workflow/plan-storage.d.ts +252 -8
  93. package/dist/workflow/plan-storage.d.ts.map +1 -1
  94. package/dist/workflow/plan-storage.js +580 -33
  95. package/dist/workflow/plan-storage.js.map +1 -1
  96. package/dist/workflow/project-verification.js +1 -1
  97. package/dist/workflow/project-verification.js.map +1 -1
  98. package/dist/workflow/task-workflow.d.ts.map +1 -1
  99. package/dist/workflow/task-workflow.js +4 -1
  100. package/dist/workflow/task-workflow.js.map +1 -1
  101. package/dist/workflow/test-runner.d.ts +8 -0
  102. package/dist/workflow/test-runner.d.ts.map +1 -1
  103. package/dist/workflow/test-runner.js +92 -0
  104. package/dist/workflow/test-runner.js.map +1 -1
  105. package/dist/workflow/workspace-manager.d.ts +342 -0
  106. package/dist/workflow/workspace-manager.d.ts.map +1 -0
  107. package/dist/workflow/workspace-manager.js +733 -0
  108. package/dist/workflow/workspace-manager.js.map +1 -0
  109. package/package.json +1 -1
  110. package/src/adapters/claude.ts +263 -24
  111. package/src/adapters/grok.ts +492 -0
  112. package/src/adapters/openai.ts +8 -2
  113. package/src/auth/grok.ts +255 -0
  114. package/src/auth/index.ts +27 -9
  115. package/src/cli/commands/auth.ts +89 -10
  116. package/src/cli/commands/create.ts +13 -4
  117. package/src/cli/interactive.ts +424 -34
  118. package/src/config/defaults.ts +9 -0
  119. package/src/config/index.ts +17 -3
  120. package/src/config/schema.ts +25 -3
  121. package/src/generators/fullstack.ts +551 -0
  122. package/src/generators/index.ts +25 -1
  123. package/src/generators/python.ts +65 -21
  124. package/src/generators/templates/fullstack.ts +1047 -0
  125. package/src/generators/typescript.ts +69 -21
  126. package/src/types/cli.ts +4 -0
  127. package/src/types/consensus.ts +135 -3
  128. package/src/types/project.ts +82 -1
  129. package/src/types/workflow.ts +56 -2
  130. package/src/workflow/consensus.ts +461 -31
  131. package/src/workflow/milestone-workflow.ts +2 -2
  132. package/src/workflow/plan-mode.ts +238 -10
  133. package/src/workflow/plan-storage.ts +835 -35
  134. package/src/workflow/project-verification.ts +1 -1
  135. package/src/workflow/task-workflow.ts +4 -1
  136. package/src/workflow/test-runner.ts +110 -0
  137. package/src/workflow/workspace-manager.ts +912 -0
@@ -289,7 +289,7 @@ async function verifyRouteCompleteness(projectDir: string): Promise<Verification
289
289
 
290
290
  // Check all page components
291
291
  const pageFiles = await findFiles(path.join(frontendDir, 'src', 'pages'), /\.tsx$/);
292
- let incompletePages: string[] = [];
292
+ const incompletePages: string[] = [];
293
293
 
294
294
  for (const file of pageFiles) {
295
295
  const content = await readFile(file);
@@ -90,7 +90,7 @@ Please provide:
90
90
  Be specific and actionable. This plan will be reviewed for consensus before implementation.
91
91
  `.trim();
92
92
 
93
- const result = await claudeCreatePlan(prompt, context, onProgress);
93
+ const result = await claudeCreatePlan(prompt, context, state.language, onProgress);
94
94
 
95
95
  if (!result.success) {
96
96
  throw new Error(`Failed to create task plan: ${result.error}`);
@@ -329,6 +329,7 @@ Task: ${task.name}
329
329
  taskId: task.id,
330
330
  taskName: task.name,
331
331
  parallelReviews: true,
332
+ isFullstack: state.language === 'fullstack',
332
333
  onIteration: (iteration, result) => {
333
334
  onProgress?.('task-consensus', `Iteration ${iteration}: ${result.score}%`);
334
335
  },
@@ -343,6 +344,8 @@ Task: ${task.name}
343
344
  {
344
345
  projectDir,
345
346
  config: consensusConfig,
347
+ isFullstack: state.language === 'fullstack',
348
+ language: state.language,
346
349
  onIteration: (iteration, result) => {
347
350
  onProgress?.('task-consensus', `Iteration ${iteration}: ${result.score}%`);
348
351
  },
@@ -37,6 +37,7 @@ export interface TestConfig {
37
37
  export const DEFAULT_TEST_COMMANDS: Record<OutputLanguage, string> = {
38
38
  python: 'python -m pytest tests/ -v',
39
39
  typescript: 'npm test',
40
+ fullstack: 'npm run test:all', // Runs both frontend and backend tests via workspace.json
40
41
  };
41
42
 
42
43
  /**
@@ -78,6 +79,12 @@ export function buildTestCommand(config: TestConfig): string {
78
79
 
79
80
  return parts.join(' ');
80
81
  }
82
+
83
+ case 'fullstack': {
84
+ // Fullstack projects use workspace.json commands
85
+ // Default to running both frontend and backend tests
86
+ return 'npm run test:all';
87
+ }
81
88
  }
82
89
  }
83
90
 
@@ -137,6 +144,38 @@ export function parseTestOutput(output: string, language: OutputLanguage): TestR
137
144
  }
138
145
  break;
139
146
  }
147
+
148
+ case 'fullstack': {
149
+ // Fullstack combines pytest and jest output
150
+ // Parse both formats
151
+ const pytestMatch = output.match(/(\d+)\s+passed/);
152
+ const pytestFailedMatch = output.match(/(\d+)\s+failed/);
153
+ const jestMatch = output.match(/Tests:\s*(?:(\d+)\s+failed,\s*)?(\d+)\s+passed,\s*(\d+)\s+total/);
154
+
155
+ if (pytestMatch) {
156
+ passed += parseInt(pytestMatch[1], 10);
157
+ }
158
+ if (pytestFailedMatch) {
159
+ failed += parseInt(pytestFailedMatch[1], 10);
160
+ }
161
+ if (jestMatch) {
162
+ failed += jestMatch[1] ? parseInt(jestMatch[1], 10) : 0;
163
+ passed += parseInt(jestMatch[2], 10);
164
+ }
165
+
166
+ total = passed + failed;
167
+
168
+ // Extract failed test names from both pytest and jest
169
+ const pytestFailedMatches = output.matchAll(/FAILED\s+([^\s]+)/g);
170
+ for (const match of pytestFailedMatches) {
171
+ failedTests.push(match[1]);
172
+ }
173
+ const jestFailedMatches = output.matchAll(/✕\s+(.+)/g);
174
+ for (const match of jestFailedMatches) {
175
+ failedTests.push(match[1].trim());
176
+ }
177
+ break;
178
+ }
140
179
  }
141
180
 
142
181
  // Success if no failures - treat "no tests found" as success (not a failure)
@@ -241,6 +280,48 @@ export async function runTypeScriptTests(
241
280
  }
242
281
  }
243
282
 
283
+ /**
284
+ * Run fullstack tests (both frontend and backend)
285
+ *
286
+ * @param cwd - Working directory
287
+ * @param config - Test configuration
288
+ * @returns Combined test result
289
+ */
290
+ export async function runFullstackTests(
291
+ cwd: string,
292
+ config: Partial<TestConfig> = {}
293
+ ): Promise<TestResult> {
294
+ const path = await import('node:path');
295
+
296
+ // Run backend tests first
297
+ const backendCwd = path.join(cwd, 'apps', 'backend');
298
+ const backendResult = await runPythonTests(backendCwd, config);
299
+
300
+ // Run frontend tests
301
+ const frontendCwd = path.join(cwd, 'apps', 'frontend');
302
+ const frontendResult = await runTypeScriptTests(frontendCwd, config);
303
+
304
+ // Combine results
305
+ const combinedOutput = `=== Backend Tests ===\n${backendResult.output}\n\n=== Frontend Tests ===\n${frontendResult.output}`;
306
+
307
+ return {
308
+ success: backendResult.success && frontendResult.success,
309
+ passed: backendResult.passed + frontendResult.passed,
310
+ failed: backendResult.failed + frontendResult.failed,
311
+ total: backendResult.total + frontendResult.total,
312
+ output: combinedOutput,
313
+ failedTests: [
314
+ ...(backendResult.failedTests || []).map((t) => `[backend] ${t}`),
315
+ ...(frontendResult.failedTests || []).map((t) => `[frontend] ${t}`),
316
+ ].length > 0 ? [
317
+ ...(backendResult.failedTests || []).map((t) => `[backend] ${t}`),
318
+ ...(frontendResult.failedTests || []).map((t) => `[frontend] ${t}`),
319
+ ] : undefined,
320
+ error: backendResult.error || frontendResult.error,
321
+ noTestsFound: backendResult.noTestsFound && frontendResult.noTestsFound,
322
+ };
323
+ }
324
+
244
325
  /**
245
326
  * Run tests for a project
246
327
  *
@@ -259,6 +340,8 @@ export async function runTests(
259
340
  return runPythonTests(cwd, config);
260
341
  case 'typescript':
261
342
  return runTypeScriptTests(cwd, config);
343
+ case 'fullstack':
344
+ return runFullstackTests(cwd, config);
262
345
  }
263
346
  }
264
347
 
@@ -318,6 +401,33 @@ export async function testsExist(
318
401
  }
319
402
  }
320
403
  }
404
+
405
+ case 'fullstack': {
406
+ // Check for tests in both frontend and backend
407
+ const backendTestsDir = path.join(cwd, 'apps', 'backend', 'tests');
408
+ const frontendTestsDir = path.join(cwd, 'apps', 'frontend', 'src');
409
+
410
+ let hasBackendTests = false;
411
+ let hasFrontendTests = false;
412
+
413
+ try {
414
+ await fs.access(backendTestsDir);
415
+ hasBackendTests = true;
416
+ } catch {
417
+ // No backend tests directory
418
+ }
419
+
420
+ try {
421
+ const files = await fs.readdir(frontendTestsDir, { recursive: true });
422
+ hasFrontendTests = files.some(
423
+ (f) => f.toString().endsWith('.test.ts') || f.toString().endsWith('.spec.ts')
424
+ );
425
+ } catch {
426
+ // No frontend test files
427
+ }
428
+
429
+ return hasBackendTests || hasFrontendTests;
430
+ }
321
431
  }
322
432
  } catch {
323
433
  return false;