@gitsense/gsc-utils 0.2.18 → 0.2.20

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.
@@ -8797,21 +8797,22 @@ function formatToolBlock(toolData) {
8797
8797
  * @param {string} markdownContent - The original markdown string containing code blocks.
8798
8798
  * @param {string} toolName - The 'tool' property value of the target GitSense Chat Tool Block to replace.
8799
8799
  * @param {Object} newToolData - The new tool data object to insert into the block.
8800
+ * @param {Object} CodeBlockUtils - The CodeBlockUtils class to avoid a circular dependency since we can't require it.
8800
8801
  * @returns {string} The markdown content with the specified tool block updated.
8801
8802
  * @throws {Error} If the target tool block is not found or if CodeBlockUtils encounters an error.
8802
8803
  */
8803
- function replaceToolBlock(markdownContent, toolName, newToolData, processCodeBlocks, updateCodeBlockByIndex) {
8804
+ function replaceToolBlock(markdownContent, toolName, newToolData, CodeBlockUtils) {
8804
8805
  if (typeof markdownContent !== 'string' || !toolName || !newToolData) {
8805
8806
  throw new Error("Missing required parameters for replaceToolBlock.");
8806
8807
  }
8807
8808
 
8808
8809
  // We can't require them as this will create a circular dependency
8809
- if (!processCodeBlocks || !updateCodeBlockByIndex) {
8810
- throw new Error("Missing required dependencies processCodeBlocks and/or updateCodeBlockByIndex.");
8810
+ if (!CodeBlockUtils) {
8811
+ throw new Error("Missing required CodeBlockUtils dependency");
8811
8812
  }
8812
8813
 
8813
8814
  // 1. Process the markdown content to find all code blocks
8814
- const { blocks, warnings } = processCodeBlocks(markdownContent, { silent: true });
8815
+ const { blocks, warnings } = CodeBlockUtils.processCodeBlocks(markdownContent, { silent: true });
8815
8816
 
8816
8817
  let targetBlockIndex = -1;
8817
8818
 
@@ -8834,7 +8835,7 @@ function replaceToolBlock(markdownContent, toolName, newToolData, processCodeBlo
8834
8835
 
8835
8836
  // 4. Use CodeBlockUtils.updateCodeBlockByIndex to perform the replacement
8836
8837
  // The language for GitSense Tool Blocks is always 'txt'
8837
- const updatedMarkdown = updateCodeBlockByIndex(
8838
+ const updatedMarkdown = CodeBlockUtils.updateCodeBlockByIndex(
8838
8839
  markdownContent,
8839
8840
  targetBlockIndex,
8840
8841
  newContentBetweenFences,
@@ -8844,11 +8845,120 @@ function replaceToolBlock(markdownContent, toolName, newToolData, processCodeBlo
8844
8845
  return updatedMarkdown;
8845
8846
  }
8846
8847
 
8848
+ /**
8849
+ * Detects an unfenced GitSense Chat Tool block within a given message string,
8850
+ * validates its JSON content, and returns a properly formatted fenced block
8851
+ * along with its original position.
8852
+ *
8853
+ * This function is designed to be forgiving, looking for the marker line
8854
+ * "# GitSense Chat Tool" followed by JSON content, even if not enclosed
8855
+ * in Markdown code fences.
8856
+ *
8857
+ * @param {string} messageContent - The full message content string.
8858
+ * @returns {{
8859
+ * found: boolean,
8860
+ * formattedToolBlock?: string,
8861
+ * originalUnfencedContent?: string,
8862
+ * startIndex?: number,
8863
+ * endIndex?: number,
8864
+ * parsedToolData?: object
8865
+ * }} An object indicating if an unfenced tool block was found, its formatted version,
8866
+ * its original content, start/end indices, and the parsed tool data.
8867
+ */
8868
+ function detectAndFormatUnfencedToolBlock(messageContent) {
8869
+ const marker = '# GitSense Chat Tool';
8870
+ const lines = messageContent.split('\n');
8871
+ let markerLineIndex = -1;
8872
+
8873
+ // 1. Find the marker line
8874
+ for (let i = 0; i < lines.length; i++) {
8875
+ if (lines[i].trim().startsWith(marker)) {
8876
+ markerLineIndex = i;
8877
+ break;
8878
+ }
8879
+ }
8880
+
8881
+ if (markerLineIndex === -1) {
8882
+ return { found: false };
8883
+ }
8884
+
8885
+ // 2. Extract potential JSON content following the marker
8886
+ let jsonStartLineIndex = -1;
8887
+ let jsonEndLineIndex = -1;
8888
+ let braceCount = 0;
8889
+ let inJsonBlock = false;
8890
+
8891
+ for (let i = markerLineIndex + 1; i < lines.length; i++) {
8892
+ const trimmedLine = lines[i].trim();
8893
+
8894
+ if (!inJsonBlock) {
8895
+ if (trimmedLine.length === 0 || trimmedLine.startsWith('#')) { // Skip empty lines or comments
8896
+ continue;
8897
+ }
8898
+ if (trimmedLine.startsWith('{')) {
8899
+ jsonStartLineIndex = i;
8900
+ inJsonBlock = true;
8901
+ } else {
8902
+ return { found: false }; // Found something after marker but not JSON
8903
+ }
8904
+ }
8905
+
8906
+ if (inJsonBlock) {
8907
+ for (const char of trimmedLine) {
8908
+ if (char === '{') braceCount++;
8909
+ else if (char === '}') braceCount--;
8910
+ }
8911
+ if (braceCount === 0 && trimmedLine.endsWith('}')) {
8912
+ jsonEndLineIndex = i;
8913
+ break; // Found the end of the JSON block
8914
+ }
8915
+ }
8916
+ }
8917
+
8918
+ if (jsonStartLineIndex === -1 || jsonEndLineIndex === -1) {
8919
+ return { found: false }; // JSON block not properly formed or not found
8920
+ }
8921
+
8922
+ // Extract the raw unfenced content including the marker and JSON
8923
+ const originalUnfencedContentLines = lines.slice(markerLineIndex, jsonEndLineIndex + 1);
8924
+ const originalUnfencedContent = originalUnfencedContentLines.join('\n');
8925
+
8926
+ // 3. Validate the extracted content using parseToolBlock
8927
+ let parsedToolData = null;
8928
+ try {
8929
+ parsedToolData = parseToolBlock$1(originalUnfencedContent);
8930
+ } catch (error) {
8931
+ console.warn("detectAndFormatUnfencedToolBlock: Failed to parse unfenced tool block JSON:", error.message);
8932
+ return { found: false }; // Invalid JSON structure
8933
+ }
8934
+
8935
+ if (!parsedToolData) {
8936
+ return { found: false };
8937
+ }
8938
+
8939
+ // 4. Format the validated tool data into a fenced block
8940
+ const formattedToolBlock = `\`\`\`txt\n${formatToolBlock(parsedToolData)}\n\`\`\``;
8941
+
8942
+ // Calculate start and end indices in the original message string
8943
+ const startIndex = messageContent.indexOf(originalUnfencedContent);
8944
+ const endIndex = startIndex + originalUnfencedContent.length;
8945
+
8946
+ return {
8947
+ found: true,
8948
+ formattedToolBlock,
8949
+ originalUnfencedContent,
8950
+ startIndex,
8951
+ endIndex,
8952
+ parsedToolData
8953
+ };
8954
+ }
8955
+
8847
8956
  var GSToolBlockUtils$3 = {
8848
8957
  isToolBlock: isToolBlock$1,
8849
8958
  parseToolBlock: parseToolBlock$1,
8850
8959
  formatToolBlock,
8851
8960
  replaceToolBlock,
8961
+ detectAndFormatUnfencedToolBlock,
8852
8962
  };
8853
8963
 
8854
8964
  /*
@@ -8795,21 +8795,22 @@ function formatToolBlock(toolData) {
8795
8795
  * @param {string} markdownContent - The original markdown string containing code blocks.
8796
8796
  * @param {string} toolName - The 'tool' property value of the target GitSense Chat Tool Block to replace.
8797
8797
  * @param {Object} newToolData - The new tool data object to insert into the block.
8798
+ * @param {Object} CodeBlockUtils - The CodeBlockUtils class to avoid a circular dependency since we can't require it.
8798
8799
  * @returns {string} The markdown content with the specified tool block updated.
8799
8800
  * @throws {Error} If the target tool block is not found or if CodeBlockUtils encounters an error.
8800
8801
  */
8801
- function replaceToolBlock(markdownContent, toolName, newToolData, processCodeBlocks, updateCodeBlockByIndex) {
8802
+ function replaceToolBlock(markdownContent, toolName, newToolData, CodeBlockUtils) {
8802
8803
  if (typeof markdownContent !== 'string' || !toolName || !newToolData) {
8803
8804
  throw new Error("Missing required parameters for replaceToolBlock.");
8804
8805
  }
8805
8806
 
8806
8807
  // We can't require them as this will create a circular dependency
8807
- if (!processCodeBlocks || !updateCodeBlockByIndex) {
8808
- throw new Error("Missing required dependencies processCodeBlocks and/or updateCodeBlockByIndex.");
8808
+ if (!CodeBlockUtils) {
8809
+ throw new Error("Missing required CodeBlockUtils dependency");
8809
8810
  }
8810
8811
 
8811
8812
  // 1. Process the markdown content to find all code blocks
8812
- const { blocks, warnings } = processCodeBlocks(markdownContent, { silent: true });
8813
+ const { blocks, warnings } = CodeBlockUtils.processCodeBlocks(markdownContent, { silent: true });
8813
8814
 
8814
8815
  let targetBlockIndex = -1;
8815
8816
 
@@ -8832,7 +8833,7 @@ function replaceToolBlock(markdownContent, toolName, newToolData, processCodeBlo
8832
8833
 
8833
8834
  // 4. Use CodeBlockUtils.updateCodeBlockByIndex to perform the replacement
8834
8835
  // The language for GitSense Tool Blocks is always 'txt'
8835
- const updatedMarkdown = updateCodeBlockByIndex(
8836
+ const updatedMarkdown = CodeBlockUtils.updateCodeBlockByIndex(
8836
8837
  markdownContent,
8837
8838
  targetBlockIndex,
8838
8839
  newContentBetweenFences,
@@ -8842,11 +8843,120 @@ function replaceToolBlock(markdownContent, toolName, newToolData, processCodeBlo
8842
8843
  return updatedMarkdown;
8843
8844
  }
8844
8845
 
8846
+ /**
8847
+ * Detects an unfenced GitSense Chat Tool block within a given message string,
8848
+ * validates its JSON content, and returns a properly formatted fenced block
8849
+ * along with its original position.
8850
+ *
8851
+ * This function is designed to be forgiving, looking for the marker line
8852
+ * "# GitSense Chat Tool" followed by JSON content, even if not enclosed
8853
+ * in Markdown code fences.
8854
+ *
8855
+ * @param {string} messageContent - The full message content string.
8856
+ * @returns {{
8857
+ * found: boolean,
8858
+ * formattedToolBlock?: string,
8859
+ * originalUnfencedContent?: string,
8860
+ * startIndex?: number,
8861
+ * endIndex?: number,
8862
+ * parsedToolData?: object
8863
+ * }} An object indicating if an unfenced tool block was found, its formatted version,
8864
+ * its original content, start/end indices, and the parsed tool data.
8865
+ */
8866
+ function detectAndFormatUnfencedToolBlock(messageContent) {
8867
+ const marker = '# GitSense Chat Tool';
8868
+ const lines = messageContent.split('\n');
8869
+ let markerLineIndex = -1;
8870
+
8871
+ // 1. Find the marker line
8872
+ for (let i = 0; i < lines.length; i++) {
8873
+ if (lines[i].trim().startsWith(marker)) {
8874
+ markerLineIndex = i;
8875
+ break;
8876
+ }
8877
+ }
8878
+
8879
+ if (markerLineIndex === -1) {
8880
+ return { found: false };
8881
+ }
8882
+
8883
+ // 2. Extract potential JSON content following the marker
8884
+ let jsonStartLineIndex = -1;
8885
+ let jsonEndLineIndex = -1;
8886
+ let braceCount = 0;
8887
+ let inJsonBlock = false;
8888
+
8889
+ for (let i = markerLineIndex + 1; i < lines.length; i++) {
8890
+ const trimmedLine = lines[i].trim();
8891
+
8892
+ if (!inJsonBlock) {
8893
+ if (trimmedLine.length === 0 || trimmedLine.startsWith('#')) { // Skip empty lines or comments
8894
+ continue;
8895
+ }
8896
+ if (trimmedLine.startsWith('{')) {
8897
+ jsonStartLineIndex = i;
8898
+ inJsonBlock = true;
8899
+ } else {
8900
+ return { found: false }; // Found something after marker but not JSON
8901
+ }
8902
+ }
8903
+
8904
+ if (inJsonBlock) {
8905
+ for (const char of trimmedLine) {
8906
+ if (char === '{') braceCount++;
8907
+ else if (char === '}') braceCount--;
8908
+ }
8909
+ if (braceCount === 0 && trimmedLine.endsWith('}')) {
8910
+ jsonEndLineIndex = i;
8911
+ break; // Found the end of the JSON block
8912
+ }
8913
+ }
8914
+ }
8915
+
8916
+ if (jsonStartLineIndex === -1 || jsonEndLineIndex === -1) {
8917
+ return { found: false }; // JSON block not properly formed or not found
8918
+ }
8919
+
8920
+ // Extract the raw unfenced content including the marker and JSON
8921
+ const originalUnfencedContentLines = lines.slice(markerLineIndex, jsonEndLineIndex + 1);
8922
+ const originalUnfencedContent = originalUnfencedContentLines.join('\n');
8923
+
8924
+ // 3. Validate the extracted content using parseToolBlock
8925
+ let parsedToolData = null;
8926
+ try {
8927
+ parsedToolData = parseToolBlock$1(originalUnfencedContent);
8928
+ } catch (error) {
8929
+ console.warn("detectAndFormatUnfencedToolBlock: Failed to parse unfenced tool block JSON:", error.message);
8930
+ return { found: false }; // Invalid JSON structure
8931
+ }
8932
+
8933
+ if (!parsedToolData) {
8934
+ return { found: false };
8935
+ }
8936
+
8937
+ // 4. Format the validated tool data into a fenced block
8938
+ const formattedToolBlock = `\`\`\`txt\n${formatToolBlock(parsedToolData)}\n\`\`\``;
8939
+
8940
+ // Calculate start and end indices in the original message string
8941
+ const startIndex = messageContent.indexOf(originalUnfencedContent);
8942
+ const endIndex = startIndex + originalUnfencedContent.length;
8943
+
8944
+ return {
8945
+ found: true,
8946
+ formattedToolBlock,
8947
+ originalUnfencedContent,
8948
+ startIndex,
8949
+ endIndex,
8950
+ parsedToolData
8951
+ };
8952
+ }
8953
+
8845
8954
  var GSToolBlockUtils$3 = {
8846
8955
  isToolBlock: isToolBlock$1,
8847
8956
  parseToolBlock: parseToolBlock$1,
8848
8957
  formatToolBlock,
8849
8958
  replaceToolBlock,
8959
+ detectAndFormatUnfencedToolBlock,
8850
8960
  };
8851
8961
 
8852
8962
  /*
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gitsense/gsc-utils",
3
- "version": "0.2.18",
3
+ "version": "0.2.20",
4
4
  "description": "Utilities for GitSense Chat (GSC)",
5
5
  "main": "dist/gsc-utils.cjs.js",
6
6
  "module": "dist/gsc-utils.esm.js",
@@ -114,21 +114,22 @@ function formatToolBlock(toolData) {
114
114
  * @param {string} markdownContent - The original markdown string containing code blocks.
115
115
  * @param {string} toolName - The 'tool' property value of the target GitSense Chat Tool Block to replace.
116
116
  * @param {Object} newToolData - The new tool data object to insert into the block.
117
+ * @param {Object} CodeBlockUtils - The CodeBlockUtils class to avoid a circular dependency since we can't require it.
117
118
  * @returns {string} The markdown content with the specified tool block updated.
118
119
  * @throws {Error} If the target tool block is not found or if CodeBlockUtils encounters an error.
119
120
  */
120
- function replaceToolBlock(markdownContent, toolName, newToolData, processCodeBlocks, updateCodeBlockByIndex) {
121
+ function replaceToolBlock(markdownContent, toolName, newToolData, CodeBlockUtils) {
121
122
  if (typeof markdownContent !== 'string' || !toolName || !newToolData) {
122
123
  throw new Error("Missing required parameters for replaceToolBlock.");
123
124
  }
124
125
 
125
126
  // We can't require them as this will create a circular dependency
126
- if (!processCodeBlocks || !updateCodeBlockByIndex) {
127
- throw new Error("Missing required dependencies processCodeBlocks and/or updateCodeBlockByIndex.");
127
+ if (!CodeBlockUtils) {
128
+ throw new Error("Missing required CodeBlockUtils dependency");
128
129
  }
129
130
 
130
131
  // 1. Process the markdown content to find all code blocks
131
- const { blocks, warnings } = processCodeBlocks(markdownContent, { silent: true });
132
+ const { blocks, warnings } = CodeBlockUtils.processCodeBlocks(markdownContent, { silent: true });
132
133
 
133
134
  let targetBlockIndex = -1;
134
135
 
@@ -151,7 +152,7 @@ function replaceToolBlock(markdownContent, toolName, newToolData, processCodeBlo
151
152
 
152
153
  // 4. Use CodeBlockUtils.updateCodeBlockByIndex to perform the replacement
153
154
  // The language for GitSense Tool Blocks is always 'txt'
154
- const updatedMarkdown = updateCodeBlockByIndex(
155
+ const updatedMarkdown = CodeBlockUtils.updateCodeBlockByIndex(
155
156
  markdownContent,
156
157
  targetBlockIndex,
157
158
  newContentBetweenFences,
@@ -161,9 +162,118 @@ function replaceToolBlock(markdownContent, toolName, newToolData, processCodeBlo
161
162
  return updatedMarkdown;
162
163
  }
163
164
 
165
+ /**
166
+ * Detects an unfenced GitSense Chat Tool block within a given message string,
167
+ * validates its JSON content, and returns a properly formatted fenced block
168
+ * along with its original position.
169
+ *
170
+ * This function is designed to be forgiving, looking for the marker line
171
+ * "# GitSense Chat Tool" followed by JSON content, even if not enclosed
172
+ * in Markdown code fences.
173
+ *
174
+ * @param {string} messageContent - The full message content string.
175
+ * @returns {{
176
+ * found: boolean,
177
+ * formattedToolBlock?: string,
178
+ * originalUnfencedContent?: string,
179
+ * startIndex?: number,
180
+ * endIndex?: number,
181
+ * parsedToolData?: object
182
+ * }} An object indicating if an unfenced tool block was found, its formatted version,
183
+ * its original content, start/end indices, and the parsed tool data.
184
+ */
185
+ function detectAndFormatUnfencedToolBlock(messageContent) {
186
+ const marker = '# GitSense Chat Tool';
187
+ const lines = messageContent.split('\n');
188
+ let markerLineIndex = -1;
189
+
190
+ // 1. Find the marker line
191
+ for (let i = 0; i < lines.length; i++) {
192
+ if (lines[i].trim().startsWith(marker)) {
193
+ markerLineIndex = i;
194
+ break;
195
+ }
196
+ }
197
+
198
+ if (markerLineIndex === -1) {
199
+ return { found: false };
200
+ }
201
+
202
+ // 2. Extract potential JSON content following the marker
203
+ let jsonStartLineIndex = -1;
204
+ let jsonEndLineIndex = -1;
205
+ let braceCount = 0;
206
+ let inJsonBlock = false;
207
+
208
+ for (let i = markerLineIndex + 1; i < lines.length; i++) {
209
+ const trimmedLine = lines[i].trim();
210
+
211
+ if (!inJsonBlock) {
212
+ if (trimmedLine.length === 0 || trimmedLine.startsWith('#')) { // Skip empty lines or comments
213
+ continue;
214
+ }
215
+ if (trimmedLine.startsWith('{')) {
216
+ jsonStartLineIndex = i;
217
+ inJsonBlock = true;
218
+ } else {
219
+ return { found: false }; // Found something after marker but not JSON
220
+ }
221
+ }
222
+
223
+ if (inJsonBlock) {
224
+ for (const char of trimmedLine) {
225
+ if (char === '{') braceCount++;
226
+ else if (char === '}') braceCount--;
227
+ }
228
+ if (braceCount === 0 && trimmedLine.endsWith('}')) {
229
+ jsonEndLineIndex = i;
230
+ break; // Found the end of the JSON block
231
+ }
232
+ }
233
+ }
234
+
235
+ if (jsonStartLineIndex === -1 || jsonEndLineIndex === -1) {
236
+ return { found: false }; // JSON block not properly formed or not found
237
+ }
238
+
239
+ // Extract the raw unfenced content including the marker and JSON
240
+ const originalUnfencedContentLines = lines.slice(markerLineIndex, jsonEndLineIndex + 1);
241
+ const originalUnfencedContent = originalUnfencedContentLines.join('\n');
242
+
243
+ // 3. Validate the extracted content using parseToolBlock
244
+ let parsedToolData = null;
245
+ try {
246
+ parsedToolData = parseToolBlock(originalUnfencedContent);
247
+ } catch (error) {
248
+ console.warn("detectAndFormatUnfencedToolBlock: Failed to parse unfenced tool block JSON:", error.message);
249
+ return { found: false }; // Invalid JSON structure
250
+ }
251
+
252
+ if (!parsedToolData) {
253
+ return { found: false };
254
+ }
255
+
256
+ // 4. Format the validated tool data into a fenced block
257
+ const formattedToolBlock = `\`\`\`txt\n${formatToolBlock(parsedToolData)}\n\`\`\``;
258
+
259
+ // Calculate start and end indices in the original message string
260
+ const startIndex = messageContent.indexOf(originalUnfencedContent);
261
+ const endIndex = startIndex + originalUnfencedContent.length;
262
+
263
+ return {
264
+ found: true,
265
+ formattedToolBlock,
266
+ originalUnfencedContent,
267
+ startIndex,
268
+ endIndex,
269
+ parsedToolData
270
+ };
271
+ }
272
+
164
273
  module.exports = {
165
274
  isToolBlock,
166
275
  parseToolBlock,
167
276
  formatToolBlock,
168
277
  replaceToolBlock,
278
+ detectAndFormatUnfencedToolBlock,
169
279
  }