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.
- package/.env.example +24 -1
- package/CONTRIBUTING.md +275 -0
- package/OPEN_SOURCE_MANIFESTO.md +172 -0
- package/README.md +832 -123
- package/dist/adapters/claude.d.ts +19 -4
- package/dist/adapters/claude.d.ts.map +1 -1
- package/dist/adapters/claude.js +908 -42
- package/dist/adapters/claude.js.map +1 -1
- package/dist/adapters/gemini.d.ts +55 -0
- package/dist/adapters/gemini.d.ts.map +1 -0
- package/dist/adapters/gemini.js +318 -0
- package/dist/adapters/gemini.js.map +1 -0
- package/dist/adapters/grok.d.ts +73 -0
- package/dist/adapters/grok.d.ts.map +1 -0
- package/dist/adapters/grok.js +430 -0
- package/dist/adapters/grok.js.map +1 -0
- package/dist/adapters/openai.d.ts +1 -1
- package/dist/adapters/openai.d.ts.map +1 -1
- package/dist/adapters/openai.js +47 -8
- package/dist/adapters/openai.js.map +1 -1
- package/dist/auth/claude.d.ts +11 -9
- package/dist/auth/claude.d.ts.map +1 -1
- package/dist/auth/claude.js +107 -71
- package/dist/auth/claude.js.map +1 -1
- package/dist/auth/gemini.d.ts +58 -0
- package/dist/auth/gemini.d.ts.map +1 -0
- package/dist/auth/gemini.js +172 -0
- package/dist/auth/gemini.js.map +1 -0
- package/dist/auth/grok.d.ts +73 -0
- package/dist/auth/grok.d.ts.map +1 -0
- package/dist/auth/grok.js +211 -0
- package/dist/auth/grok.js.map +1 -0
- package/dist/auth/index.d.ts +14 -7
- package/dist/auth/index.d.ts.map +1 -1
- package/dist/auth/index.js +41 -6
- package/dist/auth/index.js.map +1 -1
- package/dist/auth/keychain.d.ts +20 -7
- package/dist/auth/keychain.d.ts.map +1 -1
- package/dist/auth/keychain.js +85 -29
- package/dist/auth/keychain.js.map +1 -1
- package/dist/auth/openai.d.ts +2 -2
- package/dist/auth/openai.d.ts.map +1 -1
- package/dist/auth/openai.js +30 -32
- package/dist/auth/openai.js.map +1 -1
- package/dist/cli/commands/auth.d.ts +1 -1
- package/dist/cli/commands/auth.d.ts.map +1 -1
- package/dist/cli/commands/auth.js +79 -8
- package/dist/cli/commands/auth.js.map +1 -1
- package/dist/cli/commands/create.d.ts.map +1 -1
- package/dist/cli/commands/create.js +15 -4
- package/dist/cli/commands/create.js.map +1 -1
- package/dist/cli/interactive.d.ts.map +1 -1
- package/dist/cli/interactive.js +1494 -114
- package/dist/cli/interactive.js.map +1 -1
- package/dist/config/defaults.d.ts +9 -1
- package/dist/config/defaults.d.ts.map +1 -1
- package/dist/config/defaults.js +19 -2
- package/dist/config/defaults.js.map +1 -1
- package/dist/config/index.d.ts +19 -0
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js +33 -1
- package/dist/config/index.js.map +1 -1
- package/dist/config/schema.d.ts +47 -0
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/config/schema.js +29 -1
- package/dist/config/schema.js.map +1 -1
- package/dist/generators/fullstack.d.ts +32 -0
- package/dist/generators/fullstack.d.ts.map +1 -0
- package/dist/generators/fullstack.js +497 -0
- package/dist/generators/fullstack.js.map +1 -0
- package/dist/generators/index.d.ts +4 -3
- package/dist/generators/index.d.ts.map +1 -1
- package/dist/generators/index.js +15 -1
- package/dist/generators/index.js.map +1 -1
- package/dist/generators/python.d.ts +17 -1
- package/dist/generators/python.d.ts.map +1 -1
- package/dist/generators/python.js +34 -20
- package/dist/generators/python.js.map +1 -1
- package/dist/generators/templates/fullstack.d.ts +113 -0
- package/dist/generators/templates/fullstack.d.ts.map +1 -0
- package/dist/generators/templates/fullstack.js +1004 -0
- package/dist/generators/templates/fullstack.js.map +1 -0
- package/dist/generators/typescript.d.ts +19 -1
- package/dist/generators/typescript.d.ts.map +1 -1
- package/dist/generators/typescript.js +37 -20
- package/dist/generators/typescript.js.map +1 -1
- package/dist/state/index.d.ts +108 -0
- package/dist/state/index.d.ts.map +1 -1
- package/dist/state/index.js +551 -4
- package/dist/state/index.js.map +1 -1
- package/dist/state/registry.d.ts +52 -0
- package/dist/state/registry.d.ts.map +1 -0
- package/dist/state/registry.js +215 -0
- package/dist/state/registry.js.map +1 -0
- package/dist/types/cli.d.ts +8 -0
- package/dist/types/cli.d.ts.map +1 -1
- package/dist/types/cli.js.map +1 -1
- package/dist/types/consensus.d.ts +186 -4
- package/dist/types/consensus.d.ts.map +1 -1
- package/dist/types/consensus.js +35 -3
- package/dist/types/consensus.js.map +1 -1
- package/dist/types/project.d.ts +76 -0
- package/dist/types/project.d.ts.map +1 -1
- package/dist/types/project.js +1 -1
- package/dist/types/project.js.map +1 -1
- package/dist/types/workflow.d.ts +217 -16
- package/dist/types/workflow.d.ts.map +1 -1
- package/dist/types/workflow.js +40 -1
- package/dist/types/workflow.js.map +1 -1
- package/dist/workflow/auto-fix.d.ts +45 -0
- package/dist/workflow/auto-fix.d.ts.map +1 -0
- package/dist/workflow/auto-fix.js +274 -0
- package/dist/workflow/auto-fix.js.map +1 -0
- package/dist/workflow/consensus.d.ts +70 -2
- package/dist/workflow/consensus.d.ts.map +1 -1
- package/dist/workflow/consensus.js +872 -17
- package/dist/workflow/consensus.js.map +1 -1
- package/dist/workflow/execution-mode.d.ts +10 -4
- package/dist/workflow/execution-mode.d.ts.map +1 -1
- package/dist/workflow/execution-mode.js +547 -58
- package/dist/workflow/execution-mode.js.map +1 -1
- package/dist/workflow/index.d.ts +14 -2
- package/dist/workflow/index.d.ts.map +1 -1
- package/dist/workflow/index.js +69 -6
- package/dist/workflow/index.js.map +1 -1
- package/dist/workflow/milestone-workflow.d.ts +34 -0
- package/dist/workflow/milestone-workflow.d.ts.map +1 -0
- package/dist/workflow/milestone-workflow.js +414 -0
- package/dist/workflow/milestone-workflow.js.map +1 -0
- package/dist/workflow/plan-mode.d.ts +80 -3
- package/dist/workflow/plan-mode.d.ts.map +1 -1
- package/dist/workflow/plan-mode.js +767 -49
- package/dist/workflow/plan-mode.js.map +1 -1
- package/dist/workflow/plan-storage.d.ts +386 -0
- package/dist/workflow/plan-storage.d.ts.map +1 -0
- package/dist/workflow/plan-storage.js +878 -0
- package/dist/workflow/plan-storage.js.map +1 -0
- package/dist/workflow/project-verification.d.ts +37 -0
- package/dist/workflow/project-verification.d.ts.map +1 -0
- package/dist/workflow/project-verification.js +381 -0
- package/dist/workflow/project-verification.js.map +1 -0
- package/dist/workflow/task-workflow.d.ts +37 -0
- package/dist/workflow/task-workflow.d.ts.map +1 -0
- package/dist/workflow/task-workflow.js +386 -0
- package/dist/workflow/task-workflow.js.map +1 -0
- package/dist/workflow/test-runner.d.ts +9 -0
- package/dist/workflow/test-runner.d.ts.map +1 -1
- package/dist/workflow/test-runner.js +101 -5
- package/dist/workflow/test-runner.js.map +1 -1
- package/dist/workflow/ui-designer.d.ts +82 -0
- package/dist/workflow/ui-designer.d.ts.map +1 -0
- package/dist/workflow/ui-designer.js +234 -0
- package/dist/workflow/ui-designer.js.map +1 -0
- package/dist/workflow/ui-setup.d.ts +58 -0
- package/dist/workflow/ui-setup.d.ts.map +1 -0
- package/dist/workflow/ui-setup.js +685 -0
- package/dist/workflow/ui-setup.js.map +1 -0
- package/dist/workflow/ui-verification.d.ts +114 -0
- package/dist/workflow/ui-verification.d.ts.map +1 -0
- package/dist/workflow/ui-verification.js +258 -0
- package/dist/workflow/ui-verification.js.map +1 -0
- package/dist/workflow/workflow-logger.d.ts +110 -0
- package/dist/workflow/workflow-logger.d.ts.map +1 -0
- package/dist/workflow/workflow-logger.js +267 -0
- package/dist/workflow/workflow-logger.js.map +1 -0
- package/dist/workflow/workspace-manager.d.ts +342 -0
- package/dist/workflow/workspace-manager.d.ts.map +1 -0
- package/dist/workflow/workspace-manager.js +733 -0
- package/dist/workflow/workspace-manager.js.map +1 -0
- package/package.json +2 -2
- package/src/adapters/claude.ts +1067 -47
- package/src/adapters/gemini.ts +373 -0
- package/src/adapters/grok.ts +492 -0
- package/src/adapters/openai.ts +48 -9
- package/src/auth/claude.ts +120 -78
- package/src/auth/gemini.ts +207 -0
- package/src/auth/grok.ts +255 -0
- package/src/auth/index.ts +47 -9
- package/src/auth/keychain.ts +95 -28
- package/src/auth/openai.ts +29 -36
- package/src/cli/commands/auth.ts +89 -10
- package/src/cli/commands/create.ts +13 -4
- package/src/cli/interactive.ts +1774 -142
- package/src/config/defaults.ts +19 -2
- package/src/config/index.ts +36 -1
- package/src/config/schema.ts +30 -1
- package/src/generators/fullstack.ts +551 -0
- package/src/generators/index.ts +25 -1
- package/src/generators/python.ts +65 -20
- package/src/generators/templates/fullstack.ts +1047 -0
- package/src/generators/typescript.ts +69 -20
- package/src/state/index.ts +713 -4
- package/src/state/registry.ts +278 -0
- package/src/types/cli.ts +8 -0
- package/src/types/consensus.ts +197 -6
- package/src/types/project.ts +82 -1
- package/src/types/workflow.ts +90 -1
- package/src/workflow/auto-fix.ts +340 -0
- package/src/workflow/consensus.ts +1180 -16
- package/src/workflow/execution-mode.ts +673 -74
- package/src/workflow/index.ts +95 -6
- package/src/workflow/milestone-workflow.ts +576 -0
- package/src/workflow/plan-mode.ts +924 -50
- package/src/workflow/plan-storage.ts +1282 -0
- package/src/workflow/project-verification.ts +471 -0
- package/src/workflow/task-workflow.ts +528 -0
- package/src/workflow/test-runner.ts +120 -5
- package/src/workflow/ui-designer.ts +337 -0
- package/src/workflow/ui-setup.ts +797 -0
- package/src/workflow/ui-verification.ts +357 -0
- package/src/workflow/workflow-logger.ts +353 -0
- package/src/workflow/workspace-manager.ts +912 -0
- package/tests/config/config.test.ts +1 -1
- package/tests/types/consensus.test.ts +3 -3
- package/tests/workflow/plan-mode.test.ts +213 -0
- package/tests/workflow/test-runner.test.ts +5 -3
package/src/types/workflow.ts
CHANGED
|
@@ -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
|
+
}
|