@gitsense/gsc-utils 0.2.15 → 0.2.16

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.
@@ -677,7 +677,7 @@ var constants$2 = {
677
677
  * Generates a valid RFC 4122 UUID v4
678
678
  * @returns {string} A valid UUID v4
679
679
  */
680
- function generateUUID$3() {
680
+ function generateUUID$2() {
681
681
  return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
682
682
  const r = Math.random() * 16 | 0;
683
683
  const v = c === 'x' ? r : (r & 0x3 | 0x8);
@@ -690,13 +690,13 @@ function generateUUID$3() {
690
690
  * @param {string} uuid - The UUID string to validate
691
691
  * @returns {Object} Object containing validation results and replacement UUID if needed
692
692
  */
693
- function validateUUID$4(uuid) {
693
+ function validateUUID$3(uuid) {
694
694
  const uuidPattern = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
695
695
 
696
696
  if (!uuidPattern.test(uuid)) {
697
697
  return {
698
698
  "Block-UUID": "INVALID UUID",
699
- "Correct Block-UUID": generateUUID$3()
699
+ "Correct Block-UUID": generateUUID$2()
700
700
  };
701
701
  }
702
702
 
@@ -706,8 +706,8 @@ function validateUUID$4(uuid) {
706
706
  }
707
707
 
708
708
  var uuidUtils = {
709
- generateUUID: generateUUID$3,
710
- validateUUID: validateUUID$4
709
+ generateUUID: generateUUID$2,
710
+ validateUUID: validateUUID$3
711
711
  };
712
712
 
713
713
  /**
@@ -799,7 +799,7 @@ function formatBlocksWithLineNumbers$2(blocks, startLine = 1, paddingWidth = 0)
799
799
  * @param {string} formattedContent - The code content with line numbers
800
800
  * @returns {string} The original code content without line numbers
801
801
  */
802
- function removeLineNumbers$a(formattedContent) {
802
+ function removeLineNumbers$9(formattedContent) {
803
803
  if (typeof formattedContent !== 'string') {
804
804
  throw new Error("formattedContent must be a string");
805
805
  }
@@ -823,7 +823,7 @@ var lineNumberFormatter = {
823
823
  formatWithLineNumbers: formatWithLineNumbers$2,
824
824
  formatBlockWithLineNumbers: formatBlockWithLineNumbers$2,
825
825
  formatBlocksWithLineNumbers: formatBlocksWithLineNumbers$2,
826
- removeLineNumbers: removeLineNumbers$a
826
+ removeLineNumbers: removeLineNumbers$9
827
827
  };
828
828
 
829
829
  /**
@@ -839,8 +839,8 @@ var lineNumberFormatter = {
839
839
 
840
840
  // Dependencies from other modules within CodeBlockUtils
841
841
  const { COMMENT_STYLES: COMMENT_STYLES$2 } = constants$2;
842
- const { removeLineNumbers: removeLineNumbers$9 } = lineNumberFormatter; // Assuming this utility exists
843
- const { validateUUID: validateUUID$3 } = uuidUtils; // Assuming uuidUtils.js is in the same directory
842
+ const { removeLineNumbers: removeLineNumbers$8 } = lineNumberFormatter; // Assuming this utility exists
843
+ const { validateUUID: validateUUID$2 } = uuidUtils; // Assuming uuidUtils.js is in the same directory
844
844
 
845
845
  const MAX_HEADER_LINES = 12; // Maximum allowed non-empty lines for a header
846
846
  /**
@@ -865,7 +865,7 @@ function isValidISOTimestamp$2(timestamp) {
865
865
  * @returns {Object} - The parsed header object.
866
866
  * @throws {Error} If header parsing fails (e.g., missing fields, invalid format).
867
867
  */
868
- function parseHeader$3(header, language) {
868
+ function parseHeader$2(header, language) {
869
869
  const headerObject = {};
870
870
 
871
871
  // Get comment style for the language
@@ -974,7 +974,7 @@ function parseHeader$3(header, language) {
974
974
  // Special handling for Block-UUID and Parent-UUID
975
975
  if (key === "Block-UUID" || key === "Parent-UUID") {
976
976
  if (value !== 'N/A') {
977
- const uuidValidation = validateUUID$3(value); // Use imported function
977
+ const uuidValidation = validateUUID$2(value); // Use imported function
978
978
  if (uuidValidation["Block-UUID"] === "INVALID UUID") {
979
979
  // Store invalid status and the suggested correction
980
980
  headerObject[key] = "INVALID UUID";
@@ -1056,7 +1056,7 @@ function getHeaderLineCount$1(headerText, language) {
1056
1056
 
1057
1057
  var headerUtils = {
1058
1058
  isValidISOTimestamp: isValidISOTimestamp$2,
1059
- parseHeader: parseHeader$3,
1059
+ parseHeader: parseHeader$2,
1060
1060
  getHeaderLineCount: getHeaderLineCount$1 // Export the new function
1061
1061
  };
1062
1062
 
@@ -1076,7 +1076,7 @@ var headerUtils = {
1076
1076
  * @param {string} text - The input text
1077
1077
  * @returns {Object} Object containing arrays of opening and closing fence positions: { openingPositions: Array, closingPositions: Array }
1078
1078
  */
1079
- function findAllCodeFences$4(text) {
1079
+ function findAllCodeFences$2(text) {
1080
1080
  const openingPositions = [];
1081
1081
  const closingPositions = [];
1082
1082
 
@@ -1141,7 +1141,7 @@ function findAllCodeFences$4(text) {
1141
1141
  * @param {Array} closingPositions - Array of closing fence objects { position, length }
1142
1142
  * @returns {Object} Object containing: { completeBlocks: Array, incompleteBlocks: Array, warnings: Array }
1143
1143
  */
1144
- function matchFencesAndExtractBlocks$4(text, openingPositions, closingPositions) {
1144
+ function matchFencesAndExtractBlocks$2(text, openingPositions, closingPositions) {
1145
1145
  const completeBlocks = [];
1146
1146
  const incompleteBlocks = [];
1147
1147
  const warnings = [];
@@ -1300,7 +1300,7 @@ function extractCodeBlocksWithUUIDs$1(messageText) {
1300
1300
  * @param {string} blockUUID - The Block-UUID to find
1301
1301
  * @returns {Object|null} Code block object or null if not found
1302
1302
  */
1303
- function findCodeBlockByUUID$2(messageText, blockUUID) {
1303
+ function findCodeBlockByUUID$1(messageText, blockUUID) {
1304
1304
  if (!messageText || !blockUUID) {
1305
1305
  return null;
1306
1306
  }
@@ -1310,10 +1310,10 @@ function findCodeBlockByUUID$2(messageText, blockUUID) {
1310
1310
  }
1311
1311
 
1312
1312
  var blockExtractor = {
1313
- findAllCodeFences: findAllCodeFences$4,
1314
- matchFencesAndExtractBlocks: matchFencesAndExtractBlocks$4,
1313
+ findAllCodeFences: findAllCodeFences$2,
1314
+ matchFencesAndExtractBlocks: matchFencesAndExtractBlocks$2,
1315
1315
  extractCodeBlocksWithUUIDs: extractCodeBlocksWithUUIDs$1,
1316
- findCodeBlockByUUID: findCodeBlockByUUID$2
1316
+ findCodeBlockByUUID: findCodeBlockByUUID$1
1317
1317
  };
1318
1318
 
1319
1319
  /**
@@ -1460,7 +1460,7 @@ function validateAnalysisMetadata(metadata) {
1460
1460
  }
1461
1461
 
1462
1462
 
1463
- var AnalysisBlockUtils$3 = {
1463
+ var AnalysisBlockUtils$2 = {
1464
1464
  isAnalysisBlock,
1465
1465
  getAnalysisBlockType,
1466
1466
  parseOverviewMetadata: parseOverviewMetadata$1,
@@ -1809,7 +1809,7 @@ var constants$1 = {
1809
1809
  * Authors: Gemini 2.5 Flash Thinking (v1.0.0)
1810
1810
  */
1811
1811
 
1812
- const { removeLineNumbers: removeLineNumbers$8 } = lineNumberFormatter; // Assuming this utility exists
1812
+ const { removeLineNumbers: removeLineNumbers$7 } = lineNumberFormatter; // Assuming this utility exists
1813
1813
  const { CONTENT_LINE_REGEX: CONTENT_LINE_REGEX$4, HUNK_HEADER_REGEX: HUNK_HEADER_REGEX$3 } = constants$1;
1814
1814
 
1815
1815
 
@@ -1974,7 +1974,7 @@ var formatAndAddLineNumbers_1 = {
1974
1974
  * Authors: Gemini 2.5 Flash Thinking (v1.0.0), Claude 3.7 Sonnet (v1.1.0, v1.2.0)
1975
1975
  */
1976
1976
 
1977
- const { removeLineNumbers: removeLineNumbers$7 } = lineNumberFormatter; // Assuming this utility exists
1977
+ const { removeLineNumbers: removeLineNumbers$6 } = lineNumberFormatter; // Assuming this utility exists
1978
1978
  const { CONTENT_LINE_REGEX: CONTENT_LINE_REGEX$3 } = constants$1;
1979
1979
  const { formatAndAddLineNumbers: formatAndAddLineNumbers$1 } = formatAndAddLineNumbers_1; // Import the helper
1980
1980
 
@@ -2022,7 +2022,7 @@ function verifyAndCorrectLineNumbers$2(patchText, sourceText, windowSize = 251)
2022
2022
  const formatResult = formatAndAddLineNumbers$1(patchText, sourceText);
2023
2023
  const patchLines = formatResult.formattedPatchText.split('\n');
2024
2024
  // Ensure source is clean (defensive check, though caller should provide clean source)
2025
- const sourceLines = removeLineNumbers$7(sourceText).split('\n');
2025
+ const sourceLines = removeLineNumbers$6(sourceText).split('\n');
2026
2026
  const outputLines = [];
2027
2027
  const errors = [];
2028
2028
  let correctionsMade = formatResult.correctionsMade; // Inherit corrections made by formatter
@@ -6580,7 +6580,7 @@ function escapeHTML(s) {
6580
6580
 
6581
6581
  const parser$1 = patchParser;
6582
6582
  const jsdiff$1 = lib;
6583
- const { removeLineNumbers: removeLineNumbers$6 } = lineNumberFormatter;
6583
+ const { removeLineNumbers: removeLineNumbers$5 } = lineNumberFormatter;
6584
6584
 
6585
6585
  /**
6586
6586
  * Apply patch against source code. Expects traditional unified diff format.
@@ -6622,7 +6622,7 @@ function applyPatch(sourceText, patchText) {
6622
6622
  }
6623
6623
 
6624
6624
  // 1.1. Play it safe and clean sourceText by removing potential line numbers
6625
- sourceText = removeLineNumbers$6(sourceText);
6625
+ sourceText = removeLineNumbers$5(sourceText);
6626
6626
 
6627
6627
  // 2. Extract metadata (optional for applying, but good practice)
6628
6628
  const metadata = parser$1.extractPatchMetadata(patchText);
@@ -6687,8 +6687,8 @@ function createPatch(sourceText, targetCode, metadata = {}, filename = 'Original
6687
6687
 
6688
6688
  try {
6689
6689
  // --- CRITICAL: jsdiff needs CLEAN text ---
6690
- const cleanSource = removeLineNumbers$6(sourceText);
6691
- const cleanTarget = removeLineNumbers$6(targetCode);
6690
+ const cleanSource = removeLineNumbers$5(sourceText);
6691
+ const cleanTarget = removeLineNumbers$5(targetCode);
6692
6692
 
6693
6693
  // --- Generate standard diff ---
6694
6694
  // Provide filenames for standard diff headers (--- filename \n +++ filename)
@@ -7322,7 +7322,7 @@ var fuzzyMatcher$1 = {
7322
7322
  * Authors: Claude 3.7 Sonnet (v1.0.0), Claude 3.5 Sonnet (v1.1.0, v1.2.0), Claude 3.5 Sonnet (v1.3.0), Gemini 2.5 Flash (v1.3.0)
7323
7323
  */
7324
7324
  const { findBestContextMatch: findBestContextMatch$1, findBestMatchWithSlidingWindow: findBestMatchWithSlidingWindow$1 } = fuzzyMatcher$1;
7325
- const { removeLineNumbers: removeLineNumbers$5 } = lineNumberFormatter;
7325
+ const { removeLineNumbers: removeLineNumbers$4 } = lineNumberFormatter;
7326
7326
 
7327
7327
  /**
7328
7328
  * Parses a hunk into its components
@@ -7342,7 +7342,7 @@ function parseHunk(hunkText) {
7342
7342
 
7343
7343
  for (const line of contentLines) {
7344
7344
  // Clean line numbers from the content
7345
- const cleanLine = removeLineNumbers$5(line.substring(1));
7345
+ const cleanLine = removeLineNumbers$4(line.substring(1));
7346
7346
 
7347
7347
  if (line.startsWith(' ')) {
7348
7348
  // Regular context line - add to both context arrays
@@ -7993,7 +7993,7 @@ var diagnosticReporter$1 = {
7993
7993
  * Authors: Claude 3.7 Sonnet (v1.0.0)
7994
7994
  */
7995
7995
 
7996
- const { removeLineNumbers: removeLineNumbers$4 } = lineNumberFormatter;
7996
+ const { removeLineNumbers: removeLineNumbers$3 } = lineNumberFormatter;
7997
7997
 
7998
7998
  /**
7999
7999
  * Extracts individual hunks from a patch
@@ -8100,7 +8100,7 @@ function cleanHunkLineNumbers$1(hunkText) {
8100
8100
 
8101
8101
  if (line.startsWith(' ') || line.startsWith('+') || line.startsWith('-')) {
8102
8102
  const prefix = line[0];
8103
- const content = removeLineNumbers$4(line.substring(1));
8103
+ const content = removeLineNumbers$3(line.substring(1));
8104
8104
  cleanedLines.push(`${prefix}${content}`);
8105
8105
  } else {
8106
8106
  // Non-content line, keep as is
@@ -8169,7 +8169,7 @@ var patchExtractor$1 = {
8169
8169
  */
8170
8170
 
8171
8171
  const jsdiff = lib;
8172
- const { removeLineNumbers: removeLineNumbers$3 } = lineNumberFormatter;
8172
+ const { removeLineNumbers: removeLineNumbers$2 } = lineNumberFormatter;
8173
8173
  const { extractAndCleanHunks, reconstructPatch } = patchExtractor$1;
8174
8174
  const { validateHunk, validateHunks } = hunkValidator$1;
8175
8175
  const { findBestContextMatch, findBestMatchWithSlidingWindow } = fuzzyMatcher$1;
@@ -8222,7 +8222,7 @@ function applyPatchWithDiagnostics(sourceText, patchText) {
8222
8222
  }
8223
8223
 
8224
8224
  // Clean source text (remove any line numbers)
8225
- const cleanSourceText = removeLineNumbers$3(sourceText);
8225
+ const cleanSourceText = removeLineNumbers$2(sourceText);
8226
8226
 
8227
8227
  // First, clean up redundant changes
8228
8228
  const redundantFixResult = detectAndFixRedundantChanges(patchText, true);
@@ -8390,7 +8390,7 @@ function validatePatch(sourceText, patchText) {
8390
8390
  }
8391
8391
 
8392
8392
  // Clean source text
8393
- const cleanSourceText = removeLineNumbers$3(sourceText);
8393
+ const cleanSourceText = removeLineNumbers$2(sourceText);
8394
8394
 
8395
8395
  // Clean up redundant changes
8396
8396
  const redundantFixResult = detectAndFixRedundantChanges(patchText, true);
@@ -8467,7 +8467,7 @@ function applyHunk(sourceText, hunkText) {
8467
8467
  }
8468
8468
 
8469
8469
  // Clean source text
8470
- const cleanSourceText = removeLineNumbers$3(sourceText);
8470
+ const cleanSourceText = removeLineNumbers$2(sourceText);
8471
8471
 
8472
8472
  // Clean hunk line numbers
8473
8473
  const cleanHunk = cleanHunkLineNumbers(hunkText);
@@ -8601,7 +8601,7 @@ const enhancedProcessor = enhancedPatchProcessor;
8601
8601
  */
8602
8602
 
8603
8603
  // Export all modules
8604
- var PatchUtils$2 = {
8604
+ var PatchUtils$1 = {
8605
8605
  // Original exports (for backward compatibility)
8606
8606
  validateAndParseContextPatch: parser.validateAndParseContextPatch,
8607
8607
  determinePatchFormat: parser.determinePatchFormat,
@@ -8683,112 +8683,554 @@ var PatchUtils$2 = {
8683
8683
  };
8684
8684
 
8685
8685
  /**
8686
- * Component: GitSense Tool Block Utilities
8687
- * Block-UUID: 72db6a5f-3b09-49e8-8f39-d4e3d37b510a
8688
- * Parent-UUID: N/A
8689
- * Version: 1.0.0
8690
- * Description: Provides utility functions for identifying and parsing GitSense Chat Tool Blocks.
8686
+ * Component: CodeBlockUtils Update Code Block
8687
+ * Block-UUID: 6a7b8c9d-0e1f-42a3-b4c5-d6e7f8a9b0c1
8688
+ * Parent-UUID: 5f9c1e3a-7b2d-4e8f-a1d0-9c4b2e1f8a0b
8689
+ * Version: 1.4.0
8690
+ * Description: Provides functions to update the content of a specific code block within a string, identified by index, UUID, or direct reference.
8691
8691
  * Language: JavaScript
8692
- * Created-at: 2025-04-27T01:29:53.576Z
8693
- * Authors: Gemini 2.5 Pro (v1.0.0)
8692
+ * Created-at: 2025-04-16T00:56:57.352Z
8693
+ * 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 Pro (v1.3.0), Gemini 2.5 Flash Thinking (v1.4.0)
8694
8694
  */
8695
8695
 
8696
- /**
8697
- * Checks if a given code block content represents a GitSense Chat Tool Block.
8698
- * It verifies if the first non-empty line starts with "# GitSense Chat Tool".
8699
- *
8700
- * @param {string} content - The raw content of the code block (within the fences).
8701
- * @returns {boolean} True if it's a GitSense Chat Tool Block, false otherwise.
8702
- */
8703
- function isToolBlock$1(content) {
8704
- if (!content || typeof content !== 'string') {
8705
- return false;
8706
- }
8696
+ var updateCodeBlock_1;
8697
+ var hasRequiredUpdateCodeBlock;
8698
+
8699
+ function requireUpdateCodeBlock () {
8700
+ if (hasRequiredUpdateCodeBlock) return updateCodeBlock_1;
8701
+ hasRequiredUpdateCodeBlock = 1;
8702
+ // Dependencies from other modules within CodeBlockUtils
8703
+ const { findAllCodeFences, matchFencesAndExtractBlocks, findCodeBlockByUUID } = blockExtractor; // Added findCodeBlockByUUID
8704
+ const { processCodeBlocks } = requireBlockProcessor(); // To get parsed block info
8705
+
8706
+ /**
8707
+ * Replaces the content of a code block specified by its index within a message string.
8708
+ *
8709
+ * @param {string} messageContent - The original message string containing code blocks.
8710
+ * @param {number} blockIndex - The 0-based index of the code block to replace.
8711
+ * @param {string} newCodeContent - The new raw content (code, potentially including header) to insert into the block.
8712
+ * @param {string} [language] - Optional: The language identifier for the updated code block fence. If omitted, the original language is preserved.
8713
+ * @returns {string} The message content with the specified code block updated.
8714
+ * @throws {Error} If the blockIndex is out of bounds or if block boundaries cannot be determined.
8715
+ */
8716
+ function updateCodeBlockByIndex(messageContent, blockIndex, newCodeContent, language = undefined) {
8717
+ if (typeof messageContent !== 'string') {
8718
+ throw new Error("messageContent must be a string.");
8719
+ }
8720
+ if (typeof blockIndex !== 'number' || blockIndex < 0) {
8721
+ throw new Error("blockIndex must be a non-negative number.");
8722
+ }
8723
+ if (typeof newCodeContent !== 'string') {
8724
+ // Allow empty string replacement, but not other types
8725
+ throw new Error("newCodeContent must be a string.");
8726
+ }
8707
8727
 
8708
- if (content.match(/^\s*# GitSense Chat Tool\n\s*{/))
8709
- return true;
8728
+ // Step 1: Find all fence positions to get accurate start/end of the full block
8729
+ const { openingPositions, closingPositions } = findAllCodeFences(messageContent);
8730
+ const { completeBlocks, incompleteBlocks, warnings: extractorWarnings } = matchFencesAndExtractBlocks(
8731
+ messageContent, openingPositions, closingPositions
8732
+ );
8710
8733
 
8711
- return false;
8734
+ // Combine complete and incomplete for indexing, assuming we might want to update an incomplete one too
8735
+ // Note: Replacing content in an incomplete block might be unusual, but technically possible.
8736
+ const allBlocks = [...completeBlocks, ...incompleteBlocks];
8737
+
8738
+ // Step 2: Validate index
8739
+ if (blockIndex >= allBlocks.length) {
8740
+ throw new Error(`blockIndex ${blockIndex} is out of bounds. Found ${allBlocks.length} blocks.`);
8741
+ }
8742
+
8743
+ // Step 3: Get the target block's boundary information
8744
+ const targetBlockBoundaries = allBlocks[blockIndex];
8745
+ const openingFence = targetBlockBoundaries.opening;
8746
+ const closingFence = targetBlockBoundaries.closing; // Will be undefined for incomplete blocks
8747
+
8748
+ if (!openingFence) {
8749
+ // Should not happen if index is valid, but good practice to check
8750
+ throw new Error(`Could not find opening fence for block at index ${blockIndex}.`);
8751
+ }
8752
+
8753
+ // Determine the exact start and end of the full markdown block
8754
+ const blockStartPos = openingFence.position;
8755
+ // If the block is incomplete, the end is the end of the messageContent
8756
+ const blockEndPos = closingFence
8757
+ ? closingFence.position + closingFence.length
8758
+ : messageContent.length;
8759
+
8760
+ // Step 4: Construct the replacement block string
8761
+ const targetLanguage = language !== undefined ? language : (openingFence.language || ''); // Use provided language or original/empty
8762
+ const newBlockString = `\`\`\`${targetLanguage}\n${newCodeContent}\n\`\`\``;
8763
+
8764
+ // Step 5: Perform the replacement
8765
+ const updatedMessageContent =
8766
+ messageContent.substring(0, blockStartPos) +
8767
+ newBlockString +
8768
+ messageContent.substring(blockEndPos);
8769
+
8770
+ return updatedMessageContent;
8771
+ }
8772
+
8773
+ /**
8774
+ * Replaces the content of a code block specified by its Block-UUID within a message string.
8775
+ *
8776
+ * @param {string} messageContent - The original message string containing code blocks.
8777
+ * @param {string} blockUUID - The Block-UUID of the code block to replace.
8778
+ * @param {string} newCodeContent - The new raw content (code, potentially including header) to insert into the block.
8779
+ * @param {string} [language] - Optional: The language identifier for the updated code block fence. If omitted, the original language is preserved.
8780
+ * @returns {string} The message content with the specified code block updated.
8781
+ * @throws {Error} If no block with the specified UUID is found, or if multiple blocks have the same UUID.
8782
+ */
8783
+ function updateCodeBlockByUUID(messageContent, blockUUID, newCodeContent, language = undefined) {
8784
+ if (typeof messageContent !== 'string') {
8785
+ throw new Error("messageContent must be a string.");
8786
+ }
8787
+ if (typeof blockUUID !== 'string' || !blockUUID) {
8788
+ throw new Error("blockUUID must be a non-empty string.");
8789
+ }
8790
+ if (typeof newCodeContent !== 'string') {
8791
+ throw new Error("newCodeContent must be a string.");
8792
+ }
8793
+
8794
+ // Step 1: Process blocks to get headers and find the target index
8795
+ const { blocks: processedBlocks, warnings } = processCodeBlocks(messageContent, { silent: true });
8796
+
8797
+ let targetIndex = -1;
8798
+
8799
+ for (let i = 0; i < processedBlocks.length; i++) {
8800
+ const block = processedBlocks[i];
8801
+ let match = false;
8802
+
8803
+ // Check standard code blocks by Block-UUID
8804
+ if ((block.type === 'code') && block.header && block.header['Block-UUID'] === blockUUID) {
8805
+ match = true;
8806
+ }
8807
+ // Check patch blocks by Target-Block-UUID
8808
+ else if (block.type === 'patch' && block.metadata && block.metadata['Target-Block-UUID'] === blockUUID) {
8809
+ match = true;
8810
+ }
8811
+
8812
+ if (match) {
8813
+ // If we've already found one, log a warning but still update the first one found.
8814
+ if (targetIndex !== -1) {
8815
+ console.warn(`Multiple blocks found matching UUID: ${blockUUID} (could be Block-UUID or Target-Block-UUID). Updating the first occurrence.`);
8816
+ } else {
8817
+ targetIndex = i;
8818
+ }
8819
+ }
8820
+ }
8821
+
8822
+ // Step 2: Handle findings
8823
+ if (targetIndex === -1) { // Use targetIndex to check if *any* valid match was set
8824
+ throw new Error(`No code or patch block found matching UUID: ${blockUUID} (checked Block-UUID and Target-Block-UUID).`);
8825
+ }
8826
+ // Warning for multiple matches is handled inside the loop now.
8827
+
8828
+ // Step 3: Call the index-based function
8829
+ // We need the index relative to *all* blocks (complete and incomplete), not just processed ones.
8830
+ // Re-extract boundaries to get the correct index mapping.
8831
+ const { openingPositions, closingPositions } = findAllCodeFences(messageContent);
8832
+ const { completeBlocks, incompleteBlocks } = matchFencesAndExtractBlocks(
8833
+ messageContent, openingPositions, closingPositions
8834
+ );
8835
+ const allBlocks = [...completeBlocks, ...incompleteBlocks];
8836
+
8837
+ // Find the index in the combined list that corresponds to the processed block's position
8838
+ const targetProcessedBlock = processedBlocks[targetIndex];
8839
+ const actualIndex = allBlocks.findIndex(b => b.opening.position === targetProcessedBlock.position);
8840
+
8841
+ if (actualIndex === -1) {
8842
+ // This indicates a discrepancy between blockProcessor and blockExtractor results, which shouldn't happen.
8843
+ throw new Error(`Internal error: Could not map processed block at index ${targetIndex} (matching UUID: ${blockUUID}) back to extracted block boundaries.`);
8844
+ }
8845
+
8846
+ // Pass the optional language parameter down
8847
+ return updateCodeBlockByIndex(messageContent, actualIndex, newCodeContent, language);
8848
+ }
8849
+
8850
+ /**
8851
+ * Updates a code block in message text identified by UUID.
8852
+ * (Moved from original PatchUtils)
8853
+ * @param {string} messageText - The original message text
8854
+ * @param {string} blockUUID - The Block-UUID to update
8855
+ * @param {string} newCode - The new code content (raw content, including header if applicable)
8856
+ * @param {string} language - The code language for the fence
8857
+ * @returns {string} Updated message text
8858
+ * @throws {Error} If block not found or parameters missing.
8859
+ */
8860
+ function updateCodeBlockInMessage(messageText, blockUUID, newCode, language) {
8861
+ if (!messageText || !blockUUID || newCode === null || newCode === undefined) { // Allow empty string for newCode
8862
+ throw new Error("Missing required parameters for updating code block (messageText, blockUUID, newCode)");
8863
+ }
8864
+
8865
+ // Use findCodeBlockByUUID from blockExtractor
8866
+ const block = findCodeBlockByUUID(messageText, blockUUID);
8867
+
8868
+ if (!block) {
8869
+ throw new Error(`Code block with UUID ${blockUUID} not found`);
8870
+ }
8871
+
8872
+ // Construct the new full block string
8873
+ const newBlockString = '```' + (language || block.language) + '\n' + newCode + '\n```';
8874
+
8875
+ // Replace the old block with the new one using precise indices
8876
+ return messageText.substring(0, block.startIndex) +
8877
+ newBlockString +
8878
+ messageText.substring(block.endIndex);
8879
+ }
8880
+
8881
+
8882
+ /**
8883
+ * Generic router function to update a code block by index or UUID.
8884
+ *
8885
+ * @param {string} messageContent - The original message string.
8886
+ * @param {number|string} identifier - The block index (number) or Block-UUID (string).
8887
+ * @param {string} newCodeContent - The new raw content for the block.
8888
+ * @param {string} [language] - Optional: The language identifier for the updated code block fence.
8889
+ * @returns {string} The updated message content.
8890
+ * @throws {Error} If the identifier type is invalid or if the underlying update function fails.
8891
+ */
8892
+ function updateCodeBlock(messageContent, identifier, newCodeContent, language = undefined) {
8893
+ if (typeof identifier === 'number') {
8894
+ // Pass language to index-based function
8895
+ return updateCodeBlockByIndex(messageContent, identifier, newCodeContent, language);
8896
+ } else if (typeof identifier === 'string') {
8897
+ // Pass language to UUID-based function
8898
+ return updateCodeBlockByUUID(messageContent, identifier, newCodeContent, language);
8899
+ } else {
8900
+ throw new Error("Invalid identifier type. Must be a number (index) or a string (UUID).");
8901
+ }
8902
+ }
8903
+
8904
+ /**
8905
+ * Deletes a code block specified by its index within a message string.
8906
+ *
8907
+ * @param {string} messageContent - The original message string containing code blocks.
8908
+ * @param {number} blockIndex - The 0-based index of the code block to delete.
8909
+ * @returns {string} The message content with the specified code block deleted.
8910
+ * @throws {Error} If the blockIndex is out of bounds or if block boundaries cannot be determined.
8911
+ */
8912
+ function deleteCodeBlockByIndex(messageContent, blockIndex) {
8913
+ if (typeof messageContent !== 'string') {
8914
+ throw new Error("messageContent must be a string.");
8915
+ }
8916
+ if (typeof blockIndex !== 'number' || blockIndex < 0) {
8917
+ throw new Error("blockIndex must be a non-negative number.");
8918
+ }
8919
+
8920
+ // Step 1: Find all fence positions to get accurate start/end of the full block
8921
+ const { openingPositions, closingPositions } = findAllCodeFences(messageContent);
8922
+ const { completeBlocks, incompleteBlocks } = matchFencesAndExtractBlocks(
8923
+ messageContent, openingPositions, closingPositions
8924
+ );
8925
+
8926
+ // Combine complete and incomplete for indexing
8927
+ const allBlocks = [...completeBlocks, ...incompleteBlocks];
8928
+
8929
+ // Step 2: Validate index
8930
+ if (blockIndex >= allBlocks.length) {
8931
+ throw new Error(`blockIndex ${blockIndex} is out of bounds. Found ${allBlocks.length} blocks.`);
8932
+ }
8933
+
8934
+ // Step 3: Get the target block's boundary information
8935
+ const targetBlockBoundaries = allBlocks[blockIndex];
8936
+ const openingFence = targetBlockBoundaries.opening;
8937
+ const closingFence = targetBlockBoundaries.closing; // Will be undefined for incomplete blocks
8938
+
8939
+ if (!openingFence) {
8940
+ // Should not happen if index is valid, but good practice to check
8941
+ throw new Error(`Could not find opening fence for block at index ${blockIndex}.`);
8942
+ }
8943
+
8944
+ // Determine the exact start and end of the full markdown block
8945
+ const blockStartPos = openingFence.position;
8946
+ // If the block is incomplete, the end is the end of the messageContent
8947
+ const blockEndPos = closingFence
8948
+ ? closingFence.position + closingFence.length
8949
+ : messageContent.length;
8950
+
8951
+ // Step 4: Perform the deletion
8952
+ const updatedMessageContent =
8953
+ messageContent.substring(0, blockStartPos) +
8954
+ messageContent.substring(blockEndPos);
8955
+
8956
+ return updatedMessageContent;
8957
+ }
8958
+
8959
+ /**
8960
+ * Deletes a code block specified by its Block-UUID within a message string.
8961
+ *
8962
+ * @param {string} messageContent - The original message string containing code blocks.
8963
+ * @param {string} blockUUID - The Block-UUID of the code block to delete.
8964
+ * @returns {string} The message content with the specified code block deleted.
8965
+ * @throws {Error} If no block with the specified UUID is found, or if multiple blocks have the same UUID.
8966
+ */
8967
+ function deleteCodeBlockByUUID(messageContent, blockUUID) {
8968
+ if (typeof messageContent !== 'string') {
8969
+ throw new Error("messageContent must be a string.");
8970
+ }
8971
+ if (typeof blockUUID !== 'string' || !blockUUID) {
8972
+ throw new Error("blockUUID must be a non-empty string.");
8973
+ }
8974
+
8975
+ // Step 1: Process blocks to get headers and find the target index
8976
+ const { blocks: processedBlocks, warnings } = processCodeBlocks(messageContent, { silent: true });
8977
+
8978
+ let targetIndex = -1;
8979
+
8980
+ for (let i = 0; i < processedBlocks.length; i++) {
8981
+ const block = processedBlocks[i];
8982
+ let match = false;
8983
+
8984
+ // Check standard code blocks by Block-UUID
8985
+ if (block.type === 'code' && block.header && block.header['Block-UUID'] === blockUUID) {
8986
+ match = true;
8987
+ }
8988
+ // Check patch blocks by Target-Block-UUID
8989
+ else if (block.type === 'patch' && block.metadata && block.metadata['Target-Block-UUID'] === blockUUID) {
8990
+ match = true;
8991
+ }
8992
+
8993
+ if (match) {
8994
+ // If we've already found one, log a warning but still target the first one found.
8995
+ if (targetIndex !== -1) {
8996
+ console.warn(`Multiple blocks found matching UUID: ${blockUUID} (could be Block-UUID or Target-Block-UUID). Deleting the first occurrence.`);
8997
+ } else {
8998
+ targetIndex = i;
8999
+ }
9000
+ }
9001
+ }
9002
+
9003
+ // Step 2: Handle findings
9004
+ if (targetIndex === -1) { // Use targetIndex to check if *any* valid match was set
9005
+ throw new Error(`No code or patch block found matching UUID: ${blockUUID} (checked Block-UUID and Target-Block-UUID).`);
9006
+ }
9007
+ // Warning for multiple matches is handled inside the loop now.
9008
+
9009
+ // Step 3: Call the index-based function
9010
+ // We need the index relative to *all* blocks (complete and incomplete), not just processed ones.
9011
+ // Re-extract boundaries to get the correct index mapping.
9012
+ const { openingPositions, closingPositions } = findAllCodeFences(messageContent);
9013
+ const { completeBlocks, incompleteBlocks } = matchFencesAndExtractBlocks(
9014
+ messageContent, openingPositions, closingPositions
9015
+ );
9016
+ const allBlocks = [...completeBlocks, ...incompleteBlocks];
9017
+
9018
+ // Find the index in the combined list that corresponds to the processed block's position
9019
+ const targetProcessedBlock = processedBlocks[targetIndex];
9020
+ const actualIndex = allBlocks.findIndex(b => b.opening.position === targetProcessedBlock.position);
9021
+
9022
+ if (actualIndex === -1) {
9023
+ // This indicates a discrepancy between blockProcessor and blockExtractor results, which shouldn't happen.
9024
+ throw new Error(`Internal error: Could not map processed block at index ${targetIndex} (matching UUID: ${blockUUID}) back to extracted block boundaries.`);
9025
+ }
9026
+
9027
+ return deleteCodeBlockByIndex(messageContent, actualIndex);
9028
+ }
9029
+
9030
+ /**
9031
+ * Generic router function to delete a code block by index or UUID.
9032
+ *
9033
+ * @param {string} messageContent - The original message string.
9034
+ * @param {number|string} identifier - The block index (number) or Block-UUID (string).
9035
+ * @returns {string} The updated message content.
9036
+ * @throws {Error} If the identifier type is invalid or if the underlying delete function fails.
9037
+ */
9038
+ function deleteCodeBlock(messageContent, identifier) {
9039
+ if (typeof identifier === 'number') {
9040
+ return deleteCodeBlockByIndex(messageContent, identifier);
9041
+ } else if (typeof identifier === 'string') {
9042
+ return deleteCodeBlockByUUID(messageContent, identifier);
9043
+ } else {
9044
+ throw new Error("Invalid identifier type. Must be a number (index) or a string (UUID).");
9045
+ }
9046
+ }
9047
+
9048
+ updateCodeBlock_1 = {
9049
+ updateCodeBlockByIndex,
9050
+ updateCodeBlockByUUID,
9051
+ updateCodeBlock,
9052
+ updateCodeBlockInMessage,
9053
+ deleteCodeBlockByIndex,
9054
+ deleteCodeBlockByUUID,
9055
+ deleteCodeBlock,
9056
+ };
9057
+ return updateCodeBlock_1;
8712
9058
  }
8713
9059
 
8714
9060
  /**
8715
- * Parses the content of a GitSense Chat Tool Block to extract the JSON payload.
8716
- * It strips the marker line and any subsequent lines starting with '#' (comments)
8717
- * or empty lines before attempting to parse the remaining content as JSON.
8718
- *
8719
- * @param {string} content - The raw content of the code block, verified by isToolBlock.
8720
- * @returns {{ tool: string, config: object, data: object } | null} The parsed JSON object { tool, config, data } or null if parsing fails or format is invalid.
8721
- * @throws {Error} If JSON parsing fails.
8722
- */
8723
- function parseToolBlock$1(content) {
8724
- if (!isToolBlock$1(content)) {
8725
- return null; // Should already be verified, but double-check
8726
- }
9061
+ * Component: GitSense Tool Block Utilities
9062
+ * Block-UUID: 8e1f7b4e-7e30-48b4-a7fc-643bf647661f
9063
+ * Parent-UUID: 72db6a5f-3b09-49e8-8f39-d4e3d37b510a
9064
+ * Version: 1.1.0
9065
+ * Description: Provides utility functions for identifying and parsing GitSense Chat Tool Blocks, and now for replacing them within markdown.
9066
+ * Language: JavaScript
9067
+ * Created-at: 2025-09-12T17:23:34.779Z
9068
+ * Authors: Gemini 2.5 Pro (v1.0.0), Gemini 2.5 Flash (v1.1.0)
9069
+ */
9070
+
9071
+ var GSToolBlockUtils$2;
9072
+ var hasRequiredGSToolBlockUtils;
9073
+
9074
+ function requireGSToolBlockUtils () {
9075
+ if (hasRequiredGSToolBlockUtils) return GSToolBlockUtils$2;
9076
+ hasRequiredGSToolBlockUtils = 1;
9077
+ const { updateCodeBlockByIndex } = requireUpdateCodeBlock();
9078
+ const { processCodeBlocks } = requireBlockProcessor();
9079
+
9080
+ /**
9081
+ * Checks if a given code block content represents a GitSense Chat Tool Block.
9082
+ * It verifies if the first non-empty line starts with "# GitSense Chat Tool".
9083
+ *
9084
+ * @param {string} content - The raw content of the code block (within the fences).
9085
+ * @returns {boolean} True if it's a GitSense Chat Tool Block, false otherwise.
9086
+ */
9087
+ function isToolBlock(content) {
9088
+ if (!content || typeof content !== 'string') {
9089
+ return false;
9090
+ }
8727
9091
 
8728
- const lines = content.split('\n');
8729
- const jsonLines = [];
8730
- let markerFound = false;
9092
+ if (content.match(/^\s*# GitSense Chat Tool\n\s*{/))
9093
+ return true;
8731
9094
 
8732
- for (const line of lines) {
8733
- const trimmedLine = line.trim();
9095
+ return false;
9096
+ }
8734
9097
 
8735
- if (!markerFound) {
8736
- if (trimmedLine.startsWith('# GitSense Chat Tool')) {
8737
- markerFound = true; // Skip the marker line itself
8738
- }
8739
- continue; // Skip lines before the marker (shouldn't be any non-empty ones based on isToolBlock)
8740
- }
9098
+ /**
9099
+ * Parses the content of a GitSense Chat Tool Block to extract the JSON payload.
9100
+ * It strips the marker line and any subsequent lines starting with '#' (comments)
9101
+ * or empty lines before attempting to parse the remaining content as JSON.
9102
+ *
9103
+ * @param {string} content - The raw content of the code block, verified by isToolBlock.
9104
+ * @returns {{ tool: string, config: object, data: object } | null} The parsed JSON object { tool, config, data } or null if parsing fails or format is invalid.
9105
+ * @throws {Error} If JSON parsing fails.
9106
+ */
9107
+ function parseToolBlock(content) {
9108
+ if (!isToolBlock(content)) {
9109
+ return null; // Should already be verified, but double-check
9110
+ }
8741
9111
 
8742
- // After the marker, skip empty lines and comment lines
8743
- if (trimmedLine.length === 0 || /^\s*#/.test(line)) {
8744
- continue;
8745
- }
9112
+ const lines = content.split('\n');
9113
+ const jsonLines = [];
9114
+ let markerFound = false;
8746
9115
 
8747
- // Keep lines that are part of the JSON
8748
- jsonLines.push(line);
8749
- }
9116
+ for (const line of lines) {
9117
+ const trimmedLine = line.trim();
8750
9118
 
8751
- const jsonString = jsonLines.join('\n');
9119
+ if (!markerFound) {
9120
+ if (trimmedLine.startsWith('# GitSense Chat Tool')) {
9121
+ markerFound = true; // Skip the marker line itself
9122
+ }
9123
+ continue; // Skip lines before the marker (shouldn't be any non-empty ones based on isToolBlock)
9124
+ }
8752
9125
 
8753
- if (!jsonString) {
8754
- // No JSON content found after stripping comments/marker
8755
- throw new Error("No JSON content found in the tool block after stripping marker and comments.");
8756
- }
9126
+ // After the marker, skip empty lines and comment lines
9127
+ if (trimmedLine.length === 0 || /^\s*#/.test(line)) {
9128
+ continue;
9129
+ }
8757
9130
 
8758
- try {
8759
- const parsed = JSON.parse(jsonString);
9131
+ // Keep lines that are part of the JSON
9132
+ jsonLines.push(line);
9133
+ }
8760
9134
 
8761
- // Basic validation of the parsed structure
8762
- // The properties tool and config are required. Tool cannot be null or undefined but
8763
- // config can't. The reason for requiring config to be declared is to reserve the
8764
- // property name.
8765
- if (typeof parsed !== 'object' || parsed === null) {
8766
- throw new Error("Parsed content is not a valid JSON object.");
8767
- }
8768
- if (typeof parsed.tool !== 'string' || !parsed.tool) {
8769
- throw new Error("Parsed JSON object is missing the required 'tool' string property.");
8770
- }
9135
+ const jsonString = jsonLines.join('\n');
8771
9136
 
8772
- if (typeof parsed.config === 'undefined') {
8773
- throw new Error("Parsed JSON object is missing the required 'config' property.");
8774
- }
9137
+ if (!jsonString) {
9138
+ // No JSON content found after stripping comments/marker
9139
+ throw new Error("No JSON content found in the tool block after stripping marker and comments.");
9140
+ }
8775
9141
 
8776
- return parsed;
8777
- } catch (error) {
8778
- // Re-throw JSON parsing errors with more context
8779
- throw new Error(`Failed to parse tool block JSON: ${error.message}\nContent after stripping comments:\n${jsonString}`);
8780
- }
8781
- }
9142
+ try {
9143
+ const parsed = JSON.parse(jsonString);
8782
9144
 
8783
- function formatToolBlock(toolData) {
8784
- return `# GitSense Chat Tool\n\n${JSON.stringify(toolData, null, 2)}`;
8785
- }
9145
+ // Basic validation of the parsed structure
9146
+ // The properties tool and config are required. Tool cannot be null or undefined but
9147
+ // config can't. The reason for requiring config to be declared is to reserve the
9148
+ // property name.
9149
+ if (typeof parsed !== 'object' || parsed === null) {
9150
+ throw new Error("Parsed content is not a valid JSON object.");
9151
+ }
9152
+ if (typeof parsed.tool !== 'string' || !parsed.tool) {
9153
+ throw new Error("Parsed JSON object is missing the required 'tool' string property.");
9154
+ }
8786
9155
 
8787
- var GSToolBlockUtils$3 = {
8788
- isToolBlock: isToolBlock$1,
8789
- parseToolBlock: parseToolBlock$1,
8790
- formatToolBlock
8791
- };
9156
+ if (typeof parsed.config === 'undefined') {
9157
+ throw new Error("Parsed JSON object is missing the required 'config' property.");
9158
+ }
9159
+
9160
+ return parsed;
9161
+ } catch (error) {
9162
+ // Re-throw JSON parsing errors with more context
9163
+ throw new Error(`Failed to parse tool block JSON: ${error.message}\nContent after stripping comments:\n${jsonString}`);
9164
+ }
9165
+ }
9166
+
9167
+ /**
9168
+ * Formats tool data into a GitSense Chat Tool Block string.
9169
+ *
9170
+ * @param {object} toolData - The tool data object to format.
9171
+ * @returns {string} The formatted GitSense Chat Tool Block string.
9172
+ */
9173
+ function formatToolBlock(toolData) {
9174
+ return `# GitSense Chat Tool\n\n${JSON.stringify(toolData, null, 2)}`;
9175
+ }
9176
+
9177
+ /**
9178
+ * Replaces a specific GitSense Chat Tool Block within a markdown string with updated tool data.
9179
+ * This function leverages CodeBlockUtils for robust parsing and updating.
9180
+ *
9181
+ * @param {string} markdownContent - The original markdown string containing code blocks.
9182
+ * @param {string} toolName - The 'tool' property value of the target GitSense Chat Tool Block to replace.
9183
+ * @param {Object} newToolData - The new tool data object to insert into the block.
9184
+ * @returns {string} The markdown content with the specified tool block updated.
9185
+ * @throws {Error} If the target tool block is not found or if CodeBlockUtils encounters an error.
9186
+ */
9187
+ function replaceToolBlock(markdownContent, toolName, newToolData) {
9188
+ if (typeof markdownContent !== 'string' || !toolName || !newToolData) {
9189
+ throw new Error("Missing required parameters for replaceToolBlock.");
9190
+ }
9191
+
9192
+ // 1. Process the markdown content to find all code blocks
9193
+ const { blocks, warnings } = processCodeBlocks(markdownContent, { silent: true });
9194
+
9195
+ let targetBlockIndex = -1;
9196
+
9197
+ // 2. Iterate through the processed blocks to find the target GitSense Chat Tool Block
9198
+ for (let i = 0; i < blocks.length; i++) {
9199
+ const block = blocks[i];
9200
+ if (block.type === 'gs-tool' && block.toolData && block.toolData.tool === toolName) {
9201
+ targetBlockIndex = i;
9202
+ break; // Found the first matching tool block
9203
+ }
9204
+ }
9205
+
9206
+ if (targetBlockIndex === -1) {
9207
+ console.warn(`replaceToolBlock: No GitSense Chat Tool Block with name "${toolName}" found to replace.`);
9208
+ return markdownContent; // Return original content if no replacement occurred
9209
+ }
9210
+
9211
+ // 3. Format the new tool data into the content that goes *inside* the fences
9212
+ const newContentBetweenFences = formatToolBlock(newToolData);
9213
+
9214
+ // 4. Use CodeBlockUtils.updateCodeBlockByIndex to perform the replacement
9215
+ // The language for GitSense Tool Blocks is always 'txt'
9216
+ const updatedMarkdown = updateCodeBlockByIndex(
9217
+ markdownContent,
9218
+ targetBlockIndex,
9219
+ newContentBetweenFences,
9220
+ 'txt'
9221
+ );
9222
+
9223
+ return updatedMarkdown;
9224
+ }
9225
+
9226
+ GSToolBlockUtils$2 = {
9227
+ isToolBlock,
9228
+ parseToolBlock,
9229
+ formatToolBlock,
9230
+ replaceToolBlock,
9231
+ };
9232
+ return GSToolBlockUtils$2;
9233
+ }
8792
9234
 
8793
9235
  /*
8794
9236
  * Component: CodeBlockUtils Continuation Utilities
@@ -8809,7 +9251,7 @@ var GSToolBlockUtils$3 = {
8809
9251
  * @param {Object} header - The parsed header object from the incomplete block (if available).
8810
9252
  * @returns {Object} Continuation information { lastLines: Array<string>, blockUUID: string|null, partNumber: number, language: string, metadata: Object }
8811
9253
  */
8812
- function extractContinuationInfo$3(content, partNumber, language, header = {}) {
9254
+ function extractContinuationInfo$2(content, partNumber, language, header = {}) {
8813
9255
  // Extract Block-UUID directly from the parsed header if available and valid
8814
9256
  let blockUUID = (header && header['Block-UUID'] && header['Block-UUID'] !== 'INVALID UUID')
8815
9257
  ? header['Block-UUID']
@@ -8977,7 +9419,7 @@ Contribute code starting from the line immediately following the repeated code b
8977
9419
 
8978
9420
 
8979
9421
  var continuationUtils = {
8980
- extractContinuationInfo: extractContinuationInfo$3,
9422
+ extractContinuationInfo: extractContinuationInfo$2,
8981
9423
  generateContinuationPrompt: generateContinuationPrompt$2
8982
9424
  };
8983
9425
 
@@ -9000,7 +9442,7 @@ var continuationUtils = {
9000
9442
  * @param {string} jsonString The string potentially containing JSON and comments.
9001
9443
  * @returns {string[]} An array of detected comment strings (including their markers).
9002
9444
  */
9003
- function detectJsonComments$2(jsonString) {
9445
+ function detectJsonComments$1(jsonString) {
9004
9446
  const comments = [];
9005
9447
  let inString = false;
9006
9448
  let inBlockComment = false;
@@ -9079,7 +9521,7 @@ function detectJsonComments$2(jsonString) {
9079
9521
  }
9080
9522
 
9081
9523
  var JsonUtils$2 = {
9082
- detectJsonComments: detectJsonComments$2
9524
+ detectJsonComments: detectJsonComments$1
9083
9525
  };
9084
9526
 
9085
9527
  /**
@@ -9093,550 +9535,558 @@ var JsonUtils$2 = {
9093
9535
  * 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)
9094
9536
  */
9095
9537
 
9096
- const { findAllCodeFences: findAllCodeFences$3, matchFencesAndExtractBlocks: matchFencesAndExtractBlocks$3 } = blockExtractor;
9097
- const { parseHeader: parseHeader$2 } = headerUtils;
9098
- const { validateUUID: validateUUID$2, generateUUID: generateUUID$2 } = uuidUtils;
9099
- const AnalysisBlockUtils$2 = AnalysisBlockUtils$3;
9100
- const PatchUtils$1 = PatchUtils$2;
9101
- const GSToolBlockUtils$2 = GSToolBlockUtils$3;
9102
- const { extractContinuationInfo: extractContinuationInfo$2 } = continuationUtils;
9103
- const { removeLineNumbers: removeLineNumbers$2 } = lineNumberFormatter;
9104
- const { detectJsonComments: detectJsonComments$1 } = JsonUtils$2;
9105
-
9106
- /**
9107
- * Processes a single block's content (internal helper)
9108
- * @param {string} content - The block content
9109
- * @param {string} language - The language of the block
9110
- * @param {number} position - The position in the original text
9111
- * @param {boolean} incomplete - Whether the block is incomplete
9112
- * @param {Object} options - Processing options (e.g., silent, validatePatches)
9113
- * @returns {Object} Processed block object
9114
- */
9115
- function processBlockContent(content, language, position, incomplete, options = { silent: true, validatePatches: false }) {
9116
- const { silent, validatePatches } = options;
9117
- let warnings = []; // Collect warnings specific to this block
9118
- let header = {};
9119
- let headerText = ''; // Raw header text including comments
9120
- let headerParseError = null;
9121
- let headerDelimiterType = null; // 'triple' or 'double'
9122
- let codeText = content; // Default to all content being code
9123
-
9124
- // Check if this is a GitSense Tool Block *first*
9125
- if (GSToolBlockUtils$2.isToolBlock(content)) {
9126
- let ignoreDueToComments = false;
9127
- let toolData = null;
9128
- let toolParseError = null;
9129
- try {
9130
- toolData = GSToolBlockUtils$2.parseToolBlock(content);
9131
- } catch (error) {
9132
- // When chatting about the tool block, the LLM may add
9133
- // add comments in it so let's test for this
9134
- const comments = detectJsonComments$1(content);
9538
+ var blockProcessor;
9539
+ var hasRequiredBlockProcessor;
9540
+
9541
+ function requireBlockProcessor () {
9542
+ if (hasRequiredBlockProcessor) return blockProcessor;
9543
+ hasRequiredBlockProcessor = 1;
9544
+ const { findAllCodeFences, matchFencesAndExtractBlocks } = blockExtractor;
9545
+ const { parseHeader } = headerUtils;
9546
+ const { validateUUID, generateUUID } = uuidUtils;
9547
+ const AnalysisBlockUtils = AnalysisBlockUtils$2;
9548
+ const PatchUtils = PatchUtils$1;
9549
+ const GSToolBlockUtils = requireGSToolBlockUtils();
9550
+ const { extractContinuationInfo } = continuationUtils;
9551
+ const { removeLineNumbers } = lineNumberFormatter;
9552
+ const { detectJsonComments } = JsonUtils$2;
9553
+
9554
+ /**
9555
+ * Processes a single block's content (internal helper)
9556
+ * @param {string} content - The block content
9557
+ * @param {string} language - The language of the block
9558
+ * @param {number} position - The position in the original text
9559
+ * @param {boolean} incomplete - Whether the block is incomplete
9560
+ * @param {Object} options - Processing options (e.g., silent, validatePatches)
9561
+ * @returns {Object} Processed block object
9562
+ */
9563
+ function processBlockContent(content, language, position, incomplete, options = { silent: true, validatePatches: false }) {
9564
+ const { silent, validatePatches } = options;
9565
+ let warnings = []; // Collect warnings specific to this block
9566
+ let header = {};
9567
+ let headerText = ''; // Raw header text including comments
9568
+ let headerParseError = null;
9569
+ let headerDelimiterType = null; // 'triple' or 'double'
9570
+ let codeText = content; // Default to all content being code
9571
+
9572
+ // Check if this is a GitSense Tool Block *first*
9573
+ if (GSToolBlockUtils.isToolBlock(content)) {
9574
+ let ignoreDueToComments = false;
9575
+ let toolData = null;
9576
+ let toolParseError = null;
9577
+ try {
9578
+ toolData = GSToolBlockUtils.parseToolBlock(content);
9579
+ } catch (error) {
9580
+ // When chatting about the tool block, the LLM may add
9581
+ // add comments in it so let's test for this
9582
+ const comments = detectJsonComments(content);
9583
+
9584
+ if (comments.length) {
9585
+ ignoreDueToComments = true;
9586
+ } else {
9587
+ toolParseError = error.message;
9588
+ warnings.push({
9589
+ position: position,
9590
+ type: 'gs_tool_block_parse_error',
9591
+ message: `Invalid GitSense tool block: ${error.message}`,
9592
+ content: content
9593
+ });
9594
+ }
9595
+ }
9135
9596
 
9136
- if (comments.length) {
9137
- ignoreDueToComments = true;
9138
- } else {
9139
- toolParseError = error.message;
9140
- warnings.push({
9141
- position: position,
9142
- type: 'gs_tool_block_parse_error',
9143
- message: `Invalid GitSense tool block: ${error.message}`,
9144
- content: content
9145
- });
9146
- }
9147
- }
9597
+ if (ignoreDueToComments) ; else {
9598
+ return {
9599
+ type: 'gs-tool',
9600
+ language: language,
9601
+ content: content,
9602
+ position: position,
9603
+ incomplete: incomplete,
9604
+ toolData: toolData,
9605
+ toolParseError: toolParseError,
9606
+ warnings: warnings,
9607
+ };
9608
+ }
9609
+ }
9148
9610
 
9149
- if (ignoreDueToComments) ; else {
9150
- return {
9151
- type: 'gs-tool',
9152
- language: language,
9153
- content: content,
9154
- position: position,
9155
- incomplete: incomplete,
9156
- toolData: toolData,
9157
- toolParseError: toolParseError,
9158
- warnings: warnings,
9159
- };
9160
- }
9161
- }
9611
+ // Check if this is an analysis block *next*
9612
+ if (AnalysisBlockUtils.isAnalysisBlock(content)) {
9613
+ const analysisType = AnalysisBlockUtils.getAnalysisBlockType(content);
9614
+ const analysisMetadata = AnalysisBlockUtils.parseOverviewMetadata(content);
9615
+
9616
+ // Validate the metadata if it was successfully parsed
9617
+ let validationResult = { isValid: false, errors: ['Failed to parse analysis metadata'] };
9618
+ if (analysisMetadata) {
9619
+ validationResult = AnalysisBlockUtils.validateAnalysisMetadata(analysisMetadata);
9620
+
9621
+ if (!validationResult.isValid) {
9622
+ warnings.push({
9623
+ position: position,
9624
+ type: 'analysis_metadata_error',
9625
+ message: `Invalid analysis metadata: ${validationResult.errors.join(', ')}`,
9626
+ content: content,
9627
+ });
9628
+ }
9629
+ }
9162
9630
 
9163
- // Check if this is an analysis block *next*
9164
- if (AnalysisBlockUtils$2.isAnalysisBlock(content)) {
9165
- const analysisType = AnalysisBlockUtils$2.getAnalysisBlockType(content);
9166
- const analysisMetadata = AnalysisBlockUtils$2.parseOverviewMetadata(content);
9631
+ return {
9632
+ type: analysisType,
9633
+ language: language,
9634
+ content: content,
9635
+ position: position,
9636
+ incomplete: incomplete,
9637
+ analysisMetadata: analysisMetadata,
9638
+ analysisValidation: validationResult,
9639
+ warnings: warnings,
9640
+ };
9641
+ }
9167
9642
 
9168
- // Validate the metadata if it was successfully parsed
9169
- let validationResult = { isValid: false, errors: ['Failed to parse analysis metadata'] };
9170
- if (analysisMetadata) {
9171
- validationResult = AnalysisBlockUtils$2.validateAnalysisMetadata(analysisMetadata);
9643
+ // Check if this is a gitsense-search-flow block *next*
9644
+ if (language === 'gitsense-search-flow') {
9645
+ // Future parsing logic for gitsense-search-flow content would go here.
9646
+ // For now, we just identify the type and keep the raw content.
9647
+ return {
9648
+ type: 'gitsense-search-flow',
9649
+ language: language,
9650
+ content: content, // Keep raw content for now
9651
+ position: position,
9652
+ incomplete: incomplete,
9653
+ // Add parsedData: {} or similar if parsing is implemented later
9654
+ warnings: warnings, // Include any warnings collected so far
9655
+ };
9656
+ }
9172
9657
 
9173
- if (!validationResult.isValid) {
9174
- warnings.push({
9175
- position: position,
9176
- type: 'analysis_metadata_error',
9177
- message: `Invalid analysis metadata: ${validationResult.errors.join(', ')}`,
9178
- content: content,
9179
- });
9180
- }
9181
- }
9658
+ // Then check if this is a patch block
9659
+ if (PatchUtils.isPatchBlock(content)) {
9660
+ const patchFormat = PatchUtils.determinePatchFormat(content);
9661
+ const metadata = PatchUtils.extractPatchMetadata(content); // Extract metadata regardless of validation
9662
+ const patchContent = PatchUtils.extractPatchContent(content); // Extract raw patch content
9663
+
9664
+ let patchValidationResult = { valid: true, errors: [], patches: null }; // Default valid state
9665
+
9666
+ // Validate metadata (always useful)
9667
+ const errors = PatchUtils.validatePatchMetadata(metadata);
9668
+ if (errors.length) {
9669
+ warnings.push({
9670
+ position: position,
9671
+ type: 'patch_metadata_error',
9672
+ message: `Invalid patch metadata: ${errors.join(', ')}`,
9673
+ content: content,
9674
+ });
9675
+ // Decide if invalid metadata makes the whole block invalid or just adds a warning
9676
+ // For now, just warn, but return the extracted (potentially invalid) metadata.
9677
+ }
9182
9678
 
9183
- return {
9184
- type: analysisType,
9185
- language: language,
9186
- content: content,
9187
- position: position,
9188
- incomplete: incomplete,
9189
- analysisMetadata: analysisMetadata,
9190
- analysisValidation: validationResult,
9191
- warnings: warnings,
9192
- };
9193
- }
9679
+ // Optionally validate context patch structure if requested
9680
+ if (patchFormat === 'context' && validatePatches) {
9681
+ try {
9682
+ const patches = PatchUtils.extractContextPatches(content);
9683
+ if (!patches || patches.length === 0) {
9684
+ patchValidationResult = { valid: false, errors: ['No valid context patch content found.'], patches: null };
9685
+ warnings.push({
9686
+ position: position,
9687
+ type: 'patch_content_error',
9688
+ message: 'No valid context patch content found.',
9689
+ content: content,
9690
+ });
9691
+ } else {
9692
+ // Basic validation passed (structure was parsable)
9693
+ patchValidationResult = { valid: true, errors: [], patches: patches };
9694
+ }
9695
+ } catch (error) {
9696
+ patchValidationResult = { valid: false, errors: [error.message], patches: null };
9697
+ warnings.push({
9698
+ position: position,
9699
+ type: 'patch_parse_error',
9700
+ message: `Error parsing context patch: ${error.message}`,
9701
+ content: content,
9702
+ });
9703
+ }
9704
+ }
9194
9705
 
9195
- // Check if this is a gitsense-search-flow block *next*
9196
- if (language === 'gitsense-search-flow') {
9197
- // Future parsing logic for gitsense-search-flow content would go here.
9198
- // For now, we just identify the type and keep the raw content.
9199
- return {
9200
- type: 'gitsense-search-flow',
9201
- language: language,
9202
- content: content, // Keep raw content for now
9203
- position: position,
9204
- incomplete: incomplete,
9205
- // Add parsedData: {} or similar if parsing is implemented later
9206
- warnings: warnings, // Include any warnings collected so far
9207
- };
9208
- }
9706
+ return {
9707
+ type: 'patch',
9708
+ language: language === 'diff' ? 'diff' : language, // Use 'diff' if specified, else keep original
9709
+ content: content, // Full original content within fences
9710
+ position: position,
9711
+ incomplete: incomplete,
9712
+ metadata: metadata,
9713
+ patch: patchContent, // Raw patch content after metadata
9714
+ patchFormat: patchFormat,
9715
+ patchValidation: validatePatches ? patchValidationResult : undefined, // Include validation only if requested
9716
+ warnings: warnings,
9717
+ };
9718
+ } else {
9719
+ // For non-patch blocks, remove display line numbers before processing content
9720
+ content = removeLineNumbers(content); // This is the "cleaned" content
9721
+ }
9209
9722
 
9210
- // Then check if this is a patch block
9211
- if (PatchUtils$1.isPatchBlock(content)) {
9212
- const patchFormat = PatchUtils$1.determinePatchFormat(content);
9213
- const metadata = PatchUtils$1.extractPatchMetadata(content); // Extract metadata regardless of validation
9214
- const patchContent = PatchUtils$1.extractPatchContent(content); // Extract raw patch content
9723
+ // --- Process as a regular code block ---
9724
+ // Attempt 1: Look for triple newline separator (our standard)
9725
+ const tripleNewlineSeparator = '\n\n\n';
9726
+ const tripleNewlineIndex = content.indexOf(tripleNewlineSeparator);
9727
+
9728
+ if (tripleNewlineIndex !== -1) {
9729
+ const potentialHeaderText = content.substring(0, tripleNewlineIndex); // No trim here
9730
+
9731
+ try {
9732
+ header = parseHeader(potentialHeaderText, language);
9733
+ headerText = potentialHeaderText;
9734
+ codeText = content.substring(tripleNewlineIndex + tripleNewlineSeparator.length);
9735
+ headerDelimiterType = 'triple';
9736
+ } catch (error) {
9737
+ // If parsing fails, it's not a valid header, log warning and try double newline
9738
+ headerParseError = error.message;
9739
+ warnings.push({
9740
+ position: position,
9741
+ type: 'header_parse_error',
9742
+ message: `Invalid header (triple newline attempt): ${error.message}. Attempting double newline.`,
9743
+ content: potentialHeaderText // Show the problematic header text
9744
+ });
9745
+ // Do not set header, headerText, codeText, or headerDelimiterType yet.
9746
+ // Fall through to the next attempt.
9747
+ }
9748
+ }
9215
9749
 
9216
- let patchValidationResult = { valid: true, errors: [], patches: null }; // Default valid state
9750
+ // Attempt 2: Look for double newline separator (fallback)
9751
+ // Only if a valid header wasn't found in the triple newline attempt
9752
+ if (!headerDelimiterType) {
9753
+ const doubleNewlineSeparator = '\n\n';
9754
+ const doubleNewlineIndex = content.indexOf(doubleNewlineSeparator);
9755
+
9756
+ if (doubleNewlineIndex !== -1) {
9757
+ const potentialHeaderText = content.substring(0, doubleNewlineIndex); // No trim here
9758
+
9759
+ try {
9760
+ header = parseHeader(potentialHeaderText, language);
9761
+ headerText = potentialHeaderText;
9762
+ codeText = content.substring(doubleNewlineIndex + doubleNewlineSeparator.length);
9763
+ headerDelimiterType = 'double';
9764
+ } catch (error) {
9765
+ // If parsing fails, it's not a valid header, log warning and default to no header
9766
+ headerParseError = error.message;
9767
+ warnings.push({
9768
+ position: position,
9769
+ type: 'header_parse_error',
9770
+ message: `Invalid header (double newline attempt): ${error.message}. Treating block as code only.`,
9771
+ content: potentialHeaderText // Show the problematic header text
9772
+ });
9773
+ // Fall through to default case (no header)
9774
+ }
9775
+ }
9776
+ }
9217
9777
 
9218
- // Validate metadata (always useful)
9219
- const errors = PatchUtils$1.validatePatchMetadata(metadata);
9220
- if (errors.length) {
9221
- warnings.push({
9222
- position: position,
9223
- type: 'patch_metadata_error',
9224
- message: `Invalid patch metadata: ${errors.join(', ')}`,
9225
- content: content,
9226
- });
9227
- // Decide if invalid metadata makes the whole block invalid or just adds a warning
9228
- // For now, just warn, but return the extracted (potentially invalid) metadata.
9229
- }
9778
+ // Default: If no valid header was found after both attempts
9779
+ if (!headerDelimiterType && !incomplete) { // Only warn about missing header if the block is complete
9780
+ warnings.push({
9781
+ position: position,
9782
+ type: 'missing_header',
9783
+ message: `No distinct header found or parsed as valid. Treating block as code only.`,
9784
+ content: content.substring(0, 100) + '...' // Show beginning of content
9785
+ });
9786
+ }
9230
9787
 
9231
- // Optionally validate context patch structure if requested
9232
- if (patchFormat === 'context' && validatePatches) {
9233
- try {
9234
- const patches = PatchUtils$1.extractContextPatches(content);
9235
- if (!patches || patches.length === 0) {
9236
- patchValidationResult = { valid: false, errors: ['No valid context patch content found.'], patches: null };
9237
- warnings.push({
9238
- position: position,
9239
- type: 'patch_content_error',
9240
- message: 'No valid context patch content found.',
9241
- content: content,
9242
- });
9243
- } else {
9244
- // Basic validation passed (structure was parsable)
9245
- patchValidationResult = { valid: true, errors: [], patches: patches };
9246
- }
9247
- } catch (error) {
9248
- patchValidationResult = { valid: false, errors: [error.message], patches: null };
9249
- warnings.push({
9250
- position: position,
9251
- type: 'patch_parse_error',
9252
- message: `Error parsing context patch: ${error.message}`,
9253
- content: content,
9254
- });
9255
- }
9256
- }
9788
+ return {
9789
+ type: 'code',
9790
+ language: language,
9791
+ header: header,
9792
+ headerText: headerText.trim(), // Include the raw header text if successfully parsed
9793
+ content: codeText, // The code part after the header (or full content if no header)
9794
+ position: position, // Position of the opening fence
9795
+ headerDelimiterType: headerDelimiterType, // 'triple', 'double', or null
9796
+ incomplete: incomplete,
9797
+ warnings: warnings,
9798
+ headerParseError: headerParseError // Include error message if parsing failed
9799
+ };
9800
+ }
9257
9801
 
9258
- return {
9259
- type: 'patch',
9260
- language: language === 'diff' ? 'diff' : language, // Use 'diff' if specified, else keep original
9261
- content: content, // Full original content within fences
9262
- position: position,
9263
- incomplete: incomplete,
9264
- metadata: metadata,
9265
- patch: patchContent, // Raw patch content after metadata
9266
- patchFormat: patchFormat,
9267
- patchValidation: validatePatches ? patchValidationResult : undefined, // Include validation only if requested
9268
- warnings: warnings,
9269
- };
9270
- } else {
9271
- // For non-patch blocks, remove display line numbers before processing content
9272
- content = removeLineNumbers$2(content); // This is the "cleaned" content
9273
- }
9274
9802
 
9275
- // --- Process as a regular code block ---
9276
- // Attempt 1: Look for triple newline separator (our standard)
9277
- const tripleNewlineSeparator = '\n\n\n';
9278
- const tripleNewlineIndex = content.indexOf(tripleNewlineSeparator);
9803
+ /**
9804
+ * Processes the content of complete and incomplete blocks (internal helper)
9805
+ * @param {string} text - The input text
9806
+ * @param {Array} completeBlocks - Array of complete block objects from extractor
9807
+ * @param {Array} incompleteBlocks - Array of incomplete block objects from extractor
9808
+ * @param {Object} options - Processing options
9809
+ * @returns {Array} Array of processed block objects
9810
+ */
9811
+ function processBlockContents(text, completeBlocks, incompleteBlocks, options) {
9812
+ const processedBlocks = [];
9813
+ const allWarnings = []; // Collect warnings from all blocks
9814
+
9815
+ // Process complete blocks
9816
+ for (const block of completeBlocks) {
9817
+ try {
9818
+ const startPos = block.opening.position + block.opening.length;
9819
+ const endPos = block.closing.position;
9820
+ // Get content, including the newlines immediately after opening and before closing fence
9821
+ const contentWithNewlines = text.substring(startPos, endPos);
9822
+ const content = contentWithNewlines; //.replace(/^\s*\n|\n\s*$/g, '');
9823
+
9824
+ const language = block.opening.language;
9825
+
9826
+ // Process block content
9827
+ const processedBlock = processBlockContent(content, language, block.opening.position, false, options);
9828
+ processedBlocks.push(processedBlock);
9829
+ if (processedBlock.warnings) {
9830
+ allWarnings.push(...processedBlock.warnings);
9831
+ }
9832
+ } catch (error) {
9833
+ // Catch errors during the processing of a single block
9834
+ allWarnings.push({
9835
+ position: block.opening?.position || -1,
9836
+ type: 'block_processing_error',
9837
+ message: `Error processing complete block: ${error.message}`,
9838
+ content: text.substring(block.opening?.position, block.closing?.position + block.closing?.length) || "Error retrieving block content"
9839
+ });
9840
+ }
9841
+ }
9279
9842
 
9280
- if (tripleNewlineIndex !== -1) {
9281
- const potentialHeaderText = content.substring(0, tripleNewlineIndex); // No trim here
9843
+ // Process incomplete blocks
9844
+ for (const block of incompleteBlocks) {
9845
+ try {
9846
+ const startPos = block.opening.position + block.opening.length;
9847
+ // Get content from opening fence to end of text
9848
+ const contentWithNewline = text.substring(startPos);
9849
+ // Trim only leading whitespace line
9850
+ const content = contentWithNewline.replace(/^\s*\n/, '');
9851
+
9852
+ const language = block.opening.language;
9853
+
9854
+ // Process block content - it's expected to be incomplete
9855
+ const processedBlock = processBlockContent(content, language, block.opening.position, true, options);
9856
+
9857
+ // Extract continuation info for incomplete blocks
9858
+ // Requires extractContinuationInfo from continuationUtils
9859
+ try {
9860
+ // Pass the *processed* header info to continuation utils
9861
+ let { 'Continuation-Part': partNumber } = processedBlock?.header || {};
9862
+ partNumber = partNumber ? parseInt(partNumber) + 1 : 2; // Default to part 2 if not specified
9863
+ processedBlock.continuationInfo = extractContinuationInfo(processedBlock.content, partNumber, language, processedBlock.header);
9864
+ } catch (continuationError) {
9865
+ allWarnings.push({
9866
+ position: block.opening?.position || -1,
9867
+ type: 'continuation_info_error',
9868
+ message: `Error extracting continuation info: ${continuationError.message}`,
9869
+ content: content
9870
+ });
9871
+ }
9872
+
9873
+
9874
+ processedBlocks.push(processedBlock);
9875
+ if (processedBlock.warnings) {
9876
+ allWarnings.push(...processedBlock.warnings);
9877
+ }
9878
+ } catch (error) {
9879
+ // Catch errors during the processing of a single incomplete block
9880
+ allWarnings.push({
9881
+ position: block.opening?.position || -1,
9882
+ type: 'incomplete_block_processing_error',
9883
+ message: `Error processing incomplete block: ${error.message}`,
9884
+ content: text.substring(block.opening?.position) || "Error retrieving block content"
9885
+ });
9886
+ }
9887
+ }
9282
9888
 
9283
- try {
9284
- header = parseHeader$2(potentialHeaderText, language);
9285
- headerText = potentialHeaderText;
9286
- codeText = content.substring(tripleNewlineIndex + tripleNewlineSeparator.length);
9287
- headerDelimiterType = 'triple';
9288
- } catch (error) {
9289
- // If parsing fails, it's not a valid header, log warning and try double newline
9290
- headerParseError = error.message;
9291
- warnings.push({
9292
- position: position,
9293
- type: 'header_parse_error',
9294
- message: `Invalid header (triple newline attempt): ${error.message}. Attempting double newline.`,
9295
- content: potentialHeaderText // Show the problematic header text
9296
- });
9297
- // Do not set header, headerText, codeText, or headerDelimiterType yet.
9298
- // Fall through to the next attempt.
9299
- }
9300
- }
9889
+ // Return processed blocks and collected warnings
9890
+ // Note: The caller (processCodeBlocks) will handle adding these warnings to its own return object.
9891
+ return { processedBlocks, allWarnings };
9892
+ }
9301
9893
 
9302
- // Attempt 2: Look for double newline separator (fallback)
9303
- // Only if a valid header wasn't found in the triple newline attempt
9304
- if (!headerDelimiterType) {
9305
- const doubleNewlineSeparator = '\n\n';
9306
- const doubleNewlineIndex = content.indexOf(doubleNewlineSeparator);
9307
9894
 
9308
- if (doubleNewlineIndex !== -1) {
9309
- const potentialHeaderText = content.substring(0, doubleNewlineIndex); // No trim here
9895
+ /**
9896
+ * Core function to process code blocks with shared logic
9897
+ * @param {string} text - The input text containing code blocks
9898
+ * @param {Object} options - Processing options (e.g., silent, validatePatches)
9899
+ * @returns {Object} { blocks: Array, warnings: Array, hasIncompleteBlocks: boolean, lastIncompleteBlock: Object|null }
9900
+ */
9901
+ function processCodeBlocks(text, options = { silent: false, validatePatches: false }) {
9902
+ if (typeof text !== "string") { // Allow empty strings
9903
+ console.warn("Warning: Input must be a string.");
9904
+ return {
9905
+ blocks: [],
9906
+ warnings: [{ type: 'invalid_input', message: 'Input must be a string.' }],
9907
+ hasIncompleteBlocks: false,
9908
+ lastIncompleteBlock: null }
9909
+ ;
9910
+ }
9310
9911
 
9311
- try {
9312
- header = parseHeader$2(potentialHeaderText, language);
9313
- headerText = potentialHeaderText;
9314
- codeText = content.substring(doubleNewlineIndex + doubleNewlineSeparator.length);
9315
- headerDelimiterType = 'double';
9316
- } catch (error) {
9317
- // If parsing fails, it's not a valid header, log warning and default to no header
9318
- headerParseError = error.message;
9319
- warnings.push({
9320
- position: position,
9321
- type: 'header_parse_error',
9322
- message: `Invalid header (double newline attempt): ${error.message}. Treating block as code only.`,
9323
- content: potentialHeaderText // Show the problematic header text
9324
- });
9325
- // Fall through to default case (no header)
9326
- }
9327
- }
9328
- }
9912
+ if (text.trim() === "") {
9913
+ return { blocks: [], warnings: [], hasIncompleteBlocks: false, lastIncompleteBlock: null };
9914
+ }
9329
9915
 
9330
- // Default: If no valid header was found after both attempts
9331
- if (!headerDelimiterType && !incomplete) { // Only warn about missing header if the block is complete
9332
- warnings.push({
9333
- position: position,
9334
- type: 'missing_header',
9335
- message: `No distinct header found or parsed as valid. Treating block as code only.`,
9336
- content: content.substring(0, 100) + '...' // Show beginning of content
9337
- });
9338
- }
9916
+ // Step 1: Find all fence positions
9917
+ const { openingPositions, closingPositions } = findAllCodeFences(text);
9918
+
9919
+ // Step 2: Match fences to get potential block boundaries
9920
+ const { completeBlocks, incompleteBlocks, warnings: extractorWarnings } = matchFencesAndExtractBlocks(
9921
+ text, openingPositions, closingPositions
9922
+ );
9923
+
9924
+ // Step 3: Process the content of these potential blocks
9925
+ const { processedBlocks, allWarnings: processorWarnings } = processBlockContents(
9926
+ text, completeBlocks, incompleteBlocks, options
9927
+ );
9928
+
9929
+ // Combine warnings from extractor and processor
9930
+ const allWarnings = [...extractorWarnings, ...processorWarnings];
9931
+
9932
+ // Log warnings if not silent
9933
+ if (!options.silent && allWarnings.length > 0) {
9934
+ console.warn('\nCode Block Processing Warnings:');
9935
+ allWarnings.forEach((warning, index) => {
9936
+ console.warn(`\nWarning ${index + 1}:`);
9937
+ console.warn(`Type: ${warning.type}`);
9938
+ console.warn(`Message: ${warning.message}`);
9939
+ console.warn(`Position: ${warning.position}`);
9940
+ if (warning.content) {
9941
+ // Limit logged content length
9942
+ const maxLogLength = 200;
9943
+ const loggedContent = warning.content.length > maxLogLength
9944
+ ? warning.content.substring(0, maxLogLength) + '...'
9945
+ : warning.content;
9946
+ console.warn(`Content Hint: ${loggedContent}`);
9947
+ }
9948
+ });
9949
+ }
9339
9950
 
9340
- return {
9341
- type: 'code',
9342
- language: language,
9343
- header: header,
9344
- headerText: headerText.trim(), // Include the raw header text if successfully parsed
9345
- content: codeText, // The code part after the header (or full content if no header)
9346
- position: position, // Position of the opening fence
9347
- headerDelimiterType: headerDelimiterType, // 'triple', 'double', or null
9348
- incomplete: incomplete,
9349
- warnings: warnings,
9350
- headerParseError: headerParseError // Include error message if parsing failed
9351
- };
9352
- }
9951
+ const hasIncompleteBlocks = incompleteBlocks.length > 0;
9952
+ const lastIncompleteBlock = hasIncompleteBlocks
9953
+ ? processedBlocks.find(block => block.incomplete) // Find the corresponding processed block
9954
+ : null;
9955
+
9956
+ return {
9957
+ blocks: processedBlocks,
9958
+ warnings: allWarnings,
9959
+ hasIncompleteBlocks: hasIncompleteBlocks,
9960
+ lastIncompleteBlock: lastIncompleteBlock
9961
+ };
9962
+ }
9353
9963
 
9964
+ /**
9965
+ * Public API function - maintains backward compatibility for simple extraction.
9966
+ * Use processCodeBlocks for more detailed results (warnings, incomplete status).
9967
+ * @param {string} text - The input text containing code blocks
9968
+ * @param {Object} options - Options for extraction (e.g., { silent: true })
9969
+ * @returns {Object} - Object containing extracted blocks and warnings { blocks: Array, warnings: Array }
9970
+ */
9971
+ function extractCodeBlocks(text, options = { silent: false, validatePatches: false, debug: false }) {
9972
+ const { blocks, warnings } = processCodeBlocks(text, options);
9973
+ return { blocks, warnings };
9974
+ }
9354
9975
 
9355
- /**
9356
- * Processes the content of complete and incomplete blocks (internal helper)
9357
- * @param {string} text - The input text
9358
- * @param {Array} completeBlocks - Array of complete block objects from extractor
9359
- * @param {Array} incompleteBlocks - Array of incomplete block objects from extractor
9360
- * @param {Object} options - Processing options
9361
- * @returns {Array} Array of processed block objects
9362
- */
9363
- function processBlockContents(text, completeBlocks, incompleteBlocks, options) {
9364
- const processedBlocks = [];
9365
- const allWarnings = []; // Collect warnings from all blocks
9366
9976
 
9367
- // Process complete blocks
9368
- for (const block of completeBlocks) {
9369
- try {
9370
- const startPos = block.opening.position + block.opening.length;
9371
- const endPos = block.closing.position;
9372
- // Get content, including the newlines immediately after opening and before closing fence
9373
- const contentWithNewlines = text.substring(startPos, endPos);
9374
- const content = contentWithNewlines; //.replace(/^\s*\n|\n\s*$/g, '');
9375
-
9376
- const language = block.opening.language;
9377
-
9378
- // Process block content
9379
- const processedBlock = processBlockContent(content, language, block.opening.position, false, options);
9380
- processedBlocks.push(processedBlock);
9381
- if (processedBlock.warnings) {
9382
- allWarnings.push(...processedBlock.warnings);
9383
- }
9384
- } catch (error) {
9385
- // Catch errors during the processing of a single block
9386
- allWarnings.push({
9387
- position: block.opening?.position || -1,
9388
- type: 'block_processing_error',
9389
- message: `Error processing complete block: ${error.message}`,
9390
- content: text.substring(block.opening?.position, block.closing?.position + block.closing?.length) || "Error retrieving block content"
9391
- });
9392
- }
9393
- }
9394
-
9395
- // Process incomplete blocks
9396
- for (const block of incompleteBlocks) {
9397
- try {
9398
- const startPos = block.opening.position + block.opening.length;
9399
- // Get content from opening fence to end of text
9400
- const contentWithNewline = text.substring(startPos);
9401
- // Trim only leading whitespace line
9402
- const content = contentWithNewline.replace(/^\s*\n/, '');
9403
-
9404
- const language = block.opening.language;
9405
-
9406
- // Process block content - it's expected to be incomplete
9407
- const processedBlock = processBlockContent(content, language, block.opening.position, true, options);
9408
-
9409
- // Extract continuation info for incomplete blocks
9410
- // Requires extractContinuationInfo from continuationUtils
9411
- try {
9412
- // Pass the *processed* header info to continuation utils
9413
- let { 'Continuation-Part': partNumber } = processedBlock?.header || {};
9414
- partNumber = partNumber ? parseInt(partNumber) + 1 : 2; // Default to part 2 if not specified
9415
- processedBlock.continuationInfo = extractContinuationInfo$2(processedBlock.content, partNumber, language, processedBlock.header);
9416
- } catch (continuationError) {
9417
- allWarnings.push({
9418
- position: block.opening?.position || -1,
9419
- type: 'continuation_info_error',
9420
- message: `Error extracting continuation info: ${continuationError.message}`,
9421
- content: content
9422
- });
9423
- }
9424
-
9425
-
9426
- processedBlocks.push(processedBlock);
9427
- if (processedBlock.warnings) {
9428
- allWarnings.push(...processedBlock.warnings);
9429
- }
9430
- } catch (error) {
9431
- // Catch errors during the processing of a single incomplete block
9432
- allWarnings.push({
9433
- position: block.opening?.position || -1,
9434
- type: 'incomplete_block_processing_error',
9435
- message: `Error processing incomplete block: ${error.message}`,
9436
- content: text.substring(block.opening?.position) || "Error retrieving block content"
9437
- });
9438
- }
9439
- }
9440
-
9441
- // Return processed blocks and collected warnings
9442
- // Note: The caller (processCodeBlocks) will handle adding these warnings to its own return object.
9443
- return { processedBlocks, allWarnings };
9444
- }
9445
-
9446
-
9447
- /**
9448
- * Core function to process code blocks with shared logic
9449
- * @param {string} text - The input text containing code blocks
9450
- * @param {Object} options - Processing options (e.g., silent, validatePatches)
9451
- * @returns {Object} { blocks: Array, warnings: Array, hasIncompleteBlocks: boolean, lastIncompleteBlock: Object|null }
9452
- */
9453
- function processCodeBlocks$5(text, options = { silent: false, validatePatches: false }) {
9454
- if (typeof text !== "string") { // Allow empty strings
9455
- console.warn("Warning: Input must be a string.");
9456
- return {
9457
- blocks: [],
9458
- warnings: [{ type: 'invalid_input', message: 'Input must be a string.' }],
9459
- hasIncompleteBlocks: false,
9460
- lastIncompleteBlock: null }
9461
- ;
9462
- }
9463
-
9464
- if (text.trim() === "") {
9465
- return { blocks: [], warnings: [], hasIncompleteBlocks: false, lastIncompleteBlock: null };
9466
- }
9467
-
9468
- // Step 1: Find all fence positions
9469
- const { openingPositions, closingPositions } = findAllCodeFences$3(text);
9470
-
9471
- // Step 2: Match fences to get potential block boundaries
9472
- const { completeBlocks, incompleteBlocks, warnings: extractorWarnings } = matchFencesAndExtractBlocks$3(
9473
- text, openingPositions, closingPositions
9474
- );
9475
-
9476
- // Step 3: Process the content of these potential blocks
9477
- const { processedBlocks, allWarnings: processorWarnings } = processBlockContents(
9478
- text, completeBlocks, incompleteBlocks, options
9479
- );
9480
-
9481
- // Combine warnings from extractor and processor
9482
- const allWarnings = [...extractorWarnings, ...processorWarnings];
9483
-
9484
- // Log warnings if not silent
9485
- if (!options.silent && allWarnings.length > 0) {
9486
- console.warn('\nCode Block Processing Warnings:');
9487
- allWarnings.forEach((warning, index) => {
9488
- console.warn(`\nWarning ${index + 1}:`);
9489
- console.warn(`Type: ${warning.type}`);
9490
- console.warn(`Message: ${warning.message}`);
9491
- console.warn(`Position: ${warning.position}`);
9492
- if (warning.content) {
9493
- // Limit logged content length
9494
- const maxLogLength = 200;
9495
- const loggedContent = warning.content.length > maxLogLength
9496
- ? warning.content.substring(0, maxLogLength) + '...'
9497
- : warning.content;
9498
- console.warn(`Content Hint: ${loggedContent}`);
9499
- }
9500
- });
9501
- }
9502
-
9503
- const hasIncompleteBlocks = incompleteBlocks.length > 0;
9504
- const lastIncompleteBlock = hasIncompleteBlocks
9505
- ? processedBlocks.find(block => block.incomplete) // Find the corresponding processed block
9506
- : null;
9507
-
9508
- return {
9509
- blocks: processedBlocks,
9510
- warnings: allWarnings,
9511
- hasIncompleteBlocks: hasIncompleteBlocks,
9512
- lastIncompleteBlock: lastIncompleteBlock
9513
- };
9514
- }
9515
-
9516
- /**
9517
- * Public API function - maintains backward compatibility for simple extraction.
9518
- * Use processCodeBlocks for more detailed results (warnings, incomplete status).
9519
- * @param {string} text - The input text containing code blocks
9520
- * @param {Object} options - Options for extraction (e.g., { silent: true })
9521
- * @returns {Object} - Object containing extracted blocks and warnings { blocks: Array, warnings: Array }
9522
- */
9523
- function extractCodeBlocks$2(text, options = { silent: false, validatePatches: false, debug: false }) {
9524
- const { blocks, warnings } = processCodeBlocks$5(text, options);
9525
- return { blocks, warnings };
9526
- }
9527
-
9528
-
9529
- /**
9530
- * Fixes invalid UUIDs in code blocks within text
9531
- * @param {string} text - The input text containing code blocks
9532
- * @returns {Object} - Object containing fixed text and whether changes were made { text: string, modified: boolean }
9533
- */
9534
- function fixTextCodeBlocks$2(text) {
9535
- if (typeof text !== "string" || text.trim() === "") {
9536
- return {
9537
- text: text,
9538
- modified: false
9539
- };
9540
- }
9541
-
9542
- let modified = false;
9543
- let fixedText = text;
9544
-
9545
- // Regex to match code blocks with their language
9546
- const codeBlockRegex = /```([a-zA-Z-]*)\n([\s\S]*?)\n```/g;
9547
-
9548
- // Store all matches to process in reverse
9549
- const matches = Array.from(text.matchAll(codeBlockRegex));
9550
-
9551
- // Process matches in reverse to maintain correct positions
9552
- for (let i = matches.length - 1; i >= 0; i--) {
9553
- const match = matches[i];
9554
- const fullMatch = match[0];
9555
- const language = match[1];
9556
- const blockContent = match[2];
9557
-
9558
- // Check if this is a patch block or a gitsense-search-flow block (if they contain UUIDs)
9559
- if (PatchUtils$1.isPatchBlock(blockContent)) {
9560
- // Process patch block UUIDs
9561
- const lines = blockContent.split('\n');
9562
- let blockModified = false;
9563
-
9564
- const newLines = lines.map(line => {
9565
- // Check for UUID fields in patch metadata
9566
- if ((line.includes('# Source-Block-UUID:') || line.includes('# Target-Block-UUID:')) &&
9567
- !line.includes('{{GS-UUID}')) {
9568
-
9569
- const [fieldPart, valuePart] = line.split(':').map(p => p.trim());
9570
-
9571
- // Validate UUID
9572
- const validation = validateUUID$2(valuePart);
9573
- if (validation["Block-UUID"] === "INVALID UUID") {
9574
- blockModified = true;
9575
- modified = true;
9576
- return `${fieldPart}: ${validation["Correct Block-UUID"]}`;
9577
- }
9578
- }
9579
- return line;
9580
- });
9977
+ /**
9978
+ * Fixes invalid UUIDs in code blocks within text
9979
+ * @param {string} text - The input text containing code blocks
9980
+ * @returns {Object} - Object containing fixed text and whether changes were made { text: string, modified: boolean }
9981
+ */
9982
+ function fixTextCodeBlocks(text) {
9983
+ if (typeof text !== "string" || text.trim() === "") {
9984
+ return {
9985
+ text: text,
9986
+ modified: false
9987
+ };
9988
+ }
9581
9989
 
9582
- // If this block was modified, replace it in the text
9583
- if (blockModified) {
9584
- const newBlock = `\`\`\`${language}\n${newLines.join('\n')}\n\`\`\``;
9585
- fixedText = fixedText.substring(0, match.index) +
9586
- newBlock +
9587
- fixedText.substring(match.index + fullMatch.length);
9588
- }
9589
- } else {
9590
- // Process regular code block or gitsense-search-flow block for UUIDs
9591
- // Split block content into lines to check for header/UUIDs
9592
- const lines = blockContent.split('\n');
9593
- let blockModified = false;
9594
-
9595
- // Process each line
9596
- const newLines = lines.map(line => {
9597
- // Check for UUID fields
9598
- if (line.includes(' Block-UUID: ') || line.includes(' Parent-UUID: ')) {
9599
- const [fieldPart, valuePart] = line.split(':').map(p => p.trim());
9600
-
9601
- // Skip if N/A or contains unexpected characters
9602
- if (valuePart === 'N/A' || valuePart.match(/\(/)) {
9603
- return line;
9604
- }
9605
- // Validate UUID
9606
- const validation = validateUUID$2(valuePart);
9990
+ let modified = false;
9991
+ let fixedText = text;
9992
+
9993
+ // Regex to match code blocks with their language
9994
+ const codeBlockRegex = /```([a-zA-Z-]*)\n([\s\S]*?)\n```/g;
9995
+
9996
+ // Store all matches to process in reverse
9997
+ const matches = Array.from(text.matchAll(codeBlockRegex));
9998
+
9999
+ // Process matches in reverse to maintain correct positions
10000
+ for (let i = matches.length - 1; i >= 0; i--) {
10001
+ const match = matches[i];
10002
+ const fullMatch = match[0];
10003
+ const language = match[1];
10004
+ const blockContent = match[2];
10005
+
10006
+ // Check if this is a patch block or a gitsense-search-flow block (if they contain UUIDs)
10007
+ if (PatchUtils.isPatchBlock(blockContent)) {
10008
+ // Process patch block UUIDs
10009
+ const lines = blockContent.split('\n');
10010
+ let blockModified = false;
10011
+
10012
+ const newLines = lines.map(line => {
10013
+ // Check for UUID fields in patch metadata
10014
+ if ((line.includes('# Source-Block-UUID:') || line.includes('# Target-Block-UUID:')) &&
10015
+ !line.includes('{{GS-UUID}')) {
10016
+
10017
+ const [fieldPart, valuePart] = line.split(':').map(p => p.trim());
10018
+
10019
+ // Validate UUID
10020
+ const validation = validateUUID(valuePart);
10021
+ if (validation["Block-UUID"] === "INVALID UUID") {
10022
+ blockModified = true;
10023
+ modified = true;
10024
+ return `${fieldPart}: ${validation["Correct Block-UUID"]}`;
10025
+ }
10026
+ }
10027
+ return line;
10028
+ });
10029
+
10030
+ // If this block was modified, replace it in the text
10031
+ if (blockModified) {
10032
+ const newBlock = `\`\`\`${language}\n${newLines.join('\n')}\n\`\`\``;
10033
+ fixedText = fixedText.substring(0, match.index) +
10034
+ newBlock +
10035
+ fixedText.substring(match.index + fullMatch.length);
10036
+ }
10037
+ } else {
10038
+ // Process regular code block or gitsense-search-flow block for UUIDs
10039
+ // Split block content into lines to check for header/UUIDs
10040
+ const lines = blockContent.split('\n');
10041
+ let blockModified = false;
10042
+
10043
+ // Process each line
10044
+ const newLines = lines.map(line => {
10045
+ // Check for UUID fields
10046
+ if (line.includes(' Block-UUID: ') || line.includes(' Parent-UUID: ')) {
10047
+ const [fieldPart, valuePart] = line.split(':').map(p => p.trim());
10048
+
10049
+ // Skip if N/A or contains unexpected characters
10050
+ if (valuePart === 'N/A' || valuePart.match(/\(/)) {
10051
+ return line;
10052
+ }
10053
+ // Validate UUID
10054
+ const validation = validateUUID(valuePart);
10055
+
10056
+ if (validation["Block-UUID"] === "INVALID UUID") {
10057
+ blockModified = true;
10058
+ modified = true;
10059
+ return `${fieldPart}: ${validation["Correct Block-UUID"]}`;
10060
+ }
10061
+ }
10062
+ return line;
10063
+ });
10064
+
10065
+ // If this block was modified, replace it in the text
10066
+ if (blockModified) {
10067
+ const newBlock = `\`\`\`${language}\n${newLines.join('\n')}\n\`\`\``;
10068
+ fixedText = fixedText.substring(0, match.index) +
10069
+ newBlock +
10070
+ fixedText.substring(match.index + fullMatch.length);
10071
+ }
10072
+ }
10073
+ }
9607
10074
 
9608
- if (validation["Block-UUID"] === "INVALID UUID") {
9609
- blockModified = true;
9610
- modified = true;
9611
- return `${fieldPart}: ${validation["Correct Block-UUID"]}`;
9612
- }
9613
- }
9614
- return line;
9615
- });
10075
+ return {
10076
+ text: fixedText,
10077
+ modified: modified
10078
+ };
10079
+ }
9616
10080
 
9617
- // If this block was modified, replace it in the text
9618
- if (blockModified) {
9619
- const newBlock = `\`\`\`${language}\n${newLines.join('\n')}\n\`\`\``;
9620
- fixedText = fixedText.substring(0, match.index) +
9621
- newBlock +
9622
- fixedText.substring(match.index + fullMatch.length);
9623
- }
9624
- }
9625
- }
9626
10081
 
9627
- return {
9628
- text: fixedText,
9629
- modified: modified
9630
- };
10082
+ blockProcessor = {
10083
+ processCodeBlocks,
10084
+ extractCodeBlocks,
10085
+ fixTextCodeBlocks,
10086
+ };
10087
+ return blockProcessor;
9631
10088
  }
9632
10089
 
9633
-
9634
- var blockProcessor = {
9635
- processCodeBlocks: processCodeBlocks$5,
9636
- extractCodeBlocks: extractCodeBlocks$2,
9637
- fixTextCodeBlocks: fixTextCodeBlocks$2,
9638
- };
9639
-
9640
10090
  /**
9641
10091
  * Component: CodeBlockUtils Patch Integration
9642
10092
  * Block-UUID: 9f7395ef-e213-4841-a95d-f263e422288a
@@ -9649,7 +10099,7 @@ var blockProcessor = {
9649
10099
  */
9650
10100
 
9651
10101
  // Dependency on the core block processor
9652
- const { processCodeBlocks: processCodeBlocks$4 } = blockProcessor;
10102
+ const { processCodeBlocks: processCodeBlocks$3 } = requireBlockProcessor();
9653
10103
 
9654
10104
  /**
9655
10105
  * Checks if the provided text content contains at least one patch block.
@@ -9659,7 +10109,7 @@ const { processCodeBlocks: processCodeBlocks$4 } = blockProcessor;
9659
10109
  */
9660
10110
  function containsPatch$2(content, options = { silent: true }) {
9661
10111
  // Use the core processor to get all blocks
9662
- const { blocks } = processCodeBlocks$4(content, options);
10112
+ const { blocks } = processCodeBlocks$3(content, options);
9663
10113
 
9664
10114
  // Check if any processed block is of type 'patch'
9665
10115
  for (let i = 0; i < blocks.length; i++) {
@@ -9687,7 +10137,7 @@ var patchIntegration = {
9687
10137
  */
9688
10138
 
9689
10139
  // Dependency on the core block processor
9690
- const { processCodeBlocks: processCodeBlocks$3 } = blockProcessor;
10140
+ const { processCodeBlocks: processCodeBlocks$2 } = requireBlockProcessor();
9691
10141
 
9692
10142
  /**
9693
10143
  * Detects special code block relationships in a message, such as patches or parent-child links.
@@ -9721,7 +10171,7 @@ function detectCodeBlockRelationships$2(content, codeBlockService, options = { s
9721
10171
 
9722
10172
 
9723
10173
  // Extract all code blocks using the core processor
9724
- const { blocks, warnings } = processCodeBlocks$3(content, options);
10174
+ const { blocks, warnings } = processCodeBlocks$2(content, options);
9725
10175
 
9726
10176
  let hasPatch = false;
9727
10177
  let hasParentChildRelationship = false;
@@ -9795,7 +10245,7 @@ function detectIncompleteCodeBlock$2(content, options = { silent: true }) {
9795
10245
  }
9796
10246
 
9797
10247
  // Use the core processor which already identifies incomplete blocks
9798
- const { hasIncompleteBlocks, lastIncompleteBlock } = processCodeBlocks$3(content, options);
10248
+ const { hasIncompleteBlocks, lastIncompleteBlock } = processCodeBlocks$2(content, options);
9799
10249
 
9800
10250
  return {
9801
10251
  hasIncompleteBlocks,
@@ -10098,281 +10548,6 @@ Plain text block.
10098
10548
  // (Removed the check `if (typeof module !== 'undefined' && module.exports)` as it's standard in Node)
10099
10549
  var markerRemover = { removeCodeBlockMarkers: removeCodeBlockMarkers$2 };
10100
10550
 
10101
- /**
10102
- * Component: CodeBlockUtils Update Code Block
10103
- * Block-UUID: 6a7b8c9d-0e1f-42a3-b4c5-d6e7f8a9b0c1
10104
- * Parent-UUID: 5f9c1e3a-7b2d-4e8f-a1d0-9c4b2e1f8a0b
10105
- * Version: 1.4.0
10106
- * Description: Provides functions to update the content of a specific code block within a string, identified by index, UUID, or direct reference.
10107
- * Language: JavaScript
10108
- * Created-at: 2025-04-16T00:56:57.352Z
10109
- * 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 Pro (v1.3.0), Gemini 2.5 Flash Thinking (v1.4.0)
10110
- */
10111
-
10112
- // Dependencies from other modules within CodeBlockUtils
10113
- const { findAllCodeFences: findAllCodeFences$2, matchFencesAndExtractBlocks: matchFencesAndExtractBlocks$2, findCodeBlockByUUID: findCodeBlockByUUID$1 } = blockExtractor; // Added findCodeBlockByUUID
10114
- const { processCodeBlocks: processCodeBlocks$2 } = blockProcessor; // To get parsed block info
10115
-
10116
- /**
10117
- * Replaces the content of a code block specified by its index within a message string.
10118
- *
10119
- * @param {string} messageContent - The original message string containing code blocks.
10120
- * @param {number} blockIndex - The 0-based index of the code block to replace.
10121
- * @param {string} newCodeContent - The new raw content (code, potentially including header) to insert into the block.
10122
- * @param {string} [language] - Optional: The language identifier for the updated code block fence. If omitted, the original language is preserved.
10123
- * @returns {string} The message content with the specified code block updated.
10124
- * @throws {Error} If the blockIndex is out of bounds or if block boundaries cannot be determined.
10125
- */
10126
- function updateCodeBlockByIndex$2(messageContent, blockIndex, newCodeContent, language = undefined) {
10127
- if (typeof messageContent !== 'string') {
10128
- throw new Error("messageContent must be a string.");
10129
- }
10130
- if (typeof blockIndex !== 'number' || blockIndex < 0) {
10131
- throw new Error("blockIndex must be a non-negative number.");
10132
- }
10133
- if (typeof newCodeContent !== 'string') {
10134
- // Allow empty string replacement, but not other types
10135
- throw new Error("newCodeContent must be a string.");
10136
- }
10137
-
10138
- // Step 1: Find all fence positions to get accurate start/end of the full block
10139
- const { openingPositions, closingPositions } = findAllCodeFences$2(messageContent);
10140
- const { completeBlocks, incompleteBlocks, warnings: extractorWarnings } = matchFencesAndExtractBlocks$2(
10141
- messageContent, openingPositions, closingPositions
10142
- );
10143
-
10144
- // Combine complete and incomplete for indexing, assuming we might want to update an incomplete one too
10145
- // Note: Replacing content in an incomplete block might be unusual, but technically possible.
10146
- const allBlocks = [...completeBlocks, ...incompleteBlocks];
10147
-
10148
- // Step 2: Validate index
10149
- if (blockIndex >= allBlocks.length) {
10150
- throw new Error(`blockIndex ${blockIndex} is out of bounds. Found ${allBlocks.length} blocks.`);
10151
- }
10152
-
10153
- // Step 3: Get the target block's boundary information
10154
- const targetBlockBoundaries = allBlocks[blockIndex];
10155
- const openingFence = targetBlockBoundaries.opening;
10156
- const closingFence = targetBlockBoundaries.closing; // Will be undefined for incomplete blocks
10157
-
10158
- if (!openingFence) {
10159
- // Should not happen if index is valid, but good practice to check
10160
- throw new Error(`Could not find opening fence for block at index ${blockIndex}.`);
10161
- }
10162
-
10163
- // Determine the exact start and end of the full markdown block
10164
- const blockStartPos = openingFence.position;
10165
- // If the block is incomplete, the end is the end of the messageContent
10166
- const blockEndPos = closingFence
10167
- ? closingFence.position + closingFence.length
10168
- : messageContent.length;
10169
-
10170
- // Step 4: Construct the replacement block string
10171
- const targetLanguage = language !== undefined ? language : (openingFence.language || ''); // Use provided language or original/empty
10172
- const newBlockString = `\`\`\`${targetLanguage}\n${newCodeContent}\n\`\`\``;
10173
-
10174
- // Step 5: Perform the replacement
10175
- const updatedMessageContent =
10176
- messageContent.substring(0, blockStartPos) +
10177
- newBlockString +
10178
- messageContent.substring(blockEndPos);
10179
-
10180
- return updatedMessageContent;
10181
- }
10182
-
10183
- /**
10184
- * Replaces the content of a code block specified by its Block-UUID within a message string.
10185
- *
10186
- * @param {string} messageContent - The original message string containing code blocks.
10187
- * @param {string} blockUUID - The Block-UUID of the code block to replace.
10188
- * @param {string} newCodeContent - The new raw content (code, potentially including header) to insert into the block.
10189
- * @param {string} [language] - Optional: The language identifier for the updated code block fence. If omitted, the original language is preserved.
10190
- * @returns {string} The message content with the specified code block updated.
10191
- * @throws {Error} If no block with the specified UUID is found, or if multiple blocks have the same UUID.
10192
- */
10193
- function updateCodeBlockByUUID$1(messageContent, blockUUID, newCodeContent, language = undefined) {
10194
- if (typeof messageContent !== 'string') {
10195
- throw new Error("messageContent must be a string.");
10196
- }
10197
- if (typeof blockUUID !== 'string' || !blockUUID) {
10198
- throw new Error("blockUUID must be a non-empty string.");
10199
- }
10200
- if (typeof newCodeContent !== 'string') {
10201
- throw new Error("newCodeContent must be a string.");
10202
- }
10203
-
10204
- // Step 1: Process blocks to get headers and find the target index
10205
- const { blocks: processedBlocks, warnings } = processCodeBlocks$2(messageContent, { silent: true });
10206
-
10207
- let targetIndex = -1;
10208
-
10209
- for (let i = 0; i < processedBlocks.length; i++) {
10210
- const block = processedBlocks[i];
10211
- let match = false;
10212
-
10213
- // Check standard code blocks by Block-UUID
10214
- if ((block.type === 'code') && block.header && block.header['Block-UUID'] === blockUUID) {
10215
- match = true;
10216
- }
10217
- // Check patch blocks by Target-Block-UUID
10218
- else if (block.type === 'patch' && block.metadata && block.metadata['Target-Block-UUID'] === blockUUID) {
10219
- match = true;
10220
- }
10221
-
10222
- if (match) {
10223
- // If we've already found one, log a warning but still update the first one found.
10224
- if (targetIndex !== -1) {
10225
- console.warn(`Multiple blocks found matching UUID: ${blockUUID} (could be Block-UUID or Target-Block-UUID). Updating the first occurrence.`);
10226
- } else {
10227
- targetIndex = i;
10228
- }
10229
- }
10230
- }
10231
-
10232
- // Step 2: Handle findings
10233
- if (targetIndex === -1) { // Use targetIndex to check if *any* valid match was set
10234
- throw new Error(`No code or patch block found matching UUID: ${blockUUID} (checked Block-UUID and Target-Block-UUID).`);
10235
- }
10236
- // Warning for multiple matches is handled inside the loop now.
10237
-
10238
- // Step 3: Call the index-based function
10239
- // We need the index relative to *all* blocks (complete and incomplete), not just processed ones.
10240
- // Re-extract boundaries to get the correct index mapping.
10241
- const { openingPositions, closingPositions } = findAllCodeFences$2(messageContent);
10242
- const { completeBlocks, incompleteBlocks } = matchFencesAndExtractBlocks$2(
10243
- messageContent, openingPositions, closingPositions
10244
- );
10245
- const allBlocks = [...completeBlocks, ...incompleteBlocks];
10246
-
10247
- // Find the index in the combined list that corresponds to the processed block's position
10248
- const targetProcessedBlock = processedBlocks[targetIndex];
10249
- const actualIndex = allBlocks.findIndex(b => b.opening.position === targetProcessedBlock.position);
10250
-
10251
- if (actualIndex === -1) {
10252
- // This indicates a discrepancy between blockProcessor and blockExtractor results, which shouldn't happen.
10253
- throw new Error(`Internal error: Could not map processed block at index ${targetIndex} (matching UUID: ${blockUUID}) back to extracted block boundaries.`);
10254
- }
10255
-
10256
- // Pass the optional language parameter down
10257
- return updateCodeBlockByIndex$2(messageContent, actualIndex, newCodeContent, language);
10258
- }
10259
-
10260
- /**
10261
- * Updates a code block in message text identified by UUID.
10262
- * (Moved from original PatchUtils)
10263
- * @param {string} messageText - The original message text
10264
- * @param {string} blockUUID - The Block-UUID to update
10265
- * @param {string} newCode - The new code content (raw content, including header if applicable)
10266
- * @param {string} language - The code language for the fence
10267
- * @returns {string} Updated message text
10268
- * @throws {Error} If block not found or parameters missing.
10269
- */
10270
- function updateCodeBlockInMessage$1(messageText, blockUUID, newCode, language) {
10271
- if (!messageText || !blockUUID || newCode === null || newCode === undefined) { // Allow empty string for newCode
10272
- throw new Error("Missing required parameters for updating code block (messageText, blockUUID, newCode)");
10273
- }
10274
-
10275
- // Use findCodeBlockByUUID from blockExtractor
10276
- const block = findCodeBlockByUUID$1(messageText, blockUUID);
10277
-
10278
- if (!block) {
10279
- throw new Error(`Code block with UUID ${blockUUID} not found`);
10280
- }
10281
-
10282
- // Construct the new full block string
10283
- const newBlockString = '```' + (language || block.language) + '\n' + newCode + '\n```';
10284
-
10285
- // Replace the old block with the new one using precise indices
10286
- return messageText.substring(0, block.startIndex) +
10287
- newBlockString +
10288
- messageText.substring(block.endIndex);
10289
- }
10290
-
10291
-
10292
- /**
10293
- * Generic router function to update a code block by index or UUID.
10294
- *
10295
- * @param {string} messageContent - The original message string.
10296
- * @param {number|string} identifier - The block index (number) or Block-UUID (string).
10297
- * @param {string} newCodeContent - The new raw content for the block.
10298
- * @param {string} [language] - Optional: The language identifier for the updated code block fence.
10299
- * @returns {string} The updated message content.
10300
- * @throws {Error} If the identifier type is invalid or if the underlying update function fails.
10301
- */
10302
- function updateCodeBlock$1(messageContent, identifier, newCodeContent, language = undefined) {
10303
- if (typeof identifier === 'number') {
10304
- // Pass language to index-based function
10305
- return updateCodeBlockByIndex$2(messageContent, identifier, newCodeContent, language);
10306
- } else if (typeof identifier === 'string') {
10307
- // Pass language to UUID-based function
10308
- return updateCodeBlockByUUID$1(messageContent, identifier, newCodeContent, language);
10309
- } else {
10310
- throw new Error("Invalid identifier type. Must be a number (index) or a string (UUID).");
10311
- }
10312
- }
10313
-
10314
- /**
10315
- * Deletes a code block specified by its index within a message string.
10316
- *
10317
- * @param {string} messageContent - The original message string containing code blocks.
10318
- * @param {number} blockIndex - The 0-based index of the code block to delete.
10319
- * @returns {string} The message content with the specified code block deleted.
10320
- * @throws {Error} If the blockIndex is out of bounds or if block boundaries cannot be determined.
10321
- */
10322
- function deleteCodeBlockByIndex$2(messageContent, blockIndex) {
10323
- if (typeof messageContent !== 'string') {
10324
- throw new Error("messageContent must be a string.");
10325
- }
10326
- if (typeof blockIndex !== 'number' || blockIndex < 0) {
10327
- throw new Error("blockIndex must be a non-negative number.");
10328
- }
10329
-
10330
- // Step 1: Find all fence positions to get accurate start/end of the full block
10331
- const { openingPositions, closingPositions } = findAllCodeFences$2(messageContent);
10332
- const { completeBlocks, incompleteBlocks } = matchFencesAndExtractBlocks$2(
10333
- messageContent, openingPositions, closingPositions
10334
- );
10335
-
10336
- // Combine complete and incomplete for indexing
10337
- const allBlocks = [...completeBlocks, ...incompleteBlocks];
10338
-
10339
- // Step 2: Validate index
10340
- if (blockIndex >= allBlocks.length) {
10341
- throw new Error(`blockIndex ${blockIndex} is out of bounds. Found ${allBlocks.length} blocks.`);
10342
- }
10343
-
10344
- // Step 3: Get the target block's boundary information
10345
- const targetBlockBoundaries = allBlocks[blockIndex];
10346
- const openingFence = targetBlockBoundaries.opening;
10347
- const closingFence = targetBlockBoundaries.closing; // Will be undefined for incomplete blocks
10348
-
10349
- if (!openingFence) {
10350
- // Should not happen if index is valid, but good practice to check
10351
- throw new Error(`Could not find opening fence for block at index ${blockIndex}.`);
10352
- }
10353
-
10354
- // Determine the exact start and end of the full markdown block
10355
- const blockStartPos = openingFence.position;
10356
- // If the block is incomplete, the end is the end of the messageContent
10357
- const blockEndPos = closingFence
10358
- ? closingFence.position + closingFence.length
10359
- : messageContent.length;
10360
-
10361
- // Step 4: Perform the deletion
10362
- const updatedMessageContent =
10363
- messageContent.substring(0, blockStartPos) +
10364
- messageContent.substring(blockEndPos);
10365
-
10366
- return updatedMessageContent;
10367
- }
10368
-
10369
- var updateCodeBlock_1 = {
10370
- updateCodeBlockByIndex: updateCodeBlockByIndex$2,
10371
- updateCodeBlockByUUID: updateCodeBlockByUUID$1,
10372
- updateCodeBlock: updateCodeBlock$1,
10373
- updateCodeBlockInMessage: updateCodeBlockInMessage$1,
10374
- deleteCodeBlockByIndex: deleteCodeBlockByIndex$2};
10375
-
10376
10551
  /**
10377
10552
  * Component: CodeBlockUtils Index
10378
10553
  * Block-UUID: 7e9d3f8a-1b2c-4d5e-8f9a-0123456789ab
@@ -10389,13 +10564,13 @@ const { COMMENT_STYLES: COMMENT_STYLES$1 } = constants$2;
10389
10564
  const { generateUUID: generateUUID$1, validateUUID: validateUUID$1 } = uuidUtils;
10390
10565
  const { isValidISOTimestamp: isValidISOTimestamp$1, parseHeader: parseHeader$1, getHeaderLineCount } = headerUtils;
10391
10566
  const { findAllCodeFences: findAllCodeFences$1, matchFencesAndExtractBlocks: matchFencesAndExtractBlocks$1, extractCodeBlocksWithUUIDs, findCodeBlockByUUID } = blockExtractor;
10392
- const { processCodeBlocks: processCodeBlocks$1, extractCodeBlocks: extractCodeBlocks$1, fixTextCodeBlocks: fixTextCodeBlocks$1 } = blockProcessor;
10567
+ const { processCodeBlocks: processCodeBlocks$1, extractCodeBlocks: extractCodeBlocks$1, fixTextCodeBlocks: fixTextCodeBlocks$1 } = requireBlockProcessor();
10393
10568
  const { containsPatch: containsPatch$1 } = patchIntegration;
10394
10569
  const { detectCodeBlockRelationships: detectCodeBlockRelationships$1, detectIncompleteCodeBlock: detectIncompleteCodeBlock$1, extractFilePaths: extractFilePaths$1 } = relationshipUtils;
10395
10570
  const { extractContinuationInfo: extractContinuationInfo$1, generateContinuationPrompt: generateContinuationPrompt$1 } = continuationUtils;
10396
10571
  const { parseCodeBlocks: parseCommentDelimitedBlocks$1 } = headerParser;
10397
10572
  const { removeCodeBlockMarkers: removeCodeBlockMarkers$1 } = markerRemover;
10398
- const { updateCodeBlockByIndex: updateCodeBlockByIndex$1, updateCodeBlockByUUID, updateCodeBlock, updateCodeBlockInMessage, deleteCodeBlockByIndex: deleteCodeBlockByIndex$1 } = updateCodeBlock_1;
10573
+ const { updateCodeBlockByIndex: updateCodeBlockByIndex$1, updateCodeBlockByUUID, updateCodeBlock, updateCodeBlockInMessage, deleteCodeBlockByIndex: deleteCodeBlockByIndex$1 } = requireUpdateCodeBlock();
10399
10574
  const { formatWithLineNumbers: formatWithLineNumbers$1, formatBlockWithLineNumbers: formatBlockWithLineNumbers$1, formatBlocksWithLineNumbers: formatBlocksWithLineNumbers$1, removeLineNumbers: removeLineNumbers$1 } = lineNumberFormatter;
10400
10575
 
10401
10576
  // Export all imported items
@@ -10872,7 +11047,7 @@ var constants = {
10872
11047
  */
10873
11048
 
10874
11049
  const CodeBlockUtils$2 = CodeBlockUtils$4;
10875
- const GSToolBlockUtils$1 = GSToolBlockUtils$3;
11050
+ const GSToolBlockUtils$1 = requireGSToolBlockUtils();
10876
11051
  const JsonUtils$1 = JsonUtils$2;
10877
11052
  const { ANALYZE_HEADER_PREFIX } = constants;
10878
11053
 
@@ -11001,7 +11176,7 @@ var responseProcessor = {
11001
11176
  * Authors: Gemini 2.5 Flash (v1.0.0)
11002
11177
  */
11003
11178
 
11004
- const AnalysisBlockUtils$1 = AnalysisBlockUtils$3;
11179
+ const AnalysisBlockUtils$1 = AnalysisBlockUtils$2;
11005
11180
 
11006
11181
  /**
11007
11182
  * Validates the extracted analysis blocks and their metadata.
@@ -11072,11 +11247,11 @@ var dataValidator = {
11072
11247
  };
11073
11248
 
11074
11249
  /*
11075
- * Component: AnalyzerUtils Discovery
11076
- * Block-UUID: 0b1c2d3e-4f5a-6b7c-8d9e-0f1a2b3c4d5f
11250
+ * Component: AnalyzerUtils Instruction Loader
11251
+ * Block-UUID: 0a1b2c3d-4e5f-6a7b-8c9d-0e1f2a3b4c5e
11077
11252
  * Parent-UUID: N/A
11078
11253
  * Version: 1.0.0
11079
- * Description: Provides utility functions for discovering available analyzers.
11254
+ * Description: Provides utility functions for loading raw analyzer instruction content.
11080
11255
  * Language: JavaScript
11081
11256
  * Created-at: 2025-08-28T23:48:00.000Z
11082
11257
  * Authors: Gemini 2.5 Flash (v1.0.0)
@@ -11085,6 +11260,67 @@ var dataValidator = {
11085
11260
  const fs$7 = require$$0.promises;
11086
11261
  const path$5 = require$$1;
11087
11262
 
11263
+ /**
11264
+ * Retrieves the raw Markdown content of the analyzer's '1.md' instruction file.
11265
+ *
11266
+ * @param {string} analyzeMessagesBasePath - The absolute path to the base directory containing the analyzer message files (e.g., 'messages/analyze').
11267
+ * @param {string} analyzerId - The unique ID of the analyzer (format: 'analyzer_name::content_type::instructions_type').
11268
+ * @returns {Promise<string|null>} A promise that resolves with the full Markdown content of the '1.md' file, or null if not found/invalid.
11269
+ */
11270
+ async function getAnalyzerInstructionsContent$3(analyzeMessagesBasePath, analyzerId) {
11271
+ if (typeof analyzeMessagesBasePath !== 'string' || analyzeMessagesBasePath.trim() === '') {
11272
+ console.error('Error: analyzeMessagesBasePath is required.');
11273
+ return null;
11274
+ }
11275
+ if (typeof analyzerId !== 'string' || analyzerId.trim() === '') {
11276
+ console.error('Error: analyzerId is required.');
11277
+ return null;
11278
+ }
11279
+
11280
+ const parts = analyzerId.split('::');
11281
+ if (parts.length !== 3) {
11282
+ console.error(`Error: Invalid analyzerId format. Expected 'analyzer_name::content_type::instructions_type', but got '${analyzerId}'.`);
11283
+ return null;
11284
+ }
11285
+ const [analyzerName, contentType, instructionsType] = parts;
11286
+
11287
+ const instructionsFilePath = path$5.join(analyzeMessagesBasePath, analyzerName, contentType, instructionsType, '1.md');
11288
+
11289
+ try {
11290
+ const fileContent = await fs$7.readFile(instructionsFilePath, 'utf8');
11291
+ const parts = fileContent.split('\n\n\n');
11292
+ parts.shift();
11293
+ return parts.join('\n\n\n');
11294
+ } catch (error) {
11295
+ if (error.code === 'ENOENT') {
11296
+ console.warn(`Analyzer instructions file not found: ${instructionsFilePath}`);
11297
+ return null;
11298
+ } else {
11299
+ console.error(`Error reading analyzer instructions file ${instructionsFilePath}: ${error.message}`);
11300
+ throw error;
11301
+ }
11302
+ }
11303
+ }
11304
+
11305
+ var instructionLoader = {
11306
+ getAnalyzerInstructionsContent: getAnalyzerInstructionsContent$3
11307
+ };
11308
+
11309
+ /*
11310
+ * Component: AnalyzerUtils Discovery
11311
+ * Block-UUID: a87a86c4-69fc-4cbb-80a8-857f56122395
11312
+ * Parent-UUID: 0b1c2d3e-4f5a-6b7c-8d9e-0f1a2b3c4d5f
11313
+ * Version: 1.1.0
11314
+ * Description: Provides utility functions for discovering available analyzers.
11315
+ * Language: JavaScript
11316
+ * Created-at: 2025-08-28T23:48:00.000Z
11317
+ * Authors: Gemini 2.5 Flash (v1.0.0), Gemini 2.5 Flash (v1.1.0)
11318
+ */
11319
+
11320
+ const fs$6 = require$$0.promises;
11321
+ const path$4 = require$$1;
11322
+ const { getAnalyzerInstructionsContent: getAnalyzerInstructionsContent$2 } = instructionLoader;
11323
+
11088
11324
  /**
11089
11325
  * Reads and parses the config.json file in a directory.
11090
11326
  * @param {string} dirPath - The path to the directory.
@@ -11092,9 +11328,9 @@ const path$5 = require$$1;
11092
11328
  * or null if the file doesn't exist or is invalid.
11093
11329
  */
11094
11330
  async function readConfig$1(dirPath) {
11095
- const configPath = path$5.join(dirPath, 'config.json');
11331
+ const configPath = path$4.join(dirPath, 'config.json');
11096
11332
  try {
11097
- const fileContent = await fs$7.readFile(configPath, 'utf8');
11333
+ const fileContent = await fs$6.readFile(configPath, 'utf8');
11098
11334
  return JSON.parse(fileContent);
11099
11335
  } catch (error) {
11100
11336
  if (error.code !== 'ENOENT') {
@@ -11125,47 +11361,50 @@ function isValidDirName(name) {
11125
11361
  * An analyzer is considered valid if a '1.md' file exists in the instructions directory.
11126
11362
  *
11127
11363
  * @param {string} analyzeMessagesBasePath - The absolute or relative path to the base directory containing the analyzer message files (e.g., 'messages/analyze').
11128
- * @returns {Promise<Array<{id: string, label: string}>>} A promise that resolves to an array of analyzer objects.
11364
+ * @param {object} [options={}] - Optional configuration.
11365
+ * @param {boolean} [options.includeDescription=false] - Whether to include the description of the analyzer.
11366
+ * @returns {Promise<Array<{id: string, label: string, name: string, protected: boolean, description?: string}>>} A promise that resolves to an array of analyzer objects.
11129
11367
  */
11130
- async function getAnalyzers$2(analyzeMessagesBasePath) {
11368
+ async function getAnalyzers$2(analyzeMessagesBasePath, options = {}) {
11369
+ const { includeDescription = false } = options;
11131
11370
  const analyzers = [];
11132
11371
 
11133
11372
  try {
11134
- const analyzerEntries = await fs$7.readdir(analyzeMessagesBasePath, { withFileTypes: true });
11373
+ const analyzerEntries = await fs$6.readdir(analyzeMessagesBasePath, { withFileTypes: true });
11135
11374
 
11136
11375
  for (const analyzerEntry of analyzerEntries) {
11137
11376
  if (analyzerEntry.isDirectory() && isValidDirName(analyzerEntry.name)) {
11138
11377
  const analyzerName = analyzerEntry.name;
11139
- const analyzerPath = path$5.join(analyzeMessagesBasePath, analyzerName);
11378
+ const analyzerPath = path$4.join(analyzeMessagesBasePath, analyzerName);
11140
11379
  const analyzerConfig = await readConfig$1(analyzerPath);
11141
11380
  const analyzerLabel = analyzerConfig?.label || analyzerName;
11142
11381
 
11143
- const contentEntries = await fs$7.readdir(analyzerPath, { withFileTypes: true });
11382
+ const contentEntries = await fs$6.readdir(analyzerPath, { withFileTypes: true });
11144
11383
 
11145
11384
  for (const contentEntry of contentEntries) {
11146
11385
  if (contentEntry.isDirectory() && isValidDirName(contentEntry.name)) {
11147
11386
  const contentType = contentEntry.name;
11148
- const contentPath = path$5.join(analyzerPath, contentType);
11387
+ const contentPath = path$4.join(analyzerPath, contentType);
11149
11388
  const contentConfig = await readConfig$1(contentPath);
11150
11389
  const contentLabel = contentConfig?.label || contentType;
11151
11390
 
11152
- const instructionsEntries = await fs$7.readdir(contentPath, { withFileTypes: true });
11391
+ const instructionsEntries = await fs$6.readdir(contentPath, { withFileTypes: true });
11153
11392
 
11154
11393
  for (const instructionsEntry of instructionsEntries) {
11155
11394
  if (instructionsEntry.isDirectory() && isValidDirName(instructionsEntry.name)) {
11156
11395
  const instructionsType = instructionsEntry.name;
11157
- const instructionsPath = path$5.join(contentPath, instructionsType);
11396
+ const instructionsPath = path$4.join(contentPath, instructionsType);
11158
11397
  const instructionsConfig = await readConfig$1(instructionsPath);
11159
11398
  const instructionsLabel = instructionsConfig?.label || instructionsType;
11160
11399
 
11161
11400
  // Check for the existence of 1.md to confirm a valid analyzer configuration
11162
- const instructionsFilePath = path$5.join(instructionsPath, '1.md');
11401
+ const instructionsFilePath = path$4.join(instructionsPath, '1.md');
11163
11402
  try {
11164
- await fs$7.access(instructionsFilePath); // Check if file exists and is accessible
11403
+ await fs$6.access(instructionsFilePath); // Check if file exists and is accessible
11165
11404
 
11166
11405
  // If analyzerName starts with 'tutorial-', check its last modified time.
11167
11406
  if (analyzerName.startsWith('tutorial-')) {
11168
- const stats = await fs$7.stat(instructionsFilePath);
11407
+ const stats = await fs$6.stat(instructionsFilePath);
11169
11408
  const lastModified = stats.mtime.getTime(); // Get timestamp in milliseconds
11170
11409
  const sixtyMinutesAgo = Date.now() - (60 * 60 * 1000); // Current time - 60 minutes in ms
11171
11410
 
@@ -11178,11 +11417,23 @@ async function getAnalyzers$2(analyzeMessagesBasePath) {
11178
11417
  const analyzerId = `${analyzerName}::${contentType}::${instructionsType}`;
11179
11418
  const analyzerFullLabel = `${analyzerLabel} (${contentLabel} - ${instructionsLabel})`;
11180
11419
 
11420
+ let descriptionContent = null;
11421
+ if (includeDescription) {
11422
+ try {
11423
+ descriptionContent = await getAnalyzerInstructionsContent$2(analyzeMessagesBasePath, analyzerId);
11424
+ } catch (descError) {
11425
+ console.warn(`Warning: Could not load description for ${analyzerId}: ${descError.message}`);
11426
+ descriptionContent = null;
11427
+ }
11428
+ }
11429
+
11181
11430
  analyzers.push({
11182
11431
  id: analyzerId,
11183
11432
  label: analyzerFullLabel,
11184
- protected: analyzerConfig?.protected || false
11185
- });
11433
+ name: analyzerName,
11434
+ protected: analyzerConfig?.protected || false,
11435
+ ...(descriptionContent && { description: descriptionContent })
11436
+ });
11186
11437
  } catch (error) {
11187
11438
  // If 1.md doesn't exist, this is not a complete analyzer configuration, skip.
11188
11439
  if (error.code !== 'ENOENT') {
@@ -11219,8 +11470,8 @@ var discovery = {
11219
11470
  * Authors: Gemini 2.5 Flash Thinking (v1.0.0), Gemini 2.5 Flash Thinking (v1.1.0)
11220
11471
  */
11221
11472
 
11222
- const fs$6 = require$$0.promises;
11223
- const path$4 = require$$1;
11473
+ const fs$5 = require$$0.promises;
11474
+ const path$3 = require$$1;
11224
11475
 
11225
11476
  /**
11226
11477
  * Saves or updates an analyzer configuration.
@@ -11275,18 +11526,18 @@ async function saveConfiguration$1(analyzeMessagesBasePath, analyzerId, instruct
11275
11526
  }
11276
11527
 
11277
11528
  // 3. Construct directory paths
11278
- const analyzerDir = path$4.join(analyzeMessagesBasePath, analyzerName);
11279
- const contentDir = path$4.join(analyzerDir, contentType);
11280
- const instructionsDir = path$4.join(contentDir, instructionsType);
11281
- const instructionsFilePath = path$4.join(instructionsDir, '1.md');
11529
+ const analyzerDir = path$3.join(analyzeMessagesBasePath, analyzerName);
11530
+ const contentDir = path$3.join(analyzerDir, contentType);
11531
+ const instructionsDir = path$3.join(contentDir, instructionsType);
11532
+ const instructionsFilePath = path$3.join(instructionsDir, '1.md');
11282
11533
 
11283
11534
  try {
11284
11535
  // 4. Create directories recursively
11285
- await fs$6.mkdir(instructionsDir, { recursive: true });
11536
+ await fs$5.mkdir(instructionsDir, { recursive: true });
11286
11537
 
11287
11538
  // 5. Save instructions content to 1.md
11288
11539
  const finalContent = `; role: assistant\n\n\n${instructionsContent}`;
11289
- await fs$6.writeFile(instructionsFilePath, finalContent, 'utf8');
11540
+ await fs$5.writeFile(instructionsFilePath, finalContent, 'utf8');
11290
11541
 
11291
11542
  // 6. Optionally create/Update config.json files
11292
11543
  if (ensureConfigs) {
@@ -11312,11 +11563,11 @@ async function saveConfiguration$1(analyzeMessagesBasePath, analyzerId, instruct
11312
11563
  * @param {string} label - The label to ensure is in the config.json.
11313
11564
  */
11314
11565
  async function ensureConfigJson(dirPath, label) {
11315
- const configPath = path$4.join(dirPath, 'config.json');
11566
+ const configPath = path$3.join(dirPath, 'config.json');
11316
11567
  let config = {};
11317
11568
 
11318
11569
  try {
11319
- const fileContent = await fs$6.readFile(configPath, 'utf8');
11570
+ const fileContent = await fs$5.readFile(configPath, 'utf8');
11320
11571
  config = JSON.parse(fileContent);
11321
11572
  } catch (error) {
11322
11573
  // If file doesn't exist or parsing fails, start with an empty config
@@ -11333,7 +11584,7 @@ async function ensureConfigJson(dirPath, label) {
11333
11584
  }
11334
11585
 
11335
11586
  // Write the updated config back to the file
11336
- await fs$6.writeFile(configPath, JSON.stringify(config, null, 4), 'utf8');
11587
+ await fs$5.writeFile(configPath, JSON.stringify(config, null, 4), 'utf8');
11337
11588
  }
11338
11589
 
11339
11590
 
@@ -11352,8 +11603,8 @@ var saver = {
11352
11603
  * Authors: Gemini 2.5 Flash (v1.0.0)
11353
11604
  */
11354
11605
 
11355
- const fs$5 = require$$0.promises;
11356
- const path$3 = require$$1;
11606
+ const fs$4 = require$$0.promises;
11607
+ const path$2 = require$$1;
11357
11608
  const CodeBlockUtils$1 = CodeBlockUtils$4;
11358
11609
 
11359
11610
  /**
@@ -11439,10 +11690,10 @@ async function getAnalyzerSchema$2(analyzeMessagesBasePath, analyzerId) {
11439
11690
  }
11440
11691
  const [analyzerName, contentType, instructionsType] = parts;
11441
11692
 
11442
- const instructionsFilePath = path$3.join(analyzeMessagesBasePath, analyzerName, contentType, instructionsType, '1.md');
11693
+ const instructionsFilePath = path$2.join(analyzeMessagesBasePath, analyzerName, contentType, instructionsType, '1.md');
11443
11694
 
11444
11695
  try {
11445
- const fileContent = await fs$5.readFile(instructionsFilePath, 'utf8');
11696
+ const fileContent = await fs$4.readFile(instructionsFilePath, 'utf8');
11446
11697
  const { blocks } = CodeBlockUtils$1.extractCodeBlocks(fileContent, { silent: true });
11447
11698
  const jsonBlocks = blocks.filter(block => block.type === 'code' && block.language === 'json');
11448
11699
 
@@ -11513,8 +11764,8 @@ var schemaLoader = {
11513
11764
  * Authors: Gemini 2.5 Flash (v1.0.0)
11514
11765
  */
11515
11766
 
11516
- const fs$4 = require$$0.promises;
11517
- const path$2 = require$$1;
11767
+ const fs$3 = require$$0.promises;
11768
+ const path$1 = require$$1;
11518
11769
  const { readConfig } = discovery; // Import helper from discovery
11519
11770
 
11520
11771
  /**
@@ -11524,7 +11775,7 @@ const { readConfig } = discovery; // Import helper from discovery
11524
11775
  */
11525
11776
  async function isDirectoryEmpty(dirPath) {
11526
11777
  try {
11527
- const files = await fs$4.readdir(dirPath);
11778
+ const files = await fs$3.readdir(dirPath);
11528
11779
  return files.length === 0 || (files.length === 1 && files[0] === 'config.json');
11529
11780
  } catch (error) {
11530
11781
  if (error.code === 'ENOENT') {
@@ -11555,10 +11806,10 @@ async function deleteAnalyzer$2(analyzeMessagesBasePath, analyzerId) {
11555
11806
  }
11556
11807
  const [analyzerName, contentType, instructionsType] = parts;
11557
11808
 
11558
- const analyzerDir = path$2.join(analyzeMessagesBasePath, analyzerName);
11559
- const contentDir = path$2.join(analyzerDir, contentType);
11560
- const instructionsDir = path$2.join(contentDir, instructionsType);
11561
- const instructionsFilePath = path$2.join(instructionsDir, '1.md');
11809
+ const analyzerDir = path$1.join(analyzeMessagesBasePath, analyzerName);
11810
+ const contentDir = path$1.join(analyzerDir, contentType);
11811
+ const instructionsDir = path$1.join(contentDir, instructionsType);
11812
+ const instructionsFilePath = path$1.join(instructionsDir, '1.md');
11562
11813
 
11563
11814
  try {
11564
11815
  // 1. Check for protection at all levels
@@ -11579,7 +11830,7 @@ async function deleteAnalyzer$2(analyzeMessagesBasePath, analyzerId) {
11579
11830
 
11580
11831
  // 2. Delete the 1.md file
11581
11832
  try {
11582
- await fs$4.unlink(instructionsFilePath);
11833
+ await fs$3.unlink(instructionsFilePath);
11583
11834
  } catch (error) {
11584
11835
  if (error.code === 'ENOENT') {
11585
11836
  return { success: false, message: `Analyzer instructions file not found: ${instructionsFilePath}. It may have already been deleted.` };
@@ -11593,7 +11844,7 @@ async function deleteAnalyzer$2(analyzeMessagesBasePath, analyzerId) {
11593
11844
  // Check and delete instructions directory
11594
11845
  if (await isDirectoryEmpty(instructionsDir)) {
11595
11846
  try {
11596
- await fs$4.rmdir(instructionsDir);
11847
+ await fs$3.rmdir(instructionsDir);
11597
11848
  deletedDirs.push(instructionsDir);
11598
11849
  } catch (error) {
11599
11850
  console.warn(`Warning: Could not remove empty instructions directory ${instructionsDir}: ${error.message}`);
@@ -11603,7 +11854,7 @@ async function deleteAnalyzer$2(analyzeMessagesBasePath, analyzerId) {
11603
11854
  // Check and delete content directory
11604
11855
  if (await isDirectoryEmpty(contentDir)) {
11605
11856
  try {
11606
- await fs$4.rmdir(contentDir);
11857
+ await fs$3.rmdir(contentDir);
11607
11858
  deletedDirs.push(contentDir);
11608
11859
  } catch (error) {
11609
11860
  console.warn(`Warning: Could not remove empty content directory ${contentDir}: ${error.message}`);
@@ -11613,7 +11864,7 @@ async function deleteAnalyzer$2(analyzeMessagesBasePath, analyzerId) {
11613
11864
  // Check and delete analyzer directory
11614
11865
  if (await isDirectoryEmpty(analyzerDir)) {
11615
11866
  try {
11616
- await fs$4.rmdir(analyzerDir);
11867
+ await fs$3.rmdir(analyzerDir);
11617
11868
  deletedDirs.push(analyzerDir);
11618
11869
  } catch (error) {
11619
11870
  console.warn(`Warning: Could not remove empty analyzer directory ${analyzerDir}: ${error.message}`);
@@ -11631,69 +11882,9 @@ async function deleteAnalyzer$2(analyzeMessagesBasePath, analyzerId) {
11631
11882
  var management = {
11632
11883
  deleteAnalyzer: deleteAnalyzer$2};
11633
11884
 
11634
- /*
11635
- * Component: AnalyzerUtils Instruction Loader
11636
- * Block-UUID: 0a1b2c3d-4e5f-6a7b-8c9d-0e1f2a3b4c5e
11637
- * Parent-UUID: N/A
11638
- * Version: 1.0.0
11639
- * Description: Provides utility functions for loading raw analyzer instruction content.
11640
- * Language: JavaScript
11641
- * Created-at: 2025-08-28T23:48:00.000Z
11642
- * Authors: Gemini 2.5 Flash (v1.0.0)
11643
- */
11644
-
11645
- const fs$3 = require$$0.promises;
11646
- const path$1 = require$$1;
11647
-
11648
- /**
11649
- * Retrieves the raw Markdown content of the analyzer's '1.md' instruction file.
11650
- *
11651
- * @param {string} analyzeMessagesBasePath - The absolute path to the base directory containing the analyzer message files (e.g., 'messages/analyze').
11652
- * @param {string} analyzerId - The unique ID of the analyzer (format: 'analyzer_name::content_type::instructions_type').
11653
- * @returns {Promise<string|null>} A promise that resolves with the full Markdown content of the '1.md' file, or null if not found/invalid.
11654
- */
11655
- async function getAnalyzerInstructionsContent$2(analyzeMessagesBasePath, analyzerId) {
11656
- if (typeof analyzeMessagesBasePath !== 'string' || analyzeMessagesBasePath.trim() === '') {
11657
- console.error('Error: analyzeMessagesBasePath is required.');
11658
- return null;
11659
- }
11660
- if (typeof analyzerId !== 'string' || analyzerId.trim() === '') {
11661
- console.error('Error: analyzerId is required.');
11662
- return null;
11663
- }
11664
-
11665
- const parts = analyzerId.split('::');
11666
- if (parts.length !== 3) {
11667
- console.error(`Error: Invalid analyzerId format. Expected 'analyzer_name::content_type::instructions_type', but got '${analyzerId}'.`);
11668
- return null;
11669
- }
11670
- const [analyzerName, contentType, instructionsType] = parts;
11671
-
11672
- const instructionsFilePath = path$1.join(analyzeMessagesBasePath, analyzerName, contentType, instructionsType, '1.md');
11673
-
11674
- try {
11675
- const fileContent = await fs$3.readFile(instructionsFilePath, 'utf8');
11676
- const parts = fileContent.split('\n\n\n');
11677
- parts.shift();
11678
- return parts.join('\n\n\n');
11679
- } catch (error) {
11680
- if (error.code === 'ENOENT') {
11681
- console.warn(`Analyzer instructions file not found: ${instructionsFilePath}`);
11682
- return null;
11683
- } else {
11684
- console.error(`Error reading analyzer instructions file ${instructionsFilePath}: ${error.message}`);
11685
- throw error;
11686
- }
11687
- }
11688
- }
11689
-
11690
- var instructionLoader = {
11691
- getAnalyzerInstructionsContent: getAnalyzerInstructionsContent$2
11692
- };
11693
-
11694
11885
  /*
11695
11886
  * Component: AnalyzerUtils Default Prompt Loader
11696
- * Block-UUID: 0b1c2d3e-4f5a-6b7c-8d9e-0f1a2b3c4d5f
11887
+ * Block-UUID: be863744-6461-4b94-97ce-b1d177e9e881
11697
11888
  * Parent-UUID: N/A
11698
11889
  * Version: 1.0.0
11699
11890
  * Description: Provides utility functions for loading shared, default prompt components (system and start messages).
@@ -12046,10 +12237,10 @@ const ChatUtils = ChatUtils$1;
12046
12237
  const CodeBlockUtils = CodeBlockUtils$4;
12047
12238
  const ContextUtils = ContextUtils$2;
12048
12239
  const MessageUtils = MessageUtils$3;
12049
- const AnalysisBlockUtils = AnalysisBlockUtils$3;
12240
+ const AnalysisBlockUtils = AnalysisBlockUtils$2;
12050
12241
  const AnalyzerUtils = AnalyzerUtils$1;
12051
- const PatchUtils = PatchUtils$2;
12052
- const GSToolBlockUtils = GSToolBlockUtils$3;
12242
+ const PatchUtils = PatchUtils$1;
12243
+ const GSToolBlockUtils = requireGSToolBlockUtils();
12053
12244
  const LLMUtils = LLMUtils$1;
12054
12245
  const JsonUtils = JsonUtils$2;
12055
12246
  const ConfigUtils = ConfigUtils$1;