@gitsense/gsc-utils 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/dist/gitsense-chat-utils.cjs.js +10977 -0
- package/dist/gitsense-chat-utils.esm.js +10975 -0
- package/dist/gsc-utils.cjs.js +11043 -0
- package/dist/gsc-utils.esm.js +11041 -0
- package/package.json +37 -0
- package/src/AnalysisBlockUtils.js +151 -0
- package/src/ChatUtils.js +126 -0
- package/src/CodeBlockUtils/blockExtractor.js +277 -0
- package/src/CodeBlockUtils/blockProcessor.js +559 -0
- package/src/CodeBlockUtils/blockProcessor.js.rej +8 -0
- package/src/CodeBlockUtils/constants.js +62 -0
- package/src/CodeBlockUtils/continuationUtils.js +191 -0
- package/src/CodeBlockUtils/headerParser.js +175 -0
- package/src/CodeBlockUtils/headerUtils.js +236 -0
- package/src/CodeBlockUtils/index.js +83 -0
- package/src/CodeBlockUtils/lineNumberFormatter.js +117 -0
- package/src/CodeBlockUtils/markerRemover.js +89 -0
- package/src/CodeBlockUtils/patchIntegration.js +38 -0
- package/src/CodeBlockUtils/relationshipUtils.js +159 -0
- package/src/CodeBlockUtils/updateCodeBlock.js +372 -0
- package/src/CodeBlockUtils/uuidUtils.js +48 -0
- package/src/ContextUtils.js +180 -0
- package/src/GSToolBlockUtils.js +108 -0
- package/src/GitSenseChatUtils.js +386 -0
- package/src/JsonUtils.js +101 -0
- package/src/LLMUtils.js +31 -0
- package/src/MessageUtils.js +460 -0
- package/src/PatchUtils/constants.js +72 -0
- package/src/PatchUtils/diagnosticReporter.js +213 -0
- package/src/PatchUtils/enhancedPatchProcessor.js +390 -0
- package/src/PatchUtils/fuzzyMatcher.js +252 -0
- package/src/PatchUtils/hunkCorrector.js +204 -0
- package/src/PatchUtils/hunkValidator.js +305 -0
- package/src/PatchUtils/index.js +135 -0
- package/src/PatchUtils/patchExtractor.js +175 -0
- package/src/PatchUtils/patchHeaderFormatter.js +143 -0
- package/src/PatchUtils/patchParser.js +289 -0
- package/src/PatchUtils/patchProcessor.js +389 -0
- package/src/PatchUtils/patchVerifier/constants.js +23 -0
- package/src/PatchUtils/patchVerifier/detectAndFixOverlappingHunks.js +281 -0
- package/src/PatchUtils/patchVerifier/detectAndFixRedundantChanges.js +404 -0
- package/src/PatchUtils/patchVerifier/formatAndAddLineNumbers.js +165 -0
- package/src/PatchUtils/patchVerifier/index.js +25 -0
- package/src/PatchUtils/patchVerifier/verifyAndCorrectHunkHeaders.js +202 -0
- package/src/PatchUtils/patchVerifier/verifyAndCorrectLineNumbers.js +254 -0
- package/src/SharedUtils/timestampUtils.js +41 -0
- package/src/SharedUtils/versionUtils.js +58 -0
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Component: PatchUtils Diagnostic Reporter
|
|
3
|
+
* Block-UUID: 3a7b9c5d-1e3f-4a2b-8c7d-6e5f4a3b2c1d
|
|
4
|
+
* Parent-UUID: N/A
|
|
5
|
+
* Version: 1.0.0
|
|
6
|
+
* Description: Formats diagnostic information about patch hunks for humans and LLMs.
|
|
7
|
+
* Language: JavaScript
|
|
8
|
+
* Created-at: 2025-05-14T16:50:00.000Z
|
|
9
|
+
* Authors: Claude 3.7 Sonnet (v1.0.0)
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Generates a human-readable description of a hunk validation result
|
|
15
|
+
* @param {object} hunkResult - The validation result for a single hunk
|
|
16
|
+
* @returns {string} Human-readable description
|
|
17
|
+
*/
|
|
18
|
+
function describeHunkResult(hunkResult) {
|
|
19
|
+
if (!hunkResult) {
|
|
20
|
+
return "No hunk result provided";
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (hunkResult.valid) {
|
|
24
|
+
return `Hunk #${hunkResult.hunkIndex + 1} VALID: Applied successfully at specified position.`;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
let description = `Hunk #${hunkResult.hunkIndex + 1} FAILED: `;
|
|
28
|
+
|
|
29
|
+
if (hunkResult.diagnostics) {
|
|
30
|
+
if (!hunkResult.diagnostics.headerValid) {
|
|
31
|
+
description += `Invalid hunk header format. ${hunkResult.diagnostics.headerError}`;
|
|
32
|
+
} else if (hunkResult.diagnostics.method === "direct_failed") {
|
|
33
|
+
description += hunkResult.diagnostics.message || "Failed to apply at specified position.";
|
|
34
|
+
} else {
|
|
35
|
+
description += "Unknown validation error.";
|
|
36
|
+
}
|
|
37
|
+
} else {
|
|
38
|
+
description += "No diagnostic information available.";
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Add correction information if available
|
|
42
|
+
if (hunkResult.correction && hunkResult.correction.success) {
|
|
43
|
+
description += `\n - ${hunkResult.correction.explanation}`;
|
|
44
|
+
} else if (hunkResult.correction) {
|
|
45
|
+
description += `\n - Could not generate correction: ${hunkResult.correction.reason}`;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return description;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Generates human-readable diagnostics for all hunks
|
|
53
|
+
* @param {object[]} hunkResults - Array of validation results for all hunks
|
|
54
|
+
* @returns {string} Human-readable diagnostic report
|
|
55
|
+
*/
|
|
56
|
+
function generateHumanReadableDiagnostics(hunkResults) {
|
|
57
|
+
if (!Array.isArray(hunkResults) || hunkResults.length === 0) {
|
|
58
|
+
return "No hunk results to report.";
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const validHunks = hunkResults.filter(result => result.valid);
|
|
62
|
+
const invalidHunks = hunkResults.filter(result => !result.valid);
|
|
63
|
+
|
|
64
|
+
let report = `Patch Validation Report\n`;
|
|
65
|
+
report += `======================\n\n`;
|
|
66
|
+
report += `Summary: ${validHunks.length} of ${hunkResults.length} hunks valid.\n\n`;
|
|
67
|
+
|
|
68
|
+
if (invalidHunks.length > 0) {
|
|
69
|
+
report += `Invalid Hunks:\n`;
|
|
70
|
+
report += invalidHunks.map(result => describeHunkResult(result)).join('\n\n');
|
|
71
|
+
report += `\n\n`;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (validHunks.length > 0) {
|
|
75
|
+
report += `Valid Hunks: ${validHunks.map(result => `#${result.hunkIndex + 1}`).join(', ')}`;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return report;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Formats a hunk result for LLM consumption
|
|
83
|
+
* @param {object} hunkResult - The validation result for a single hunk
|
|
84
|
+
* @returns {object} Structured hunk result for LLM
|
|
85
|
+
*/
|
|
86
|
+
function formatHunkResultForLLM(hunkResult) {
|
|
87
|
+
if (!hunkResult) {
|
|
88
|
+
return { error: "No hunk result provided" };
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const result = {
|
|
92
|
+
hunkIndex: hunkResult.hunkIndex,
|
|
93
|
+
status: hunkResult.valid ? "valid" : "invalid",
|
|
94
|
+
header: hunkResult.hunk?.header || null
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
if (hunkResult.valid) {
|
|
98
|
+
result.message = "Hunk applied successfully at specified position";
|
|
99
|
+
} else {
|
|
100
|
+
result.issueType = hunkResult.diagnostics?.method === "direct_failed"
|
|
101
|
+
? "context_mismatch"
|
|
102
|
+
: "header_invalid";
|
|
103
|
+
|
|
104
|
+
result.message = hunkResult.diagnostics?.message || "Unknown validation error";
|
|
105
|
+
|
|
106
|
+
if (hunkResult.correction && hunkResult.correction.success) {
|
|
107
|
+
result.correction = {
|
|
108
|
+
correctedHeader: hunkResult.correction.correctedHeader,
|
|
109
|
+
confidence: hunkResult.correction.confidence,
|
|
110
|
+
explanation: hunkResult.correction.explanation
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return result;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Generates structured feedback for LLMs about patch validation
|
|
120
|
+
* @param {object[]} hunkResults - Array of validation results for all hunks
|
|
121
|
+
* @returns {object} Structured feedback for LLM consumption
|
|
122
|
+
*/
|
|
123
|
+
function generateLLMFeedback(hunkResults) {
|
|
124
|
+
if (!Array.isArray(hunkResults) || hunkResults.length === 0) {
|
|
125
|
+
return { error: "No hunk results to report" };
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const validCount = hunkResults.filter(result => result.valid).length;
|
|
129
|
+
|
|
130
|
+
return {
|
|
131
|
+
summary: {
|
|
132
|
+
totalHunks: hunkResults.length,
|
|
133
|
+
validHunks: validCount,
|
|
134
|
+
invalidHunks: hunkResults.length - validCount,
|
|
135
|
+
overallStatus: validCount === hunkResults.length ? "valid" : "invalid"
|
|
136
|
+
},
|
|
137
|
+
hunks: hunkResults.map(formatHunkResultForLLM)
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Formats a diagnostic summary for the entire patch
|
|
143
|
+
* @param {object} patchResult - The overall patch validation result
|
|
144
|
+
* @returns {object} Formatted diagnostic summary
|
|
145
|
+
*/
|
|
146
|
+
function formatDiagnosticSummary(patchResult) {
|
|
147
|
+
if (!patchResult) {
|
|
148
|
+
return { error: "No patch result provided" };
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const summary = {
|
|
152
|
+
success: patchResult.success,
|
|
153
|
+
hunkCount: patchResult.hunkResults?.length || 0,
|
|
154
|
+
validHunks: patchResult.hunkResults?.filter(h => h.valid)?.length || 0,
|
|
155
|
+
hasCorrections: patchResult.hunkResults?.some(h => h.correction?.success) || false
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
summary.allHunksValid = summary.validHunks === summary.hunkCount;
|
|
159
|
+
summary.allHunksFixable = summary.allHunksValid ||
|
|
160
|
+
(patchResult.hunkResults?.filter(h => !h.valid)
|
|
161
|
+
.every(h => h.correction?.success) || false);
|
|
162
|
+
|
|
163
|
+
if (!summary.allHunksValid) {
|
|
164
|
+
summary.invalidHunkIndices = patchResult.hunkResults
|
|
165
|
+
?.filter(h => !h.valid)
|
|
166
|
+
.map(h => h.hunkIndex);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return summary;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Generates a concise error message for a failed patch
|
|
174
|
+
* @param {object} patchResult - The overall patch validation result
|
|
175
|
+
* @returns {string} Concise error message
|
|
176
|
+
*/
|
|
177
|
+
function generateErrorMessage(patchResult) {
|
|
178
|
+
if (!patchResult) {
|
|
179
|
+
return "Patch validation failed: No result available";
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (patchResult.success) {
|
|
183
|
+
return "Patch applied successfully";
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const summary = formatDiagnosticSummary(patchResult);
|
|
187
|
+
|
|
188
|
+
if (summary.allHunksFixable) {
|
|
189
|
+
return "Patch contains fixable errors in hunk headers";
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const invalidHunks = patchResult.hunkResults?.filter(h => !h.valid) || [];
|
|
193
|
+
|
|
194
|
+
if (invalidHunks.length === 0) {
|
|
195
|
+
return "Patch validation failed: Unknown error";
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if (invalidHunks.length === 1) {
|
|
199
|
+
const hunk = invalidHunks[0];
|
|
200
|
+
return `Patch validation failed: Hunk #${hunk.hunkIndex + 1} - ${hunk.diagnostics?.message || "Unknown error"}`;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
return `Patch validation failed: ${invalidHunks.length} invalid hunks (indices: ${summary.invalidHunkIndices?.join(', ')})`;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
module.exports = {
|
|
207
|
+
describeHunkResult,
|
|
208
|
+
generateHumanReadableDiagnostics,
|
|
209
|
+
formatHunkResultForLLM,
|
|
210
|
+
generateLLMFeedback,
|
|
211
|
+
formatDiagnosticSummary,
|
|
212
|
+
generateErrorMessage
|
|
213
|
+
};
|
|
@@ -0,0 +1,390 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Component: PatchUtils Enhanced Processor
|
|
3
|
+
* Block-UUID: 3e9a9106-7f16-4b49-9b38-86d95b413c2a
|
|
4
|
+
* Parent-UUID: 5bfb717c-aaec-462e-a50e-d096aba0d63f
|
|
5
|
+
* Version: 1.2.0
|
|
6
|
+
* Description: Enhanced patch processor with hunk-level validation, diagnostics, and fuzzy matching.
|
|
7
|
+
* Language: JavaScript
|
|
8
|
+
* Created-at: 2025-05-14T18:26:31.210Z
|
|
9
|
+
* Authors: Claude 3.7 Sonnet (v1.0.0), Gemini 2.5 Flash (v1.1.0), Claude 3.7 Sonnet (v1.2.0)
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
const jsdiff = require('diff');
|
|
14
|
+
const { removeLineNumbers } = require('../CodeBlockUtils/lineNumberFormatter');
|
|
15
|
+
const { extractAndCleanHunks, reconstructPatch } = require('./patchExtractor');
|
|
16
|
+
const { validateHunk, validateHunks } = require('./hunkValidator');
|
|
17
|
+
const { findBestContextMatch, findBestMatchWithSlidingWindow } = require('./fuzzyMatcher');
|
|
18
|
+
const { generateCorrectedHunk, generateHunkCorrection } = require('./hunkCorrector');
|
|
19
|
+
const { generateHumanReadableDiagnostics, generateLLMFeedback } = require('./diagnosticReporter');
|
|
20
|
+
const { detectAndFixRedundantChanges } = require('./patchVerifier/detectAndFixRedundantChanges');
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* @typedef {object} PatchValidationResult
|
|
24
|
+
* @property {boolean} valid - Whether the patch is valid
|
|
25
|
+
* @property {boolean} fixable - Whether invalid hunks can be fixed
|
|
26
|
+
* @property {object[]} hunkResults - Detailed results for each hunk
|
|
27
|
+
* @property {string} humanReadableDiagnostics - Human-readable diagnostic report
|
|
28
|
+
* @property {object} llmFeedback - Structured feedback for LLMs
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* @typedef {object} PatchApplicationResult
|
|
33
|
+
* @property {boolean} success - Whether the patch was applied successfully
|
|
34
|
+
* @property {string} patchedText - The resulting text after applying the patch
|
|
35
|
+
* @property {boolean} patchWasCorrected - Whether the patch needed correction
|
|
36
|
+
* @property {string} correctedPatch - The corrected patch if applicable
|
|
37
|
+
* @property {object[]} hunkResults - Detailed results for each hunk
|
|
38
|
+
* @property {string} humanReadableDiagnostics - Human-readable diagnostic report
|
|
39
|
+
* @property {object} llmFeedback - Structured feedback for LLMs
|
|
40
|
+
*/
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* @typedef {object} HunkValidationResult
|
|
44
|
+
* @property {boolean} valid - Whether the hunk is valid
|
|
45
|
+
* @property {object} hunk - Parsed hunk information
|
|
46
|
+
* @property {object} headerInfo - Parsed header information
|
|
47
|
+
* @property {object} diagnostics - Detailed diagnostic information
|
|
48
|
+
*/
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Applies a patch with detailed per-hunk diagnostics
|
|
52
|
+
* @param {string} sourceText - Original source code
|
|
53
|
+
* @param {string} patchText - Patch text from LLM
|
|
54
|
+
* @returns {object} Comprehensive results including diagnostics
|
|
55
|
+
*/
|
|
56
|
+
function applyPatchWithDiagnostics(sourceText, patchText) {
|
|
57
|
+
if (!sourceText || !patchText) {
|
|
58
|
+
return {
|
|
59
|
+
success: false,
|
|
60
|
+
error: "Missing source or patch text",
|
|
61
|
+
patchedText: null,
|
|
62
|
+
hunkResults: []
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Clean source text (remove any line numbers)
|
|
67
|
+
const cleanSourceText = removeLineNumbers(sourceText);
|
|
68
|
+
|
|
69
|
+
// First, clean up redundant changes
|
|
70
|
+
const redundantFixResult = detectAndFixRedundantChanges(patchText, true);
|
|
71
|
+
const cleanedPatchText = redundantFixResult.correctionsMade
|
|
72
|
+
? redundantFixResult.correctedPatchText
|
|
73
|
+
: patchText;
|
|
74
|
+
|
|
75
|
+
// Extract hunks from the patch
|
|
76
|
+
const { metadata, hunks } = extractAndCleanHunks(cleanedPatchText);
|
|
77
|
+
|
|
78
|
+
if (hunks.length === 0) {
|
|
79
|
+
return {
|
|
80
|
+
success: false,
|
|
81
|
+
error: "No hunks found in patch",
|
|
82
|
+
patchedText: null,
|
|
83
|
+
hunkResults: []
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Validate each hunk
|
|
88
|
+
const hunkResults = validateHunks(cleanSourceText, hunks);
|
|
89
|
+
|
|
90
|
+
// For invalid hunks, try fuzzy matching
|
|
91
|
+
for (let i = 0; i < hunkResults.length; i++) {
|
|
92
|
+
const result = hunkResults[i];
|
|
93
|
+
|
|
94
|
+
if (!result.valid && result.diagnostics?.needsFuzzyMatching) {
|
|
95
|
+
// Try fuzzy matching using the correct context for matching
|
|
96
|
+
const matchResult = findBestMatchWithSlidingWindow(
|
|
97
|
+
result.hunk.contextLinesForMatching, // Use the new array
|
|
98
|
+
cleanSourceText
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
// Generate correction if match found
|
|
102
|
+
if (matchResult.found) {
|
|
103
|
+
const correction = generateHunkCorrection(result, matchResult);
|
|
104
|
+
hunkResults[i].correction = correction;
|
|
105
|
+
hunkResults[i].matchResult = matchResult;
|
|
106
|
+
} else {
|
|
107
|
+
hunkResults[i].correction = {
|
|
108
|
+
success: false,
|
|
109
|
+
reason: matchResult.reason || "No suitable match found"
|
|
110
|
+
};
|
|
111
|
+
hunkResults[i].matchResult = matchResult;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Check if all hunks are valid or fixable
|
|
117
|
+
const allValid = hunkResults.every(result => result.valid);
|
|
118
|
+
const allFixable = hunkResults
|
|
119
|
+
.filter(result => !result.valid)
|
|
120
|
+
.every(result => result.correction && result.correction.success);
|
|
121
|
+
|
|
122
|
+
// If all hunks are valid, apply the patch directly
|
|
123
|
+
if (allValid) {
|
|
124
|
+
try {
|
|
125
|
+
const patchedText = jsdiff.applyPatch(cleanSourceText, patchText);
|
|
126
|
+
|
|
127
|
+
if (patchedText === false) {
|
|
128
|
+
return {
|
|
129
|
+
success: false,
|
|
130
|
+
error: "Failed to apply patch despite all hunks validating individually",
|
|
131
|
+
patchedText: null,
|
|
132
|
+
hunkResults,
|
|
133
|
+
humanReadableDiagnostics: generateHumanReadableDiagnostics(hunkResults),
|
|
134
|
+
llmFeedback: generateLLMFeedback(hunkResults)
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return {
|
|
139
|
+
success: true,
|
|
140
|
+
patchedText,
|
|
141
|
+
hunkResults,
|
|
142
|
+
humanReadableDiagnostics: generateHumanReadableDiagnostics(hunkResults),
|
|
143
|
+
llmFeedback: generateLLMFeedback(hunkResults)
|
|
144
|
+
};
|
|
145
|
+
} catch (error) {
|
|
146
|
+
return {
|
|
147
|
+
success: false,
|
|
148
|
+
error: `Error applying patch: ${error.message}`,
|
|
149
|
+
patchedText: null,
|
|
150
|
+
hunkResults,
|
|
151
|
+
humanReadableDiagnostics: generateHumanReadableDiagnostics(hunkResults),
|
|
152
|
+
llmFeedback: generateLLMFeedback(hunkResults)
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// If all invalid hunks are fixable, create a corrected patch and apply it
|
|
158
|
+
if (allFixable) {
|
|
159
|
+
try {
|
|
160
|
+
// Create corrected hunks
|
|
161
|
+
const correctedHunks = hunkResults.map(result => {
|
|
162
|
+
if (result.valid) {
|
|
163
|
+
return hunks[result.hunkIndex];
|
|
164
|
+
} else {
|
|
165
|
+
return result.correction.correctedHunkText;
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
// Reconstruct the patch
|
|
170
|
+
const correctedPatch = reconstructPatch(metadata, correctedHunks);
|
|
171
|
+
|
|
172
|
+
// Apply the corrected patch
|
|
173
|
+
const patchedText = jsdiff.applyPatch(cleanSourceText, correctedPatch.replace(/# --- PATCH (START|END) MARKER ---/g, ''));
|
|
174
|
+
|
|
175
|
+
if (patchedText === false) {
|
|
176
|
+
return {
|
|
177
|
+
success: false,
|
|
178
|
+
error: "Failed to apply corrected patch",
|
|
179
|
+
patchedText: null,
|
|
180
|
+
correctedPatch,
|
|
181
|
+
hunkResults,
|
|
182
|
+
humanReadableDiagnostics: generateHumanReadableDiagnostics(hunkResults),
|
|
183
|
+
llmFeedback: generateLLMFeedback(hunkResults)
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
return {
|
|
188
|
+
success: true,
|
|
189
|
+
patchedText,
|
|
190
|
+
correctedPatch,
|
|
191
|
+
hunkResults,
|
|
192
|
+
patchWasCorrected: true,
|
|
193
|
+
humanReadableDiagnostics: generateHumanReadableDiagnostics(hunkResults),
|
|
194
|
+
llmFeedback: generateLLMFeedback(hunkResults)
|
|
195
|
+
};
|
|
196
|
+
} catch (error) {
|
|
197
|
+
return {
|
|
198
|
+
success: false,
|
|
199
|
+
error: `Error applying corrected patch: ${error.message}`,
|
|
200
|
+
patchedText: null,
|
|
201
|
+
hunkResults,
|
|
202
|
+
humanReadableDiagnostics: generateHumanReadableDiagnostics(hunkResults),
|
|
203
|
+
llmFeedback: generateLLMFeedback(hunkResults)
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// If we get here, some hunks are invalid and not fixable
|
|
209
|
+
return {
|
|
210
|
+
success: false,
|
|
211
|
+
error: "Some hunks are invalid and could not be fixed",
|
|
212
|
+
patchedText: null,
|
|
213
|
+
hunkResults,
|
|
214
|
+
humanReadableDiagnostics: generateHumanReadableDiagnostics(hunkResults),
|
|
215
|
+
llmFeedback: generateLLMFeedback(hunkResults)
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Validates a patch without applying it
|
|
221
|
+
* @param {string} sourceText - Original source code
|
|
222
|
+
* @param {string} patchText - Patch text from LLM
|
|
223
|
+
* @returns {object} Validation results with diagnostics
|
|
224
|
+
*/
|
|
225
|
+
function validatePatch(sourceText, patchText) {
|
|
226
|
+
if (!sourceText || !patchText) {
|
|
227
|
+
return {
|
|
228
|
+
valid: false,
|
|
229
|
+
error: "Missing source or patch text",
|
|
230
|
+
hunkResults: []
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// Clean source text
|
|
235
|
+
const cleanSourceText = removeLineNumbers(sourceText);
|
|
236
|
+
|
|
237
|
+
// Clean up redundant changes
|
|
238
|
+
const redundantFixResult = detectAndFixRedundantChanges(patchText, true);
|
|
239
|
+
const cleanedPatchText = redundantFixResult.correctionsMade
|
|
240
|
+
? redundantFixResult.correctedPatchText
|
|
241
|
+
: patchText;
|
|
242
|
+
|
|
243
|
+
// Extract hunks
|
|
244
|
+
const { metadata, hunks } = extractAndCleanHunks(cleanedPatchText);
|
|
245
|
+
|
|
246
|
+
if (hunks.length === 0) {
|
|
247
|
+
return {
|
|
248
|
+
valid: false,
|
|
249
|
+
error: "No hunks found in patch",
|
|
250
|
+
hunkResults: []
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// Validate each hunk
|
|
255
|
+
const hunkResults = validateHunks(cleanSourceText, hunks);
|
|
256
|
+
|
|
257
|
+
// For invalid hunks, try fuzzy matching
|
|
258
|
+
for (let i = 0; i < hunkResults.length; i++) {
|
|
259
|
+
const result = hunkResults[i];
|
|
260
|
+
|
|
261
|
+
if (!result.valid && result.diagnostics?.needsFuzzyMatching) {
|
|
262
|
+
const matchResult = findBestMatchWithSlidingWindow(
|
|
263
|
+
result.hunk.contextLinesForMatching, // Use the new array
|
|
264
|
+
cleanSourceText
|
|
265
|
+
);
|
|
266
|
+
|
|
267
|
+
if (matchResult.found) {
|
|
268
|
+
const correction = generateHunkCorrection(result, matchResult);
|
|
269
|
+
hunkResults[i].correction = correction;
|
|
270
|
+
hunkResults[i].matchResult = matchResult;
|
|
271
|
+
} else {
|
|
272
|
+
hunkResults[i].correction = {
|
|
273
|
+
success: false,
|
|
274
|
+
reason: matchResult.reason || "No suitable match found"
|
|
275
|
+
};
|
|
276
|
+
hunkResults[i].matchResult = matchResult;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// Check if all hunks are valid or fixable
|
|
282
|
+
const allValid = hunkResults.every(result => result.valid);
|
|
283
|
+
const allFixable = hunkResults
|
|
284
|
+
.filter(result => !result.valid)
|
|
285
|
+
.every(result => result.correction && result.correction.success);
|
|
286
|
+
|
|
287
|
+
return {
|
|
288
|
+
valid: allValid,
|
|
289
|
+
fixable: !allValid && allFixable,
|
|
290
|
+
hunkResults,
|
|
291
|
+
humanReadableDiagnostics: generateHumanReadableDiagnostics(hunkResults),
|
|
292
|
+
llmFeedback: generateLLMFeedback(hunkResults)
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Applies a single hunk to source code
|
|
298
|
+
* @param {string} sourceText - Original source code
|
|
299
|
+
* @param {string} hunkText - Text of a single hunk
|
|
300
|
+
* @returns {object} Result of applying the hunk
|
|
301
|
+
*/
|
|
302
|
+
function applyHunk(sourceText, hunkText) {
|
|
303
|
+
if (!sourceText || !hunkText) {
|
|
304
|
+
return {
|
|
305
|
+
success: false,
|
|
306
|
+
error: "Missing source or hunk text",
|
|
307
|
+
patchedText: null
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// Clean source text
|
|
312
|
+
const cleanSourceText = removeLineNumbers(sourceText);
|
|
313
|
+
|
|
314
|
+
// Clean hunk line numbers
|
|
315
|
+
const cleanHunk = cleanHunkLineNumbers(hunkText);
|
|
316
|
+
|
|
317
|
+
// Validate the hunk
|
|
318
|
+
const validation = validateHunk(cleanSourceText, cleanHunk);
|
|
319
|
+
|
|
320
|
+
if (validation.valid) {
|
|
321
|
+
return {
|
|
322
|
+
success: true,
|
|
323
|
+
patchedText: validation.appliedText,
|
|
324
|
+
validation
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// Try fuzzy matching
|
|
329
|
+
const matchResult = findBestMatchWithSlidingWindow(
|
|
330
|
+
validation.hunk.contextLinesForMatching, // Use the new array
|
|
331
|
+
cleanSourceText
|
|
332
|
+
);
|
|
333
|
+
|
|
334
|
+
if (matchResult.found) {
|
|
335
|
+
const correction = generateHunkCorrection(validation, matchResult);
|
|
336
|
+
|
|
337
|
+
if (correction.success) {
|
|
338
|
+
try {
|
|
339
|
+
// Create a minimal patch with just this hunk
|
|
340
|
+
const minimalPatch = `--- Original\n+++ Modified\n${correction.correctedHunkText}`;
|
|
341
|
+
|
|
342
|
+
// Apply the corrected hunk
|
|
343
|
+
const patchedText = jsdiff.applyPatch(cleanSourceText, minimalPatch);
|
|
344
|
+
|
|
345
|
+
if (patchedText === false) {
|
|
346
|
+
return {
|
|
347
|
+
success: false,
|
|
348
|
+
error: "Failed to apply corrected hunk",
|
|
349
|
+
patchedText: null,
|
|
350
|
+
validation,
|
|
351
|
+
correction,
|
|
352
|
+
matchResult
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
return {
|
|
357
|
+
success: true,
|
|
358
|
+
patchedText,
|
|
359
|
+
validation,
|
|
360
|
+
correction,
|
|
361
|
+
matchResult,
|
|
362
|
+
hunkWasCorrected: true
|
|
363
|
+
};
|
|
364
|
+
} catch (error) {
|
|
365
|
+
return {
|
|
366
|
+
success: false,
|
|
367
|
+
error: `Error applying corrected hunk: ${error.message}`,
|
|
368
|
+
patchedText: null,
|
|
369
|
+
validation,
|
|
370
|
+
correction,
|
|
371
|
+
matchResult
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
return {
|
|
378
|
+
success: false,
|
|
379
|
+
error: "Hunk is invalid and could not be fixed",
|
|
380
|
+
patchedText: null,
|
|
381
|
+
validation,
|
|
382
|
+
matchResult: matchResult || null
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
module.exports = {
|
|
387
|
+
applyPatchWithDiagnostics,
|
|
388
|
+
validatePatch,
|
|
389
|
+
applyHunk
|
|
390
|
+
};
|