popeye-cli 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (171) hide show
  1. package/README.md +521 -125
  2. package/dist/adapters/claude.d.ts +16 -4
  3. package/dist/adapters/claude.d.ts.map +1 -1
  4. package/dist/adapters/claude.js +679 -33
  5. package/dist/adapters/claude.js.map +1 -1
  6. package/dist/adapters/gemini.d.ts +55 -0
  7. package/dist/adapters/gemini.d.ts.map +1 -0
  8. package/dist/adapters/gemini.js +318 -0
  9. package/dist/adapters/gemini.js.map +1 -0
  10. package/dist/adapters/openai.d.ts.map +1 -1
  11. package/dist/adapters/openai.js +41 -7
  12. package/dist/adapters/openai.js.map +1 -1
  13. package/dist/auth/claude.d.ts +11 -9
  14. package/dist/auth/claude.d.ts.map +1 -1
  15. package/dist/auth/claude.js +107 -71
  16. package/dist/auth/claude.js.map +1 -1
  17. package/dist/auth/gemini.d.ts +58 -0
  18. package/dist/auth/gemini.d.ts.map +1 -0
  19. package/dist/auth/gemini.js +172 -0
  20. package/dist/auth/gemini.js.map +1 -0
  21. package/dist/auth/index.d.ts +11 -7
  22. package/dist/auth/index.d.ts.map +1 -1
  23. package/dist/auth/index.js +23 -5
  24. package/dist/auth/index.js.map +1 -1
  25. package/dist/auth/keychain.d.ts +20 -7
  26. package/dist/auth/keychain.d.ts.map +1 -1
  27. package/dist/auth/keychain.js +85 -29
  28. package/dist/auth/keychain.js.map +1 -1
  29. package/dist/auth/openai.d.ts +2 -2
  30. package/dist/auth/openai.d.ts.map +1 -1
  31. package/dist/auth/openai.js +30 -32
  32. package/dist/auth/openai.js.map +1 -1
  33. package/dist/cli/index.d.ts.map +1 -1
  34. package/dist/cli/index.js +4 -7
  35. package/dist/cli/index.js.map +1 -1
  36. package/dist/cli/interactive.d.ts +2 -2
  37. package/dist/cli/interactive.d.ts.map +1 -1
  38. package/dist/cli/interactive.js +1380 -183
  39. package/dist/cli/interactive.js.map +1 -1
  40. package/dist/config/defaults.d.ts +6 -1
  41. package/dist/config/defaults.d.ts.map +1 -1
  42. package/dist/config/defaults.js +10 -2
  43. package/dist/config/defaults.js.map +1 -1
  44. package/dist/config/index.d.ts +10 -0
  45. package/dist/config/index.d.ts.map +1 -1
  46. package/dist/config/index.js +19 -0
  47. package/dist/config/index.js.map +1 -1
  48. package/dist/config/schema.d.ts +20 -0
  49. package/dist/config/schema.d.ts.map +1 -1
  50. package/dist/config/schema.js +7 -0
  51. package/dist/config/schema.js.map +1 -1
  52. package/dist/generators/python.d.ts.map +1 -1
  53. package/dist/generators/python.js +1 -0
  54. package/dist/generators/python.js.map +1 -1
  55. package/dist/generators/typescript.d.ts.map +1 -1
  56. package/dist/generators/typescript.js +1 -0
  57. package/dist/generators/typescript.js.map +1 -1
  58. package/dist/state/index.d.ts +108 -0
  59. package/dist/state/index.d.ts.map +1 -1
  60. package/dist/state/index.js +551 -4
  61. package/dist/state/index.js.map +1 -1
  62. package/dist/state/registry.d.ts +52 -0
  63. package/dist/state/registry.d.ts.map +1 -0
  64. package/dist/state/registry.js +215 -0
  65. package/dist/state/registry.js.map +1 -0
  66. package/dist/types/cli.d.ts +4 -0
  67. package/dist/types/cli.d.ts.map +1 -1
  68. package/dist/types/cli.js.map +1 -1
  69. package/dist/types/consensus.d.ts +69 -4
  70. package/dist/types/consensus.d.ts.map +1 -1
  71. package/dist/types/consensus.js +24 -3
  72. package/dist/types/consensus.js.map +1 -1
  73. package/dist/types/workflow.d.ts +55 -0
  74. package/dist/types/workflow.d.ts.map +1 -1
  75. package/dist/types/workflow.js +16 -0
  76. package/dist/types/workflow.js.map +1 -1
  77. package/dist/workflow/auto-fix.d.ts +45 -0
  78. package/dist/workflow/auto-fix.d.ts.map +1 -0
  79. package/dist/workflow/auto-fix.js +274 -0
  80. package/dist/workflow/auto-fix.js.map +1 -0
  81. package/dist/workflow/consensus.d.ts +44 -2
  82. package/dist/workflow/consensus.d.ts.map +1 -1
  83. package/dist/workflow/consensus.js +565 -17
  84. package/dist/workflow/consensus.js.map +1 -1
  85. package/dist/workflow/execution-mode.d.ts +10 -4
  86. package/dist/workflow/execution-mode.d.ts.map +1 -1
  87. package/dist/workflow/execution-mode.js +547 -58
  88. package/dist/workflow/execution-mode.js.map +1 -1
  89. package/dist/workflow/index.d.ts +14 -2
  90. package/dist/workflow/index.d.ts.map +1 -1
  91. package/dist/workflow/index.js +69 -6
  92. package/dist/workflow/index.js.map +1 -1
  93. package/dist/workflow/milestone-workflow.d.ts +34 -0
  94. package/dist/workflow/milestone-workflow.d.ts.map +1 -0
  95. package/dist/workflow/milestone-workflow.js +414 -0
  96. package/dist/workflow/milestone-workflow.js.map +1 -0
  97. package/dist/workflow/plan-mode.d.ts +14 -1
  98. package/dist/workflow/plan-mode.d.ts.map +1 -1
  99. package/dist/workflow/plan-mode.js +589 -47
  100. package/dist/workflow/plan-mode.js.map +1 -1
  101. package/dist/workflow/plan-storage.d.ts +142 -0
  102. package/dist/workflow/plan-storage.d.ts.map +1 -0
  103. package/dist/workflow/plan-storage.js +331 -0
  104. package/dist/workflow/plan-storage.js.map +1 -0
  105. package/dist/workflow/project-verification.d.ts +37 -0
  106. package/dist/workflow/project-verification.d.ts.map +1 -0
  107. package/dist/workflow/project-verification.js +381 -0
  108. package/dist/workflow/project-verification.js.map +1 -0
  109. package/dist/workflow/task-workflow.d.ts +37 -0
  110. package/dist/workflow/task-workflow.d.ts.map +1 -0
  111. package/dist/workflow/task-workflow.js +383 -0
  112. package/dist/workflow/task-workflow.js.map +1 -0
  113. package/dist/workflow/test-runner.d.ts +1 -0
  114. package/dist/workflow/test-runner.d.ts.map +1 -1
  115. package/dist/workflow/test-runner.js +9 -5
  116. package/dist/workflow/test-runner.js.map +1 -1
  117. package/dist/workflow/ui-designer.d.ts +82 -0
  118. package/dist/workflow/ui-designer.d.ts.map +1 -0
  119. package/dist/workflow/ui-designer.js +234 -0
  120. package/dist/workflow/ui-designer.js.map +1 -0
  121. package/dist/workflow/ui-setup.d.ts +58 -0
  122. package/dist/workflow/ui-setup.d.ts.map +1 -0
  123. package/dist/workflow/ui-setup.js +685 -0
  124. package/dist/workflow/ui-setup.js.map +1 -0
  125. package/dist/workflow/ui-verification.d.ts +114 -0
  126. package/dist/workflow/ui-verification.d.ts.map +1 -0
  127. package/dist/workflow/ui-verification.js +258 -0
  128. package/dist/workflow/ui-verification.js.map +1 -0
  129. package/dist/workflow/workflow-logger.d.ts +110 -0
  130. package/dist/workflow/workflow-logger.d.ts.map +1 -0
  131. package/dist/workflow/workflow-logger.js +267 -0
  132. package/dist/workflow/workflow-logger.js.map +1 -0
  133. package/package.json +2 -2
  134. package/src/adapters/claude.ts +815 -34
  135. package/src/adapters/gemini.ts +373 -0
  136. package/src/adapters/openai.ts +40 -7
  137. package/src/auth/claude.ts +120 -78
  138. package/src/auth/gemini.ts +207 -0
  139. package/src/auth/index.ts +28 -8
  140. package/src/auth/keychain.ts +95 -28
  141. package/src/auth/openai.ts +29 -36
  142. package/src/cli/index.ts +4 -7
  143. package/src/cli/interactive.ts +1641 -216
  144. package/src/config/defaults.ts +10 -2
  145. package/src/config/index.ts +21 -0
  146. package/src/config/schema.ts +7 -0
  147. package/src/generators/python.ts +1 -0
  148. package/src/generators/typescript.ts +1 -0
  149. package/src/state/index.ts +713 -4
  150. package/src/state/registry.ts +278 -0
  151. package/src/types/cli.ts +4 -0
  152. package/src/types/consensus.ts +65 -6
  153. package/src/types/workflow.ts +35 -0
  154. package/src/workflow/auto-fix.ts +340 -0
  155. package/src/workflow/consensus.ts +750 -16
  156. package/src/workflow/execution-mode.ts +673 -74
  157. package/src/workflow/index.ts +95 -6
  158. package/src/workflow/milestone-workflow.ts +576 -0
  159. package/src/workflow/plan-mode.ts +696 -50
  160. package/src/workflow/plan-storage.ts +482 -0
  161. package/src/workflow/project-verification.ts +471 -0
  162. package/src/workflow/task-workflow.ts +525 -0
  163. package/src/workflow/test-runner.ts +10 -5
  164. package/src/workflow/ui-designer.ts +337 -0
  165. package/src/workflow/ui-setup.ts +797 -0
  166. package/src/workflow/ui-verification.ts +357 -0
  167. package/src/workflow/workflow-logger.ts +353 -0
  168. package/tests/config/config.test.ts +1 -1
  169. package/tests/types/consensus.test.ts +3 -3
  170. package/tests/workflow/plan-mode.test.ts +213 -0
  171. package/tests/workflow/test-runner.test.ts +5 -3
@@ -0,0 +1,471 @@
1
+ /**
2
+ * Comprehensive Project Verification
3
+ * Ensures generated projects are actually complete and runnable
4
+ */
5
+
6
+ import { promises as fs } from 'node:fs';
7
+ import path from 'node:path';
8
+ import { exec } from 'node:child_process';
9
+ import { promisify } from 'node:util';
10
+
11
+ const execAsync = promisify(exec);
12
+
13
+ /**
14
+ * Verification result
15
+ */
16
+ export interface VerificationResult {
17
+ passed: boolean;
18
+ category: string;
19
+ check: string;
20
+ message: string;
21
+ severity: 'error' | 'warning' | 'info';
22
+ autoFixable: boolean;
23
+ fix?: () => Promise<void>;
24
+ }
25
+
26
+ /**
27
+ * Project verification report
28
+ */
29
+ export interface VerificationReport {
30
+ passed: boolean;
31
+ totalChecks: number;
32
+ passedChecks: number;
33
+ failedChecks: number;
34
+ warnings: number;
35
+ results: VerificationResult[];
36
+ criticalIssues: string[];
37
+ }
38
+
39
+ /**
40
+ * Check if a file exists
41
+ */
42
+ async function fileExists(filePath: string): Promise<boolean> {
43
+ try {
44
+ await fs.access(filePath);
45
+ return true;
46
+ } catch {
47
+ return false;
48
+ }
49
+ }
50
+
51
+ /**
52
+ * Read file content safely
53
+ */
54
+ async function readFile(filePath: string): Promise<string | null> {
55
+ try {
56
+ return await fs.readFile(filePath, 'utf-8');
57
+ } catch {
58
+ return null;
59
+ }
60
+ }
61
+
62
+ /**
63
+ * Find files matching a pattern recursively
64
+ */
65
+ async function findFiles(dir: string, pattern: RegExp, maxDepth = 5): Promise<string[]> {
66
+ const results: string[] = [];
67
+
68
+ async function search(currentDir: string, depth: number): Promise<void> {
69
+ if (depth > maxDepth) return;
70
+
71
+ try {
72
+ const entries = await fs.readdir(currentDir, { withFileTypes: true });
73
+
74
+ for (const entry of entries) {
75
+ const fullPath = path.join(currentDir, entry.name);
76
+
77
+ if (entry.isDirectory() && !entry.name.startsWith('.') && entry.name !== 'node_modules' && entry.name !== 'dist') {
78
+ await search(fullPath, depth + 1);
79
+ } else if (entry.isFile() && pattern.test(entry.name)) {
80
+ results.push(fullPath);
81
+ }
82
+ }
83
+ } catch {
84
+ // Directory not accessible
85
+ }
86
+ }
87
+
88
+ await search(dir, 0);
89
+ return results;
90
+ }
91
+
92
+ /**
93
+ * Check if content contains Tailwind classes
94
+ */
95
+ function hasTailwindClasses(content: string): boolean {
96
+ const tailwindPatterns = [
97
+ /className=["'][^"']*(?:flex|grid|block|inline|hidden)/,
98
+ /className=["'][^"']*(?:bg-|text-|border-|rounded-|shadow-|p-|m-|w-|h-)/,
99
+ /className=["'][^"']*(?:hover:|focus:|active:|disabled:)/,
100
+ /className=["'][^"']*(?:sm:|md:|lg:|xl:|2xl:)/,
101
+ ];
102
+
103
+ return tailwindPatterns.some(pattern => pattern.test(content));
104
+ }
105
+
106
+ /**
107
+ * Check if content contains TODO placeholders
108
+ */
109
+ function findTodoPlaceholders(content: string): string[] {
110
+ const patterns = [
111
+ /TODO:?\s*(.+)/gi,
112
+ /<div>.*TODO.*<\/div>/gi,
113
+ /\{\/\*\s*TODO.*\*\/\}/gi,
114
+ /placeholder.*TODO/gi,
115
+ ];
116
+
117
+ const todos: string[] = [];
118
+ for (const pattern of patterns) {
119
+ const matches = content.matchAll(pattern);
120
+ for (const match of matches) {
121
+ todos.push(match[0].trim());
122
+ }
123
+ }
124
+
125
+ return todos;
126
+ }
127
+
128
+ /**
129
+ * Verify CSS/Styling setup
130
+ */
131
+ async function verifyStylingSetup(projectDir: string): Promise<VerificationResult[]> {
132
+ const results: VerificationResult[] = [];
133
+ const frontendDir = path.join(projectDir, 'packages', 'frontend');
134
+
135
+ // Check if frontend uses Tailwind classes
136
+ const tsxFiles = await findFiles(path.join(frontendDir, 'src'), /\.tsx$/);
137
+ let usesTailwind = false;
138
+
139
+ for (const file of tsxFiles.slice(0, 20)) { // Check first 20 files
140
+ const content = await readFile(file);
141
+ if (content && hasTailwindClasses(content)) {
142
+ usesTailwind = true;
143
+ break;
144
+ }
145
+ }
146
+
147
+ if (usesTailwind) {
148
+ // Check Tailwind is in package.json
149
+ const pkgJson = await readFile(path.join(frontendDir, 'package.json'));
150
+ const hasTailwindDep = pkgJson?.includes('tailwindcss');
151
+
152
+ results.push({
153
+ passed: !!hasTailwindDep,
154
+ category: 'Styling',
155
+ check: 'Tailwind CSS dependency',
156
+ message: hasTailwindDep
157
+ ? 'Tailwind CSS is installed'
158
+ : 'Components use Tailwind classes but tailwindcss is not in package.json',
159
+ severity: hasTailwindDep ? 'info' : 'error',
160
+ autoFixable: true,
161
+ fix: hasTailwindDep ? undefined : async () => {
162
+ await execAsync('npm install -D tailwindcss @tailwindcss/postcss', { cwd: frontendDir });
163
+ },
164
+ });
165
+
166
+ // Check PostCSS config
167
+ const hasPostcssConfig = await fileExists(path.join(frontendDir, 'postcss.config.js')) ||
168
+ await fileExists(path.join(frontendDir, 'postcss.config.cjs'));
169
+
170
+ results.push({
171
+ passed: hasPostcssConfig,
172
+ category: 'Styling',
173
+ check: 'PostCSS configuration',
174
+ message: hasPostcssConfig
175
+ ? 'PostCSS is configured'
176
+ : 'Missing postcss.config.js for Tailwind CSS',
177
+ severity: hasPostcssConfig ? 'info' : 'error',
178
+ autoFixable: true,
179
+ });
180
+
181
+ // Check CSS file exists and is imported
182
+ const mainTsx = await readFile(path.join(frontendDir, 'src', 'main.tsx'));
183
+ const hasCssImport = mainTsx?.includes("import './index.css'") ||
184
+ mainTsx?.includes('import "./index.css"') ||
185
+ mainTsx?.includes("import '../index.css'");
186
+
187
+ results.push({
188
+ passed: !!hasCssImport,
189
+ category: 'Styling',
190
+ check: 'CSS import in main.tsx',
191
+ message: hasCssImport
192
+ ? 'CSS is imported in main entry point'
193
+ : 'No CSS import found in main.tsx - styles will not load',
194
+ severity: hasCssImport ? 'info' : 'error',
195
+ autoFixable: true,
196
+ });
197
+
198
+ // Check index.css exists
199
+ const hasIndexCss = await fileExists(path.join(frontendDir, 'src', 'index.css'));
200
+
201
+ results.push({
202
+ passed: hasIndexCss,
203
+ category: 'Styling',
204
+ check: 'Global CSS file',
205
+ message: hasIndexCss
206
+ ? 'Global CSS file exists'
207
+ : 'Missing src/index.css',
208
+ severity: hasIndexCss ? 'info' : 'error',
209
+ autoFixable: true,
210
+ });
211
+ }
212
+
213
+ return results;
214
+ }
215
+
216
+ /**
217
+ * Verify authentication setup
218
+ */
219
+ async function verifyAuthSetup(projectDir: string): Promise<VerificationResult[]> {
220
+ const results: VerificationResult[] = [];
221
+ const frontendDir = path.join(projectDir, 'packages', 'frontend');
222
+
223
+ // Check if project uses Auth0
224
+ const pkgJson = await readFile(path.join(frontendDir, 'package.json'));
225
+ const usesAuth0 = pkgJson?.includes('@auth0/auth0-react');
226
+
227
+ if (usesAuth0) {
228
+ // Check for dev mode bypass
229
+ const useAuthFile = await readFile(path.join(frontendDir, 'src', 'hooks', 'useAuth.ts'));
230
+ const hasDevBypass = useAuthFile?.includes('isAuth0Configured') ||
231
+ useAuthFile?.includes('DEV_MOCK_USER') ||
232
+ useAuthFile?.includes('development mode');
233
+
234
+ results.push({
235
+ passed: !!hasDevBypass,
236
+ category: 'Authentication',
237
+ check: 'Development mode bypass',
238
+ message: hasDevBypass
239
+ ? 'Auth has development mode bypass'
240
+ : 'Auth requires Auth0 configuration - app will not work without it',
241
+ severity: hasDevBypass ? 'info' : 'error',
242
+ autoFixable: false,
243
+ });
244
+
245
+ // Check Auth0 provider has fallback
246
+ const authProviderFile = await readFile(path.join(frontendDir, 'src', 'providers', 'Auth0ProviderWithNavigate.tsx'));
247
+ const hasFallback = authProviderFile?.includes('isAuth0Configured') &&
248
+ authProviderFile?.includes('return <>{children}</>');
249
+
250
+ results.push({
251
+ passed: !!hasFallback,
252
+ category: 'Authentication',
253
+ check: 'Auth0 provider fallback',
254
+ message: hasFallback
255
+ ? 'Auth0 provider has fallback for unconfigured state'
256
+ : 'Auth0 provider may hang if not configured',
257
+ severity: hasFallback ? 'info' : 'warning',
258
+ autoFixable: false,
259
+ });
260
+ }
261
+
262
+ return results;
263
+ }
264
+
265
+ /**
266
+ * Verify routes are complete (no TODO placeholders)
267
+ */
268
+ async function verifyRouteCompleteness(projectDir: string): Promise<VerificationResult[]> {
269
+ const results: VerificationResult[] = [];
270
+ const frontendDir = path.join(projectDir, 'packages', 'frontend');
271
+
272
+ // Check routes file
273
+ const routesFile = await readFile(path.join(frontendDir, 'src', 'routes', 'index.tsx'));
274
+
275
+ if (routesFile) {
276
+ const todos = findTodoPlaceholders(routesFile);
277
+
278
+ results.push({
279
+ passed: todos.length === 0,
280
+ category: 'Routes',
281
+ check: 'Route completeness',
282
+ message: todos.length === 0
283
+ ? 'All routes are implemented'
284
+ : `Found ${todos.length} TODO placeholder(s) in routes: ${todos.slice(0, 3).join(', ')}`,
285
+ severity: todos.length === 0 ? 'info' : 'warning',
286
+ autoFixable: false,
287
+ });
288
+ }
289
+
290
+ // Check all page components
291
+ const pageFiles = await findFiles(path.join(frontendDir, 'src', 'pages'), /\.tsx$/);
292
+ let incompletePages: string[] = [];
293
+
294
+ for (const file of pageFiles) {
295
+ const content = await readFile(file);
296
+ if (content) {
297
+ // Check if page is just a placeholder
298
+ const lineCount = content.split('\n').length;
299
+ if (lineCount < 15) {
300
+ incompletePages.push(path.basename(file));
301
+ }
302
+
303
+ // Check for TODO in page content
304
+ const todos = findTodoPlaceholders(content);
305
+ if (todos.length > 0) {
306
+ incompletePages.push(`${path.basename(file)} (TODOs)`);
307
+ }
308
+ }
309
+ }
310
+
311
+ results.push({
312
+ passed: incompletePages.length === 0,
313
+ category: 'Pages',
314
+ check: 'Page completeness',
315
+ message: incompletePages.length === 0
316
+ ? 'All pages are implemented'
317
+ : `Incomplete pages: ${incompletePages.slice(0, 5).join(', ')}`,
318
+ severity: incompletePages.length === 0 ? 'info' : 'warning',
319
+ autoFixable: false,
320
+ });
321
+
322
+ return results;
323
+ }
324
+
325
+ /**
326
+ * Verify database setup
327
+ */
328
+ async function verifyDatabaseSetup(projectDir: string): Promise<VerificationResult[]> {
329
+ const results: VerificationResult[] = [];
330
+ const backendDir = path.join(projectDir, 'packages', 'backend');
331
+
332
+ // Check .env.example has database config
333
+ const envExample = await readFile(path.join(projectDir, '.env.example')) ||
334
+ await readFile(path.join(backendDir, '.env.example'));
335
+
336
+ const hasDbConfig = envExample?.includes('DATABASE_URL') ||
337
+ envExample?.includes('DB_HOST');
338
+
339
+ results.push({
340
+ passed: !!hasDbConfig,
341
+ category: 'Database',
342
+ check: 'Database configuration documented',
343
+ message: hasDbConfig
344
+ ? 'Database configuration is documented in .env.example'
345
+ : 'Missing database configuration in .env.example',
346
+ severity: hasDbConfig ? 'info' : 'warning',
347
+ autoFixable: true,
348
+ });
349
+
350
+ // Check for docker-compose
351
+ const hasDocker = await fileExists(path.join(projectDir, 'docker-compose.yml')) ||
352
+ await fileExists(path.join(projectDir, 'docker-compose.yaml'));
353
+
354
+ results.push({
355
+ passed: hasDocker,
356
+ category: 'Database',
357
+ check: 'Docker Compose for local development',
358
+ message: hasDocker
359
+ ? 'Docker Compose file exists for local development'
360
+ : 'No docker-compose.yml - users need to set up database manually',
361
+ severity: hasDocker ? 'info' : 'warning',
362
+ autoFixable: true,
363
+ });
364
+
365
+ return results;
366
+ }
367
+
368
+ /**
369
+ * Verify the app actually starts
370
+ */
371
+ async function verifyAppStarts(projectDir: string): Promise<VerificationResult[]> {
372
+ const results: VerificationResult[] = [];
373
+ const frontendDir = path.join(projectDir, 'packages', 'frontend');
374
+
375
+ // Try to start frontend briefly
376
+ try {
377
+ await execAsync('npm run build', {
378
+ cwd: frontendDir,
379
+ timeout: 120000,
380
+ });
381
+
382
+ results.push({
383
+ passed: true,
384
+ category: 'Build',
385
+ check: 'Frontend builds successfully',
386
+ message: 'Frontend production build completed',
387
+ severity: 'info',
388
+ autoFixable: false,
389
+ });
390
+ } catch (err: unknown) {
391
+ const error = err as { stderr?: string; message?: string };
392
+ results.push({
393
+ passed: false,
394
+ category: 'Build',
395
+ check: 'Frontend builds successfully',
396
+ message: `Frontend build failed: ${error.stderr?.slice(0, 200) || error.message}`,
397
+ severity: 'error',
398
+ autoFixable: false,
399
+ });
400
+ }
401
+
402
+ return results;
403
+ }
404
+
405
+ /**
406
+ * Run comprehensive project verification
407
+ */
408
+ export async function runComprehensiveVerification(
409
+ projectDir: string,
410
+ onProgress?: (message: string) => void
411
+ ): Promise<VerificationReport> {
412
+ const allResults: VerificationResult[] = [];
413
+
414
+ onProgress?.('Checking styling setup...');
415
+ allResults.push(...await verifyStylingSetup(projectDir));
416
+
417
+ onProgress?.('Checking authentication setup...');
418
+ allResults.push(...await verifyAuthSetup(projectDir));
419
+
420
+ onProgress?.('Checking route completeness...');
421
+ allResults.push(...await verifyRouteCompleteness(projectDir));
422
+
423
+ onProgress?.('Checking database setup...');
424
+ allResults.push(...await verifyDatabaseSetup(projectDir));
425
+
426
+ onProgress?.('Verifying app builds...');
427
+ allResults.push(...await verifyAppStarts(projectDir));
428
+
429
+ // Calculate summary
430
+ const passedChecks = allResults.filter(r => r.passed).length;
431
+ const failedChecks = allResults.filter(r => !r.passed && r.severity === 'error').length;
432
+ const warnings = allResults.filter(r => !r.passed && r.severity === 'warning').length;
433
+
434
+ const criticalIssues = allResults
435
+ .filter(r => !r.passed && r.severity === 'error')
436
+ .map(r => `[${r.category}] ${r.message}`);
437
+
438
+ return {
439
+ passed: failedChecks === 0,
440
+ totalChecks: allResults.length,
441
+ passedChecks,
442
+ failedChecks,
443
+ warnings,
444
+ results: allResults,
445
+ criticalIssues,
446
+ };
447
+ }
448
+
449
+ /**
450
+ * Auto-fix fixable issues
451
+ */
452
+ export async function autoFixIssues(
453
+ report: VerificationReport,
454
+ onProgress?: (message: string) => void
455
+ ): Promise<number> {
456
+ let fixed = 0;
457
+
458
+ for (const result of report.results) {
459
+ if (!result.passed && result.autoFixable && result.fix) {
460
+ try {
461
+ onProgress?.(`Fixing: ${result.check}...`);
462
+ await result.fix();
463
+ fixed++;
464
+ } catch (err) {
465
+ onProgress?.(`Failed to fix ${result.check}: ${err instanceof Error ? err.message : 'Unknown error'}`);
466
+ }
467
+ }
468
+ }
469
+
470
+ return fixed;
471
+ }