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