@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.
- package/dist/gsc-utils.cjs.js +1222 -1029
- package/dist/gsc-utils.esm.js +1222 -1029
- package/package.json +1 -1
- package/src/AnalyzerUtils/defaultPromptLoader.js +1 -1
- package/src/AnalyzerUtils/discovery.js +24 -8
- package/src/AnalyzerUtils/instructionLoader.js +3 -1
- package/src/GSToolBlockUtils.js +66 -7
package/dist/gsc-utils.esm.js
CHANGED
|
@@ -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$
|
|
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$
|
|
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$
|
|
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$
|
|
708
|
-
validateUUID: validateUUID$
|
|
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$
|
|
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$
|
|
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$
|
|
841
|
-
const { validateUUID: validateUUID$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
1312
|
-
matchFencesAndExtractBlocks: matchFencesAndExtractBlocks$
|
|
1311
|
+
findAllCodeFences: findAllCodeFences$2,
|
|
1312
|
+
matchFencesAndExtractBlocks: matchFencesAndExtractBlocks$2,
|
|
1313
1313
|
extractCodeBlocksWithUUIDs: extractCodeBlocksWithUUIDs$1,
|
|
1314
|
-
findCodeBlockByUUID: findCodeBlockByUUID$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
6689
|
-
const cleanTarget = removeLineNumbers$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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:
|
|
8685
|
-
* Block-UUID:
|
|
8686
|
-
* Parent-UUID:
|
|
8687
|
-
* Version: 1.
|
|
8688
|
-
* Description: Provides
|
|
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-
|
|
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
|
-
|
|
8696
|
-
|
|
8697
|
-
|
|
8698
|
-
|
|
8699
|
-
|
|
8700
|
-
|
|
8701
|
-
|
|
8702
|
-
|
|
8703
|
-
|
|
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
|
-
|
|
8707
|
-
|
|
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
|
-
|
|
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
|
-
*
|
|
8714
|
-
*
|
|
8715
|
-
*
|
|
8716
|
-
*
|
|
8717
|
-
*
|
|
8718
|
-
*
|
|
8719
|
-
*
|
|
8720
|
-
|
|
8721
|
-
|
|
8722
|
-
|
|
8723
|
-
|
|
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
|
-
|
|
8727
|
-
|
|
8728
|
-
let markerFound = false;
|
|
9090
|
+
if (content.match(/^\s*# GitSense Chat Tool\n\s*{/))
|
|
9091
|
+
return true;
|
|
8729
9092
|
|
|
8730
|
-
|
|
8731
|
-
|
|
9093
|
+
return false;
|
|
9094
|
+
}
|
|
8732
9095
|
|
|
8733
|
-
|
|
8734
|
-
|
|
8735
|
-
|
|
8736
|
-
|
|
8737
|
-
|
|
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
|
-
|
|
8741
|
-
|
|
8742
|
-
|
|
8743
|
-
}
|
|
9110
|
+
const lines = content.split('\n');
|
|
9111
|
+
const jsonLines = [];
|
|
9112
|
+
let markerFound = false;
|
|
8744
9113
|
|
|
8745
|
-
|
|
8746
|
-
|
|
8747
|
-
}
|
|
9114
|
+
for (const line of lines) {
|
|
9115
|
+
const trimmedLine = line.trim();
|
|
8748
9116
|
|
|
8749
|
-
|
|
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
|
-
|
|
8752
|
-
|
|
8753
|
-
|
|
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
|
-
|
|
8757
|
-
|
|
9129
|
+
// Keep lines that are part of the JSON
|
|
9130
|
+
jsonLines.push(line);
|
|
9131
|
+
}
|
|
8758
9132
|
|
|
8759
|
-
|
|
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
|
-
|
|
8771
|
-
|
|
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
|
-
|
|
8775
|
-
|
|
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
|
-
|
|
8782
|
-
|
|
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
|
-
|
|
8786
|
-
|
|
8787
|
-
|
|
8788
|
-
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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
|
-
|
|
9095
|
-
|
|
9096
|
-
|
|
9097
|
-
|
|
9098
|
-
|
|
9099
|
-
|
|
9100
|
-
const {
|
|
9101
|
-
const {
|
|
9102
|
-
const {
|
|
9103
|
-
|
|
9104
|
-
|
|
9105
|
-
|
|
9106
|
-
|
|
9107
|
-
|
|
9108
|
-
|
|
9109
|
-
|
|
9110
|
-
|
|
9111
|
-
|
|
9112
|
-
|
|
9113
|
-
|
|
9114
|
-
|
|
9115
|
-
|
|
9116
|
-
|
|
9117
|
-
|
|
9118
|
-
|
|
9119
|
-
|
|
9120
|
-
|
|
9121
|
-
|
|
9122
|
-
|
|
9123
|
-
|
|
9124
|
-
|
|
9125
|
-
|
|
9126
|
-
|
|
9127
|
-
|
|
9128
|
-
|
|
9129
|
-
|
|
9130
|
-
|
|
9131
|
-
|
|
9132
|
-
|
|
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
|
-
|
|
9135
|
-
|
|
9136
|
-
|
|
9137
|
-
|
|
9138
|
-
|
|
9139
|
-
|
|
9140
|
-
|
|
9141
|
-
|
|
9142
|
-
|
|
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
|
-
|
|
9148
|
-
|
|
9149
|
-
|
|
9150
|
-
|
|
9151
|
-
|
|
9152
|
-
|
|
9153
|
-
|
|
9154
|
-
|
|
9155
|
-
|
|
9156
|
-
|
|
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
|
-
|
|
9162
|
-
|
|
9163
|
-
|
|
9164
|
-
|
|
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
|
-
|
|
9167
|
-
|
|
9168
|
-
|
|
9169
|
-
|
|
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
|
-
|
|
9172
|
-
|
|
9173
|
-
|
|
9174
|
-
|
|
9175
|
-
|
|
9176
|
-
|
|
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
|
-
|
|
9182
|
-
|
|
9183
|
-
|
|
9184
|
-
|
|
9185
|
-
|
|
9186
|
-
|
|
9187
|
-
|
|
9188
|
-
|
|
9189
|
-
|
|
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
|
-
|
|
9194
|
-
|
|
9195
|
-
|
|
9196
|
-
|
|
9197
|
-
|
|
9198
|
-
|
|
9199
|
-
|
|
9200
|
-
|
|
9201
|
-
|
|
9202
|
-
|
|
9203
|
-
|
|
9204
|
-
|
|
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
|
-
|
|
9209
|
-
|
|
9210
|
-
|
|
9211
|
-
|
|
9212
|
-
|
|
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
|
-
|
|
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
|
-
|
|
9217
|
-
|
|
9218
|
-
|
|
9219
|
-
|
|
9220
|
-
|
|
9221
|
-
|
|
9222
|
-
|
|
9223
|
-
|
|
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
|
-
|
|
9230
|
-
|
|
9231
|
-
|
|
9232
|
-
|
|
9233
|
-
|
|
9234
|
-
|
|
9235
|
-
|
|
9236
|
-
|
|
9237
|
-
|
|
9238
|
-
|
|
9239
|
-
|
|
9240
|
-
|
|
9241
|
-
|
|
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
|
-
|
|
9274
|
-
|
|
9275
|
-
|
|
9276
|
-
|
|
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
|
-
|
|
9279
|
-
|
|
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
|
-
|
|
9282
|
-
|
|
9283
|
-
|
|
9284
|
-
|
|
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
|
-
|
|
9307
|
-
|
|
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
|
-
|
|
9310
|
-
|
|
9311
|
-
|
|
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
|
-
|
|
9329
|
-
|
|
9330
|
-
|
|
9331
|
-
|
|
9332
|
-
|
|
9333
|
-
|
|
9334
|
-
|
|
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
|
-
|
|
9339
|
-
|
|
9340
|
-
|
|
9341
|
-
|
|
9342
|
-
|
|
9343
|
-
|
|
9344
|
-
|
|
9345
|
-
|
|
9346
|
-
|
|
9347
|
-
|
|
9348
|
-
|
|
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
|
-
|
|
9366
|
-
|
|
9367
|
-
|
|
9368
|
-
|
|
9369
|
-
|
|
9370
|
-
|
|
9371
|
-
|
|
9372
|
-
|
|
9373
|
-
|
|
9374
|
-
|
|
9375
|
-
|
|
9376
|
-
|
|
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
|
-
|
|
9581
|
-
|
|
9582
|
-
|
|
9583
|
-
|
|
9584
|
-
|
|
9585
|
-
|
|
9586
|
-
|
|
9587
|
-
|
|
9588
|
-
|
|
9589
|
-
|
|
9590
|
-
|
|
9591
|
-
|
|
9592
|
-
|
|
9593
|
-
|
|
9594
|
-
|
|
9595
|
-
|
|
9596
|
-
|
|
9597
|
-
|
|
9598
|
-
|
|
9599
|
-
|
|
9600
|
-
|
|
9601
|
-
|
|
9602
|
-
|
|
9603
|
-
|
|
9604
|
-
|
|
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
|
-
|
|
9607
|
-
|
|
9608
|
-
|
|
9609
|
-
|
|
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
|
-
|
|
9626
|
-
|
|
9627
|
-
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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 } =
|
|
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 } =
|
|
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 =
|
|
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$
|
|
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
|
|
11074
|
-
* Block-UUID:
|
|
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
|
|
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$
|
|
11329
|
+
const configPath = path$4.join(dirPath, 'config.json');
|
|
11094
11330
|
try {
|
|
11095
|
-
const fileContent = await fs$
|
|
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
|
-
* @
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
11399
|
+
const instructionsFilePath = path$4.join(instructionsPath, '1.md');
|
|
11161
11400
|
try {
|
|
11162
|
-
await fs$
|
|
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$
|
|
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
|
-
|
|
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$
|
|
11221
|
-
const path$
|
|
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$
|
|
11277
|
-
const contentDir = path$
|
|
11278
|
-
const instructionsDir = path$
|
|
11279
|
-
const instructionsFilePath = path$
|
|
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$
|
|
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$
|
|
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$
|
|
11564
|
+
const configPath = path$3.join(dirPath, 'config.json');
|
|
11314
11565
|
let config = {};
|
|
11315
11566
|
|
|
11316
11567
|
try {
|
|
11317
|
-
const fileContent = await fs$
|
|
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$
|
|
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$
|
|
11354
|
-
const path$
|
|
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$
|
|
11691
|
+
const instructionsFilePath = path$2.join(analyzeMessagesBasePath, analyzerName, contentType, instructionsType, '1.md');
|
|
11441
11692
|
|
|
11442
11693
|
try {
|
|
11443
|
-
const fileContent = await fs$
|
|
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$
|
|
11515
|
-
const path$
|
|
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$
|
|
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$
|
|
11557
|
-
const contentDir = path$
|
|
11558
|
-
const instructionsDir = path$
|
|
11559
|
-
const instructionsFilePath = path$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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:
|
|
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$
|
|
12238
|
+
const AnalysisBlockUtils = AnalysisBlockUtils$2;
|
|
12046
12239
|
const AnalyzerUtils = AnalyzerUtils$1;
|
|
12047
|
-
const PatchUtils = PatchUtils$
|
|
12048
|
-
const GSToolBlockUtils =
|
|
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;
|