@nclamvn/vibecode-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.
- package/bin/vibecode.js +17 -0
- package/package.json +1 -1
- package/src/commands/build.js +495 -3
- package/src/commands/config.js +149 -0
- package/src/config/constants.js +1 -1
- package/src/core/error-analyzer.js +237 -0
- package/src/core/fix-generator.js +195 -0
- package/src/core/iteration.js +226 -0
- package/src/core/test-runner.js +248 -0
- package/src/index.js +7 -0
- package/src/providers/claude-code.js +159 -0
- package/src/providers/index.js +45 -0
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
2
|
+
// VIBECODE CLI - Config Command
|
|
3
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
4
|
+
|
|
5
|
+
import chalk from 'chalk';
|
|
6
|
+
import path from 'path';
|
|
7
|
+
import { workspaceExists, getWorkspacePath } from '../core/workspace.js';
|
|
8
|
+
import { pathExists, readJson, writeJson } from '../utils/files.js';
|
|
9
|
+
import { PROVIDERS, getDefaultProvider } from '../providers/index.js';
|
|
10
|
+
import { printBox, printError, printSuccess, printInfo } from '../ui/output.js';
|
|
11
|
+
|
|
12
|
+
const CONFIG_FILE = 'config.json';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Get config file path
|
|
16
|
+
*/
|
|
17
|
+
function getConfigPath() {
|
|
18
|
+
return path.join(getWorkspacePath(), CONFIG_FILE);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Load config
|
|
23
|
+
*/
|
|
24
|
+
async function loadConfig() {
|
|
25
|
+
const configPath = getConfigPath();
|
|
26
|
+
if (await pathExists(configPath)) {
|
|
27
|
+
return await readJson(configPath);
|
|
28
|
+
}
|
|
29
|
+
return getDefaultConfig();
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Save config
|
|
34
|
+
*/
|
|
35
|
+
async function saveConfig(config) {
|
|
36
|
+
const configPath = getConfigPath();
|
|
37
|
+
await writeJson(configPath, config);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Get default config
|
|
42
|
+
*/
|
|
43
|
+
function getDefaultConfig() {
|
|
44
|
+
return {
|
|
45
|
+
provider: 'claude-code',
|
|
46
|
+
claudeCode: {
|
|
47
|
+
flags: ['--dangerously-skip-permissions'],
|
|
48
|
+
timeout: 30 // minutes
|
|
49
|
+
},
|
|
50
|
+
autoEvidence: true,
|
|
51
|
+
verbose: false
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export async function configCommand(options = {}) {
|
|
56
|
+
try {
|
|
57
|
+
// Check workspace for set operations
|
|
58
|
+
if (options.provider || options.set) {
|
|
59
|
+
if (!await workspaceExists()) {
|
|
60
|
+
printError('No Vibecode workspace found. Run `vibecode init` first.');
|
|
61
|
+
process.exit(1);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Show current config
|
|
66
|
+
if (options.show || (!options.provider && !options.set)) {
|
|
67
|
+
await showConfig();
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Set provider
|
|
72
|
+
if (options.provider) {
|
|
73
|
+
await setProvider(options.provider);
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
} catch (error) {
|
|
78
|
+
printError(error.message);
|
|
79
|
+
process.exit(1);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
async function showConfig() {
|
|
84
|
+
console.log();
|
|
85
|
+
|
|
86
|
+
// Check if workspace exists
|
|
87
|
+
if (!await workspaceExists()) {
|
|
88
|
+
// Show default config
|
|
89
|
+
console.log(chalk.cyan('Default Configuration (no workspace):'));
|
|
90
|
+
console.log();
|
|
91
|
+
const defaultConfig = getDefaultConfig();
|
|
92
|
+
console.log(chalk.gray('Provider: ') + chalk.white(defaultConfig.provider));
|
|
93
|
+
console.log(chalk.gray('Flags: ') + chalk.white(defaultConfig.claudeCode.flags.join(', ')));
|
|
94
|
+
console.log(chalk.gray('Timeout: ') + chalk.white(`${defaultConfig.claudeCode.timeout} minutes`));
|
|
95
|
+
console.log();
|
|
96
|
+
console.log(chalk.gray('Run `vibecode init` to create a workspace.'));
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const config = await loadConfig();
|
|
101
|
+
|
|
102
|
+
const content = `Current Configuration
|
|
103
|
+
|
|
104
|
+
Provider: ${config.provider}
|
|
105
|
+
Flags: ${config.claudeCode?.flags?.join(', ') || 'none'}
|
|
106
|
+
Timeout: ${config.claudeCode?.timeout || 30} minutes
|
|
107
|
+
Auto Evidence: ${config.autoEvidence ? 'enabled' : 'disabled'}
|
|
108
|
+
Verbose: ${config.verbose ? 'enabled' : 'disabled'}`;
|
|
109
|
+
|
|
110
|
+
printBox(content, { borderColor: 'cyan' });
|
|
111
|
+
|
|
112
|
+
// Show available providers
|
|
113
|
+
console.log();
|
|
114
|
+
console.log(chalk.cyan('Available Providers:'));
|
|
115
|
+
Object.entries(PROVIDERS).forEach(([key, provider]) => {
|
|
116
|
+
const status = provider.available ? chalk.green('available') : chalk.gray('coming soon');
|
|
117
|
+
const current = key === config.provider ? chalk.yellow(' (current)') : '';
|
|
118
|
+
console.log(chalk.gray(` • ${key}: ${provider.description} [${status}]${current}`));
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
console.log();
|
|
122
|
+
console.log(chalk.gray('To change: vibecode config --provider <name>'));
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
async function setProvider(providerName) {
|
|
126
|
+
// Validate provider
|
|
127
|
+
if (!PROVIDERS[providerName]) {
|
|
128
|
+
printError(`Unknown provider: ${providerName}`);
|
|
129
|
+
console.log();
|
|
130
|
+
console.log(chalk.cyan('Available providers:'));
|
|
131
|
+
Object.keys(PROVIDERS).forEach(key => {
|
|
132
|
+
console.log(chalk.gray(` • ${key}`));
|
|
133
|
+
});
|
|
134
|
+
process.exit(1);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (!PROVIDERS[providerName].available) {
|
|
138
|
+
printError(`Provider "${providerName}" is not yet available.`);
|
|
139
|
+
process.exit(1);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Load and update config
|
|
143
|
+
const config = await loadConfig();
|
|
144
|
+
config.provider = providerName;
|
|
145
|
+
await saveConfig(config);
|
|
146
|
+
|
|
147
|
+
printSuccess(`Provider set to: ${providerName}`);
|
|
148
|
+
console.log(chalk.gray(`Config saved to: ${getConfigPath()}`));
|
|
149
|
+
}
|
package/src/config/constants.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// Spec Hash: 0fe43335f5a325e3279a079ce616c052
|
|
4
4
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
5
5
|
|
|
6
|
-
export const VERSION = '1.1
|
|
6
|
+
export const VERSION = '1.0.1';
|
|
7
7
|
export const SPEC_HASH = '0fe43335f5a325e3279a079ce616c052';
|
|
8
8
|
|
|
9
9
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
2
|
+
// VIBECODE CLI - Error Analyzer
|
|
3
|
+
// Intelligent error analysis for iterative builds
|
|
4
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Analyze test results and extract actionable errors
|
|
8
|
+
* @param {TestResults} testResult - Results from test runner
|
|
9
|
+
* @returns {AnalyzedError[]}
|
|
10
|
+
*/
|
|
11
|
+
export function analyzeErrors(testResult) {
|
|
12
|
+
const errors = [];
|
|
13
|
+
|
|
14
|
+
for (const test of testResult.tests) {
|
|
15
|
+
if (!test.passed) {
|
|
16
|
+
const testErrors = test.errors || [];
|
|
17
|
+
|
|
18
|
+
for (const error of testErrors) {
|
|
19
|
+
errors.push({
|
|
20
|
+
source: test.name,
|
|
21
|
+
type: categorizeError(error),
|
|
22
|
+
file: error.file || null,
|
|
23
|
+
line: error.line || null,
|
|
24
|
+
column: error.column || null,
|
|
25
|
+
message: error.message,
|
|
26
|
+
suggestion: generateSuggestion(error),
|
|
27
|
+
priority: calculatePriority(error),
|
|
28
|
+
raw: error.raw
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// If no specific errors but test failed, add generic error
|
|
33
|
+
if (testErrors.length === 0) {
|
|
34
|
+
errors.push({
|
|
35
|
+
source: test.name,
|
|
36
|
+
type: 'unknown',
|
|
37
|
+
message: test.error || `${test.name} failed`,
|
|
38
|
+
suggestion: `Check ${test.name} output for details`,
|
|
39
|
+
priority: 'medium',
|
|
40
|
+
raw: test.output
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Sort by priority
|
|
47
|
+
return errors.sort((a, b) => {
|
|
48
|
+
const priorityOrder = { critical: 0, high: 1, medium: 2, low: 3 };
|
|
49
|
+
return (priorityOrder[a.priority] || 2) - (priorityOrder[b.priority] || 2);
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Categorize error type
|
|
55
|
+
*/
|
|
56
|
+
function categorizeError(error) {
|
|
57
|
+
const message = (error.message || '').toLowerCase();
|
|
58
|
+
const raw = (error.raw || '').toLowerCase();
|
|
59
|
+
const combined = message + ' ' + raw;
|
|
60
|
+
|
|
61
|
+
if (combined.includes('syntaxerror') || combined.includes('unexpected token')) {
|
|
62
|
+
return 'syntax';
|
|
63
|
+
}
|
|
64
|
+
if (combined.includes('typeerror') || combined.includes('type error') || combined.includes('is not a function')) {
|
|
65
|
+
return 'type';
|
|
66
|
+
}
|
|
67
|
+
if (combined.includes('referenceerror') || combined.includes('is not defined')) {
|
|
68
|
+
return 'reference';
|
|
69
|
+
}
|
|
70
|
+
if (combined.includes('import') || combined.includes('require') || combined.includes('module not found')) {
|
|
71
|
+
return 'import';
|
|
72
|
+
}
|
|
73
|
+
if (combined.includes('eslint') || combined.includes('lint')) {
|
|
74
|
+
return 'lint';
|
|
75
|
+
}
|
|
76
|
+
if (combined.includes('test') || combined.includes('expect') || combined.includes('assert')) {
|
|
77
|
+
return 'test';
|
|
78
|
+
}
|
|
79
|
+
if (combined.includes('typescript') || combined.includes('ts(')) {
|
|
80
|
+
return 'typescript';
|
|
81
|
+
}
|
|
82
|
+
if (combined.includes('build') || combined.includes('compile')) {
|
|
83
|
+
return 'build';
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return 'unknown';
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Generate fix suggestion based on error type
|
|
91
|
+
*/
|
|
92
|
+
function generateSuggestion(error) {
|
|
93
|
+
const type = categorizeError(error);
|
|
94
|
+
const message = error.message || '';
|
|
95
|
+
|
|
96
|
+
switch (type) {
|
|
97
|
+
case 'syntax':
|
|
98
|
+
return 'Check for missing brackets, semicolons, or typos near the error location';
|
|
99
|
+
|
|
100
|
+
case 'type':
|
|
101
|
+
if (message.includes('undefined')) {
|
|
102
|
+
return 'Check if the variable/property is properly initialized';
|
|
103
|
+
}
|
|
104
|
+
if (message.includes('is not a function')) {
|
|
105
|
+
return 'Verify the function exists and is properly imported';
|
|
106
|
+
}
|
|
107
|
+
return 'Check type compatibility and ensure proper type handling';
|
|
108
|
+
|
|
109
|
+
case 'reference':
|
|
110
|
+
return 'Ensure the variable/function is defined or imported before use';
|
|
111
|
+
|
|
112
|
+
case 'import':
|
|
113
|
+
if (message.includes('module not found')) {
|
|
114
|
+
return 'Install missing package with npm install or fix import path';
|
|
115
|
+
}
|
|
116
|
+
return 'Check import path and ensure the module exports correctly';
|
|
117
|
+
|
|
118
|
+
case 'lint':
|
|
119
|
+
return 'Fix the linting issue as specified in the error message';
|
|
120
|
+
|
|
121
|
+
case 'test':
|
|
122
|
+
return 'Update the implementation to match expected behavior, or fix the test assertion';
|
|
123
|
+
|
|
124
|
+
case 'typescript':
|
|
125
|
+
return 'Fix type errors by adding proper types or type guards';
|
|
126
|
+
|
|
127
|
+
case 'build':
|
|
128
|
+
return 'Check build configuration and dependencies';
|
|
129
|
+
|
|
130
|
+
default:
|
|
131
|
+
return 'Review the error message and fix accordingly';
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Calculate error priority
|
|
137
|
+
*/
|
|
138
|
+
function calculatePriority(error) {
|
|
139
|
+
const type = categorizeError(error);
|
|
140
|
+
|
|
141
|
+
// Critical - blocks everything
|
|
142
|
+
if (type === 'syntax' || type === 'import') {
|
|
143
|
+
return 'critical';
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// High - likely causes cascading failures
|
|
147
|
+
if (type === 'reference' || type === 'type') {
|
|
148
|
+
return 'high';
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Medium - should be fixed
|
|
152
|
+
if (type === 'typescript' || type === 'build' || type === 'test') {
|
|
153
|
+
return 'medium';
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Low - nice to fix
|
|
157
|
+
if (type === 'lint') {
|
|
158
|
+
return 'low';
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return 'medium';
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Group errors by file for better organization
|
|
166
|
+
*/
|
|
167
|
+
export function groupErrorsByFile(errors) {
|
|
168
|
+
const grouped = {};
|
|
169
|
+
|
|
170
|
+
for (const error of errors) {
|
|
171
|
+
const file = error.file || 'unknown';
|
|
172
|
+
if (!grouped[file]) {
|
|
173
|
+
grouped[file] = [];
|
|
174
|
+
}
|
|
175
|
+
grouped[file].push(error);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return grouped;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Get unique files with errors
|
|
183
|
+
*/
|
|
184
|
+
export function getAffectedFiles(errors) {
|
|
185
|
+
const files = new Set();
|
|
186
|
+
for (const error of errors) {
|
|
187
|
+
if (error.file) {
|
|
188
|
+
files.add(error.file);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
return Array.from(files);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Format errors for display
|
|
196
|
+
*/
|
|
197
|
+
export function formatErrors(errors) {
|
|
198
|
+
const lines = [];
|
|
199
|
+
|
|
200
|
+
lines.push(`Found ${errors.length} error(s):`);
|
|
201
|
+
lines.push('');
|
|
202
|
+
|
|
203
|
+
const grouped = groupErrorsByFile(errors);
|
|
204
|
+
|
|
205
|
+
for (const [file, fileErrors] of Object.entries(grouped)) {
|
|
206
|
+
lines.push(`📄 ${file}`);
|
|
207
|
+
for (const error of fileErrors) {
|
|
208
|
+
const loc = error.line ? `:${error.line}` : '';
|
|
209
|
+
const priority = error.priority === 'critical' ? '🔴' :
|
|
210
|
+
error.priority === 'high' ? '🟠' :
|
|
211
|
+
error.priority === 'medium' ? '🟡' : '🟢';
|
|
212
|
+
lines.push(` ${priority} ${error.type}: ${error.message?.substring(0, 60) || 'Unknown error'}`);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
return lines.join('\n');
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Create a summary of errors for logging
|
|
221
|
+
*/
|
|
222
|
+
export function createErrorSummary(errors) {
|
|
223
|
+
const byType = {};
|
|
224
|
+
const byPriority = { critical: 0, high: 0, medium: 0, low: 0 };
|
|
225
|
+
|
|
226
|
+
for (const error of errors) {
|
|
227
|
+
byType[error.type] = (byType[error.type] || 0) + 1;
|
|
228
|
+
byPriority[error.priority] = (byPriority[error.priority] || 0) + 1;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
return {
|
|
232
|
+
total: errors.length,
|
|
233
|
+
byType,
|
|
234
|
+
byPriority,
|
|
235
|
+
files: getAffectedFiles(errors)
|
|
236
|
+
};
|
|
237
|
+
}
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
2
|
+
// VIBECODE CLI - Fix Generator
|
|
3
|
+
// Generate fix prompts for iterative builds
|
|
4
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
5
|
+
|
|
6
|
+
import { formatErrors, createErrorSummary } from './error-analyzer.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Generate a fix prompt based on errors from previous iteration
|
|
10
|
+
* @param {AnalyzedError[]} errors - Analyzed errors from test runner
|
|
11
|
+
* @param {string} originalPack - Original coder pack content
|
|
12
|
+
* @param {number} iteration - Current iteration number
|
|
13
|
+
* @returns {string} - Fix prompt for Claude Code
|
|
14
|
+
*/
|
|
15
|
+
export function generateFixPrompt(errors, originalPack, iteration = 1) {
|
|
16
|
+
const summary = createErrorSummary(errors);
|
|
17
|
+
|
|
18
|
+
const sections = [
|
|
19
|
+
'# 🔧 FIX REQUIRED - Iteration ' + iteration,
|
|
20
|
+
'',
|
|
21
|
+
`The previous build had **${errors.length} error(s)**. Please fix them.`,
|
|
22
|
+
'',
|
|
23
|
+
'---',
|
|
24
|
+
'',
|
|
25
|
+
'## 📋 Error Summary',
|
|
26
|
+
'',
|
|
27
|
+
`- **Total Errors:** ${summary.total}`,
|
|
28
|
+
`- **Critical:** ${summary.byPriority.critical}`,
|
|
29
|
+
`- **High:** ${summary.byPriority.high}`,
|
|
30
|
+
`- **Medium:** ${summary.byPriority.medium}`,
|
|
31
|
+
`- **Low:** ${summary.byPriority.low}`,
|
|
32
|
+
'',
|
|
33
|
+
`**Affected Files:** ${summary.files.length > 0 ? summary.files.join(', ') : 'Unknown'}`,
|
|
34
|
+
'',
|
|
35
|
+
'---',
|
|
36
|
+
'',
|
|
37
|
+
'## 🚨 Errors to Fix',
|
|
38
|
+
'',
|
|
39
|
+
];
|
|
40
|
+
|
|
41
|
+
// Add detailed errors grouped by priority
|
|
42
|
+
const byPriority = groupByPriority(errors);
|
|
43
|
+
|
|
44
|
+
for (const [priority, priorityErrors] of Object.entries(byPriority)) {
|
|
45
|
+
if (priorityErrors.length === 0) continue;
|
|
46
|
+
|
|
47
|
+
const emoji = priority === 'critical' ? '🔴' :
|
|
48
|
+
priority === 'high' ? '🟠' :
|
|
49
|
+
priority === 'medium' ? '🟡' : '🟢';
|
|
50
|
+
|
|
51
|
+
sections.push(`### ${emoji} ${priority.toUpperCase()} Priority`);
|
|
52
|
+
sections.push('');
|
|
53
|
+
|
|
54
|
+
for (const error of priorityErrors) {
|
|
55
|
+
const location = error.file
|
|
56
|
+
? `\`${error.file}${error.line ? ':' + error.line : ''}\``
|
|
57
|
+
: 'Unknown location';
|
|
58
|
+
|
|
59
|
+
sections.push(`**${error.type}** at ${location}`);
|
|
60
|
+
sections.push(`- Message: ${error.message}`);
|
|
61
|
+
sections.push(`- Suggestion: ${error.suggestion}`);
|
|
62
|
+
if (error.raw && error.raw !== error.message) {
|
|
63
|
+
sections.push(`- Raw output: \`${truncate(error.raw, 200)}\``);
|
|
64
|
+
}
|
|
65
|
+
sections.push('');
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
sections.push('---');
|
|
70
|
+
sections.push('');
|
|
71
|
+
sections.push('## 📝 Original Task Reference');
|
|
72
|
+
sections.push('');
|
|
73
|
+
sections.push('<details>');
|
|
74
|
+
sections.push('<summary>Click to expand original task</summary>');
|
|
75
|
+
sections.push('');
|
|
76
|
+
sections.push(originalPack);
|
|
77
|
+
sections.push('');
|
|
78
|
+
sections.push('</details>');
|
|
79
|
+
sections.push('');
|
|
80
|
+
sections.push('---');
|
|
81
|
+
sections.push('');
|
|
82
|
+
sections.push('## ⚡ Fix Instructions');
|
|
83
|
+
sections.push('');
|
|
84
|
+
sections.push('1. **Fix ONLY the errors listed above** - Do not refactor or change working code');
|
|
85
|
+
sections.push('2. **Start with CRITICAL errors** - They likely cause cascading failures');
|
|
86
|
+
sections.push('3. **Run tests after each fix** - Verify the error is resolved');
|
|
87
|
+
sections.push('4. **Keep changes minimal** - Focus on the specific issue');
|
|
88
|
+
sections.push('');
|
|
89
|
+
sections.push('When all errors are fixed, the build will be validated again.');
|
|
90
|
+
sections.push('');
|
|
91
|
+
|
|
92
|
+
return sections.join('\n');
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Generate a minimal fix prompt for single error
|
|
97
|
+
*/
|
|
98
|
+
export function generateSingleFixPrompt(error) {
|
|
99
|
+
const location = error.file
|
|
100
|
+
? `${error.file}${error.line ? ':' + error.line : ''}`
|
|
101
|
+
: 'unknown location';
|
|
102
|
+
|
|
103
|
+
return `# Fix Required
|
|
104
|
+
|
|
105
|
+
**Error Type:** ${error.type}
|
|
106
|
+
**Location:** ${location}
|
|
107
|
+
**Message:** ${error.message}
|
|
108
|
+
|
|
109
|
+
**Suggestion:** ${error.suggestion}
|
|
110
|
+
|
|
111
|
+
Please fix this specific error. Keep the change minimal and focused.`;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Generate iteration context for logging
|
|
116
|
+
*/
|
|
117
|
+
export function generateIterationContext(iteration, errors, duration) {
|
|
118
|
+
return {
|
|
119
|
+
iteration,
|
|
120
|
+
timestamp: new Date().toISOString(),
|
|
121
|
+
errorCount: errors.length,
|
|
122
|
+
errorTypes: [...new Set(errors.map(e => e.type))],
|
|
123
|
+
affectedFiles: [...new Set(errors.filter(e => e.file).map(e => e.file))],
|
|
124
|
+
duration
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Group errors by priority
|
|
130
|
+
*/
|
|
131
|
+
function groupByPriority(errors) {
|
|
132
|
+
const grouped = {
|
|
133
|
+
critical: [],
|
|
134
|
+
high: [],
|
|
135
|
+
medium: [],
|
|
136
|
+
low: []
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
for (const error of errors) {
|
|
140
|
+
const priority = error.priority || 'medium';
|
|
141
|
+
if (grouped[priority]) {
|
|
142
|
+
grouped[priority].push(error);
|
|
143
|
+
} else {
|
|
144
|
+
grouped.medium.push(error);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return grouped;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Truncate string with ellipsis
|
|
153
|
+
*/
|
|
154
|
+
function truncate(str, maxLen) {
|
|
155
|
+
if (!str) return '';
|
|
156
|
+
if (str.length <= maxLen) return str;
|
|
157
|
+
return str.substring(0, maxLen - 3) + '...';
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Check if errors are fixable (not system/config errors)
|
|
162
|
+
*/
|
|
163
|
+
export function areErrorsFixable(errors) {
|
|
164
|
+
// If all errors are unknown type with no file info, might be config issue
|
|
165
|
+
const unknownWithoutFile = errors.filter(e => e.type === 'unknown' && !e.file);
|
|
166
|
+
|
|
167
|
+
if (unknownWithoutFile.length === errors.length) {
|
|
168
|
+
return {
|
|
169
|
+
fixable: false,
|
|
170
|
+
reason: 'All errors are unstructured with no file information. This may indicate a configuration or environment issue.'
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
return { fixable: true };
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Estimate fix complexity
|
|
179
|
+
*/
|
|
180
|
+
export function estimateFixComplexity(errors) {
|
|
181
|
+
let score = 0;
|
|
182
|
+
|
|
183
|
+
for (const error of errors) {
|
|
184
|
+
switch (error.priority) {
|
|
185
|
+
case 'critical': score += 3; break;
|
|
186
|
+
case 'high': score += 2; break;
|
|
187
|
+
case 'medium': score += 1; break;
|
|
188
|
+
case 'low': score += 0.5; break;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
if (score <= 3) return 'simple';
|
|
193
|
+
if (score <= 8) return 'moderate';
|
|
194
|
+
return 'complex';
|
|
195
|
+
}
|