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.
Files changed (216) 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 +832 -123
  5. package/dist/adapters/claude.d.ts +19 -4
  6. package/dist/adapters/claude.d.ts.map +1 -1
  7. package/dist/adapters/claude.js +908 -42
  8. package/dist/adapters/claude.js.map +1 -1
  9. package/dist/adapters/gemini.d.ts +55 -0
  10. package/dist/adapters/gemini.d.ts.map +1 -0
  11. package/dist/adapters/gemini.js +318 -0
  12. package/dist/adapters/gemini.js.map +1 -0
  13. package/dist/adapters/grok.d.ts +73 -0
  14. package/dist/adapters/grok.d.ts.map +1 -0
  15. package/dist/adapters/grok.js +430 -0
  16. package/dist/adapters/grok.js.map +1 -0
  17. package/dist/adapters/openai.d.ts +1 -1
  18. package/dist/adapters/openai.d.ts.map +1 -1
  19. package/dist/adapters/openai.js +47 -8
  20. package/dist/adapters/openai.js.map +1 -1
  21. package/dist/auth/claude.d.ts +11 -9
  22. package/dist/auth/claude.d.ts.map +1 -1
  23. package/dist/auth/claude.js +107 -71
  24. package/dist/auth/claude.js.map +1 -1
  25. package/dist/auth/gemini.d.ts +58 -0
  26. package/dist/auth/gemini.d.ts.map +1 -0
  27. package/dist/auth/gemini.js +172 -0
  28. package/dist/auth/gemini.js.map +1 -0
  29. package/dist/auth/grok.d.ts +73 -0
  30. package/dist/auth/grok.d.ts.map +1 -0
  31. package/dist/auth/grok.js +211 -0
  32. package/dist/auth/grok.js.map +1 -0
  33. package/dist/auth/index.d.ts +14 -7
  34. package/dist/auth/index.d.ts.map +1 -1
  35. package/dist/auth/index.js +41 -6
  36. package/dist/auth/index.js.map +1 -1
  37. package/dist/auth/keychain.d.ts +20 -7
  38. package/dist/auth/keychain.d.ts.map +1 -1
  39. package/dist/auth/keychain.js +85 -29
  40. package/dist/auth/keychain.js.map +1 -1
  41. package/dist/auth/openai.d.ts +2 -2
  42. package/dist/auth/openai.d.ts.map +1 -1
  43. package/dist/auth/openai.js +30 -32
  44. package/dist/auth/openai.js.map +1 -1
  45. package/dist/cli/commands/auth.d.ts +1 -1
  46. package/dist/cli/commands/auth.d.ts.map +1 -1
  47. package/dist/cli/commands/auth.js +79 -8
  48. package/dist/cli/commands/auth.js.map +1 -1
  49. package/dist/cli/commands/create.d.ts.map +1 -1
  50. package/dist/cli/commands/create.js +15 -4
  51. package/dist/cli/commands/create.js.map +1 -1
  52. package/dist/cli/interactive.d.ts.map +1 -1
  53. package/dist/cli/interactive.js +1494 -114
  54. package/dist/cli/interactive.js.map +1 -1
  55. package/dist/config/defaults.d.ts +9 -1
  56. package/dist/config/defaults.d.ts.map +1 -1
  57. package/dist/config/defaults.js +19 -2
  58. package/dist/config/defaults.js.map +1 -1
  59. package/dist/config/index.d.ts +19 -0
  60. package/dist/config/index.d.ts.map +1 -1
  61. package/dist/config/index.js +33 -1
  62. package/dist/config/index.js.map +1 -1
  63. package/dist/config/schema.d.ts +47 -0
  64. package/dist/config/schema.d.ts.map +1 -1
  65. package/dist/config/schema.js +29 -1
  66. package/dist/config/schema.js.map +1 -1
  67. package/dist/generators/fullstack.d.ts +32 -0
  68. package/dist/generators/fullstack.d.ts.map +1 -0
  69. package/dist/generators/fullstack.js +497 -0
  70. package/dist/generators/fullstack.js.map +1 -0
  71. package/dist/generators/index.d.ts +4 -3
  72. package/dist/generators/index.d.ts.map +1 -1
  73. package/dist/generators/index.js +15 -1
  74. package/dist/generators/index.js.map +1 -1
  75. package/dist/generators/python.d.ts +17 -1
  76. package/dist/generators/python.d.ts.map +1 -1
  77. package/dist/generators/python.js +34 -20
  78. package/dist/generators/python.js.map +1 -1
  79. package/dist/generators/templates/fullstack.d.ts +113 -0
  80. package/dist/generators/templates/fullstack.d.ts.map +1 -0
  81. package/dist/generators/templates/fullstack.js +1004 -0
  82. package/dist/generators/templates/fullstack.js.map +1 -0
  83. package/dist/generators/typescript.d.ts +19 -1
  84. package/dist/generators/typescript.d.ts.map +1 -1
  85. package/dist/generators/typescript.js +37 -20
  86. package/dist/generators/typescript.js.map +1 -1
  87. package/dist/state/index.d.ts +108 -0
  88. package/dist/state/index.d.ts.map +1 -1
  89. package/dist/state/index.js +551 -4
  90. package/dist/state/index.js.map +1 -1
  91. package/dist/state/registry.d.ts +52 -0
  92. package/dist/state/registry.d.ts.map +1 -0
  93. package/dist/state/registry.js +215 -0
  94. package/dist/state/registry.js.map +1 -0
  95. package/dist/types/cli.d.ts +8 -0
  96. package/dist/types/cli.d.ts.map +1 -1
  97. package/dist/types/cli.js.map +1 -1
  98. package/dist/types/consensus.d.ts +186 -4
  99. package/dist/types/consensus.d.ts.map +1 -1
  100. package/dist/types/consensus.js +35 -3
  101. package/dist/types/consensus.js.map +1 -1
  102. package/dist/types/project.d.ts +76 -0
  103. package/dist/types/project.d.ts.map +1 -1
  104. package/dist/types/project.js +1 -1
  105. package/dist/types/project.js.map +1 -1
  106. package/dist/types/workflow.d.ts +217 -16
  107. package/dist/types/workflow.d.ts.map +1 -1
  108. package/dist/types/workflow.js +40 -1
  109. package/dist/types/workflow.js.map +1 -1
  110. package/dist/workflow/auto-fix.d.ts +45 -0
  111. package/dist/workflow/auto-fix.d.ts.map +1 -0
  112. package/dist/workflow/auto-fix.js +274 -0
  113. package/dist/workflow/auto-fix.js.map +1 -0
  114. package/dist/workflow/consensus.d.ts +70 -2
  115. package/dist/workflow/consensus.d.ts.map +1 -1
  116. package/dist/workflow/consensus.js +872 -17
  117. package/dist/workflow/consensus.js.map +1 -1
  118. package/dist/workflow/execution-mode.d.ts +10 -4
  119. package/dist/workflow/execution-mode.d.ts.map +1 -1
  120. package/dist/workflow/execution-mode.js +547 -58
  121. package/dist/workflow/execution-mode.js.map +1 -1
  122. package/dist/workflow/index.d.ts +14 -2
  123. package/dist/workflow/index.d.ts.map +1 -1
  124. package/dist/workflow/index.js +69 -6
  125. package/dist/workflow/index.js.map +1 -1
  126. package/dist/workflow/milestone-workflow.d.ts +34 -0
  127. package/dist/workflow/milestone-workflow.d.ts.map +1 -0
  128. package/dist/workflow/milestone-workflow.js +414 -0
  129. package/dist/workflow/milestone-workflow.js.map +1 -0
  130. package/dist/workflow/plan-mode.d.ts +80 -3
  131. package/dist/workflow/plan-mode.d.ts.map +1 -1
  132. package/dist/workflow/plan-mode.js +767 -49
  133. package/dist/workflow/plan-mode.js.map +1 -1
  134. package/dist/workflow/plan-storage.d.ts +386 -0
  135. package/dist/workflow/plan-storage.d.ts.map +1 -0
  136. package/dist/workflow/plan-storage.js +878 -0
  137. package/dist/workflow/plan-storage.js.map +1 -0
  138. package/dist/workflow/project-verification.d.ts +37 -0
  139. package/dist/workflow/project-verification.d.ts.map +1 -0
  140. package/dist/workflow/project-verification.js +381 -0
  141. package/dist/workflow/project-verification.js.map +1 -0
  142. package/dist/workflow/task-workflow.d.ts +37 -0
  143. package/dist/workflow/task-workflow.d.ts.map +1 -0
  144. package/dist/workflow/task-workflow.js +386 -0
  145. package/dist/workflow/task-workflow.js.map +1 -0
  146. package/dist/workflow/test-runner.d.ts +9 -0
  147. package/dist/workflow/test-runner.d.ts.map +1 -1
  148. package/dist/workflow/test-runner.js +101 -5
  149. package/dist/workflow/test-runner.js.map +1 -1
  150. package/dist/workflow/ui-designer.d.ts +82 -0
  151. package/dist/workflow/ui-designer.d.ts.map +1 -0
  152. package/dist/workflow/ui-designer.js +234 -0
  153. package/dist/workflow/ui-designer.js.map +1 -0
  154. package/dist/workflow/ui-setup.d.ts +58 -0
  155. package/dist/workflow/ui-setup.d.ts.map +1 -0
  156. package/dist/workflow/ui-setup.js +685 -0
  157. package/dist/workflow/ui-setup.js.map +1 -0
  158. package/dist/workflow/ui-verification.d.ts +114 -0
  159. package/dist/workflow/ui-verification.d.ts.map +1 -0
  160. package/dist/workflow/ui-verification.js +258 -0
  161. package/dist/workflow/ui-verification.js.map +1 -0
  162. package/dist/workflow/workflow-logger.d.ts +110 -0
  163. package/dist/workflow/workflow-logger.d.ts.map +1 -0
  164. package/dist/workflow/workflow-logger.js +267 -0
  165. package/dist/workflow/workflow-logger.js.map +1 -0
  166. package/dist/workflow/workspace-manager.d.ts +342 -0
  167. package/dist/workflow/workspace-manager.d.ts.map +1 -0
  168. package/dist/workflow/workspace-manager.js +733 -0
  169. package/dist/workflow/workspace-manager.js.map +1 -0
  170. package/package.json +2 -2
  171. package/src/adapters/claude.ts +1067 -47
  172. package/src/adapters/gemini.ts +373 -0
  173. package/src/adapters/grok.ts +492 -0
  174. package/src/adapters/openai.ts +48 -9
  175. package/src/auth/claude.ts +120 -78
  176. package/src/auth/gemini.ts +207 -0
  177. package/src/auth/grok.ts +255 -0
  178. package/src/auth/index.ts +47 -9
  179. package/src/auth/keychain.ts +95 -28
  180. package/src/auth/openai.ts +29 -36
  181. package/src/cli/commands/auth.ts +89 -10
  182. package/src/cli/commands/create.ts +13 -4
  183. package/src/cli/interactive.ts +1774 -142
  184. package/src/config/defaults.ts +19 -2
  185. package/src/config/index.ts +36 -1
  186. package/src/config/schema.ts +30 -1
  187. package/src/generators/fullstack.ts +551 -0
  188. package/src/generators/index.ts +25 -1
  189. package/src/generators/python.ts +65 -20
  190. package/src/generators/templates/fullstack.ts +1047 -0
  191. package/src/generators/typescript.ts +69 -20
  192. package/src/state/index.ts +713 -4
  193. package/src/state/registry.ts +278 -0
  194. package/src/types/cli.ts +8 -0
  195. package/src/types/consensus.ts +197 -6
  196. package/src/types/project.ts +82 -1
  197. package/src/types/workflow.ts +90 -1
  198. package/src/workflow/auto-fix.ts +340 -0
  199. package/src/workflow/consensus.ts +1180 -16
  200. package/src/workflow/execution-mode.ts +673 -74
  201. package/src/workflow/index.ts +95 -6
  202. package/src/workflow/milestone-workflow.ts +576 -0
  203. package/src/workflow/plan-mode.ts +924 -50
  204. package/src/workflow/plan-storage.ts +1282 -0
  205. package/src/workflow/project-verification.ts +471 -0
  206. package/src/workflow/task-workflow.ts +528 -0
  207. package/src/workflow/test-runner.ts +120 -5
  208. package/src/workflow/ui-designer.ts +337 -0
  209. package/src/workflow/ui-setup.ts +797 -0
  210. package/src/workflow/ui-verification.ts +357 -0
  211. package/src/workflow/workflow-logger.ts +353 -0
  212. package/src/workflow/workspace-manager.ts +912 -0
  213. package/tests/config/config.test.ts +1 -1
  214. package/tests/types/consensus.test.ts +3 -3
  215. package/tests/workflow/plan-mode.test.ts +213 -0
  216. package/tests/workflow/test-runner.test.ts +5 -3
@@ -25,6 +25,16 @@ export type ProjectStatus = z.infer<typeof ProjectStatusSchema>;
25
25
  export const TaskStatusSchema = z.enum(['pending', 'in-progress', 'complete', 'failed']);
26
26
  export type TaskStatus = z.infer<typeof TaskStatusSchema>;
27
27
 
28
+ /**
29
+ * Per-app consensus tracking (for fullstack projects)
30
+ */
31
+ export interface AppConsensusTracking {
32
+ score?: number;
33
+ iterations?: number;
34
+ approved?: boolean;
35
+ feedbackDoc?: string; // Path to app-specific feedback
36
+ }
37
+
28
38
  /**
29
39
  * Individual task within a milestone
30
40
  */
@@ -36,8 +46,34 @@ export interface Task {
36
46
  testsPassed?: boolean;
37
47
  testPlan?: string;
38
48
  error?: string;
49
+ // Per-task consensus tracking
50
+ plan?: string; // Detailed task implementation plan
51
+ consensusScore?: number; // Consensus score for task plan (0-100)
52
+ consensusIterations?: number; // Number of iterations to reach consensus
53
+ consensusApproved?: boolean; // Whether task plan was approved
54
+ planDoc?: string; // Path to task plan document
55
+ testResultsDoc?: string; // Path to test results document
56
+ implementationComplete?: boolean; // Whether code implementation finished (for resume)
57
+
58
+ // Per-app consensus tracking (fullstack projects)
59
+ frontendConsensus?: AppConsensusTracking;
60
+ backendConsensus?: AppConsensusTracking;
61
+ unifiedConsensus?: AppConsensusTracking;
62
+
63
+ // App target (which app this task affects)
64
+ appTarget?: 'frontend' | 'backend' | 'unified';
39
65
  }
40
66
 
67
+ /**
68
+ * Zod schema for per-app consensus tracking
69
+ */
70
+ export const AppConsensusTrackingSchema = z.object({
71
+ score: z.number().optional(),
72
+ iterations: z.number().optional(),
73
+ approved: z.boolean().optional(),
74
+ feedbackDoc: z.string().optional(),
75
+ });
76
+
41
77
  /**
42
78
  * Zod schema for Task
43
79
  */
@@ -49,6 +85,18 @@ export const TaskSchema = z.object({
49
85
  testsPassed: z.boolean().optional(),
50
86
  testPlan: z.string().optional(),
51
87
  error: z.string().optional(),
88
+ plan: z.string().optional(),
89
+ consensusScore: z.number().optional(),
90
+ consensusIterations: z.number().optional(),
91
+ consensusApproved: z.boolean().optional(),
92
+ planDoc: z.string().optional(),
93
+ testResultsDoc: z.string().optional(),
94
+ implementationComplete: z.boolean().optional(),
95
+ // Per-app consensus tracking (fullstack)
96
+ frontendConsensus: AppConsensusTrackingSchema.optional(),
97
+ backendConsensus: AppConsensusTrackingSchema.optional(),
98
+ unifiedConsensus: AppConsensusTrackingSchema.optional(),
99
+ appTarget: z.enum(['frontend', 'backend', 'unified']).optional(),
52
100
  });
53
101
 
54
102
  /**
@@ -60,6 +108,29 @@ export interface Milestone {
60
108
  description: string;
61
109
  status: TaskStatus;
62
110
  tasks: Task[];
111
+ // Per-milestone consensus tracking
112
+ plan?: string; // Detailed milestone plan
113
+ consensusScore?: number; // Consensus score for milestone plan
114
+ consensusIterations?: number; // Number of iterations to reach consensus
115
+ consensusApproved?: boolean; // Whether milestone plan was approved
116
+ planDoc?: string; // Path: docs/plans/milestone-N/plan.md
117
+ // Milestone completion review
118
+ completionReview?: string; // Code review and summary
119
+ completionScore?: number; // Consensus score for completion
120
+ completionApproved?: boolean; // Whether milestone completion was approved
121
+ completionDoc?: string; // Path: docs/milestone_N_complete.md
122
+
123
+ // Per-app consensus tracking (fullstack projects)
124
+ frontendConsensus?: AppConsensusTracking;
125
+ backendConsensus?: AppConsensusTracking;
126
+ unifiedConsensus?: AppConsensusTracking;
127
+
128
+ // Feedback document paths (fullstack - separate by app)
129
+ feedbackDocs?: {
130
+ frontend?: string; // docs/plans/milestone-N/frontend/feedback.md
131
+ backend?: string; // docs/plans/milestone-N/backend/feedback.md
132
+ unified?: string; // docs/plans/milestone-N/unified/feedback.md
133
+ };
63
134
  }
64
135
 
65
136
  /**
@@ -71,6 +142,24 @@ export const MilestoneSchema = z.object({
71
142
  description: z.string(),
72
143
  status: TaskStatusSchema,
73
144
  tasks: z.array(TaskSchema),
145
+ plan: z.string().optional(),
146
+ consensusScore: z.number().optional(),
147
+ consensusIterations: z.number().optional(),
148
+ consensusApproved: z.boolean().optional(),
149
+ planDoc: z.string().optional(),
150
+ completionReview: z.string().optional(),
151
+ completionScore: z.number().optional(),
152
+ completionApproved: z.boolean().optional(),
153
+ completionDoc: z.string().optional(),
154
+ // Per-app consensus tracking (fullstack)
155
+ frontendConsensus: AppConsensusTrackingSchema.optional(),
156
+ backendConsensus: AppConsensusTrackingSchema.optional(),
157
+ unifiedConsensus: AppConsensusTrackingSchema.optional(),
158
+ feedbackDocs: z.object({
159
+ frontend: z.string().optional(),
160
+ backend: z.string().optional(),
161
+ unified: z.string().optional(),
162
+ }).optional(),
74
163
  });
75
164
 
76
165
  /**
@@ -102,7 +191,7 @@ export const ProjectStateSchema = z.object({
102
191
  id: z.string(),
103
192
  name: z.string(),
104
193
  idea: z.string(),
105
- language: z.enum(['python', 'typescript']),
194
+ language: z.enum(['python', 'typescript', 'fullstack']),
106
195
  openaiModel: z.enum(['gpt-4o', 'gpt-4o-mini', 'gpt-4-turbo', 'o1-preview', 'o1-mini']),
107
196
  phase: WorkflowPhaseSchema,
108
197
  status: ProjectStatusSchema,
@@ -0,0 +1,340 @@
1
+ /**
2
+ * Auto-fix module for automatically fixing build and TypeScript errors
3
+ * Uses Claude to analyze errors and apply fixes
4
+ */
5
+
6
+ import { promises as fs } from 'node:fs';
7
+ import path from 'node:path';
8
+ import { executePrompt } from '../adapters/claude.js';
9
+
10
+ /**
11
+ * Build error details
12
+ */
13
+ export interface BuildError {
14
+ file: string;
15
+ line?: number;
16
+ column?: number;
17
+ message: string;
18
+ code?: string;
19
+ }
20
+
21
+ /**
22
+ * Auto-fix result
23
+ */
24
+ export interface AutoFixResult {
25
+ success: boolean;
26
+ fixedErrors: number;
27
+ remainingErrors: number;
28
+ attempts: number;
29
+ fixes: Array<{
30
+ file: string;
31
+ description: string;
32
+ }>;
33
+ error?: string;
34
+ }
35
+
36
+ /**
37
+ * Parse TypeScript compiler errors from output
38
+ */
39
+ export function parseTypeScriptErrors(output: string): BuildError[] {
40
+ const errors: BuildError[] = [];
41
+ const errorPattern = /^(.+?)\((\d+),(\d+)\): error (TS\d+): (.+)$/gm;
42
+
43
+ let match;
44
+ while ((match = errorPattern.exec(output)) !== null) {
45
+ errors.push({
46
+ file: match[1],
47
+ line: parseInt(match[2], 10),
48
+ column: parseInt(match[3], 10),
49
+ code: match[4],
50
+ message: match[5],
51
+ });
52
+ }
53
+
54
+ return errors;
55
+ }
56
+
57
+ /**
58
+ * Group errors by file for efficient fixing
59
+ */
60
+ function groupErrorsByFile(errors: BuildError[]): Map<string, BuildError[]> {
61
+ const grouped = new Map<string, BuildError[]>();
62
+
63
+ for (const error of errors) {
64
+ const existing = grouped.get(error.file) || [];
65
+ existing.push(error);
66
+ grouped.set(error.file, existing);
67
+ }
68
+
69
+ return grouped;
70
+ }
71
+
72
+ /**
73
+ * Generate fix prompt for a file with errors
74
+ */
75
+ function generateFixPrompt(filePath: string, fileContent: string, errors: BuildError[]): string {
76
+ const errorList = errors
77
+ .map(e => `- Line ${e.line}: ${e.code} - ${e.message}`)
78
+ .join('\n');
79
+
80
+ return `
81
+ Fix the following TypeScript errors in this file. Return ONLY the complete fixed file content, no explanations.
82
+
83
+ ## File: ${filePath}
84
+
85
+ ## Errors to fix:
86
+ ${errorList}
87
+
88
+ ## Current file content:
89
+ \`\`\`typescript
90
+ ${fileContent}
91
+ \`\`\`
92
+
93
+ ## Instructions:
94
+ 1. Fix ALL the errors listed above
95
+ 2. Do not change any working code
96
+ 3. Preserve all imports, exports, and functionality
97
+ 4. For type-only exports (interfaces, types), use \`export type { ... }\` syntax
98
+ 5. For unused variables, either use them or prefix with underscore
99
+ 6. For missing properties, add them with appropriate default values
100
+ 7. Return ONLY the fixed TypeScript code, no markdown formatting
101
+
102
+ Fixed code:
103
+ `.trim();
104
+ }
105
+
106
+ /**
107
+ * Extract code from Claude's response
108
+ */
109
+ function extractCodeFromResponse(response: string): string {
110
+ // Try to extract from code block first
111
+ const codeBlockMatch = response.match(/```(?:typescript|ts)?\n([\s\S]*?)\n```/);
112
+ if (codeBlockMatch) {
113
+ return codeBlockMatch[1].trim();
114
+ }
115
+
116
+ // If no code block, assume the entire response is code
117
+ // But strip any leading/trailing explanation text
118
+ const lines = response.split('\n');
119
+
120
+ // Find first line that looks like code (import, export, const, etc.)
121
+ const codeStartPatterns = [
122
+ /^import\s/,
123
+ /^export\s/,
124
+ /^const\s/,
125
+ /^let\s/,
126
+ /^var\s/,
127
+ /^function\s/,
128
+ /^class\s/,
129
+ /^interface\s/,
130
+ /^type\s/,
131
+ /^\/\//,
132
+ /^\/\*/,
133
+ /^['"`]/,
134
+ ];
135
+
136
+ let startIndex = 0;
137
+ for (let i = 0; i < lines.length; i++) {
138
+ const line = lines[i].trim();
139
+ if (codeStartPatterns.some(p => p.test(line))) {
140
+ startIndex = i;
141
+ break;
142
+ }
143
+ }
144
+
145
+ return lines.slice(startIndex).join('\n').trim();
146
+ }
147
+
148
+ /**
149
+ * Auto-fix TypeScript errors in a project
150
+ */
151
+ export async function autoFixTypeScriptErrors(
152
+ projectDir: string,
153
+ buildOutput: string,
154
+ maxAttempts: number = 3,
155
+ onProgress?: (message: string) => void
156
+ ): Promise<AutoFixResult> {
157
+ const fixes: Array<{ file: string; description: string }> = [];
158
+ let attempts = 0;
159
+ let currentOutput = buildOutput;
160
+
161
+ while (attempts < maxAttempts) {
162
+ attempts++;
163
+ onProgress?.(`Auto-fix attempt ${attempts}/${maxAttempts}...`);
164
+
165
+ const errors = parseTypeScriptErrors(currentOutput);
166
+
167
+ if (errors.length === 0) {
168
+ onProgress?.('No TypeScript errors found');
169
+ return {
170
+ success: true,
171
+ fixedErrors: fixes.length,
172
+ remainingErrors: 0,
173
+ attempts,
174
+ fixes,
175
+ };
176
+ }
177
+
178
+ onProgress?.(`Found ${errors.length} TypeScript error(s) to fix`);
179
+
180
+ // Group errors by file
181
+ const errorsByFile = groupErrorsByFile(errors);
182
+ let fixedInThisAttempt = 0;
183
+
184
+ // Fix each file
185
+ for (const [filePath, fileErrors] of errorsByFile) {
186
+ const absolutePath = path.isAbsolute(filePath) ? filePath : path.join(projectDir, filePath);
187
+
188
+ try {
189
+ // Read current file content
190
+ const fileContent = await fs.readFile(absolutePath, 'utf-8');
191
+
192
+ onProgress?.(`Fixing ${fileErrors.length} error(s) in ${path.basename(filePath)}...`);
193
+
194
+ // Generate fix prompt
195
+ const prompt = generateFixPrompt(filePath, fileContent, fileErrors);
196
+
197
+ // Ask Claude to fix
198
+ const result = await executePrompt(prompt, {
199
+ allowedTools: [],
200
+ permissionMode: 'default',
201
+ });
202
+
203
+ if (result.success && result.response) {
204
+ const fixedCode = extractCodeFromResponse(result.response);
205
+
206
+ // Validate the fix is not empty and looks like code
207
+ if (fixedCode.length > 100 && (fixedCode.includes('import') || fixedCode.includes('export'))) {
208
+ // Write fixed content
209
+ await fs.writeFile(absolutePath, fixedCode, 'utf-8');
210
+
211
+ fixes.push({
212
+ file: filePath,
213
+ description: `Fixed ${fileErrors.length} error(s): ${fileErrors.map(e => e.code).join(', ')}`,
214
+ });
215
+
216
+ fixedInThisAttempt += fileErrors.length;
217
+ onProgress?.(`Fixed ${path.basename(filePath)}`);
218
+ } else {
219
+ onProgress?.(`Skipped ${path.basename(filePath)} - fix doesn't look valid`);
220
+ }
221
+ } else {
222
+ onProgress?.(`Failed to get fix for ${path.basename(filePath)}: ${result.error}`);
223
+ }
224
+ } catch (err) {
225
+ onProgress?.(`Error fixing ${filePath}: ${err instanceof Error ? err.message : 'Unknown error'}`);
226
+ }
227
+ }
228
+
229
+ if (fixedInThisAttempt === 0) {
230
+ onProgress?.('No fixes were applied in this attempt');
231
+ break;
232
+ }
233
+
234
+ // Re-run TypeScript check to get remaining errors
235
+ onProgress?.('Re-checking for remaining errors...');
236
+ const { exec } = await import('node:child_process');
237
+ const { promisify } = await import('node:util');
238
+ const execAsync = promisify(exec);
239
+
240
+ try {
241
+ await execAsync('npx tsc --noEmit', { cwd: projectDir });
242
+ currentOutput = '';
243
+ } catch (err: unknown) {
244
+ if (err && typeof err === 'object' && 'stdout' in err) {
245
+ currentOutput = (err as { stdout: string; stderr: string }).stdout + (err as { stdout: string; stderr: string }).stderr;
246
+ } else {
247
+ currentOutput = '';
248
+ }
249
+ }
250
+ }
251
+
252
+ // Final error count
253
+ const remainingErrors = parseTypeScriptErrors(currentOutput);
254
+
255
+ return {
256
+ success: remainingErrors.length === 0,
257
+ fixedErrors: fixes.length,
258
+ remainingErrors: remainingErrors.length,
259
+ attempts,
260
+ fixes,
261
+ error: remainingErrors.length > 0
262
+ ? `${remainingErrors.length} error(s) remain after ${attempts} fix attempt(s)`
263
+ : undefined,
264
+ };
265
+ }
266
+
267
+ /**
268
+ * Run build with auto-fix
269
+ */
270
+ export async function buildWithAutoFix(
271
+ projectDir: string,
272
+ language: string,
273
+ maxAttempts: number = 3,
274
+ onProgress?: (message: string) => void
275
+ ): Promise<{ success: boolean; output: string; autoFixed: boolean }> {
276
+ const { exec } = await import('node:child_process');
277
+ const { promisify } = await import('node:util');
278
+ const execAsync = promisify(exec);
279
+
280
+ // Determine build command based on language
281
+ let buildCommand: string;
282
+ if (language === 'typescript' || language === 'javascript') {
283
+ // Check for package.json build script
284
+ try {
285
+ const pkgJson = JSON.parse(await fs.readFile(path.join(projectDir, 'package.json'), 'utf-8'));
286
+ if (pkgJson.scripts?.build) {
287
+ buildCommand = 'npm run build';
288
+ } else {
289
+ buildCommand = 'npx tsc --noEmit';
290
+ }
291
+ } catch {
292
+ buildCommand = 'npx tsc --noEmit';
293
+ }
294
+ } else if (language === 'python') {
295
+ buildCommand = 'python -m py_compile $(find . -name "*.py" -not -path "./venv/*")';
296
+ } else {
297
+ buildCommand = 'npm run build';
298
+ }
299
+
300
+ // Initial build attempt
301
+ onProgress?.(`Running build: ${buildCommand}`);
302
+
303
+ try {
304
+ const { stdout, stderr } = await execAsync(buildCommand, {
305
+ cwd: projectDir,
306
+ timeout: 120000,
307
+ });
308
+ return { success: true, output: stdout + stderr, autoFixed: false };
309
+ } catch (err: unknown) {
310
+ const output = err && typeof err === 'object' && 'stdout' in err
311
+ ? (err as { stdout: string; stderr: string }).stdout + (err as { stdout: string; stderr: string }).stderr
312
+ : String(err);
313
+
314
+ onProgress?.('Build failed, attempting auto-fix...');
315
+
316
+ // Try auto-fix for TypeScript errors
317
+ if (language === 'typescript' || language === 'javascript') {
318
+ const fixResult = await autoFixTypeScriptErrors(projectDir, output, maxAttempts, onProgress);
319
+
320
+ if (fixResult.success) {
321
+ // Retry build after fixes
322
+ onProgress?.('Auto-fix successful, retrying build...');
323
+ try {
324
+ const { stdout: retryStdout, stderr: retryStderr } = await execAsync(buildCommand, {
325
+ cwd: projectDir,
326
+ timeout: 120000,
327
+ });
328
+ return { success: true, output: retryStdout + retryStderr, autoFixed: true };
329
+ } catch (retryErr: unknown) {
330
+ const retryOutput = retryErr && typeof retryErr === 'object' && 'stdout' in retryErr
331
+ ? (retryErr as { stdout: string; stderr: string }).stdout + (retryErr as { stdout: string; stderr: string }).stderr
332
+ : String(retryErr);
333
+ return { success: false, output: retryOutput, autoFixed: true };
334
+ }
335
+ }
336
+ }
337
+
338
+ return { success: false, output, autoFixed: false };
339
+ }
340
+ }