@gitsense/gsc-utils 0.2.24 → 0.2.27
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 +380 -62
- package/dist/gsc-utils.cjs.js +14270 -523
- package/dist/gsc-utils.esm.js +14270 -523
- package/package.json +1 -1
- package/src/AnalyzerUtils/cloner.js +149 -0
- package/src/AnalyzerUtils/constants.js +1 -1
- package/src/AnalyzerUtils/defaultPromptLoader.js +10 -10
- package/src/AnalyzerUtils/discovery.js +48 -39
- package/src/AnalyzerUtils/index.js +13 -7
- package/src/AnalyzerUtils/instructionLoader.js +6 -6
- package/src/AnalyzerUtils/jsonParser.js +35 -0
- package/src/AnalyzerUtils/management.js +6 -6
- package/src/AnalyzerUtils/saver.js +5 -5
- package/src/AnalyzerUtils/schemaLoader.js +194 -26
- package/src/AnalyzerUtils/updater.js +187 -0
- package/src/CodeBlockUtils/blockProcessor.js +14 -32
- package/src/CodeBlockUtils/constants.js +8 -4
- package/src/CodeBlockUtils/headerUtils.js +19 -3
- package/src/CodeBlockUtils/index.js +7 -6
- package/src/CodeBlockUtils/lineageTracer.js +95 -0
- package/src/CompactChatUtils/CompactedMessageUtils.js +224 -0
- package/src/CompactChatUtils/README.md +321 -0
- package/src/CompactChatUtils/ReferenceMessageUtils.js +143 -0
- package/src/CompactChatUtils/index.js +40 -0
- package/src/ContextUtils.js +41 -5
- package/src/DomUtils.js +559 -0
- package/src/GSToolBlockUtils.js +66 -1
- package/src/GitSenseChatUtils.js +108 -16
- package/src/LanguageNameUtils.js +171 -0
- package/src/MarkdownUtils.js +127 -0
- package/src/MessageUtils.js +1 -1
- package/src/MetaRawResultUtils.js +244 -0
- package/src/ObjectUtils.js +54 -0
- package/src/PatchUtils/constants.js +9 -3
- package/src/PatchUtils/patchParser.js +60 -37
- package/src/PatchUtils/patchProcessor.js +8 -5
- package/src/PatchUtils/patchVerifier/detectAndFixOverlappingHunks.js +1 -1
- package/src/SVGUtils.js +1467 -0
- package/src/SharedUtils/stringUtils.js +303 -0
- package/src/CodeBlockUtils/blockProcessor.js.rej +0 -8
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Component: MetaRawResultUtils
|
|
3
|
+
* Block-UUID: 8a4d235b-a9d3-44b8-9c08-47b3678ba1c0
|
|
4
|
+
* Parent-UUID: N/A
|
|
5
|
+
* Version: 1.0.0
|
|
6
|
+
* Description: Utility for parsing and extracting mappings from meta-raw-result messages
|
|
7
|
+
* Language: JavaScript
|
|
8
|
+
* Created-at: 2025-11-15T19:15:00.000Z
|
|
9
|
+
* Authors: GLM-4.6 (v1.0.0)
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
const ChatUtils = require('./ChatUtils');
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Extracts and parses all meta-raw-result messages from a chat
|
|
17
|
+
* @param {Object} chat - The chat object containing messages
|
|
18
|
+
* @param {string} model - The model name (optional, for message filtering)
|
|
19
|
+
* @returns {Map<number, Object>} Map of chatId to file metadata
|
|
20
|
+
*/
|
|
21
|
+
function extractMetaRawResultMappings(chat, model = null) {
|
|
22
|
+
const mappings = new Map();
|
|
23
|
+
|
|
24
|
+
try {
|
|
25
|
+
// Get all messages from the chat
|
|
26
|
+
const allMessages = ChatUtils.getChatMessages(chat, model);
|
|
27
|
+
|
|
28
|
+
// Filter for meta-raw-result messages
|
|
29
|
+
const metaRawMessages = allMessages.filter(msg => isMetaRawResultMessage(msg));
|
|
30
|
+
|
|
31
|
+
// Parse each meta-raw-result message
|
|
32
|
+
for (const message of metaRawMessages) {
|
|
33
|
+
const messageMappings = parseMetaRawResultContent(message.message);
|
|
34
|
+
|
|
35
|
+
// Merge mappings into the main map
|
|
36
|
+
for (const [chatId, fileInfo] of messageMappings) {
|
|
37
|
+
mappings.set(chatId, fileInfo);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return mappings;
|
|
42
|
+
} catch (error) {
|
|
43
|
+
console.error('Error extracting meta-raw-result mappings:', error);
|
|
44
|
+
return mappings;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Validates if a message is a meta-raw-result message
|
|
50
|
+
* @param {Object} message - The message object to validate
|
|
51
|
+
* @returns {boolean} True if message is meta-raw-result type
|
|
52
|
+
*/
|
|
53
|
+
function isMetaRawResultMessage(message) {
|
|
54
|
+
if (!message || !message.message) {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Check if message type is meta-raw-result
|
|
59
|
+
if (message.type === 'meta-raw-result') {
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Parses a single meta-raw-result message content
|
|
68
|
+
* @param {string} messageContent - The message content to parse
|
|
69
|
+
* @returns {Map<number, Object>} Map of chatId to file metadata
|
|
70
|
+
*/
|
|
71
|
+
function parseMetaRawResultContent(messageContent) {
|
|
72
|
+
const mappings = new Map();
|
|
73
|
+
|
|
74
|
+
try {
|
|
75
|
+
// Split content into lines
|
|
76
|
+
const lines = messageContent.split('\n');
|
|
77
|
+
|
|
78
|
+
// Find the data section
|
|
79
|
+
let dataSectionStart = -1;
|
|
80
|
+
for (let i = 0; i < lines.length; i++) {
|
|
81
|
+
if (lines[i].trimStart().startsWith('## Data ')) {
|
|
82
|
+
dataSectionStart = i;
|
|
83
|
+
break;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (dataSectionStart === -1) {
|
|
88
|
+
return mappings; // No data section found
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Find the table header (line with |)
|
|
92
|
+
let headerLine = -1;
|
|
93
|
+
for (let i = dataSectionStart; i < lines.length; i++) {
|
|
94
|
+
if (lines[i].includes('|') && lines[i].includes('Chat ID')) {
|
|
95
|
+
headerLine = i;
|
|
96
|
+
break;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (headerLine === -1) {
|
|
101
|
+
return mappings; // No table header found
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Find the separator line (|---|---|...)
|
|
105
|
+
let separatorLine = -1;
|
|
106
|
+
for (let i = headerLine + 1; i < lines.length; i++) {
|
|
107
|
+
if (lines[i].match(/^\|[\s\-\|]*\|$/)) {
|
|
108
|
+
separatorLine = i;
|
|
109
|
+
break;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (separatorLine === -1) {
|
|
114
|
+
return mappings; // No separator found
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Parse data rows (everything after the separator)
|
|
118
|
+
for (let i = separatorLine + 1; i < lines.length; i++) {
|
|
119
|
+
const line = lines[i].trim();
|
|
120
|
+
|
|
121
|
+
// Skip empty lines or separator lines
|
|
122
|
+
if (!line || line.match(/^\|[\s\-\|]*\|$/) || !line.includes('|')) {
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Parse the table row
|
|
127
|
+
const fileInfo = parseTableRow(line);
|
|
128
|
+
if (fileInfo && fileInfo.id) {
|
|
129
|
+
mappings.set(fileInfo.id, fileInfo);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return mappings;
|
|
134
|
+
} catch (error) {
|
|
135
|
+
console.error('Error parsing meta-raw-result content:', error);
|
|
136
|
+
return mappings;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Parses a single table row from meta-raw-result content
|
|
142
|
+
* @param {string} rowLine - The table row line to parse
|
|
143
|
+
* @returns {Object|null} Parsed file information object or null if parsing failed
|
|
144
|
+
*/
|
|
145
|
+
function parseTableRow(rowLine) {
|
|
146
|
+
try {
|
|
147
|
+
// Split the row by | and clean up
|
|
148
|
+
const cells = rowLine.split('|').map(cell => cell.trim()).filter(cell => cell);
|
|
149
|
+
|
|
150
|
+
if (cells.length < 6) {
|
|
151
|
+
return null; // Not enough columns
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Extract basic information from the table cells
|
|
155
|
+
const repo = cells[0];
|
|
156
|
+
const branch = cells[1];
|
|
157
|
+
const filePath = cells[2];
|
|
158
|
+
const language = cells[3];
|
|
159
|
+
const chatId = parseInt(cells[4], 10);
|
|
160
|
+
|
|
161
|
+
// Parse file path to extract name and path
|
|
162
|
+
const pathParts = filePath.split('/');
|
|
163
|
+
const name = pathParts[pathParts.length - 1];
|
|
164
|
+
const path = filePath;
|
|
165
|
+
|
|
166
|
+
// Create full path
|
|
167
|
+
const fullPath = `${repo}/${path}`;
|
|
168
|
+
|
|
169
|
+
return {
|
|
170
|
+
id: chatId,
|
|
171
|
+
name: name,
|
|
172
|
+
path: path,
|
|
173
|
+
repo: repo,
|
|
174
|
+
branch: branch,
|
|
175
|
+
language: language,
|
|
176
|
+
fullPath: fullPath
|
|
177
|
+
};
|
|
178
|
+
} catch (error) {
|
|
179
|
+
console.error('Error parsing table row:', error);
|
|
180
|
+
return null;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Parses size string to bytes
|
|
186
|
+
* @param {string} sizeStr - Size string (e.g., "1.2 KB", "500 B")
|
|
187
|
+
* @returns {number} Size in bytes
|
|
188
|
+
*/
|
|
189
|
+
function parseSize(sizeStr) {
|
|
190
|
+
if (!sizeStr || sizeStr === 'N/A') {
|
|
191
|
+
return 0;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const match = sizeStr.match(/^([\d.]+)\s*(B|KB|MB|GB)?$/i);
|
|
195
|
+
if (!match) {
|
|
196
|
+
return 0;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const value = parseFloat(match[1]);
|
|
200
|
+
const unit = (match[2] || 'B').toUpperCase();
|
|
201
|
+
|
|
202
|
+
switch (unit) {
|
|
203
|
+
case 'B': return Math.round(value);
|
|
204
|
+
case 'KB': return Math.round(value * 1024);
|
|
205
|
+
case 'MB': return Math.round(value * 1024 * 1024);
|
|
206
|
+
case 'GB': return Math.round(value * 1024 * 1024 * 1024);
|
|
207
|
+
default: return 0;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Parses tokens string to number
|
|
213
|
+
* @param {string} tokensStr - Tokens string (e.g., "1.2k", "500")
|
|
214
|
+
* @returns {number} Token count
|
|
215
|
+
*/
|
|
216
|
+
function parseTokens(tokensStr) {
|
|
217
|
+
if (!tokensStr || tokensStr === 'N/A') {
|
|
218
|
+
return 0;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const match = tokensStr.match(/^([\d.]+)(k|m|b)?$/i);
|
|
222
|
+
if (!match) {
|
|
223
|
+
return 0;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const value = parseFloat(match[1]);
|
|
227
|
+
const suffix = (match[2] || '').toLowerCase();
|
|
228
|
+
|
|
229
|
+
switch (suffix) {
|
|
230
|
+
case 'k': return Math.round(value * 1000);
|
|
231
|
+
case 'm': return Math.round(value * 1000000);
|
|
232
|
+
case 'b': return Math.round(value * 1000000000);
|
|
233
|
+
default: return Math.round(value);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
module.exports = {
|
|
238
|
+
extractMetaRawResultMappings,
|
|
239
|
+
isMetaRawResultMessage,
|
|
240
|
+
parseMetaRawResultContent,
|
|
241
|
+
parseTableRow,
|
|
242
|
+
parseSize,
|
|
243
|
+
parseTokens
|
|
244
|
+
};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Component: ObjectUtils
|
|
3
|
+
* Block-UUID: 6f3c6c7a-1c5a-40c4-be26-332703b68d31
|
|
4
|
+
* Parent-UUID: N/A
|
|
5
|
+
* Version: 1.0.0
|
|
6
|
+
* Description: Utility functions for processing and manipulating JavaScript objects, including string property trimming for LLM token optimization
|
|
7
|
+
* Language: JavaScript
|
|
8
|
+
* Created-at: 2025-10-17T17:02:12.153Z
|
|
9
|
+
* Authors: Qwen 3 Coder 480B - Cerebras (v1.0.0)
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Recursively trims string properties in an object to a maximum length
|
|
15
|
+
* @param {Object|Array} obj - The object or array to process
|
|
16
|
+
* @param {number} maxLength - Maximum length for strings (default: 100)
|
|
17
|
+
* @param {string} suffix - Suffix to append to trimmed strings (default: '...')
|
|
18
|
+
* @returns {Object|Array} - New object with trimmed strings
|
|
19
|
+
*/
|
|
20
|
+
function trimObjectStrings(obj, maxLength = 100, suffix = '...') {
|
|
21
|
+
// Handle null or undefined
|
|
22
|
+
if (obj === null || obj === undefined) {
|
|
23
|
+
return obj;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Handle strings
|
|
27
|
+
if (typeof obj === 'string') {
|
|
28
|
+
if (obj.length <= maxLength) {
|
|
29
|
+
return obj;
|
|
30
|
+
}
|
|
31
|
+
return obj.substring(0, maxLength - suffix.length) + suffix;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Handle arrays
|
|
35
|
+
if (Array.isArray(obj)) {
|
|
36
|
+
return obj.map(item => trimObjectStrings(item, maxLength, suffix));
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Handle objects
|
|
40
|
+
if (typeof obj === 'object') {
|
|
41
|
+
const trimmedObj = {};
|
|
42
|
+
for (const key in obj) {
|
|
43
|
+
if (obj.hasOwnProperty(key)) {
|
|
44
|
+
trimmedObj[key] = trimObjectStrings(obj[key], maxLength, suffix);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return trimmedObj;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Return other types unchanged
|
|
51
|
+
return obj;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
module.exports = { trimObjectStrings };
|
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
* Component: PatchUtils Constants
|
|
3
3
|
* Block-UUID: c6747054-2c8b-461f-910d-0fd725d9a350
|
|
4
4
|
* Parent-UUID: 32adc00e-7509-4219-8e40-6c1319371db9
|
|
5
|
-
* Version: 2.
|
|
5
|
+
* Version: 2.1.0
|
|
6
6
|
* Description: Contains shared constants and regular expressions used by the enhanced patch utilities.
|
|
7
7
|
* Language: JavaScript
|
|
8
8
|
* Created-at: 2025-05-14T16:55:00.000Z
|
|
9
|
-
* Authors: Gemini 2.5 Flash Thinking (v1.0.0), Claude 3.7 Sonnet (v2.0.0)
|
|
9
|
+
* Authors: Gemini 2.5 Flash Thinking (v1.0.0), Claude 3.7 Sonnet (v2.0.0), Qwen 3 Coder 480B - Cerebras (v2.1.0)
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
|
|
@@ -34,10 +34,14 @@ const DEFAULT_SLIDING_WINDOW_SIZE = 3;
|
|
|
34
34
|
// If context is longer than this, we'll use a sliding window approach
|
|
35
35
|
const MAX_CONTEXT_LINES_FOR_DIRECT_MATCH = 10;
|
|
36
36
|
|
|
37
|
-
//
|
|
37
|
+
// Traditional patch markers
|
|
38
38
|
const PATCH_START_MARKER = '# --- PATCH START MARKER ---';
|
|
39
39
|
const PATCH_END_MARKER = '# --- PATCH END MARKER ---';
|
|
40
40
|
|
|
41
|
+
// Abbreviated patch markers
|
|
42
|
+
const ABBREVIATED_PATCH_START_MARKER = '# --- ABBREVIATED PATCH START MARKER ---';
|
|
43
|
+
const ABBREVIATED_PATCH_END_MARKER = '# --- ABBREVIATED PATCH END MARKER ---';
|
|
44
|
+
|
|
41
45
|
// Patch metadata header
|
|
42
46
|
const PATCH_METADATA_HEADER = '# Patch Metadata';
|
|
43
47
|
|
|
@@ -65,6 +69,8 @@ module.exports = {
|
|
|
65
69
|
MAX_CONTEXT_LINES_FOR_DIRECT_MATCH,
|
|
66
70
|
PATCH_START_MARKER,
|
|
67
71
|
PATCH_END_MARKER,
|
|
72
|
+
ABBREVIATED_PATCH_START_MARKER,
|
|
73
|
+
ABBREVIATED_PATCH_END_MARKER,
|
|
68
74
|
PATCH_METADATA_HEADER,
|
|
69
75
|
REQUIRED_METADATA_FIELDS,
|
|
70
76
|
ORIGINAL_FILE_HEADER,
|
|
@@ -2,21 +2,24 @@
|
|
|
2
2
|
* Component: PatchUtils Parser
|
|
3
3
|
* Block-UUID: ce634df9-ff99-482a-a261-56ab34a2546e
|
|
4
4
|
* Parent-UUID: fcc85ec1-0146-40bf-a937-6f7928258cbf
|
|
5
|
-
* Version: 1.
|
|
6
|
-
* Description: Handles parsing, validation, extraction, and detection of traditional unified diff patches. Extracts metadata and raw diff content.
|
|
5
|
+
* Version: 1.2.0
|
|
6
|
+
* Description: Handles parsing, validation, extraction, and detection of traditional unified diff patches and abbreviated patches. Extracts metadata and raw diff content.
|
|
7
7
|
* Language: JavaScript
|
|
8
8
|
* Created-at: 2025-04-18T02:59:04.322Z
|
|
9
|
-
* Authors: Gemini 2.5 Pro (v1.0.0), Gemini 2.5 Pro (v1.1.0)
|
|
9
|
+
* Authors: Gemini 2.5 Pro (v1.0.0), Gemini 2.5 Pro (v1.1.0), Qwen 3 Coder 480B - Cerebras (v1.2.0)
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
const { isValidVersion } = require('../SharedUtils/versionUtils'); // Assuming SharedUtils is one level up
|
|
14
|
+
const { PATCH_START_MARKER, PATCH_END_MARKER, PATCH_METADATA_HEADER,
|
|
15
|
+
ORIGINAL_FILE_HEADER, MODIFIED_FILE_HEADER, ABBREVIATED_PATCH_START_MARKER,
|
|
16
|
+
ABBREVIATED_PATCH_END_MARKER } = require('./constants');
|
|
14
17
|
|
|
15
18
|
/**
|
|
16
19
|
* Determines the type of patch format used based on markers.
|
|
17
|
-
*
|
|
20
|
+
* Looks for traditional unified diff markers or abbreviated patch markers.
|
|
18
21
|
* @param {string} patchText - The patch text
|
|
19
|
-
* @returns {string} 'traditional', or 'unknown'
|
|
22
|
+
* @returns {string} 'traditional', 'abbreviated', or 'unknown'
|
|
20
23
|
*/
|
|
21
24
|
function determinePatchFormat(patchText) {
|
|
22
25
|
if (!patchText || typeof patchText !== 'string') {
|
|
@@ -24,29 +27,33 @@ function determinePatchFormat(patchText) {
|
|
|
24
27
|
}
|
|
25
28
|
|
|
26
29
|
// Check for required metadata header first
|
|
27
|
-
if (!patchText.includes(
|
|
30
|
+
if (!patchText.includes(PATCH_METADATA_HEADER)) {
|
|
28
31
|
return 'unknown'; // Must have metadata header
|
|
29
32
|
}
|
|
30
33
|
|
|
31
34
|
// Check for traditional unified diff markers *after* potential metadata and start marker
|
|
32
35
|
// Look for ---, +++, @@ after the start marker
|
|
33
|
-
const
|
|
34
|
-
if (
|
|
35
|
-
|
|
36
|
-
}
|
|
36
|
+
const traditionalStartMarkerIndex = patchText.indexOf(PATCH_START_MARKER);
|
|
37
|
+
if (traditionalStartMarkerIndex !== -1) {
|
|
38
|
+
const contentAfterMarker = patchText.substring(traditionalStartMarkerIndex);
|
|
37
39
|
|
|
38
|
-
|
|
40
|
+
// Use regex for more reliable detection within the content part
|
|
41
|
+
const traditionalMarkers = /^\s*--- Original\s*\n\s*\+\+\+ Modified\s*\n\s*@@ -\d+(?:,\d+)? \+\d+(?:,\d+)? @@/m;
|
|
39
42
|
|
|
40
|
-
|
|
41
|
-
|
|
43
|
+
if (traditionalMarkers.test(contentAfterMarker)) {
|
|
44
|
+
return 'traditional';
|
|
45
|
+
}
|
|
46
|
+
}
|
|
42
47
|
|
|
43
|
-
|
|
44
|
-
|
|
48
|
+
// Check for abbreviated patch markers
|
|
49
|
+
const abbreviatedStartMarkerIndex = patchText.indexOf(ABBREVIATED_PATCH_START_MARKER);
|
|
50
|
+
if (abbreviatedStartMarkerIndex !== -1) {
|
|
51
|
+
return 'abbreviated';
|
|
45
52
|
}
|
|
46
53
|
|
|
47
54
|
// Removed check for obsolete '@=' context marker
|
|
48
55
|
|
|
49
|
-
return 'unknown'; // Format doesn't match expected
|
|
56
|
+
return 'unknown'; // Format doesn't match expected structures after markers
|
|
50
57
|
}
|
|
51
58
|
|
|
52
59
|
/**
|
|
@@ -68,7 +75,8 @@ function extractPatchMetadata(patchText) {
|
|
|
68
75
|
const trimmedLine = line.trim();
|
|
69
76
|
|
|
70
77
|
// Stop processing metadata if we hit content markers or the start marker
|
|
71
|
-
if (trimmedLine ===
|
|
78
|
+
if (trimmedLine === PATCH_START_MARKER ||
|
|
79
|
+
trimmedLine === ABBREVIATED_PATCH_START_MARKER ||
|
|
72
80
|
line.startsWith('--- ') || // Standard diff header start
|
|
73
81
|
line.startsWith('+++ ') || // Standard diff header start
|
|
74
82
|
line.startsWith('@@ ')) // Standard diff hunk header start
|
|
@@ -91,7 +99,7 @@ function extractPatchMetadata(patchText) {
|
|
|
91
99
|
}
|
|
92
100
|
}
|
|
93
101
|
// Allow lines that are just comments (e.g., # Description continued)
|
|
94
|
-
} else if (trimmedLine !== '' && !trimmedLine.startsWith(
|
|
102
|
+
} else if (trimmedLine !== '' && !trimmedLine.startsWith(PATCH_METADATA_HEADER)) {
|
|
95
103
|
// If we encounter non-empty, non-comment lines before the start marker,
|
|
96
104
|
// it's likely malformed, stop metadata parsing.
|
|
97
105
|
inMetadata = false;
|
|
@@ -135,11 +143,11 @@ function validatePatchMetadata(metadata) {
|
|
|
135
143
|
}
|
|
136
144
|
|
|
137
145
|
// Validate UUIDs are not the template string
|
|
138
|
-
if (metadata['Source-Block-UUID'] === '
|
|
139
|
-
errors.push('Source-Block-UUID contains placeholder value "
|
|
146
|
+
if (metadata['Source-Block-UUID'] === '{{GS-UUID}}') {
|
|
147
|
+
errors.push('Source-Block-UUID contains placeholder value "{{GS-UUID}}".');
|
|
140
148
|
}
|
|
141
|
-
if (metadata['Target-Block-UUID'] === '
|
|
142
|
-
errors.push('Target-Block-UUID contains placeholder value "
|
|
149
|
+
if (metadata['Target-Block-UUID'] === '{{GS-UUID}}') {
|
|
150
|
+
errors.push('Target-Block-UUID contains placeholder value "{{GS-UUID}}".');
|
|
143
151
|
}
|
|
144
152
|
|
|
145
153
|
return errors;
|
|
@@ -152,22 +160,34 @@ function validatePatchMetadata(metadata) {
|
|
|
152
160
|
* @param {string} format - Expected format (should be 'traditional').
|
|
153
161
|
* @returns {string} Raw unified diff content, or empty string if not found/invalid.
|
|
154
162
|
*/
|
|
155
|
-
function extractPatchContent(patchText, format) {
|
|
156
|
-
if (!patchText
|
|
163
|
+
function extractPatchContent(patchText, format = 'traditional') {
|
|
164
|
+
if (!patchText) {
|
|
157
165
|
return "";
|
|
158
166
|
}
|
|
159
167
|
|
|
160
168
|
const lines = patchText.split('\n');
|
|
161
169
|
let contentLines = [];
|
|
162
170
|
let inContentBlock = false;
|
|
171
|
+
let startMarker, endMarker;
|
|
172
|
+
|
|
173
|
+
// Determine which markers to look for based on format
|
|
174
|
+
if (format === 'traditional') {
|
|
175
|
+
startMarker = PATCH_START_MARKER;
|
|
176
|
+
endMarker = PATCH_END_MARKER;
|
|
177
|
+
} else if (format === 'abbreviated') {
|
|
178
|
+
startMarker = ABBREVIATED_PATCH_START_MARKER;
|
|
179
|
+
endMarker = ABBREVIATED_PATCH_END_MARKER;
|
|
180
|
+
} else {
|
|
181
|
+
return ""; // Unsupported format
|
|
182
|
+
}
|
|
163
183
|
|
|
164
184
|
for (const line of lines) {
|
|
165
|
-
if (line.trim() ===
|
|
185
|
+
if (line.trim() === startMarker) {
|
|
166
186
|
inContentBlock = true;
|
|
167
187
|
continue; // Skip the marker line itself
|
|
168
188
|
}
|
|
169
189
|
|
|
170
|
-
if (line.trim() ===
|
|
190
|
+
if (line.trim() === endMarker) {
|
|
171
191
|
inContentBlock = false;
|
|
172
192
|
break; // Stop processing once end marker is found
|
|
173
193
|
}
|
|
@@ -180,8 +200,6 @@ function extractPatchContent(patchText, format) {
|
|
|
180
200
|
return contentLines.join('\n');
|
|
181
201
|
}
|
|
182
202
|
|
|
183
|
-
// Removed extractContextPatches function (obsolete context format)
|
|
184
|
-
|
|
185
203
|
/**
|
|
186
204
|
* Determines if a code block's content represents a patch block
|
|
187
205
|
* by checking for the mandatory metadata header.
|
|
@@ -208,23 +226,25 @@ function detectPatch(messageText) {
|
|
|
208
226
|
}
|
|
209
227
|
|
|
210
228
|
// Find the first code block that contains patch metadata
|
|
211
|
-
// Regex looks for ``` optionally followed by
|
|
212
|
-
const codeBlockRegex = /```(
|
|
229
|
+
// Regex looks for ``` optionally followed by language hint
|
|
230
|
+
const codeBlockRegex = /```([a-zA-Z0-9#+_-]*)?\s*\n([\s\S]*?)```/g;
|
|
213
231
|
let match;
|
|
214
232
|
while ((match = codeBlockRegex.exec(messageText)) !== null) {
|
|
215
|
-
const
|
|
233
|
+
const language = match[1] || '';
|
|
234
|
+
const blockContent = match[2];
|
|
216
235
|
if (isPatchBlock(blockContent)) { // Use isPatchBlock for check
|
|
217
236
|
const metadata = extractPatchMetadata(blockContent); // Use extractPatchMetadata
|
|
218
237
|
// Basic validation: Check for essential UUIDs
|
|
219
238
|
if (metadata['Source-Block-UUID'] && metadata['Target-Block-UUID'] &&
|
|
220
|
-
metadata['Source-Block-UUID'] !== '
|
|
221
|
-
metadata['Target-Block-UUID'] !== '
|
|
239
|
+
metadata['Source-Block-UUID'] !== '{{GS-UUID}}' && // Check placeholders
|
|
240
|
+
metadata['Target-Block-UUID'] !== '{{GS-UUID}}')
|
|
222
241
|
{
|
|
223
242
|
return {
|
|
224
243
|
patchText: blockContent, // Return the content inside the fence
|
|
225
244
|
metadata,
|
|
226
245
|
sourceBlockUUID: metadata['Source-Block-UUID'],
|
|
227
246
|
targetBlockUUID: metadata['Target-Block-UUID'],
|
|
247
|
+
language: language,
|
|
228
248
|
startIndex: match.index, // Store block position if needed
|
|
229
249
|
endIndex: match.index + match[0].length
|
|
230
250
|
};
|
|
@@ -246,25 +266,28 @@ function findAllPatches(messageText) {
|
|
|
246
266
|
}
|
|
247
267
|
|
|
248
268
|
const patches = [];
|
|
249
|
-
|
|
269
|
+
// Find code blocks with any language identifier
|
|
270
|
+
const codeBlockRegex = /```([a-zA-Z0-9#+_-]*)?\s*\n([\s\S]*?)```/g;
|
|
250
271
|
|
|
251
272
|
let match;
|
|
252
273
|
while ((match = codeBlockRegex.exec(messageText)) !== null) {
|
|
253
|
-
const
|
|
274
|
+
const language = match[1] || '';
|
|
275
|
+
const blockContent = match[2];
|
|
254
276
|
|
|
255
277
|
if (isPatchBlock(blockContent)) {
|
|
256
278
|
const metadata = extractPatchMetadata(blockContent);
|
|
257
279
|
|
|
258
280
|
// Basic validation: Check for essential UUIDs and non-placeholder values
|
|
259
281
|
if (metadata['Source-Block-UUID'] && metadata['Target-Block-UUID'] &&
|
|
260
|
-
metadata['Source-Block-UUID'] !== '
|
|
261
|
-
metadata['Target-Block-UUID'] !== '
|
|
282
|
+
metadata['Source-Block-UUID'] !== '{{GS-UUID}}' &&
|
|
283
|
+
metadata['Target-Block-UUID'] !== '{{GS-UUID}}')
|
|
262
284
|
{
|
|
263
285
|
patches.push({
|
|
264
286
|
patchText: blockContent,
|
|
265
287
|
metadata,
|
|
266
288
|
sourceBlockUUID: metadata['Source-Block-UUID'],
|
|
267
289
|
targetBlockUUID: metadata['Target-Block-UUID'],
|
|
290
|
+
language: language,
|
|
268
291
|
startIndex: match.index,
|
|
269
292
|
endIndex: match.index + match[0].length
|
|
270
293
|
});
|
|
@@ -113,7 +113,7 @@ function applyPatch(sourceText, patchText) {
|
|
|
113
113
|
}
|
|
114
114
|
|
|
115
115
|
// 4. Apply the patch
|
|
116
|
-
const patchedResult = jsdiff.applyPatch(sourceText, rawDiffContent, { fuzzFactor:
|
|
116
|
+
const patchedResult = jsdiff.applyPatch(sourceText, rawDiffContent, { fuzzFactor: 20 });
|
|
117
117
|
|
|
118
118
|
if (patchedResult === false) {
|
|
119
119
|
// jsdiff.applyPatch returns false on failure
|
|
@@ -276,11 +276,13 @@ function createPatchFromCodeBlocks(CodeBlockUtils, sourceCodeBlockText, targetCo
|
|
|
276
276
|
const [ sourceHeaderText ] = sourceCodeBlockText.split('\n\n\n');
|
|
277
277
|
const [ targetHeaderText ] = targetCodeBlockText.split('\n\n\n');
|
|
278
278
|
|
|
279
|
-
const sourceContentTemp = sourceCodeBlockText.split('\n\n\n');
|
|
280
|
-
|
|
279
|
+
const sourceContentTemp = sourceCodeBlockText.split('\n\n\n');
|
|
280
|
+
sourceContentTemp.shift();
|
|
281
|
+
const sourceContent = sourceContentTemp.join('\n\n\n');
|
|
281
282
|
|
|
282
|
-
const targetContentTemp = targetCodeBlockText.split('\n\n\n');
|
|
283
|
-
|
|
283
|
+
const targetContentTemp = targetCodeBlockText.split('\n\n\n');
|
|
284
|
+
targetContentTemp.shift();
|
|
285
|
+
const targetContent = targetContentTemp.join('\n\n\n');
|
|
284
286
|
|
|
285
287
|
// Get the number of lines in the header + two blank lines
|
|
286
288
|
// Note, before the code block header format was solidified, we use to include 'Updated-at' but no more so
|
|
@@ -346,6 +348,7 @@ function createPatchFromCodeBlocks(CodeBlockUtils, sourceCodeBlockText, targetCo
|
|
|
346
348
|
`# --- PATCH START MARKER ---\n`+
|
|
347
349
|
`${adjustedDiffPatch}\n`+
|
|
348
350
|
`# --- PATCH END MARKER ---`;
|
|
351
|
+
|
|
349
352
|
return {
|
|
350
353
|
formatted,
|
|
351
354
|
formattedMetadata,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Component: PatchUtils Detect and Fix Overlapping Hunks
|
|
3
|
-
* Block-UUID:
|
|
3
|
+
* Block-UUID: 7324dab8-24ae-4a42-9dc3-766e69104ef6
|
|
4
4
|
* Parent-UUID: 2308ed72-91ff-48ba-bc80-310c23c01ff1
|
|
5
5
|
* Version: 1.0.0
|
|
6
6
|
* Description: Detects and optionally fixes overlapping hunks in a patch file by merging them.
|