@gitsense/gsc-utils 0.2.25 → 0.2.28

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.
Files changed (39) hide show
  1. package/README.md +380 -62
  2. package/dist/gsc-utils.cjs.js +2290 -326
  3. package/dist/gsc-utils.esm.js +2290 -326
  4. package/package.json +1 -1
  5. package/src/AnalyzerUtils/cloner.js +149 -0
  6. package/src/AnalyzerUtils/constants.js +1 -1
  7. package/src/AnalyzerUtils/defaultPromptLoader.js +10 -10
  8. package/src/AnalyzerUtils/discovery.js +48 -39
  9. package/src/AnalyzerUtils/index.js +13 -7
  10. package/src/AnalyzerUtils/instructionLoader.js +6 -6
  11. package/src/AnalyzerUtils/jsonParser.js +35 -0
  12. package/src/AnalyzerUtils/management.js +6 -6
  13. package/src/AnalyzerUtils/saver.js +5 -5
  14. package/src/AnalyzerUtils/schemaLoader.js +194 -26
  15. package/src/AnalyzerUtils/updater.js +187 -0
  16. package/src/CodeBlockUtils/blockProcessor.js +14 -32
  17. package/src/CodeBlockUtils/index.js +7 -6
  18. package/src/CodeBlockUtils/lineageTracer.js +95 -0
  19. package/src/CompactChatUtils/CompactedMessageUtils.js +224 -0
  20. package/src/CompactChatUtils/README.md +321 -0
  21. package/src/CompactChatUtils/ReferenceMessageUtils.js +143 -0
  22. package/src/CompactChatUtils/index.js +40 -0
  23. package/src/ContextUtils.js +40 -4
  24. package/src/DomUtils.js +102 -14
  25. package/src/FormatterUtils.js +1 -1
  26. package/src/GSToolBlockUtils.js +66 -1
  27. package/src/GitSenseChatUtils.js +58 -5
  28. package/src/LLMUtils.js +1 -1
  29. package/src/MarkdownUtils.js +4 -1
  30. package/src/MessageUtils.js +1 -1
  31. package/src/MetaRawResultUtils.js +244 -0
  32. package/src/PatchUtils/constants.js +9 -3
  33. package/src/PatchUtils/patchParser.js +60 -36
  34. package/src/PatchUtils/patchVerifier/detectAndFixOverlappingHunks.js +1 -1
  35. package/src/PatchUtils/patchVerifier/detectAndFixRedundantChanges.js +1 -1
  36. package/src/PatchUtils/patchVerifier/verifyAndCorrectHunkHeaders.js +1 -1
  37. package/src/SVGUtils.js +137 -2
  38. package/src/SharedUtils/stringUtils.js +303 -0
  39. package/src/CodeBlockUtils/blockProcessor.js.rej +0 -8
@@ -0,0 +1,143 @@
1
+ /**
2
+ * Component: ReferenceMessageUtils
3
+ * Block-UUID: 2870551a-4311-4d64-9491-c9cc62f276e8
4
+ * Parent-UUID: N/A
5
+ * Description: Utility for parsing reference messages in the compact message workflow, extracting session data, metadata, and message sections.
6
+ * Language: JavaScript
7
+ * Created-at: 2025-12-07T00:08:42.573Z
8
+ * Authors: GLM-4.6 (v1.0.0)
9
+ */
10
+
11
+
12
+ class ReferenceMessageUtils {
13
+ /**
14
+ * Extracts all data from a reference message including session data, metadata, and message sections
15
+ * @param {string} referenceMessage - The reference message content to parse
16
+ * @returns {Object} Parsed data with messages to compact and keep, metadata, and session information
17
+ */
18
+ static extractReferenceMessageData(referenceMessage) {
19
+ if (!referenceMessage) {
20
+ return {
21
+ messagesToCompact: [],
22
+ messagesToKeep: [],
23
+ originalChatUuid: null,
24
+ validationHash: null,
25
+ sessionId: null,
26
+ range: null,
27
+ from: null,
28
+ to: null
29
+ };
30
+ }
31
+
32
+ // Extract Original Chat UUID
33
+ const originalChatUuidMatch = referenceMessage.match(/Original Chat UUID:\s*([a-f0-9-]+)/i);
34
+ const originalChatUuid = originalChatUuidMatch ? originalChatUuidMatch[1] : null;
35
+
36
+ // Extract Validation Hash
37
+ const validationHashMatch = referenceMessage.match(/Validation Hash:\s*([a-f0-9]+)/i);
38
+ const validationHash = validationHashMatch ? validationHashMatch[1] : null;
39
+
40
+ // Extract Session ID
41
+ const sessionIdMatch = referenceMessage.match(/Session ID:\s*(\d+)/i);
42
+ const sessionId = sessionIdMatch ? sessionIdMatch[1] : null;
43
+
44
+ // Extract Range
45
+ const rangeMatch = referenceMessage.match(/Range:\s*(\d+-\d+)/i);
46
+ const range = rangeMatch ? rangeMatch[1] : null;
47
+
48
+ // Extract From and To values
49
+ const fromMatch = referenceMessage.match(/From:\s*(\d+)/i);
50
+ const from = fromMatch ? parseInt(fromMatch[1], 10) : null;
51
+
52
+ const toMatch = referenceMessage.match(/To:\s*(\d+)/i);
53
+ const to = toMatch ? parseInt(toMatch[1], 10) : null;
54
+
55
+ // If range is not found but from and to are available, construct the range
56
+ if (!range && from !== null && to !== null) {
57
+ range = `${from}-${to}`;
58
+ }
59
+
60
+ // Extract Messages to Compact section
61
+ const compactSectionMatch = referenceMessage.match(/# Messages to Compact\n([\s\S]*?)(?=\n# |\n$|$)/);
62
+ const compactSection = compactSectionMatch ? compactSectionMatch[1] : '';
63
+
64
+ // Extract Messages to Keep section
65
+ const keepSectionMatch = referenceMessage.match(/# Messages to Keep \(Context\)\n([\s\S]*?)(?=\n# |\n$|$)/);
66
+ const keepSection = keepSectionMatch ? keepSectionMatch[1] : '';
67
+
68
+ // Parse messages from each section
69
+ const messagesToCompact = this.parseMessageSection(compactSection);
70
+ const messagesToKeep = this.parseMessageSection(keepSection);
71
+
72
+ return {
73
+ messagesToCompact,
74
+ messagesToKeep,
75
+ originalChatUuid,
76
+ validationHash,
77
+ sessionId,
78
+ range,
79
+ from,
80
+ to
81
+ };
82
+ }
83
+
84
+ /**
85
+ * Parses a message section to extract individual messages
86
+ * @param {string} section - The section content to parse
87
+ * @returns {Array} Array of parsed message objects
88
+ */
89
+ static parseMessageSection(section) {
90
+ if (!section) return [];
91
+
92
+ const messages = [];
93
+ const messageRegex = /<(\w+) message number (\d+)>\n([\s\S]*?)\n<\/\1 message number \2>/g;
94
+ let match;
95
+
96
+ while ((match = messageRegex.exec(section)) !== null) {
97
+ const [, role, positionStr, content] = match;
98
+ const position = parseInt(positionStr, 10);
99
+
100
+ messages.push({
101
+ role,
102
+ position,
103
+ content: content.trim()
104
+ });
105
+ }
106
+
107
+ return messages;
108
+ }
109
+
110
+ /**
111
+ * Extracts just the metadata from a reference message without the message sections
112
+ * @param {string} referenceMessage - The reference message content to parse
113
+ * @returns {Object} Metadata object with original chat UUID, validation hash, session ID, and range information
114
+ */
115
+ static extractReferenceMessageMetadata(referenceMessage) {
116
+ const data = this.extractReferenceMessageData(referenceMessage);
117
+
118
+ return {
119
+ originalChatUuid: data.originalChatUuid,
120
+ validationHash: data.validationHash,
121
+ sessionId: data.sessionId,
122
+ range: data.range,
123
+ from: data.from,
124
+ to: data.to
125
+ };
126
+ }
127
+
128
+ /**
129
+ * Checks if a message is a reference message
130
+ * @param {string} message - The message content to check
131
+ * @returns {boolean} True if the message is a reference message
132
+ */
133
+ static isReferenceMessage(message) {
134
+ if (!message) return false;
135
+
136
+ // Check for key indicators of a reference message
137
+ return message.includes('# Compact Messages Reference') &&
138
+ message.includes('Original Chat UUID:') &&
139
+ message.includes('Session ID:');
140
+ }
141
+ }
142
+
143
+ module.exports = { ReferenceMessageUtils };
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Component: CompactChatUtils Index
3
+ * Block-UUID: cd0a6091-edb6-4e92-9439-63dd8f6a6796
4
+ * Parent-UUID: 5ffa0338-b4fb-4968-908c-f23a26d4923b
5
+ * Version: 2.0.0
6
+ * Description: Entry point for CompactChatUtils that exports both ReferenceMessageUtils and CompactedMessageUtils methods directly for easier access.
7
+ * Language: JavaScript
8
+ * Created-at: 2025-12-07T00:10:05.123Z
9
+ * Authors: GLM-4.6 (v1.0.0), GLM-4.6 (v2.0.0)
10
+ */
11
+
12
+
13
+ const { ReferenceMessageUtils } = require('./ReferenceMessageUtils');
14
+ const { CompactedMessageUtils } = require('./CompactedMessageUtils');
15
+
16
+ /**
17
+ * CompactChatUtils provides utilities for working with compacted messages in GitSense Chat.
18
+ * It includes functionality for parsing reference messages and formatting/parsing compacted messages.
19
+ */
20
+ module.exports = {
21
+ // Export the classes for those who want to access them directly
22
+ ReferenceMessageUtils,
23
+ CompactedMessageUtils,
24
+
25
+ // Export all methods from ReferenceMessageUtils directly
26
+ extractReferenceMessageData: ReferenceMessageUtils.extractReferenceMessageData,
27
+ parseMessageSection: ReferenceMessageUtils.parseMessageSection,
28
+ extractReferenceMessageMetadata: ReferenceMessageUtils.extractReferenceMessageMetadata,
29
+ isReferenceMessage: ReferenceMessageUtils.isReferenceMessage,
30
+
31
+ // Export all methods from CompactedMessageUtils directly
32
+ formatCompactedMessage: CompactedMessageUtils.formatCompactedMessage,
33
+ extractCompactedMessageMetadata: CompactedMessageUtils.extractCompactedMessageMetadata,
34
+ extractCompactedMessageContent: CompactedMessageUtils.extractCompactedMessageContent,
35
+ validateCompactedMessageFormat: CompactedMessageUtils.validateCompactedMessageFormat,
36
+ isCompactedMessage: CompactedMessageUtils.isCompactedMessage,
37
+ isValidUUID: CompactedMessageUtils.isValidUUID,
38
+ isValidMessageRange: CompactedMessageUtils.isValidMessageRange,
39
+ isValidISOTimestamp: CompactedMessageUtils.isValidISOTimestamp
40
+ };
@@ -1,12 +1,12 @@
1
1
  /*
2
2
  * Component: ContextUtils
3
- * Block-UUID: a0b71292-b1cc-401a-8ce2-544a047b0fef
4
- * Parent-UUID: c018b1f9-2291-4bc9-9c4b-ab53a5db745e
5
- * Version: 1.3.0
3
+ * Block-UUID: 534c348d-a11f-4de6-ad15-f33068d51fb8
4
+ * Parent-UUID: a0b71292-b1cc-401a-8ce2-544a047b0fef
5
+ * Version: 1.4.0
6
6
  * Description: Provides utility functions for parsing context message sections to extract file details and code blocks, and for formatting content for LLM context.
7
7
  * Language: JavaScript
8
8
  * Created-at: 2025-05-09T01:36:20.107Z
9
- * Authors: Gemini 2.5 Flash Thinking (v1.0.0), Gemini 2.5 Flash (v1.1.0), Qwen 3 Coder 480B - Cerebras (v1.2.0), Qwen 3 Coder 480B - Cerebras (v1.3.0)
9
+ * Authors: Gemini 2.5 Flash Thinking (v1.0.0), Gemini 2.5 Flash (v1.1.0), Qwen 3 Coder 480B - Cerebras (v1.2.0), Qwen 3 Coder 480B - Cerebras (v1.3.0), Qwen 3 Coder 480B - Cerebras (v1.4.0)
10
10
  */
11
11
 
12
12
 
@@ -77,6 +77,41 @@ function _createContextSummary(items, contentType) {
77
77
  return summary + "\n";
78
78
  }
79
79
 
80
+ /**
81
+ * Extracts all context files from a given chat object.
82
+ * Iterates through chat messages, identifies context loader outputs,
83
+ * and parses them into individual file objects.
84
+ *
85
+ * @param {object} chat - The GitSense chat object.
86
+ * @returns {Array<object>} An array of parsed context file objects.
87
+ * Returns an empty array if no context files are found or on error.
88
+ */
89
+ function getContextFiles(chat) {
90
+ const ChatUtils = require('./ChatUtils');
91
+ const contextFiles = [];
92
+ const messages = ChatUtils.getChatMessages(chat);
93
+
94
+ if (!Array.isArray(messages)) {
95
+ console.warn("getContextFiles: Provided chat object does not contain a valid messages array.");
96
+ return contextFiles;
97
+ }
98
+
99
+ messages.forEach(message => {
100
+ // Check if the message content is a context message
101
+ if (MessageUtils.isContextMessage(message.message)) {
102
+ try {
103
+ // Extract all context sections from the message content
104
+ const extractedFiles = extractContextSections(message.message);
105
+ contextFiles.push(...extractedFiles);
106
+ } catch (error) {
107
+ console.warn(`getContextFiles: Failed to extract context sections from message ID ${message.id}:`, error);
108
+ }
109
+ }
110
+ });
111
+
112
+ return contextFiles;
113
+ }
114
+
80
115
  /**
81
116
  * Escapes backticks in code blocks to prevent premature termination of LLM-generated code blocks.
82
117
  * @param {string} content - The content string to escape.
@@ -351,6 +386,7 @@ function formatContextContent(items, contentType, contentOption) {
351
386
  module.exports = {
352
387
  parseContextSection,
353
388
  extractContextSections,
389
+ getContextFiles,
354
390
  extractContextItemsOverviewTableRows,
355
391
  formatContextContent,
356
392
  };
package/src/DomUtils.js CHANGED
@@ -1,12 +1,12 @@
1
1
  /**
2
2
  * Component: DomUtils Helper Functions
3
- * Block-UUID: e85d4e1d-f45e-450b-a0f2-941af362c2be
4
- * Parent-UUID: fe462d17-e1c8-45d6-9fb0-f53322c9cda9
5
- * Version: 1.1.0
3
+ * Block-UUID: 9a8e8e8e-8b15-4346-bbfa-740e6a5d2d79
4
+ * Parent-UUID: 8a528728-ce54-4445-946d-1743fb4b16be
5
+ * Version: 1.3.1
6
6
  * Description: Provides helper functions for creating and manipulating DOM elements.
7
7
  * Language: JavaScript
8
- * Created-at: 2025-09-11T19:03:50.026Z
9
- * Authors: Gemini 2.5 Pro (v1.0.0), Gemini 2.5 Flash (v1.1.0)
8
+ * Created-at: 2025-12-14T04:18:40.180Z
9
+ * Authors: Gemini 2.5 Pro (v1.0.0), Gemini 2.5 Flash (v1.1.0), Qwen 3 Coder 480B - Cerebras (v1.2.0), GLM-4.6 (v1.2.1), GLM-4.6 (v1.3.0), GLM-4.6 (v1.3.1)
10
10
  */
11
11
 
12
12
 
@@ -19,6 +19,7 @@ function createElement(type, params) {
19
19
  let {
20
20
  id,
21
21
  ariaLabel,
22
+ xmlns,
22
23
  role,
23
24
  html,
24
25
  text,
@@ -41,11 +42,15 @@ function createElement(type, params) {
41
42
  "data-message-role": dmr,
42
43
  attrs = {},
43
44
  dataset,
45
+ rows
44
46
  } = params;
45
47
 
46
48
  // Set standard attributes
47
49
  if (id != null) elem.id = id;
48
50
  if (ariaLabel) elem.setAttribute("aria-label", ariaLabel);
51
+ if (xmlns) elem.setAttribute("xmlns", xmlns);
52
+ if (params.viewBox) elem.setAttribute("viewBox", params.viewBox);
53
+ if (params.fill) elem.setAttribute("fill", params.fill);
49
54
  if (role) elem.setAttribute("role", role);
50
55
  if (placeholder) elem.setAttribute("placeholder", placeholder);
51
56
  if (cls || className) elem.setAttribute("class", cls || className);
@@ -53,6 +58,9 @@ function createElement(type, params) {
53
58
  if (title != null) elem.setAttribute("title", title);
54
59
  if (inputType) elem.setAttribute("type", inputType);
55
60
  if (name != null) elem.setAttribute("name", name);
61
+ if (params.width) elem.setAttribute("width", params.width);
62
+ if (params.height) elem.setAttribute("height", params.height);
63
+ if (params.stroke) elem.setAttribute("stroke", params.stroke);
56
64
  if (value != null) elem.value = value;
57
65
  if (checked != null && checked) elem.checked = true; // Use property for checked
58
66
  if (selected != null && selected) elem.selected = true; // Use property for selected
@@ -60,13 +68,21 @@ function createElement(type, params) {
60
68
  if (dmi != null) elem.setAttribute("data-message-id", dmi);
61
69
  if (dmr != null) elem.setAttribute("data-message-role", dmr);
62
70
  if (_for != null ) elem.setAttribute("for", _for);
71
+ if (params.strokeWidth) elem.setAttribute("stroke-width", params.strokeWidth);
72
+ if (params.strokeLinecap) elem.setAttribute("stroke-linecap", params.strokeLinecap);
73
+ if (params.strokeLinejoin) elem.setAttribute("stroke-linejoin", params.strokeLinejoin);
63
74
  if (colSpan != null) elem.colSpan = colSpan;
75
+ if (rows) elem.setAttribute("rows", params.rows);
64
76
 
65
77
  // Set content
66
78
  if (html != null) {
67
79
  if (typeof html === "object" && html instanceof Node) { // Ensure html is a Node
68
80
  elem.appendChild(html);
69
81
  } else if (typeof html === "string") {
82
+ // For SVG elements, use innerHTML to support path elements
83
+ if (type === 'svg' && params.innerHTML) {
84
+ elem.innerHTML = params.innerHTML;
85
+ }
70
86
  elem.innerHTML = html;
71
87
  }
72
88
  } else if (text != null) {
@@ -92,12 +108,12 @@ function createElement(type, params) {
92
108
  });
93
109
  }
94
110
 
95
- // NEW: Set additional attributes from attrs object
111
+ // Set additional attributes from attrs object
96
112
  if (attrs && typeof attrs === 'object') {
97
113
  for (const [key, value] of Object.entries(attrs)) {
98
114
  if (value !== undefined && value !== null) {
99
115
  // Skip if already set by standard properties or handled directly
100
- if (!['id', 'class', 'role', 'aria-label', 'title', 'style', 'checked', 'selected', 'disabled', 'value', 'name', 'type', 'for'].includes(key) && !key.startsWith('data-')) {
116
+ if (!['id', 'class', 'role', 'aria-label', 'title', 'style', 'selected', 'disabled', 'value', 'name', 'type', 'for'].includes(key) && !key.startsWith('data-')) {
101
117
  elem.setAttribute(key, value);
102
118
  }
103
119
  }
@@ -210,14 +226,17 @@ const h = {
210
226
  },
211
227
  createLink: (params) => {
212
228
  const link = createElement("a", params);
213
- // href and target are handled by createElement
214
- if (params?.href) link.href = params.href; // Ensure href is set if provided
229
+ if (params?.href) link.href = params.href;
230
+ if (params?.target) link.target = params.target;
215
231
  return link;
216
232
  },
217
233
  createNav: (params) => {
218
234
  return createElement("nav", params);
219
235
  },
220
- createOL: (params) => { // Added
236
+ createOl: (params) => {
237
+ return createElement("ol", params);
238
+ },
239
+ createOL: (params) => {
221
240
  return createElement("ol", params);
222
241
  },
223
242
  createOption: (params) => { // Added
@@ -233,13 +252,49 @@ const h = {
233
252
  createPre: (params) => {
234
253
  return createElement("pre", params);
235
254
  },
236
- createSelect: (params) => { // Added
237
- // name, disabled etc handled by createElement
238
- return createElement("select", params);
255
+ createSelect: (params) => {
256
+ // Extract value before creating the select element
257
+ const { value, ...selectParams } = params || {};
258
+
259
+ // Create select element without the value initially
260
+ const select = createElement("select", selectParams);
261
+
262
+ // Handle options array if provided
263
+ if (params && params.options && Array.isArray(params.options)) {
264
+ params.options.forEach(option => {
265
+ if (option && typeof option === 'object') {
266
+ // Check if this option should be selected based on the value
267
+ const isSelected = (value !== undefined && value !== null) &&
268
+ (option.value === value ||
269
+ (value === '' && option.value === '') ||
270
+ (value === null && option.value === null));
271
+
272
+ const optionElement = createElement("option", {
273
+ value: option.value,
274
+ text: option.text,
275
+ selected: isSelected || option.selected
276
+ });
277
+ select.appendChild(optionElement);
278
+ }
279
+ });
280
+ }
281
+
282
+ // Set the value after options are added to ensure proper selection
283
+ if (value !== undefined && value !== null) {
284
+ select.value = value;
285
+ }
286
+
287
+ return select;
239
288
  },
240
289
  createSpan: (params) => {
241
290
  return createElement("span", params);
242
291
  },
292
+ createSvg: (params) => {
293
+ // Create SVG element with xmlns attribute
294
+ const svgParams = { ...params, xmlns: "http://www.w3.org/2000/svg" };
295
+ const svg = createElement("svg", svgParams);
296
+ return svg;
297
+ },
243
298
  createStrong: (params) => { // Added
244
299
  return createElement("strong", params);
245
300
  },
@@ -252,6 +307,10 @@ const h = {
252
307
  createText: (text) => {
253
308
  return document.createTextNode(text);
254
309
  },
310
+ createTextarea: (params) => {
311
+ const textArea = createElement("textarea", params);
312
+ return textArea;
313
+ },
255
314
  createTextArea: (params) => {
256
315
  const textArea = createElement("textarea", params);
257
316
  return textArea;
@@ -450,6 +509,35 @@ const h = {
450
509
  })
451
510
  .join('');
452
511
  },
512
+
513
+ /**
514
+ * Injects CSS styles into the document head.
515
+ * @param {string} cssString - The CSS string to inject.
516
+ * @param {string} [id] - Optional ID for the style element to prevent duplicates.
517
+ * @returns {HTMLStyleElement} The created or existing style element.
518
+ */
519
+ injectStyles: (cssString, id) => {
520
+ // If an ID is provided, check if styles with this ID already exist
521
+ if (id) {
522
+ const existingStyle = document.getElementById(id);
523
+ if (existingStyle) {
524
+ // Styles already injected, return the existing element
525
+ return existingStyle;
526
+ }
527
+ }
528
+
529
+ // Create a new style element
530
+ const styleElement = document.createElement('style');
531
+ if (id) {
532
+ styleElement.id = id;
533
+ }
534
+ styleElement.textContent = cssString;
535
+
536
+ // Append to the document head
537
+ document.head.appendChild(styleElement);
538
+
539
+ return styleElement;
540
+ },
453
541
 
454
542
  /**
455
543
  * Calculates the distance between an element's edge and the viewport's corresponding edge
@@ -480,6 +568,6 @@ const h = {
480
568
  return rect.left;
481
569
  }
482
570
  }
483
- }
571
+ };
484
572
 
485
573
  module.exports = { h };
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Component: Formatter Utilities
3
- * Block-UUID: 9b1e07bf-e92c-43c1-9ac0-5dacdb7ae618
3
+ * Block-UUID: 8512c809-87b0-4f90-af9d-b3dca204c4de
4
4
  * Parent-UUID: 9c07d12f-5a05-4402-99d2-85d872d7b2f7
5
5
  * Version: 1.0.0
6
6
  * Description: Utility functions for formatting content in GitSense Chat
@@ -10,6 +10,8 @@
10
10
  */
11
11
 
12
12
 
13
+ const GS_TOOL_BLOCK_TYPE = 'gs-tool';
14
+
13
15
  /**
14
16
  * Checks if a given code block content represents a GitSense Chat Tool Block.
15
17
  * It verifies if the first non-empty line starts with "# GitSense Chat Tool".
@@ -28,6 +30,65 @@ function isToolBlock(content) {
28
30
  return false;
29
31
  }
30
32
 
33
+
34
+ // TODO: Add JSDocs
35
+ function getToolBlocksByTool(content, tool, CodeBlockUtils) {
36
+ const { blocks, warnings } = CodeBlockUtils.extractCodeBlocks(content, { silent: true });
37
+
38
+ return blocks.filter((block, index) => {
39
+ if (block.type !== GS_TOOL_BLOCK_TYPE)
40
+ return;
41
+
42
+ try {
43
+ const data = parseToolBlock(block.content);
44
+
45
+ if (data.tool === tool) {
46
+ block.index = index;
47
+ return block;
48
+ }
49
+ } catch(error) {
50
+ // FIXME: We need a more elegant way to identify examples.
51
+ if (!content.includes(/Internal INTEGER IDs/)) {
52
+ console.warn(`Invalid tool block JSON: ${error.message}`);
53
+ }
54
+ }
55
+ });
56
+ }
57
+
58
+ // TODO: Add JSDocs
59
+ function getToolBlockElemsByTool(dom, tool) {
60
+ const elems = dom.querySelectorAll('pre');
61
+ const toolBockElems = [];
62
+
63
+ for ( let i = 0; i < elems.length; i++ ) {
64
+ const elem = elems[i];
65
+ let content = elem.textContent;
66
+
67
+ // We need to strip out the first two lines since this is not part of the actual code
68
+ // block. These two lines are designed to make it easy to identify the language
69
+ content = content.split('\n').slice(2).join('\n');
70
+
71
+ if (!isToolBlock(content))
72
+ continue;
73
+
74
+ try {
75
+ const data = parseToolBlock(content, { silent: true });
76
+
77
+ if (data.tool === tool)
78
+ toolBockElems.push(elem);
79
+ } catch(error) {
80
+ // FIXME: We need a more elegant way to identify examples.
81
+ if (!content.match(/Internal INTEGER IDs/)) {
82
+ console.warn("getToolBlockElemsByTool: ",error.message);
83
+ }
84
+ continue;
85
+ }
86
+ }
87
+
88
+ return toolBockElems;
89
+ }
90
+
91
+
31
92
  /**
32
93
  * Parses the content of a GitSense Chat Tool Block to extract the JSON payload.
33
94
  * It strips the marker line and any subsequent lines starting with '#' (comments)
@@ -136,7 +197,7 @@ function replaceToolBlock(markdownContent, toolName, newToolData, CodeBlockUtils
136
197
  // 2. Iterate through the processed blocks to find the target GitSense Chat Tool Block
137
198
  for (let i = 0; i < blocks.length; i++) {
138
199
  const block = blocks[i];
139
- if (block.type === 'gs-tool' && block.toolData && block.toolData.tool === toolName) {
200
+ if (block.type === GS_TOOL_BLOCK_TYPE && block.toolData && block.toolData.tool === toolName) {
140
201
  targetBlockIndex = i;
141
202
  break; // Found the first matching tool block
142
203
  }
@@ -270,10 +331,14 @@ function detectAndFormatUnfencedToolBlock(messageContent) {
270
331
  };
271
332
  }
272
333
 
334
+
335
+
273
336
  module.exports = {
274
337
  isToolBlock,
275
338
  parseToolBlock,
276
339
  formatToolBlock,
277
340
  replaceToolBlock,
341
+ getToolBlocksByTool,
342
+ getToolBlockElemsByTool,
278
343
  detectAndFormatUnfencedToolBlock,
279
344
  }