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