phantom-pr 0.4.19 → 0.5.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/README.md +216 -80
- package/dist/core/analysis/astExtractor.d.ts +15 -0
- package/dist/core/analysis/astExtractor.js +354 -0
- package/dist/core/analysis/astExtractor.js.map +1 -0
- package/dist/core/analysis/index.d.ts +3 -0
- package/dist/core/analysis/index.js +3 -0
- package/dist/core/analysis/index.js.map +1 -0
- package/dist/core/analysis/types.d.ts +50 -0
- package/dist/core/analysis/types.js +16 -0
- package/dist/core/analysis/types.js.map +1 -0
- package/dist/core/context/contextSelector.d.ts +29 -0
- package/dist/core/context/contextSelector.js +293 -0
- package/dist/core/context/contextSelector.js.map +1 -0
- package/dist/core/context/index.d.ts +4 -0
- package/dist/core/context/index.js +3 -0
- package/dist/core/context/index.js.map +1 -0
- package/dist/core/generator/enhancedContext.d.ts +34 -0
- package/dist/core/generator/enhancedContext.js +144 -0
- package/dist/core/generator/enhancedContext.js.map +1 -0
- package/dist/core/generator/postValidation.d.ts +24 -0
- package/dist/core/generator/postValidation.js +57 -0
- package/dist/core/generator/postValidation.js.map +1 -0
- package/dist/core/retry/errorParser.d.ts +12 -0
- package/dist/core/retry/errorParser.js +264 -0
- package/dist/core/retry/errorParser.js.map +1 -0
- package/dist/core/retry/feedbackGenerator.d.ts +12 -0
- package/dist/core/retry/feedbackGenerator.js +164 -0
- package/dist/core/retry/feedbackGenerator.js.map +1 -0
- package/dist/core/retry/index.d.ts +3 -0
- package/dist/core/retry/index.js +3 -0
- package/dist/core/retry/index.js.map +1 -0
- package/dist/core/retry/types.d.ts +40 -0
- package/dist/core/retry/types.js +5 -0
- package/dist/core/retry/types.js.map +1 -0
- package/dist/core/testRunner/retryEnhancer.d.ts +32 -0
- package/dist/core/testRunner/retryEnhancer.js +58 -0
- package/dist/core/testRunner/retryEnhancer.js.map +1 -0
- package/dist/core/validation/coverageVerifier.d.ts +16 -0
- package/dist/core/validation/coverageVerifier.js +226 -0
- package/dist/core/validation/coverageVerifier.js.map +1 -0
- package/dist/core/validation/index.d.ts +2 -0
- package/dist/core/validation/index.js +2 -0
- package/dist/core/validation/index.js.map +1 -0
- package/dist/core/validation/types.d.ts +24 -0
- package/dist/core/validation/types.js +6 -0
- package/dist/core/validation/types.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parser that extracts actionable information from Jest/Vitest test failures.
|
|
3
|
+
* Returns structured feedback for retry generation.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Extract the test name from a FAIL line or surrounding context.
|
|
7
|
+
*/
|
|
8
|
+
function extractTestName(lines, index) {
|
|
9
|
+
// Look backwards for test name
|
|
10
|
+
for (let i = index; i >= Math.max(0, index - 10); i--) {
|
|
11
|
+
const line = lines[i] ?? '';
|
|
12
|
+
// Match patterns like: ✕ test name (123 ms)
|
|
13
|
+
const failMatch = line.match(/[✕×✗]\s+(.+?)(?:\s+\(\d+\s*m?s\))?$/);
|
|
14
|
+
if (failMatch)
|
|
15
|
+
return failMatch[1]?.trim() ?? 'unknown';
|
|
16
|
+
// Match patterns like: FAIL src/file.test.ts › test name
|
|
17
|
+
const failPathMatch = line.match(/FAIL\s+.+?›\s+(.+)$/);
|
|
18
|
+
if (failPathMatch)
|
|
19
|
+
return failPathMatch[1]?.trim() ?? 'unknown';
|
|
20
|
+
// Match patterns like: ● test name
|
|
21
|
+
const bulletMatch = line.match(/[●•]\s+(.+)$/);
|
|
22
|
+
if (bulletMatch)
|
|
23
|
+
return bulletMatch[1]?.trim() ?? 'unknown';
|
|
24
|
+
}
|
|
25
|
+
return 'unknown';
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Parse assertion mismatch errors.
|
|
29
|
+
* Example:
|
|
30
|
+
* expect(received).toEqual(expected)
|
|
31
|
+
* Expected: "foo"
|
|
32
|
+
* Received: "bar"
|
|
33
|
+
*/
|
|
34
|
+
function parseAssertionMismatch(lines, startIndex) {
|
|
35
|
+
let expected;
|
|
36
|
+
let received;
|
|
37
|
+
for (let i = startIndex; i < Math.min(lines.length, startIndex + 10); i++) {
|
|
38
|
+
const line = lines[i] ?? '';
|
|
39
|
+
const expectedMatch = line.match(/Expected:\s*(.+)/);
|
|
40
|
+
if (expectedMatch)
|
|
41
|
+
expected = expectedMatch[1]?.trim();
|
|
42
|
+
const receivedMatch = line.match(/Received:\s*(.+)/);
|
|
43
|
+
if (receivedMatch)
|
|
44
|
+
received = receivedMatch[1]?.trim();
|
|
45
|
+
if (expected !== undefined && received !== undefined)
|
|
46
|
+
break;
|
|
47
|
+
}
|
|
48
|
+
if (expected === undefined && received === undefined)
|
|
49
|
+
return null;
|
|
50
|
+
const details = {};
|
|
51
|
+
if (expected !== undefined)
|
|
52
|
+
details.expected = expected;
|
|
53
|
+
if (received !== undefined)
|
|
54
|
+
details.received = received;
|
|
55
|
+
return {
|
|
56
|
+
kind: 'assertion_mismatch',
|
|
57
|
+
testName: extractTestName(lines, startIndex),
|
|
58
|
+
details,
|
|
59
|
+
suggestedFix: `Change expected value from ${expected ?? 'unknown'} to ${received ?? 'unknown'}, or fix the test setup/mock to return the expected value`,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Parse mock not called errors.
|
|
64
|
+
* Example:
|
|
65
|
+
* expect(jest.fn()).toHaveBeenCalled()
|
|
66
|
+
* Expected number of calls: >= 1
|
|
67
|
+
* Received number of calls: 0
|
|
68
|
+
*/
|
|
69
|
+
function parseMockNotCalled(lines, startIndex) {
|
|
70
|
+
let expectedCalls = 1;
|
|
71
|
+
let receivedCalls = 0;
|
|
72
|
+
for (let i = startIndex; i < Math.min(lines.length, startIndex + 10); i++) {
|
|
73
|
+
const line = lines[i] ?? '';
|
|
74
|
+
const expectedMatch = line.match(/Expected number of calls:\s*(?:>=\s*)?(\d+)/);
|
|
75
|
+
if (expectedMatch)
|
|
76
|
+
expectedCalls = parseInt(expectedMatch[1] ?? '1', 10);
|
|
77
|
+
const receivedMatch = line.match(/Received number of calls:\s*(\d+)/);
|
|
78
|
+
if (receivedMatch)
|
|
79
|
+
receivedCalls = parseInt(receivedMatch[1] ?? '0', 10);
|
|
80
|
+
}
|
|
81
|
+
return {
|
|
82
|
+
kind: 'mock_not_called',
|
|
83
|
+
testName: extractTestName(lines, startIndex),
|
|
84
|
+
details: {
|
|
85
|
+
callCount: { expected: expectedCalls, received: receivedCalls },
|
|
86
|
+
},
|
|
87
|
+
suggestedFix: 'Mock was never called - check if the code path that should call it is actually executed. Verify conditions/props trigger the expected behavior.',
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Parse mock wrong arguments errors.
|
|
92
|
+
* Example:
|
|
93
|
+
* expect(jest.fn()).toHaveBeenCalledWith(...expected)
|
|
94
|
+
* Expected: "arg1", "arg2"
|
|
95
|
+
* Received: "arg1", "arg3"
|
|
96
|
+
*/
|
|
97
|
+
function parseMockWrongArgs(lines, startIndex) {
|
|
98
|
+
let expected;
|
|
99
|
+
let received;
|
|
100
|
+
for (let i = startIndex; i < Math.min(lines.length, startIndex + 10); i++) {
|
|
101
|
+
const line = lines[i] ?? '';
|
|
102
|
+
const expectedMatch = line.match(/Expected:\s*(.+)/);
|
|
103
|
+
if (expectedMatch && !expected)
|
|
104
|
+
expected = expectedMatch[1]?.trim();
|
|
105
|
+
const receivedMatch = line.match(/Received:\s*(.+)/);
|
|
106
|
+
if (receivedMatch && !received)
|
|
107
|
+
received = receivedMatch[1]?.trim();
|
|
108
|
+
}
|
|
109
|
+
const details = {};
|
|
110
|
+
if (expected !== undefined)
|
|
111
|
+
details.expected = expected;
|
|
112
|
+
if (received !== undefined)
|
|
113
|
+
details.received = received;
|
|
114
|
+
return {
|
|
115
|
+
kind: 'mock_wrong_args',
|
|
116
|
+
testName: extractTestName(lines, startIndex),
|
|
117
|
+
details,
|
|
118
|
+
suggestedFix: `Mock was called with different arguments than expected. Update expected args to match actual: got ${received ?? 'unknown'}, expected ${expected ?? 'unknown'}`,
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Parse import/module errors.
|
|
123
|
+
* Example:
|
|
124
|
+
* Cannot find module './SomeComponent'
|
|
125
|
+
*/
|
|
126
|
+
function parseImportError(line, lines, index) {
|
|
127
|
+
const match = line.match(/Cannot find module ['"]([^'"]+)['"]/);
|
|
128
|
+
if (!match)
|
|
129
|
+
return null;
|
|
130
|
+
const modulePath = match[1] ?? 'unknown';
|
|
131
|
+
return {
|
|
132
|
+
kind: 'import_error',
|
|
133
|
+
testName: extractTestName(lines, index),
|
|
134
|
+
details: { missingImport: modulePath },
|
|
135
|
+
suggestedFix: `Add jest.mock('${modulePath}') or vi.mock('${modulePath}') at the top of the test file to mock this missing module`,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Parse undefined variable errors.
|
|
140
|
+
* Example:
|
|
141
|
+
* ReferenceError: someVar is not defined
|
|
142
|
+
*/
|
|
143
|
+
function parseUndefinedVariable(line, lines, index) {
|
|
144
|
+
const match = line.match(/ReferenceError:\s*(\w+)\s+is not defined/);
|
|
145
|
+
if (!match)
|
|
146
|
+
return null;
|
|
147
|
+
const variableName = match[1] ?? 'unknown';
|
|
148
|
+
return {
|
|
149
|
+
kind: 'undefined_variable',
|
|
150
|
+
testName: extractTestName(lines, index),
|
|
151
|
+
details: { variableName },
|
|
152
|
+
suggestedFix: `Variable '${variableName}' is not defined. Import it from the appropriate module, or check for typos in the variable name.`,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Parse timeout errors.
|
|
157
|
+
* Example:
|
|
158
|
+
* Timeout - Async callback was not invoked within the 5000 ms timeout
|
|
159
|
+
*/
|
|
160
|
+
function parseTimeout(line, lines, index) {
|
|
161
|
+
if (!/timeout/i.test(line))
|
|
162
|
+
return null;
|
|
163
|
+
return {
|
|
164
|
+
kind: 'timeout',
|
|
165
|
+
testName: extractTestName(lines, index),
|
|
166
|
+
details: {},
|
|
167
|
+
suggestedFix: 'Test timed out. Check if async operations complete properly, or if act() is needed around state updates. Consider increasing timeout or using waitFor() correctly.',
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Parse type errors.
|
|
172
|
+
* Example:
|
|
173
|
+
* TypeError: Cannot read properties of undefined (reading 'map')
|
|
174
|
+
*/
|
|
175
|
+
function parseTypeError(line, lines, index) {
|
|
176
|
+
const match = line.match(/TypeError:\s*(.+)/);
|
|
177
|
+
if (!match)
|
|
178
|
+
return null;
|
|
179
|
+
const stackTrace = match[1];
|
|
180
|
+
const details = {};
|
|
181
|
+
if (stackTrace !== undefined)
|
|
182
|
+
details.stackTrace = stackTrace;
|
|
183
|
+
return {
|
|
184
|
+
kind: 'type_error',
|
|
185
|
+
testName: extractTestName(lines, index),
|
|
186
|
+
details,
|
|
187
|
+
suggestedFix: 'Type error occurred. Check if props/data are correctly mocked and not undefined. Ensure mock return values have the expected shape.',
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Parse test errors from Jest/Vitest output.
|
|
192
|
+
*
|
|
193
|
+
* @param jestOutput - Raw test runner output
|
|
194
|
+
* @returns Array of parsed errors with actionable information
|
|
195
|
+
*/
|
|
196
|
+
export function parseTestErrors(jestOutput) {
|
|
197
|
+
const errors = [];
|
|
198
|
+
const lines = jestOutput.split('\n');
|
|
199
|
+
const seen = new Set();
|
|
200
|
+
for (let i = 0; i < lines.length; i++) {
|
|
201
|
+
const line = lines[i] ?? '';
|
|
202
|
+
const lineLower = line.toLowerCase();
|
|
203
|
+
// Skip empty lines
|
|
204
|
+
if (!line.trim())
|
|
205
|
+
continue;
|
|
206
|
+
let error = null;
|
|
207
|
+
// Check for import errors first (highest priority)
|
|
208
|
+
if (line.includes('Cannot find module')) {
|
|
209
|
+
error = parseImportError(line, lines, i);
|
|
210
|
+
}
|
|
211
|
+
// Check for undefined variable
|
|
212
|
+
else if (line.includes('ReferenceError') && line.includes('is not defined')) {
|
|
213
|
+
error = parseUndefinedVariable(line, lines, i);
|
|
214
|
+
}
|
|
215
|
+
// Check for type errors
|
|
216
|
+
else if (line.includes('TypeError:')) {
|
|
217
|
+
error = parseTypeError(line, lines, i);
|
|
218
|
+
}
|
|
219
|
+
// Check for timeout
|
|
220
|
+
else if (lineLower.includes('timeout') && (lineLower.includes('async') || lineLower.includes('callback'))) {
|
|
221
|
+
error = parseTimeout(line, lines, i);
|
|
222
|
+
}
|
|
223
|
+
// Check for mock wrong args (must come before mock not called since it contains 'toHaveBeenCalled')
|
|
224
|
+
else if (line.includes('toHaveBeenCalledWith')) {
|
|
225
|
+
error = parseMockWrongArgs(lines, i);
|
|
226
|
+
}
|
|
227
|
+
// Check for mock not called
|
|
228
|
+
else if (line.includes('toHaveBeenCalled') ||
|
|
229
|
+
(line.includes('Expected number of calls') && line.includes('>= 1'))) {
|
|
230
|
+
error = parseMockNotCalled(lines, i);
|
|
231
|
+
}
|
|
232
|
+
// Check for assertion mismatch (general case)
|
|
233
|
+
else if ((line.includes('expect(') && (line.includes(').toBe') || line.includes(').toEqual'))) ||
|
|
234
|
+
line.match(/^\s*Expected:/)) {
|
|
235
|
+
error = parseAssertionMismatch(lines, i);
|
|
236
|
+
}
|
|
237
|
+
if (error) {
|
|
238
|
+
// Deduplicate by test name and kind
|
|
239
|
+
const key = `${error.kind}:${error.testName}`;
|
|
240
|
+
if (!seen.has(key)) {
|
|
241
|
+
seen.add(key);
|
|
242
|
+
errors.push(error);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
// If no specific errors found but output suggests failure, add unknown
|
|
247
|
+
if (errors.length === 0 && /FAIL|failed|Error:/i.test(jestOutput)) {
|
|
248
|
+
// Look for any error-like content
|
|
249
|
+
for (let i = 0; i < lines.length; i++) {
|
|
250
|
+
const line = lines[i] ?? '';
|
|
251
|
+
if (/Error:|FAIL|failed|✕|×/i.test(line) && line.trim().length > 10) {
|
|
252
|
+
errors.push({
|
|
253
|
+
kind: 'unknown',
|
|
254
|
+
testName: extractTestName(lines, i),
|
|
255
|
+
details: { stackTrace: line.trim() },
|
|
256
|
+
suggestedFix: 'Unknown error occurred. Review the test output and fix the issue based on the error message.',
|
|
257
|
+
});
|
|
258
|
+
break; // Only add one unknown error
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
return errors;
|
|
263
|
+
}
|
|
264
|
+
//# sourceMappingURL=errorParser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errorParser.js","sourceRoot":"","sources":["../../../src/core/retry/errorParser.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Feedback generator that creates LLM-friendly retry prompts from parsed errors.
|
|
3
|
+
* Outputs valid prompt text with prioritized, actionable fixes.
|
|
4
|
+
*/
|
|
5
|
+
import type { RetryContext, RetryPrompt } from './types.js';
|
|
6
|
+
/**
|
|
7
|
+
* Generate a retry prompt from the context.
|
|
8
|
+
*
|
|
9
|
+
* @param context - The retry context with errors and previous test
|
|
10
|
+
* @returns A structured retry prompt, or null if no actionable retry possible
|
|
11
|
+
*/
|
|
12
|
+
export declare function generateRetryPrompt(context: RetryContext): RetryPrompt | null;
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Feedback generator that creates LLM-friendly retry prompts from parsed errors.
|
|
3
|
+
* Outputs valid prompt text with prioritized, actionable fixes.
|
|
4
|
+
*/
|
|
5
|
+
/** Maximum characters for generated prompt (roughly 500 tokens). */
|
|
6
|
+
const MAX_PROMPT_CHARS = 2000;
|
|
7
|
+
/**
|
|
8
|
+
* Get priority for an error kind.
|
|
9
|
+
*/
|
|
10
|
+
function getErrorPriority(kind) {
|
|
11
|
+
switch (kind) {
|
|
12
|
+
case 'import_error':
|
|
13
|
+
case 'undefined_variable':
|
|
14
|
+
return 'critical';
|
|
15
|
+
case 'mock_not_called':
|
|
16
|
+
case 'mock_wrong_args':
|
|
17
|
+
case 'type_error':
|
|
18
|
+
return 'high';
|
|
19
|
+
case 'assertion_mismatch':
|
|
20
|
+
case 'timeout':
|
|
21
|
+
return 'medium';
|
|
22
|
+
default:
|
|
23
|
+
return 'medium';
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Get focus area description for an error kind.
|
|
28
|
+
*/
|
|
29
|
+
function getFocusArea(kind) {
|
|
30
|
+
switch (kind) {
|
|
31
|
+
case 'import_error':
|
|
32
|
+
return 'Add missing mock at the top of the file';
|
|
33
|
+
case 'undefined_variable':
|
|
34
|
+
return 'Fix missing import or variable declaration';
|
|
35
|
+
case 'mock_not_called':
|
|
36
|
+
return 'Ensure the code path triggers the expected function call';
|
|
37
|
+
case 'mock_wrong_args':
|
|
38
|
+
return 'Update mock expectations to match actual arguments';
|
|
39
|
+
case 'type_error':
|
|
40
|
+
return 'Fix undefined/null access by properly mocking data';
|
|
41
|
+
case 'assertion_mismatch':
|
|
42
|
+
return 'Update expected value or fix test setup';
|
|
43
|
+
case 'timeout':
|
|
44
|
+
return 'Fix async handling with proper await/act patterns';
|
|
45
|
+
default:
|
|
46
|
+
return 'Review and fix the error based on the message';
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Format error details for the prompt.
|
|
51
|
+
*/
|
|
52
|
+
function formatErrorDetails(error) {
|
|
53
|
+
const parts = [];
|
|
54
|
+
parts.push(`**Error Type:** ${error.kind.replace(/_/g, ' ')}`);
|
|
55
|
+
parts.push(`**Test:** ${error.testName}`);
|
|
56
|
+
if (error.details.expected !== undefined) {
|
|
57
|
+
parts.push(`**Expected:** ${error.details.expected}`);
|
|
58
|
+
}
|
|
59
|
+
if (error.details.received !== undefined) {
|
|
60
|
+
parts.push(`**Received:** ${error.details.received}`);
|
|
61
|
+
}
|
|
62
|
+
if (error.details.callCount !== undefined) {
|
|
63
|
+
parts.push(`**Expected calls:** ${error.details.callCount.expected}`);
|
|
64
|
+
parts.push(`**Received calls:** ${error.details.callCount.received}`);
|
|
65
|
+
}
|
|
66
|
+
if (error.details.missingImport !== undefined) {
|
|
67
|
+
parts.push(`**Missing module:** ${error.details.missingImport}`);
|
|
68
|
+
}
|
|
69
|
+
if (error.details.variableName !== undefined) {
|
|
70
|
+
parts.push(`**Undefined variable:** ${error.details.variableName}`);
|
|
71
|
+
}
|
|
72
|
+
return parts.join('\n');
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Sort errors by priority (most actionable first).
|
|
76
|
+
*/
|
|
77
|
+
function sortByPriority(errors) {
|
|
78
|
+
const priorityOrder = { critical: 0, high: 1, medium: 2 };
|
|
79
|
+
return [...errors].sort((a, b) => {
|
|
80
|
+
const pa = priorityOrder[getErrorPriority(a.kind)];
|
|
81
|
+
const pb = priorityOrder[getErrorPriority(b.kind)];
|
|
82
|
+
return pa - pb;
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Generate a retry prompt from the context.
|
|
87
|
+
*
|
|
88
|
+
* @param context - The retry context with errors and previous test
|
|
89
|
+
* @returns A structured retry prompt, or null if no actionable retry possible
|
|
90
|
+
*/
|
|
91
|
+
export function generateRetryPrompt(context) {
|
|
92
|
+
// Return null if we've exceeded max attempts
|
|
93
|
+
if (context.attempt >= context.maxAttempts) {
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
// Return null if no errors provided
|
|
97
|
+
if (context.errors.length === 0) {
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
// Sort errors by priority and get the most actionable one
|
|
101
|
+
const sortedErrors = sortByPriority(context.errors);
|
|
102
|
+
const primaryError = sortedErrors[0];
|
|
103
|
+
// Return null if all errors are unknown (can't provide actionable feedback)
|
|
104
|
+
if (primaryError === undefined) {
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
if (primaryError.kind === 'unknown' && sortedErrors.every(e => e.kind === 'unknown')) {
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
// If primary is unknown but there are other errors, use the first non-unknown
|
|
111
|
+
const actionableError = primaryError.kind === 'unknown'
|
|
112
|
+
? sortedErrors.find(e => e.kind !== 'unknown') ?? primaryError
|
|
113
|
+
: primaryError;
|
|
114
|
+
const priority = getErrorPriority(actionableError.kind);
|
|
115
|
+
const focusArea = getFocusArea(actionableError.kind);
|
|
116
|
+
// Build the prompt
|
|
117
|
+
const promptParts = [];
|
|
118
|
+
promptParts.push(`## RETRY ATTEMPT ${context.attempt + 1}/${context.maxAttempts}`);
|
|
119
|
+
promptParts.push('');
|
|
120
|
+
promptParts.push('Your previous test failed with the following error:');
|
|
121
|
+
promptParts.push('');
|
|
122
|
+
promptParts.push(formatErrorDetails(actionableError));
|
|
123
|
+
promptParts.push('');
|
|
124
|
+
promptParts.push('## REQUIRED FIX');
|
|
125
|
+
promptParts.push(actionableError.suggestedFix);
|
|
126
|
+
promptParts.push('');
|
|
127
|
+
promptParts.push('## FOCUS AREA');
|
|
128
|
+
promptParts.push(focusArea);
|
|
129
|
+
promptParts.push('');
|
|
130
|
+
// Include other errors as context if there are more
|
|
131
|
+
if (sortedErrors.length > 1) {
|
|
132
|
+
const otherErrors = sortedErrors.slice(1, 4); // Max 3 additional errors
|
|
133
|
+
if (otherErrors.length > 0) {
|
|
134
|
+
promptParts.push('## ADDITIONAL ERRORS (fix after primary)');
|
|
135
|
+
for (const err of otherErrors) {
|
|
136
|
+
promptParts.push(`- ${err.kind.replace(/_/g, ' ')}: ${err.suggestedFix.slice(0, 100)}`);
|
|
137
|
+
}
|
|
138
|
+
promptParts.push('');
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
promptParts.push('## ORIGINAL TEST (with error)');
|
|
142
|
+
promptParts.push('```');
|
|
143
|
+
// Truncate test if needed to stay under limit
|
|
144
|
+
let testContent = context.generatedTest;
|
|
145
|
+
const headerLength = promptParts.join('\n').length + 100; // Buffer for closing
|
|
146
|
+
const maxTestChars = MAX_PROMPT_CHARS - headerLength;
|
|
147
|
+
if (testContent.length > maxTestChars) {
|
|
148
|
+
testContent = testContent.slice(0, maxTestChars - 50) + '\n// ... (truncated)';
|
|
149
|
+
}
|
|
150
|
+
promptParts.push(testContent);
|
|
151
|
+
promptParts.push('```');
|
|
152
|
+
promptParts.push('');
|
|
153
|
+
promptParts.push('## INSTRUCTIONS');
|
|
154
|
+
promptParts.push('1. Fix ONLY the identified error');
|
|
155
|
+
promptParts.push('2. Do NOT change unrelated parts of the test');
|
|
156
|
+
promptParts.push('3. Output the complete corrected test file');
|
|
157
|
+
const prompt = promptParts.join('\n');
|
|
158
|
+
return {
|
|
159
|
+
prompt,
|
|
160
|
+
priority,
|
|
161
|
+
focusArea,
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
//# sourceMappingURL=feedbackGenerator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"feedbackGenerator.js","sourceRoot":"","sources":["../../../src/core/retry/feedbackGenerator.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/core/retry/index.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Types for error parsing and retry feedback generation.
|
|
3
|
+
*/
|
|
4
|
+
/** Kind of test error detected. */
|
|
5
|
+
export type ErrorKind = 'assertion_mismatch' | 'mock_not_called' | 'mock_wrong_args' | 'undefined_variable' | 'import_error' | 'timeout' | 'type_error' | 'unknown';
|
|
6
|
+
/** Details extracted from the error. */
|
|
7
|
+
export type ErrorDetails = {
|
|
8
|
+
expected?: string;
|
|
9
|
+
received?: string;
|
|
10
|
+
callCount?: {
|
|
11
|
+
expected: number;
|
|
12
|
+
received: number;
|
|
13
|
+
};
|
|
14
|
+
missingImport?: string;
|
|
15
|
+
variableName?: string;
|
|
16
|
+
stackTrace?: string;
|
|
17
|
+
};
|
|
18
|
+
/** A parsed test error with actionable information. */
|
|
19
|
+
export type ParsedTestError = {
|
|
20
|
+
kind: ErrorKind;
|
|
21
|
+
testName: string;
|
|
22
|
+
details: ErrorDetails;
|
|
23
|
+
suggestedFix: string;
|
|
24
|
+
};
|
|
25
|
+
/** Priority of a retry prompt. */
|
|
26
|
+
export type RetryPriority = 'critical' | 'high' | 'medium';
|
|
27
|
+
/** Context for generating a retry prompt. */
|
|
28
|
+
export type RetryContext = {
|
|
29
|
+
originalPrompt: string;
|
|
30
|
+
generatedTest: string;
|
|
31
|
+
errors: ParsedTestError[];
|
|
32
|
+
attempt: number;
|
|
33
|
+
maxAttempts: number;
|
|
34
|
+
};
|
|
35
|
+
/** A generated retry prompt. */
|
|
36
|
+
export type RetryPrompt = {
|
|
37
|
+
prompt: string;
|
|
38
|
+
priority: RetryPriority;
|
|
39
|
+
focusArea: string;
|
|
40
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/core/retry/types.ts"],"names":[],"mappings":"AAAA;;GAEG"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Retry enhancement utilities.
|
|
3
|
+
* Provides smarter retry prompts based on parsed test errors.
|
|
4
|
+
*/
|
|
5
|
+
import { type ParsedTestError, type RetryPrompt } from '../retry/index.js';
|
|
6
|
+
export type RetryEnhanceOptions = {
|
|
7
|
+
originalPrompt: string;
|
|
8
|
+
generatedTest: string;
|
|
9
|
+
testOutput: string;
|
|
10
|
+
attempt: number;
|
|
11
|
+
maxAttempts: number;
|
|
12
|
+
enabled?: boolean;
|
|
13
|
+
};
|
|
14
|
+
export type RetryEnhanceResult = {
|
|
15
|
+
hasActionableErrors: boolean;
|
|
16
|
+
retryPrompt: RetryPrompt | null;
|
|
17
|
+
parsedErrors: ParsedTestError[];
|
|
18
|
+
enabled: boolean;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Enhance retry feedback using parsed test errors.
|
|
22
|
+
* Generates a smarter retry prompt with specific fixes.
|
|
23
|
+
*
|
|
24
|
+
* @param opts - Retry options
|
|
25
|
+
* @returns Enhanced retry result with prompt and parsed errors
|
|
26
|
+
*/
|
|
27
|
+
export declare function enhanceRetryFeedback(opts: RetryEnhanceOptions): RetryEnhanceResult;
|
|
28
|
+
/**
|
|
29
|
+
* Format parsed errors for feedback section in existing prompt format.
|
|
30
|
+
* This provides backward-compatible integration with existing feedback mechanism.
|
|
31
|
+
*/
|
|
32
|
+
export declare function formatErrorsForFeedback(errors: ParsedTestError[]): string;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Retry enhancement utilities.
|
|
3
|
+
* Provides smarter retry prompts based on parsed test errors.
|
|
4
|
+
*/
|
|
5
|
+
import { parseTestErrors, generateRetryPrompt } from '../retry/index.js';
|
|
6
|
+
/**
|
|
7
|
+
* Enhance retry feedback using parsed test errors.
|
|
8
|
+
* Generates a smarter retry prompt with specific fixes.
|
|
9
|
+
*
|
|
10
|
+
* @param opts - Retry options
|
|
11
|
+
* @returns Enhanced retry result with prompt and parsed errors
|
|
12
|
+
*/
|
|
13
|
+
export function enhanceRetryFeedback(opts) {
|
|
14
|
+
const enabled = opts.enabled ?? true;
|
|
15
|
+
if (!enabled) {
|
|
16
|
+
return {
|
|
17
|
+
hasActionableErrors: false,
|
|
18
|
+
retryPrompt: null,
|
|
19
|
+
parsedErrors: [],
|
|
20
|
+
enabled: false,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
// Parse errors from test output
|
|
24
|
+
const parsedErrors = parseTestErrors(opts.testOutput);
|
|
25
|
+
// Generate retry prompt if we have actionable errors
|
|
26
|
+
const retryPrompt = generateRetryPrompt({
|
|
27
|
+
originalPrompt: opts.originalPrompt,
|
|
28
|
+
generatedTest: opts.generatedTest,
|
|
29
|
+
errors: parsedErrors,
|
|
30
|
+
attempt: opts.attempt,
|
|
31
|
+
maxAttempts: opts.maxAttempts,
|
|
32
|
+
});
|
|
33
|
+
// Check if we have actionable errors (not all unknown)
|
|
34
|
+
const hasActionableErrors = parsedErrors.some(e => e.kind !== 'unknown');
|
|
35
|
+
return {
|
|
36
|
+
hasActionableErrors,
|
|
37
|
+
retryPrompt,
|
|
38
|
+
parsedErrors,
|
|
39
|
+
enabled: true,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Format parsed errors for feedback section in existing prompt format.
|
|
44
|
+
* This provides backward-compatible integration with existing feedback mechanism.
|
|
45
|
+
*/
|
|
46
|
+
export function formatErrorsForFeedback(errors) {
|
|
47
|
+
if (errors.length === 0) {
|
|
48
|
+
return '';
|
|
49
|
+
}
|
|
50
|
+
const lines = [];
|
|
51
|
+
lines.push('## PARSED ERRORS (prioritized fixes)');
|
|
52
|
+
for (const error of errors.slice(0, 5)) {
|
|
53
|
+
lines.push(`- ${error.kind}: ${error.suggestedFix.slice(0, 100)}`);
|
|
54
|
+
}
|
|
55
|
+
lines.push('');
|
|
56
|
+
return lines.join('\n');
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=retryEnhancer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retryEnhancer.js","sourceRoot":"","sources":["../../../src/core/testRunner/retryEnhancer.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,eAAe,EAAE,mBAAmB,EAA0C,MAAM,mBAAmB,CAAC;AAkBjH;;;;;;GAMG;AACH,MAAM,UAAU,oBAAoB,CAAC,IAAyB;IAC5D,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC;IAErC,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO;YACL,mBAAmB,EAAE,KAAK;YAC1B,WAAW,EAAE,IAAI;YACjB,YAAY,EAAE,EAAE;YAChB,OAAO,EAAE,KAAK;SACf,CAAC;IACJ,CAAC;IAED,gCAAgC;IAChC,MAAM,YAAY,GAAG,eAAe,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IAEtD,qDAAqD;IACrD,MAAM,WAAW,GAAG,mBAAmB,CAAC;QACtC,cAAc,EAAE,IAAI,CAAC,cAAc;QACnC,aAAa,EAAE,IAAI,CAAC,aAAa;QACjC,MAAM,EAAE,YAAY;QACpB,OAAO,EAAE,IAAI,CAAC,OAAO;QACrB,WAAW,EAAE,IAAI,CAAC,WAAW;KAC9B,CAAC,CAAC;IAEH,uDAAuD;IACvD,MAAM,mBAAmB,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC;IAEzE,OAAO;QACL,mBAAmB;QACnB,WAAW;QACX,YAAY;QACZ,OAAO,EAAE,IAAI;KACd,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,uBAAuB,CAAC,MAAyB;IAC/D,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxB,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,CAAC,IAAI,CAAC,sCAAsC,CAAC,CAAC;IAEnD,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC;QACvC,KAAK,CAAC,IAAI,CAAC,KAAK,KAAK,CAAC,IAAI,KAAK,KAAK,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;IACrE,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Post-generation validator that verifies generated tests cover all identified behaviors.
|
|
3
|
+
* Returns structured validation result with specific rejection codes.
|
|
4
|
+
*
|
|
5
|
+
* Static analysis only - does NOT execute the test.
|
|
6
|
+
*/
|
|
7
|
+
import type { ComponentAnalysis } from '../analysis/types.js';
|
|
8
|
+
import type { ValidationResult } from './types.js';
|
|
9
|
+
/**
|
|
10
|
+
* Verify that generated test adequately covers the analyzed component.
|
|
11
|
+
*
|
|
12
|
+
* @param analysis - Component analysis from AST extraction
|
|
13
|
+
* @param generatedTestContent - The generated test file content
|
|
14
|
+
* @returns ValidationResult with coverage details and any rejections
|
|
15
|
+
*/
|
|
16
|
+
export declare function verifyCoverage(analysis: ComponentAnalysis, generatedTestContent: string): ValidationResult;
|