@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,559 @@
1
+ /**
2
+ * Component: CodeBlockUtils Block Processor
3
+ * Block-UUID: 1d8a559b-4f7a-4b0b-9e0a-13786f94a74e
4
+ * Parent-UUID: 082abf8b-079f-4c95-8464-0e66c0de45eb
5
+ * Version: 1.4.0
6
+ * Description: Processes the content of identified code blocks, parsing headers or patch details, and provides utilities like fixing UUIDs within blocks.
7
+ * Language: JavaScript
8
+ * Created-at: 2025-07-21T00:33:22.312Z
9
+ * Authors: Gemini 2.5 Pro (v1.0.0), Gemini 2.5 Pro (v1.1.0), Gemini 2.5 Pro (v1.2.0), Gemini 2.5 Flash Thinking (v1.3.0), Gemini 2.5 Flash Thinking (v1.4.0)
10
+ */
11
+
12
+
13
+ const { findAllCodeFences, matchFencesAndExtractBlocks } = require('./blockExtractor');
14
+ const { parseHeader } = require('./headerUtils');
15
+ const { validateUUID, generateUUID } = require('./uuidUtils');
16
+ const AnalysisBlockUtils = require('../AnalysisBlockUtils');
17
+ const PatchUtils = require('../PatchUtils');
18
+ const GSToolBlockUtils = require('../GSToolBlockUtils');
19
+ const { extractContinuationInfo } = require('./continuationUtils');
20
+ const { removeLineNumbers } = require('./lineNumberFormatter');
21
+ const { detectJsonComments } = require('../JsonUtils');
22
+
23
+ /**
24
+ * Processes a single block's content (internal helper)
25
+ * @param {string} content - The block content
26
+ * @param {string} language - The language of the block
27
+ * @param {number} position - The position in the original text
28
+ * @param {boolean} incomplete - Whether the block is incomplete
29
+ * @param {Object} options - Processing options (e.g., silent, validatePatches)
30
+ * @returns {Object} Processed block object
31
+ */
32
+ function processBlockContent(content, language, position, incomplete, options = { silent: true, validatePatches: false }) {
33
+ const { silent, validatePatches } = options;
34
+ let warnings = []; // Collect warnings specific to this block
35
+ let header = {};
36
+ let headerText = ''; // Raw header text including comments
37
+ let headerParseError = null;
38
+ let headerDelimiterType = null; // 'triple' or 'double'
39
+ let codeText = content; // Default to all content being code
40
+
41
+ // Check if this is a GitSense Tool Block *first*
42
+ if (GSToolBlockUtils.isToolBlock(content)) {
43
+ let ignoreDueToComments = false;
44
+ let toolData = null;
45
+ let toolParseError = null;
46
+ try {
47
+ toolData = GSToolBlockUtils.parseToolBlock(content);
48
+ } catch (error) {
49
+ // When chatting about the tool block, the LLM may add
50
+ // add comments in it so let's test for this
51
+ const comments = detectJsonComments(content);
52
+
53
+ if (comments.length) {
54
+ ignoreDueToComments = true;
55
+ } else {
56
+ toolParseError = error.message;
57
+ warnings.push({
58
+ position: position,
59
+ type: 'gs_tool_block_parse_error',
60
+ message: `Invalid GitSense tool block: ${error.message}`,
61
+ content: content
62
+ });
63
+ }
64
+ }
65
+
66
+ if (ignoreDueToComments) {
67
+ // Uncomment to show warning
68
+ // console.warn('GitSense Chat Tool JSON contains one or more comments');
69
+ } else {
70
+ return {
71
+ type: 'gs-tool',
72
+ language: language,
73
+ content: content,
74
+ position: position,
75
+ incomplete: incomplete,
76
+ toolData: toolData,
77
+ toolParseError: toolParseError,
78
+ warnings: warnings,
79
+ };
80
+ }
81
+ }
82
+
83
+ // Check if this is an analysis block *next*
84
+ if (AnalysisBlockUtils.isAnalysisBlock(content)) {
85
+ const analysisType = AnalysisBlockUtils.getAnalysisBlockType(content);
86
+ const analysisMetadata = AnalysisBlockUtils.parseOverviewMetadata(content);
87
+
88
+ // Validate the metadata if it was successfully parsed
89
+ let validationResult = { isValid: false, errors: ['Failed to parse analysis metadata'] };
90
+ if (analysisMetadata) {
91
+ validationResult = AnalysisBlockUtils.validateAnalysisMetadata(analysisMetadata);
92
+
93
+ if (!validationResult.isValid) {
94
+ warnings.push({
95
+ position: position,
96
+ type: 'analysis_metadata_error',
97
+ message: `Invalid analysis metadata: ${validationResult.errors.join(', ')}`,
98
+ content: content,
99
+ });
100
+ }
101
+ }
102
+
103
+ return {
104
+ type: analysisType,
105
+ language: language,
106
+ content: content,
107
+ position: position,
108
+ incomplete: incomplete,
109
+ analysisMetadata: analysisMetadata,
110
+ analysisValidation: validationResult,
111
+ warnings: warnings,
112
+ };
113
+ }
114
+
115
+ // Check if this is a gitsense-search-flow block *next*
116
+ if (language === 'gitsense-search-flow') {
117
+ // Future parsing logic for gitsense-search-flow content would go here.
118
+ // For now, we just identify the type and keep the raw content.
119
+ return {
120
+ type: 'gitsense-search-flow',
121
+ language: language,
122
+ content: content, // Keep raw content for now
123
+ position: position,
124
+ incomplete: incomplete,
125
+ // Add parsedData: {} or similar if parsing is implemented later
126
+ warnings: warnings, // Include any warnings collected so far
127
+ };
128
+ }
129
+
130
+ // Then check if this is a patch block
131
+ if (PatchUtils.isPatchBlock(content)) {
132
+ const patchFormat = PatchUtils.determinePatchFormat(content);
133
+ const metadata = PatchUtils.extractPatchMetadata(content); // Extract metadata regardless of validation
134
+ const patchContent = PatchUtils.extractPatchContent(content); // Extract raw patch content
135
+
136
+ let patchValidationResult = { valid: true, errors: [], patches: null }; // Default valid state
137
+
138
+ // Validate metadata (always useful)
139
+ const errors = PatchUtils.validatePatchMetadata(metadata);
140
+ if (errors.length) {
141
+ warnings.push({
142
+ position: position,
143
+ type: 'patch_metadata_error',
144
+ message: `Invalid patch metadata: ${errors.join(', ')}`,
145
+ content: content,
146
+ });
147
+ // Decide if invalid metadata makes the whole block invalid or just adds a warning
148
+ // For now, just warn, but return the extracted (potentially invalid) metadata.
149
+ }
150
+
151
+ // Optionally validate context patch structure if requested
152
+ if (patchFormat === 'context' && validatePatches) {
153
+ try {
154
+ const patches = PatchUtils.extractContextPatches(content);
155
+ if (!patches || patches.length === 0) {
156
+ patchValidationResult = { valid: false, errors: ['No valid context patch content found.'], patches: null };
157
+ warnings.push({
158
+ position: position,
159
+ type: 'patch_content_error',
160
+ message: 'No valid context patch content found.',
161
+ content: content,
162
+ });
163
+ } else {
164
+ // Basic validation passed (structure was parsable)
165
+ patchValidationResult = { valid: true, errors: [], patches: patches };
166
+ }
167
+ } catch (error) {
168
+ patchValidationResult = { valid: false, errors: [error.message], patches: null };
169
+ warnings.push({
170
+ position: position,
171
+ type: 'patch_parse_error',
172
+ message: `Error parsing context patch: ${error.message}`,
173
+ content: content,
174
+ });
175
+ }
176
+ }
177
+
178
+ return {
179
+ type: 'patch',
180
+ language: language === 'diff' ? 'diff' : language, // Use 'diff' if specified, else keep original
181
+ content: content, // Full original content within fences
182
+ position: position,
183
+ incomplete: incomplete,
184
+ metadata: metadata,
185
+ patch: patchContent, // Raw patch content after metadata
186
+ patchFormat: patchFormat,
187
+ patchValidation: validatePatches ? patchValidationResult : undefined, // Include validation only if requested
188
+ warnings: warnings,
189
+ };
190
+ } else {
191
+ // For non-patch blocks, remove display line numbers before processing content
192
+ content = removeLineNumbers(content); // This is the "cleaned" content
193
+ }
194
+
195
+ // --- Process as a regular code block ---
196
+ // Attempt 1: Look for triple newline separator (our standard)
197
+ const tripleNewlineSeparator = '\n\n\n';
198
+ const tripleNewlineIndex = content.indexOf(tripleNewlineSeparator);
199
+
200
+ if (tripleNewlineIndex !== -1) {
201
+ const potentialHeaderText = content.substring(0, tripleNewlineIndex); // No trim here
202
+
203
+ try {
204
+ header = parseHeader(potentialHeaderText, language);
205
+ headerText = potentialHeaderText;
206
+ codeText = content.substring(tripleNewlineIndex + tripleNewlineSeparator.length);
207
+ headerDelimiterType = 'triple';
208
+ } catch (error) {
209
+ // If parsing fails, it's not a valid header, log warning and try double newline
210
+ headerParseError = error.message;
211
+ warnings.push({
212
+ position: position,
213
+ type: 'header_parse_error',
214
+ message: `Invalid header (triple newline attempt): ${error.message}. Attempting double newline.`,
215
+ content: potentialHeaderText // Show the problematic header text
216
+ });
217
+ // Do not set header, headerText, codeText, or headerDelimiterType yet.
218
+ // Fall through to the next attempt.
219
+ }
220
+ }
221
+
222
+ // Attempt 2: Look for double newline separator (fallback)
223
+ // Only if a valid header wasn't found in the triple newline attempt
224
+ if (!headerDelimiterType) {
225
+ const doubleNewlineSeparator = '\n\n';
226
+ const doubleNewlineIndex = content.indexOf(doubleNewlineSeparator);
227
+
228
+ if (doubleNewlineIndex !== -1) {
229
+ const potentialHeaderText = content.substring(0, doubleNewlineIndex); // No trim here
230
+
231
+ try {
232
+ header = parseHeader(potentialHeaderText, language);
233
+ headerText = potentialHeaderText;
234
+ codeText = content.substring(doubleNewlineIndex + doubleNewlineSeparator.length);
235
+ headerDelimiterType = 'double';
236
+ } catch (error) {
237
+ // If parsing fails, it's not a valid header, log warning and default to no header
238
+ headerParseError = error.message;
239
+ warnings.push({
240
+ position: position,
241
+ type: 'header_parse_error',
242
+ message: `Invalid header (double newline attempt): ${error.message}. Treating block as code only.`,
243
+ content: potentialHeaderText // Show the problematic header text
244
+ });
245
+ // Fall through to default case (no header)
246
+ }
247
+ }
248
+ }
249
+
250
+ // Default: If no valid header was found after both attempts
251
+ if (!headerDelimiterType && !incomplete) { // Only warn about missing header if the block is complete
252
+ warnings.push({
253
+ position: position,
254
+ type: 'missing_header',
255
+ message: `No distinct header found or parsed as valid. Treating block as code only.`,
256
+ content: content.substring(0, 100) + '...' // Show beginning of content
257
+ });
258
+ }
259
+
260
+ return {
261
+ type: 'code',
262
+ language: language,
263
+ header: header,
264
+ headerText: headerText.trim(), // Include the raw header text if successfully parsed
265
+ content: codeText, // The code part after the header (or full content if no header)
266
+ position: position, // Position of the opening fence
267
+ headerDelimiterType: headerDelimiterType, // 'triple', 'double', or null
268
+ incomplete: incomplete,
269
+ warnings: warnings,
270
+ headerParseError: headerParseError // Include error message if parsing failed
271
+ };
272
+ }
273
+
274
+
275
+ /**
276
+ * Processes the content of complete and incomplete blocks (internal helper)
277
+ * @param {string} text - The input text
278
+ * @param {Array} completeBlocks - Array of complete block objects from extractor
279
+ * @param {Array} incompleteBlocks - Array of incomplete block objects from extractor
280
+ * @param {Object} options - Processing options
281
+ * @returns {Array} Array of processed block objects
282
+ */
283
+ function processBlockContents(text, completeBlocks, incompleteBlocks, options) {
284
+ const processedBlocks = [];
285
+ const allWarnings = []; // Collect warnings from all blocks
286
+
287
+ // Process complete blocks
288
+ for (const block of completeBlocks) {
289
+ try {
290
+ const startPos = block.opening.position + block.opening.length;
291
+ const endPos = block.closing.position;
292
+ // Get content, including the newlines immediately after opening and before closing fence
293
+ const contentWithNewlines = text.substring(startPos, endPos);
294
+ const content = contentWithNewlines; //.replace(/^\s*\n|\n\s*$/g, '');
295
+
296
+ const language = block.opening.language;
297
+
298
+ // Process block content
299
+ const processedBlock = processBlockContent(content, language, block.opening.position, false, options);
300
+ processedBlocks.push(processedBlock);
301
+ if (processedBlock.warnings) {
302
+ allWarnings.push(...processedBlock.warnings);
303
+ }
304
+ } catch (error) {
305
+ // Catch errors during the processing of a single block
306
+ allWarnings.push({
307
+ position: block.opening?.position || -1,
308
+ type: 'block_processing_error',
309
+ message: `Error processing complete block: ${error.message}`,
310
+ content: text.substring(block.opening?.position, block.closing?.position + block.closing?.length) || "Error retrieving block content"
311
+ });
312
+ }
313
+ }
314
+
315
+ // Process incomplete blocks
316
+ for (const block of incompleteBlocks) {
317
+ try {
318
+ const startPos = block.opening.position + block.opening.length;
319
+ // Get content from opening fence to end of text
320
+ const contentWithNewline = text.substring(startPos);
321
+ // Trim only leading whitespace line
322
+ const content = contentWithNewline.replace(/^\s*\n/, '');
323
+
324
+ const language = block.opening.language;
325
+
326
+ // Process block content - it's expected to be incomplete
327
+ const processedBlock = processBlockContent(content, language, block.opening.position, true, options);
328
+
329
+ // Extract continuation info for incomplete blocks
330
+ // Requires extractContinuationInfo from continuationUtils
331
+ try {
332
+ // Pass the *processed* header info to continuation utils
333
+ let { 'Continuation-Part': partNumber } = processedBlock?.header || {};
334
+ partNumber = partNumber ? parseInt(partNumber) + 1 : 2; // Default to part 2 if not specified
335
+ processedBlock.continuationInfo = extractContinuationInfo(processedBlock.content, partNumber, language, processedBlock.header);
336
+ } catch (continuationError) {
337
+ allWarnings.push({
338
+ position: block.opening?.position || -1,
339
+ type: 'continuation_info_error',
340
+ message: `Error extracting continuation info: ${continuationError.message}`,
341
+ content: content
342
+ });
343
+ }
344
+
345
+
346
+ processedBlocks.push(processedBlock);
347
+ if (processedBlock.warnings) {
348
+ allWarnings.push(...processedBlock.warnings);
349
+ }
350
+ } catch (error) {
351
+ // Catch errors during the processing of a single incomplete block
352
+ allWarnings.push({
353
+ position: block.opening?.position || -1,
354
+ type: 'incomplete_block_processing_error',
355
+ message: `Error processing incomplete block: ${error.message}`,
356
+ content: text.substring(block.opening?.position) || "Error retrieving block content"
357
+ });
358
+ }
359
+ }
360
+
361
+ // Return processed blocks and collected warnings
362
+ // Note: The caller (processCodeBlocks) will handle adding these warnings to its own return object.
363
+ return { processedBlocks, allWarnings };
364
+ }
365
+
366
+
367
+ /**
368
+ * Core function to process code blocks with shared logic
369
+ * @param {string} text - The input text containing code blocks
370
+ * @param {Object} options - Processing options (e.g., silent, validatePatches)
371
+ * @returns {Object} { blocks: Array, warnings: Array, hasIncompleteBlocks: boolean, lastIncompleteBlock: Object|null }
372
+ */
373
+ function processCodeBlocks(text, options = { silent: false, validatePatches: false }) {
374
+ if (typeof text !== "string") { // Allow empty strings
375
+ console.warn("Warning: Input must be a string.");
376
+ return {
377
+ blocks: [],
378
+ warnings: [{ type: 'invalid_input', message: 'Input must be a string.' }],
379
+ hasIncompleteBlocks: false,
380
+ lastIncompleteBlock: null }
381
+ ;
382
+ }
383
+
384
+ if (text.trim() === "") {
385
+ return { blocks: [], warnings: [], hasIncompleteBlocks: false, lastIncompleteBlock: null };
386
+ }
387
+
388
+ // Step 1: Find all fence positions
389
+ const { openingPositions, closingPositions } = findAllCodeFences(text);
390
+
391
+ // Step 2: Match fences to get potential block boundaries
392
+ const { completeBlocks, incompleteBlocks, warnings: extractorWarnings } = matchFencesAndExtractBlocks(
393
+ text, openingPositions, closingPositions
394
+ );
395
+
396
+ // Step 3: Process the content of these potential blocks
397
+ const { processedBlocks, allWarnings: processorWarnings } = processBlockContents(
398
+ text, completeBlocks, incompleteBlocks, options
399
+ );
400
+
401
+ // Combine warnings from extractor and processor
402
+ const allWarnings = [...extractorWarnings, ...processorWarnings];
403
+
404
+ // Log warnings if not silent
405
+ if (!options.silent && allWarnings.length > 0) {
406
+ console.warn('\nCode Block Processing Warnings:');
407
+ allWarnings.forEach((warning, index) => {
408
+ console.warn(`\nWarning ${index + 1}:`);
409
+ console.warn(`Type: ${warning.type}`);
410
+ console.warn(`Message: ${warning.message}`);
411
+ console.warn(`Position: ${warning.position}`);
412
+ if (warning.content) {
413
+ // Limit logged content length
414
+ const maxLogLength = 200;
415
+ const loggedContent = warning.content.length > maxLogLength
416
+ ? warning.content.substring(0, maxLogLength) + '...'
417
+ : warning.content;
418
+ console.warn(`Content Hint: ${loggedContent}`);
419
+ }
420
+ });
421
+ }
422
+
423
+ const hasIncompleteBlocks = incompleteBlocks.length > 0;
424
+ const lastIncompleteBlock = hasIncompleteBlocks
425
+ ? processedBlocks.find(block => block.incomplete) // Find the corresponding processed block
426
+ : null;
427
+
428
+ return {
429
+ blocks: processedBlocks,
430
+ warnings: allWarnings,
431
+ hasIncompleteBlocks: hasIncompleteBlocks,
432
+ lastIncompleteBlock: lastIncompleteBlock
433
+ };
434
+ }
435
+
436
+ /**
437
+ * Public API function - maintains backward compatibility for simple extraction.
438
+ * Use processCodeBlocks for more detailed results (warnings, incomplete status).
439
+ * @param {string} text - The input text containing code blocks
440
+ * @param {Object} options - Options for extraction (e.g., { silent: true })
441
+ * @returns {Object} - Object containing extracted blocks and warnings { blocks: Array, warnings: Array }
442
+ */
443
+ function extractCodeBlocks(text, options = { silent: false, validatePatches: false, debug: false }) {
444
+ const { blocks, warnings } = processCodeBlocks(text, options);
445
+ return { blocks, warnings };
446
+ }
447
+
448
+
449
+ /**
450
+ * Fixes invalid UUIDs in code blocks within text
451
+ * @param {string} text - The input text containing code blocks
452
+ * @returns {Object} - Object containing fixed text and whether changes were made { text: string, modified: boolean }
453
+ */
454
+ function fixTextCodeBlocks(text) {
455
+ if (typeof text !== "string" || text.trim() === "") {
456
+ return {
457
+ text: text,
458
+ modified: false
459
+ };
460
+ }
461
+
462
+ let modified = false;
463
+ let fixedText = text;
464
+
465
+ // Regex to match code blocks with their language
466
+ const codeBlockRegex = /```([a-zA-Z-]*)\n([\s\S]*?)\n```/g;
467
+
468
+ // Store all matches to process in reverse
469
+ const matches = Array.from(text.matchAll(codeBlockRegex));
470
+
471
+ // Process matches in reverse to maintain correct positions
472
+ for (let i = matches.length - 1; i >= 0; i--) {
473
+ const match = matches[i];
474
+ const fullMatch = match[0];
475
+ const language = match[1];
476
+ const blockContent = match[2];
477
+ let blockModified = false;
478
+
479
+ // Check if this is a patch block or a gitsense-search-flow block (if they contain UUIDs)
480
+ if (PatchUtils.isPatchBlock(blockContent)) {
481
+ // Process patch block UUIDs
482
+ const lines = blockContent.split('\n');
483
+ let blockModified = false;
484
+
485
+ const newLines = lines.map(line => {
486
+ // Check for UUID fields in patch metadata
487
+ if ((line.includes('# Source-Block-UUID:') || line.includes('# Target-Block-UUID:')) &&
488
+ !line.includes('{{GS-UUID}')) {
489
+
490
+ const [fieldPart, valuePart] = line.split(':').map(p => p.trim());
491
+
492
+ // Validate UUID
493
+ const validation = validateUUID(valuePart);
494
+ if (validation["Block-UUID"] === "INVALID UUID") {
495
+ blockModified = true;
496
+ modified = true;
497
+ return `${fieldPart}: ${validation["Correct Block-UUID"]}`;
498
+ }
499
+ }
500
+ return line;
501
+ });
502
+
503
+ // If this block was modified, replace it in the text
504
+ if (blockModified) {
505
+ const newBlock = `\`\`\`${language}\n${newLines.join('\n')}\n\`\`\``;
506
+ fixedText = fixedText.substring(0, match.index) +
507
+ newBlock +
508
+ fixedText.substring(match.index + fullMatch.length);
509
+ }
510
+ } else {
511
+ // Process regular code block or gitsense-search-flow block for UUIDs
512
+ // Split block content into lines to check for header/UUIDs
513
+ const lines = blockContent.split('\n');
514
+ let blockModified = false;
515
+
516
+ // Process each line
517
+ const newLines = lines.map(line => {
518
+ // Check for UUID fields
519
+ if (line.includes(' Block-UUID: ') || line.includes(' Parent-UUID: ')) {
520
+ const [fieldPart, valuePart] = line.split(':').map(p => p.trim());
521
+
522
+ // Skip if N/A or contains unexpected characters
523
+ if (valuePart === 'N/A' || valuePart.match(/\(/)) {
524
+ return line;
525
+ }
526
+ // Validate UUID
527
+ const validation = validateUUID(valuePart);
528
+
529
+ if (validation["Block-UUID"] === "INVALID UUID") {
530
+ blockModified = true;
531
+ modified = true;
532
+ return `${fieldPart}: ${validation["Correct Block-UUID"]}`;
533
+ }
534
+ }
535
+ return line;
536
+ });
537
+
538
+ // If this block was modified, replace it in the text
539
+ if (blockModified) {
540
+ const newBlock = `\`\`\`${language}\n${newLines.join('\n')}\n\`\`\``;
541
+ fixedText = fixedText.substring(0, match.index) +
542
+ newBlock +
543
+ fixedText.substring(match.index + fullMatch.length);
544
+ }
545
+ }
546
+ }
547
+
548
+ return {
549
+ text: fixedText,
550
+ modified: modified
551
+ };
552
+ }
553
+
554
+
555
+ module.exports = {
556
+ processCodeBlocks,
557
+ extractCodeBlocks,
558
+ fixTextCodeBlocks,
559
+ };
@@ -0,0 +1,8 @@
1
+ @@ -262,6 +283,7 @@
2
+ function processCodeBlocks(text, options = { silent: false, validatePatches: false }) {
3
+ if (typeof text !== "string") { // Allow empty strings
4
+ console.warn("Warning: Input must be a string.");
5
+ + return { blocks: [], warnings: [{ type: 'invalid_input', message: 'Input must be a string.' }], hasIncompleteBlocks: false, lastIncompleteBlock: null };
6
+ }
7
+ if (text.trim() === "") {
8
+ return { blocks: [], warnings: [], hasIncompleteBlocks: false, lastIncompleteBlock: null };
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Component: CodeBlockUtils Constants
3
+ * Block-UUID: 01b2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d
4
+ * Parent-UUID: 144d19a5-139a-4e80-8abe-84965583e35e
5
+ * Version: 1.0.1
6
+ * Description: Defines constants used across the CodeBlockUtils modules, such as comment styles for various languages.
7
+ * Language: JavaScript
8
+ * Created-at: 2025-04-15T15:51:16.973Z
9
+ * Authors: Gemini 2.5 Pro (v1.0.0), Gemini 2.5 Flash Thinking (v1.0.1)
10
+ */
11
+
12
+
13
+ // Defines comment styles for various programming languages and block types.
14
+ const COMMENT_STYLES = {
15
+ // C-style comments
16
+ 'javascript': { type: 'c-style' },
17
+ 'typescript': { type: 'c-style' },
18
+ 'java': { type: 'c-style' },
19
+ 'c': { type: 'c-style' },
20
+ 'cpp': { type: 'c-style' },
21
+ 'csharp': { type: 'c-style' },
22
+ 'php': { type: 'c-style' },
23
+ 'swift': { type: 'c-style' },
24
+ 'kotlin': { type: 'c-style' },
25
+ 'scala': { type: 'c-style' },
26
+ 'rust': { type: 'c-style' },
27
+ 'go': { type: 'c-style' },
28
+
29
+ // Custom GitSense block types
30
+ 'gitsense-search-flow': { type: 'none' },
31
+
32
+ // Hash-style comments
33
+ 'python': { type: 'hash' },
34
+ 'ruby': { type: 'ruby-block' },
35
+ 'perl': { type: 'hash' },
36
+ 'bash': { type: 'hash' },
37
+ 'shell': { type: 'hash' },
38
+ 'yaml': { type: 'hash' },
39
+ 'powershell': { type: 'hash' },
40
+ 'r': { type: 'hash' },
41
+ 'julia': { type: 'hash' },
42
+
43
+ // Other language styles
44
+ 'lisp': { type: 'semicolon' },
45
+ 'clojure': { type: 'semicolon' },
46
+ 'scheme': { type: 'semicolon' },
47
+ 'ada': { type: 'ada' },
48
+ 'lua': { type: 'lua' },
49
+ 'matlab': { type: 'percent' },
50
+ 'octave': { type: 'percent' },
51
+ 'haskell': { type: 'haskell' },
52
+ 'sql': { type: 'sql' },
53
+ 'fortran': { type: 'exclamation' },
54
+ 'vb': { type: 'apostrophe' },
55
+ 'vbscript': { type: 'apostrophe' }
56
+ };
57
+
58
+ module.exports = {
59
+ COMMENT_STYLES
60
+ };
61
+
62
+