@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.
Files changed (48) hide show
  1. package/LICENSE +21 -0
  2. package/dist/gitsense-chat-utils.cjs.js +10977 -0
  3. package/dist/gitsense-chat-utils.esm.js +10975 -0
  4. package/dist/gsc-utils.cjs.js +11043 -0
  5. package/dist/gsc-utils.esm.js +11041 -0
  6. package/package.json +37 -0
  7. package/src/AnalysisBlockUtils.js +151 -0
  8. package/src/ChatUtils.js +126 -0
  9. package/src/CodeBlockUtils/blockExtractor.js +277 -0
  10. package/src/CodeBlockUtils/blockProcessor.js +559 -0
  11. package/src/CodeBlockUtils/blockProcessor.js.rej +8 -0
  12. package/src/CodeBlockUtils/constants.js +62 -0
  13. package/src/CodeBlockUtils/continuationUtils.js +191 -0
  14. package/src/CodeBlockUtils/headerParser.js +175 -0
  15. package/src/CodeBlockUtils/headerUtils.js +236 -0
  16. package/src/CodeBlockUtils/index.js +83 -0
  17. package/src/CodeBlockUtils/lineNumberFormatter.js +117 -0
  18. package/src/CodeBlockUtils/markerRemover.js +89 -0
  19. package/src/CodeBlockUtils/patchIntegration.js +38 -0
  20. package/src/CodeBlockUtils/relationshipUtils.js +159 -0
  21. package/src/CodeBlockUtils/updateCodeBlock.js +372 -0
  22. package/src/CodeBlockUtils/uuidUtils.js +48 -0
  23. package/src/ContextUtils.js +180 -0
  24. package/src/GSToolBlockUtils.js +108 -0
  25. package/src/GitSenseChatUtils.js +386 -0
  26. package/src/JsonUtils.js +101 -0
  27. package/src/LLMUtils.js +31 -0
  28. package/src/MessageUtils.js +460 -0
  29. package/src/PatchUtils/constants.js +72 -0
  30. package/src/PatchUtils/diagnosticReporter.js +213 -0
  31. package/src/PatchUtils/enhancedPatchProcessor.js +390 -0
  32. package/src/PatchUtils/fuzzyMatcher.js +252 -0
  33. package/src/PatchUtils/hunkCorrector.js +204 -0
  34. package/src/PatchUtils/hunkValidator.js +305 -0
  35. package/src/PatchUtils/index.js +135 -0
  36. package/src/PatchUtils/patchExtractor.js +175 -0
  37. package/src/PatchUtils/patchHeaderFormatter.js +143 -0
  38. package/src/PatchUtils/patchParser.js +289 -0
  39. package/src/PatchUtils/patchProcessor.js +389 -0
  40. package/src/PatchUtils/patchVerifier/constants.js +23 -0
  41. package/src/PatchUtils/patchVerifier/detectAndFixOverlappingHunks.js +281 -0
  42. package/src/PatchUtils/patchVerifier/detectAndFixRedundantChanges.js +404 -0
  43. package/src/PatchUtils/patchVerifier/formatAndAddLineNumbers.js +165 -0
  44. package/src/PatchUtils/patchVerifier/index.js +25 -0
  45. package/src/PatchUtils/patchVerifier/verifyAndCorrectHunkHeaders.js +202 -0
  46. package/src/PatchUtils/patchVerifier/verifyAndCorrectLineNumbers.js +254 -0
  47. package/src/SharedUtils/timestampUtils.js +41 -0
  48. package/src/SharedUtils/versionUtils.js +58 -0
@@ -0,0 +1,460 @@
1
+ /**
2
+ * Component: MessageUtils
3
+ * Block-UUID: 01147ddf-320f-498a-a744-198d42a9d2ee
4
+ * Parent-UUID: fb0b40b3-ce0b-47e2-bb3e-b39092e9b829
5
+ * Version: 1.2.0
6
+ * Description: Utility functions for processing message files, including context message detection.
7
+ * Language: JavaScript
8
+ * Created-at: 2025-07-11T19:56:54.949Z
9
+ * Authors: Claude 3.7 Sonnet (v1.0.0), Gemini 2.5 Pro (v1.1.0), Gemini 2.5 Flash Thinking (v1.2.0)
10
+ */
11
+
12
+
13
+ const fs = require('fs');
14
+ const path = require('path');
15
+ const ANALYZE_MESSAGE_REGEXP = /^# Analyze - `([^`]+)`\n/;
16
+
17
+ /**
18
+ * Extracts the role from the metadata section of a message file
19
+ * @param {string} metadata - The metadata section of the message file
20
+ * @returns {string} The extracted role
21
+ */
22
+ function extractRole(metadata) {
23
+ const lines = metadata.split('\n');
24
+ for (const line of lines) {
25
+ if (line.trim().startsWith('; role:')) {
26
+ return line.trim().substring('; role:'.length).trim();
27
+ }
28
+ }
29
+ return 'user'; // Default to user if role is not found
30
+ }
31
+
32
+ /**
33
+ * Unescapes code blocks by removing forward slashes before backticks
34
+ * @param {string} content - The message content
35
+ * @returns {string} The content with unescaped code blocks
36
+ */
37
+ function unescapeCodeBlocks(content) {
38
+ return content.replace(/\\(`+)/g, '$1');
39
+ }
40
+
41
+ /**
42
+ * Gets template messages from a specific message type directory
43
+ * @param {string} dirname- The directory containting the template messages directory
44
+ * @param {string} messageType - The type of messages to retrieve (e.g., 'notes', 'draft')
45
+ * @returns {Array} An array of message objects with role and content properties
46
+ */
47
+ function getChatTemplateMessages(dirname, messageType) {
48
+ const messagesDir = path.join(dirname, messageType);
49
+ const messages = [];
50
+
51
+ try {
52
+ // Read all files in the directory
53
+ const files = fs.readdirSync(messagesDir);
54
+
55
+ // Sort files numerically (1.md, 2.md, etc.)
56
+ const sortedFiles = files.sort((a, b) => {
57
+ const numA = parseInt(a.split('.')[0]);
58
+ const numB = parseInt(b.split('.')[0]);
59
+ return numA - numB;
60
+ });
61
+
62
+ // Process each file
63
+ for (const file of sortedFiles) {
64
+ if (path.extname(file) === '.md') {
65
+ const filePath = path.join(messagesDir, file);
66
+ const fileContent = fs.readFileSync(filePath, 'utf8');
67
+
68
+ // Split by triple newline to separate metadata from content
69
+ const parts = fileContent.split('\n\n\n');
70
+
71
+ if (parts.length >= 2) {
72
+ const metadata = parts[0];
73
+ // Join all remaining parts in case there are other triple newlines in the content
74
+ const content = parts.slice(1).join('\n\n\n').trim();
75
+
76
+ const role = extractRole(metadata);
77
+ const processedContent = unescapeCodeBlocks(content);
78
+
79
+ messages.push({
80
+ role,
81
+ content: processedContent
82
+ });
83
+ } else {
84
+ // Handle case where there's no triple newline
85
+ const role = extractRole(fileContent);
86
+ messages.push({
87
+ role,
88
+ content: ''
89
+ });
90
+ }
91
+ }
92
+ }
93
+
94
+ return messages;
95
+ } catch (error) {
96
+ console.error(`Error reading messages from ${messageType}:`, error);
97
+ return [];
98
+ }
99
+ }
100
+
101
+ /**
102
+ * Gets messages before a specific ID
103
+ * @param {string} model - The model to filter messages by
104
+ * @param {Object} message - The message object to start from
105
+ * @param {string} stopId - Optional ID to stop at
106
+ * @param {Array} messages - Optional array to accumulate messages
107
+ * @returns {Array} Array of messages
108
+ */
109
+ function getMessagesBeforeId(model, message, stopId, messages = [], debug = false) {
110
+ if (!model)
111
+ throw new Error('MessageUtils: No model defined');
112
+
113
+ if (!message)
114
+ throw new Error('MessageUtils: No message defined');
115
+
116
+ const { role, id, kids, model: m } = message;
117
+
118
+ if (id === stopId) {
119
+ return messages;
120
+ }
121
+
122
+ if (!m || m === model) {
123
+ messages.push(message);
124
+ }
125
+
126
+ if (!kids || !kids.length) {
127
+ return messages;
128
+ }
129
+
130
+ return getMessagesBeforeId(model, kids[0], stopId, messages);
131
+ }
132
+
133
+ /**
134
+ * Gets a specific message by its ID from a chat tree.
135
+ * @param {Object} chatOrMessage - The root chat object or a message node to start searching from.
136
+ * @param {number|string} id - The ID of the message to find.
137
+ * @returns {Object|null} The found message object or null if not found.
138
+ */
139
+ function getMessageById(chatOrMessage, id) {
140
+ // Use a stack for iterative depth-first search to avoid deep recursion issues
141
+ const stack = [];
142
+
143
+ // Determine starting point: root chat object or a specific message node
144
+ if (chatOrMessage && chatOrMessage.messages && Array.isArray(chatOrMessage.messages)) {
145
+ // If it's the root chat object, start with its top-level messages
146
+ stack.push(...chatOrMessage.messages);
147
+ } else if (chatOrMessage && chatOrMessage.id) {
148
+ // If it's a message node itself
149
+ stack.push(chatOrMessage);
150
+ } else {
151
+ // Invalid input
152
+ console.error("getMessageById: Invalid input provided.", chatOrMessage);
153
+ return null;
154
+ }
155
+
156
+ while (stack.length > 0) {
157
+ const currentMessage = stack.pop();
158
+
159
+ if (!currentMessage) continue; // Skip if undefined/null somehow got pushed
160
+
161
+ // Check if the current message is the one we're looking for
162
+ // Ensure type consistency for comparison if needed (e.g., both numbers or both strings)
163
+ if (currentMessage.id == id) { // Use == for potential type coercion if IDs might be numbers or strings
164
+ return currentMessage;
165
+ }
166
+
167
+ // Add children to the stack to search deeper
168
+ if (currentMessage.kids && Array.isArray(currentMessage.kids)) {
169
+ // Push children in reverse order to maintain depth-first order
170
+ for (let i = currentMessage.kids.length - 1; i >= 0; i--) {
171
+ stack.push(currentMessage.kids[i]);
172
+ }
173
+ }
174
+ }
175
+
176
+ // Message not found
177
+ return null;
178
+ }
179
+
180
+ /**
181
+ * Gets the last message in a specific conversation thread (follows the first child path).
182
+ * @param {Object} message - The message to start from.
183
+ * @returns {Object} The last message in the thread.
184
+ */
185
+ function getLastMessage(message) {
186
+ let current = message;
187
+ while (current && current.kids && current.kids.length > 0) {
188
+ // Assuming the main thread follows the first child
189
+ current = current.kids[0];
190
+ }
191
+ return current;
192
+ }
193
+
194
+ /**
195
+ * Recursively finds all messages in a chat tree that match a given filter function.
196
+ * Uses an iterative approach with a stack to prevent deep recursion errors.
197
+ *
198
+ * @param {Object} rootNode - The starting node (can be the root chat object with a `messages` array, or any message node).
199
+ * @param {function(Object): boolean} filterFn - A function that takes a message object and returns true if it matches the criteria.
200
+ * @returns {Array<Object>} An array of message objects that matched the filter.
201
+ */
202
+ function findMessages(rootNode, filterFn) {
203
+ const matchingMessages = [];
204
+ const stack = [];
205
+
206
+ // Initialize stack based on rootNode type
207
+ if (rootNode && rootNode.messages && Array.isArray(rootNode.messages)) {
208
+ // If it's the root chat object, start with its top-level messages (in reverse for stack processing)
209
+ for (let i = rootNode.messages.length - 1; i >= 0; i--) {
210
+ stack.push(rootNode.messages[i]);
211
+ }
212
+ } else if (rootNode && rootNode.id) {
213
+ // If it's a message node itself
214
+ stack.push(rootNode);
215
+ } else {
216
+ console.warn("findMessages: Invalid rootNode provided.", rootNode);
217
+ return []; // Return empty array for invalid input
218
+ }
219
+
220
+ while (stack.length > 0) {
221
+ const currentMessage = stack.pop();
222
+
223
+ if (!currentMessage) continue; // Skip if undefined/null
224
+
225
+ // Apply the filter function to the current message
226
+ try {
227
+ if (filterFn(currentMessage)) {
228
+ matchingMessages.push(currentMessage);
229
+ }
230
+ } catch (error) {
231
+ console.error("Error executing filter function on message:", currentMessage.id, error);
232
+ // Decide whether to skip this message or re-throw, depending on desired robustness
233
+ }
234
+
235
+
236
+ // Add children to the stack to search deeper (push in reverse order to maintain logical traversal order)
237
+ if (currentMessage.kids && Array.isArray(currentMessage.kids)) {
238
+ for (let i = currentMessage.kids.length - 1; i >= 0; i--) {
239
+ stack.push(currentMessage.kids[i]);
240
+ }
241
+ }
242
+ }
243
+
244
+ return matchingMessages;
245
+ }
246
+
247
+ /**
248
+ * Deletes messages by their IDs from a nested chat structure.
249
+ * Modifies the structure in place or returns a new root depending on input.
250
+ *
251
+ * @param {Object} rootNode - The root node of the chat structure. This can be the main chat object
252
+ * (containing a `messages` array) or a specific message node.
253
+ * @param {Array<number|string>} idsToDeleteArray - An array of message IDs to delete.
254
+ * @returns {Object} The modified rootNode. If the provided rootNode itself was deleted
255
+ * (and it was a message node), this function's behavior might need adjustment
256
+ * based on caller expectations (e.g., return null or throw error).
257
+ * Currently assumes the rootNode itself is not in idsToDeleteArray if it's a message node.
258
+ * @throws {Error} If idsToDeleteArray is not an array.
259
+ */
260
+ function deleteMessagesByIds(rootNode, idsToDeleteArray) {
261
+ if (!Array.isArray(idsToDeleteArray)) {
262
+ throw new Error("idsToDeleteArray must be an array.");
263
+ }
264
+ if (!rootNode) {
265
+ console.warn("deleteMessagesByIds: rootNode is null or undefined.");
266
+ return rootNode; // Return unchanged if root is invalid
267
+ }
268
+
269
+ const idsToDeleteSet = new Set(idsToDeleteArray);
270
+
271
+ // Handle based on whether rootNode is the main chat object or a message node
272
+ if (rootNode.messages && Array.isArray(rootNode.messages)) {
273
+ // rootNode is the main chat object containing the top-level messages array
274
+ rootNode.messages = _processNodeListForDeletion(rootNode.messages, null, idsToDeleteSet);
275
+ } else if (rootNode.id) {
276
+ // rootNode is a message node itself.
277
+ // Check if the root message node itself needs deletion (this case is complex)
278
+ if (idsToDeleteSet.has(rootNode.id)) {
279
+ // This scenario is ambiguous. Deleting the root node passed in means
280
+ // the reference becomes invalid. The caller needs to handle this.
281
+ // For now, we'll log a warning and potentially return null or an empty structure.
282
+ // A safer approach is for the caller to ensure the root isn't deleted,
283
+ // or to always pass the main chat object.
284
+ console.warn(`deleteMessagesByIds: The provided rootNode (ID: ${rootNode.id}) itself is marked for deletion. Returning null.`);
285
+ // To properly handle this, we'd need to return the *children* of the deleted root,
286
+ // but the function signature returns the 'modified rootNode'.
287
+ // Returning null might be the clearest signal.
288
+ const children = rootNode.kids || [];
289
+ const parentIdForOrphanedChildren = 0; // Assuming root's parent is 0
290
+ const processedChildren = _processNodeListForDeletion(children, null, idsToDeleteSet);
291
+ for (const child of processedChildren) {
292
+ child.parent_id = parentIdForOrphanedChildren;
293
+ }
294
+ // This isn't ideal as it returns an array, not the expected rootNode object.
295
+ // Returning null might be the clearest signal.
296
+ return null; // Indicate the root was deleted.
297
+ }
298
+ // Process the children of the root message node
299
+ rootNode.kids = _processNodeListForDeletion(rootNode.kids || [], rootNode, idsToDeleteSet);
300
+ } else {
301
+ console.warn("deleteMessagesByIds: Invalid rootNode structure.", rootNode);
302
+ }
303
+
304
+ return rootNode;
305
+ }
306
+
307
+ /**
308
+ * Recursively processes a list of message nodes, deleting specified messages
309
+ * and re-parenting their children.
310
+ *
311
+ * @param {Array<Object>} nodeList - The list of message nodes to process.
312
+ * @param {Object|null} parentNode - The parent node of the current list (null for root messages).
313
+ * @param {Set<number|string>} idsToDeleteSet - A Set containing the IDs of messages to delete.
314
+ * @returns {Array<Object>} A new list of nodes with specified messages removed and children re-parented.
315
+ * @private
316
+ */
317
+ function _processNodeListForDeletion(nodeList, parentNode, idsToDeleteSet) {
318
+ if (!nodeList || nodeList.length === 0) {
319
+ return [];
320
+ }
321
+
322
+ const newNodelist = [];
323
+ // Determine the correct parent ID for children of deleted nodes
324
+ // Assuming 0 or null is used for top-level messages if parentNode is the root chat object
325
+ const parentIdForOrphanedChildren = parentNode ? parentNode.id : 0;
326
+
327
+ for (const node of nodeList) {
328
+ if (idsToDeleteSet.has(node.id)) {
329
+ // Node needs to be deleted
330
+ const children = node.kids || [];
331
+ // Recursively process the children of the deleted node *before* adding them
332
+ // Pass the *original parent* of the deleted node to maintain the level
333
+ const processedChildren = _processNodeListForDeletion(children, parentNode, idsToDeleteSet);
334
+
335
+ // Re-parent the processed children
336
+ for (const child of processedChildren) {
337
+ child.parent_id = parentIdForOrphanedChildren;
338
+ // Optional: Adjust child.level if necessary, though level might become less meaningful
339
+ // child.level = parentNode ? parentNode.level + 1 : 0;
340
+ }
341
+ // Add the re-parented children directly to the new list, effectively skipping the deleted node
342
+ newNodelist.push(...processedChildren);
343
+
344
+ } else {
345
+ // Node is kept
346
+ // Recursively process the children of the *kept* node
347
+ node.kids = _processNodeListForDeletion(node.kids || [], node, idsToDeleteSet);
348
+ // Add the processed node (with potentially modified kids) to the new list
349
+ newNodelist.push(node);
350
+ }
351
+ }
352
+
353
+ return newNodelist;
354
+ }
355
+
356
+ /**
357
+ * Determines the type of message content based on specific prefixes.
358
+ * @param {string} messageContent - The content of the message.
359
+ * @returns {'overview-context'|'file-content-context'|'analyze-message'|'regular'} The type of the message content.
360
+ */
361
+ function getMessageContentType(messageContent) {
362
+ if (typeof messageContent !== 'string') {
363
+ return 'regular'; // Handle non-string input gracefully
364
+ }
365
+ const trimmedContent = messageContent.trimStart(); // Check from the beginning, ignoring leading whitespace
366
+ if (trimmedContent.startsWith('## FILE CONTENT')) {
367
+ return 'file-content-context';
368
+ } else if (trimmedContent.startsWith('## OVERVIEW')) {
369
+ return 'overview-context';
370
+ } else if (trimmedContent.startsWith('## Context Items Overview\n\nThis section provides')) {
371
+ return 'context-items-overview';
372
+ } else if (trimmedContent.match(ANALYZE_MESSAGE_REGEXP)) {
373
+ return 'analyze-message';
374
+ } else {
375
+ return 'regular';
376
+ }
377
+ }
378
+
379
+ /**
380
+ * Checks if a message is a context message (overview or file content).
381
+ * @param {string} messageContent - The content of the message.
382
+ * @returns {boolean} True if the message is a context message, false otherwise.
383
+ */
384
+ function isContextMessage(messageContent) {
385
+ const type = getMessageContentType(messageContent);
386
+ return type === 'overview-context' || type === 'file-content-context';
387
+ }
388
+
389
+ function isContextItemsOverviewMessage(messageContent) {
390
+ const type = getMessageContentType(messageContent);
391
+ return type === 'context-items-overview';
392
+ }
393
+
394
+ function isAnalyzeMessage(messageContent) {
395
+ const type = getMessageContentType(messageContent);
396
+ return type === 'analyze-message';
397
+ }
398
+
399
+ /**
400
+ * Checks if a message is the final "New Analyzer Instructions" message.
401
+ * @param {string} messageContent - The content of the message.
402
+ * @param {boolean} strict - Enable or disable strict parsing
403
+ * @returns {boolean} True if the message is the new analyzer instructions message, false otherwise.
404
+ */
405
+ function isNewAnalyzerInstructionsMessage(messageContent, strict = true) {
406
+ if (typeof messageContent !== 'string')
407
+ return false;
408
+
409
+ const trimmedMessage = messageContent.trimStart()
410
+ const header = '# New Analyzer Instructions';
411
+
412
+ if (strict) {
413
+ // In strict mode, we need the first list to contain the header
414
+ return trimmedMessage.startsWith(header);
415
+ } else {
416
+ // LLMs are instructed to always start with the header but they don't always
417
+ // comply so we will need to find the header and check the next line to make
418
+ // sure it has a unique identiifer
419
+ const lines = trimmedMessage.split('\n');
420
+ const newAnalyzerHeaderIndex = lines.indexOf(header);
421
+
422
+ if (newAnalyzerHeaderIndex === -1)
423
+ return false;
424
+
425
+ const analyzerId = lines[newAnalyzerHeaderIndex+1];
426
+
427
+ if (analyzerId.split('::').length === 3) {
428
+ return true;
429
+ }
430
+
431
+ return false;
432
+ }
433
+ }
434
+
435
+ function parseAnalyzeMessage(messageContent) {
436
+ const match = messageContent.match(ANALYZE_MESSAGE_REGEXP);
437
+
438
+ if (!match || match.length !== 2)
439
+ return {};
440
+
441
+ const uniqueAnalyzerId = match[1];
442
+ const [ analyzer, content, instructions ] = uniqueAnalyzerId.split('::');
443
+
444
+ return { id: uniqueAnalyzerId, analyzer, content, instructions };
445
+ }
446
+
447
+ module.exports = {
448
+ deleteMessagesByIds,
449
+ getChatTemplateMessages,
450
+ getMessagesBeforeId,
451
+ getMessageById,
452
+ getLastMessage,
453
+ getMessageContentType,
454
+ isNewAnalyzerInstructionsMessage,
455
+ findMessages,
456
+ isAnalyzeMessage,
457
+ isContextMessage,
458
+ isContextItemsOverviewMessage,
459
+ parseAnalyzeMessage
460
+ };
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Component: PatchUtils Constants
3
+ * Block-UUID: c6747054-2c8b-461f-910d-0fd725d9a350
4
+ * Parent-UUID: 32adc00e-7509-4219-8e40-6c1319371db9
5
+ * Version: 2.0.0
6
+ * Description: Contains shared constants and regular expressions used by the enhanced patch utilities.
7
+ * Language: JavaScript
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)
10
+ */
11
+
12
+
13
+ // Regex to parse context/deletion/addition lines with line numbers
14
+ // Captures: 1: diff prefix (' ', '-', or '+'), 2: line number, 3: content after 'NNN: '
15
+ const CONTENT_LINE_REGEX = /^([ +-])\s*[-+]*(\d+):\s?(.*)$/;
16
+
17
+ // Regex to parse hunk headers
18
+ // Captures: 1: old_start, 2: old_count (optional), 3: new_start, 4: new_count (optional)
19
+ const HUNK_HEADER_REGEX = /^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/;
20
+
21
+ // Regex to detect line number prefixes (e.g., " 12: ")
22
+ const LINE_NUMBER_PREFIX_REGEX = /^\s*\d+:\s/;
23
+
24
+ // Minimum confidence threshold for fuzzy matching
25
+ const FUZZY_MATCH_THRESHOLD = 0.7;
26
+
27
+ // Maximum number of alternative matches to consider
28
+ const MAX_ALTERNATIVE_MATCHES = 3;
29
+
30
+ // Sliding window size for fuzzy matching
31
+ const DEFAULT_SLIDING_WINDOW_SIZE = 3;
32
+
33
+ // Maximum context lines to use for fuzzy matching
34
+ // If context is longer than this, we'll use a sliding window approach
35
+ const MAX_CONTEXT_LINES_FOR_DIRECT_MATCH = 10;
36
+
37
+ // Patch markers
38
+ const PATCH_START_MARKER = '# --- PATCH START MARKER ---';
39
+ const PATCH_END_MARKER = '# --- PATCH END MARKER ---';
40
+
41
+ // Patch metadata header
42
+ const PATCH_METADATA_HEADER = '# Patch Metadata';
43
+
44
+ // Required metadata fields
45
+ const REQUIRED_METADATA_FIELDS = [
46
+ 'Source-Block-UUID',
47
+ 'Target-Block-UUID',
48
+ 'Source-Version',
49
+ 'Target-Version',
50
+ 'Description',
51
+ 'Authors'
52
+ ];
53
+
54
+ // Diff file headers
55
+ const ORIGINAL_FILE_HEADER = '--- Original';
56
+ const MODIFIED_FILE_HEADER = '+++ Modified';
57
+
58
+ module.exports = {
59
+ CONTENT_LINE_REGEX,
60
+ HUNK_HEADER_REGEX,
61
+ LINE_NUMBER_PREFIX_REGEX,
62
+ FUZZY_MATCH_THRESHOLD,
63
+ MAX_ALTERNATIVE_MATCHES,
64
+ DEFAULT_SLIDING_WINDOW_SIZE,
65
+ MAX_CONTEXT_LINES_FOR_DIRECT_MATCH,
66
+ PATCH_START_MARKER,
67
+ PATCH_END_MARKER,
68
+ PATCH_METADATA_HEADER,
69
+ REQUIRED_METADATA_FIELDS,
70
+ ORIGINAL_FILE_HEADER,
71
+ MODIFIED_FILE_HEADER
72
+ };