@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
@@ -45,8 +45,8 @@ function getAugmentedNamespace(n) {
45
45
  * Authors: Claude 3.7 Sonnet (v1.0.0), Gemini 2.5 Pro (v1.1.0), Gemini 2.5 Flash Thinking (v1.2.0)
46
46
  */
47
47
 
48
- const fs$8 = require$$0;
49
- const path$6 = require$$1;
48
+ const fs$9 = require$$0;
49
+ const path$7 = require$$1;
50
50
  const ANALYZE_MESSAGE_REGEXP = /^# Analyze - `([^`]+)`\n/;
51
51
 
52
52
  /**
@@ -80,12 +80,12 @@ function unescapeCodeBlocks(content) {
80
80
  * @returns {Array} An array of message objects with role and content properties
81
81
  */
82
82
  function getChatTemplateMessages$1(dirname, messageType) {
83
- const messagesDir = messageType ? path$6.join(dirname, messageType) : dirname;
83
+ const messagesDir = messageType ? path$7.join(dirname, messageType) : dirname;
84
84
  const messages = [];
85
85
 
86
86
  try {
87
87
  // Read all files in the directory
88
- const files = fs$8.readdirSync(messagesDir);
88
+ const files = fs$9.readdirSync(messagesDir);
89
89
 
90
90
  // Sort files numerically (1.md, 2.md, etc.)
91
91
  const sortedFiles = files.sort((a, b) => {
@@ -96,9 +96,9 @@ function getChatTemplateMessages$1(dirname, messageType) {
96
96
 
97
97
  // Process each file
98
98
  for (const file of sortedFiles) {
99
- if (path$6.extname(file) === '.md') {
100
- const filePath = path$6.join(messagesDir, file);
101
- const fileContent = fs$8.readFileSync(filePath, 'utf8');
99
+ if (path$7.extname(file) === '.md') {
100
+ const filePath = path$7.join(messagesDir, file);
101
+ const fileContent = fs$9.readFileSync(filePath, 'utf8');
102
102
 
103
103
  // Split by triple newline to separate metadata from content
104
104
  const parts = fileContent.split('\n\n\n');
@@ -398,7 +398,7 @@ function getMessageContentType$1(messageContent) {
398
398
  return 'regular'; // Handle non-string input gracefully
399
399
  }
400
400
  const trimmedContent = messageContent.trimStart(); // Check from the beginning, ignoring leading whitespace
401
- if (trimmedContent.startsWith('## FILE CONTENT')) {
401
+ if (trimmedContent.startsWith('## FILE CONTENT') || trimmedContent.startsWith('## REFERENCE FILE CONTENT')) {
402
402
  return 'file-content-context';
403
403
  } else if (trimmedContent.startsWith('## OVERVIEW')) {
404
404
  return 'overview-context';
@@ -619,7 +619,7 @@ function getChatMessages$1(chat, model) {
619
619
  return getMessagesBeforeId$1(model || chat.main_model, chat.messages[0], null);
620
620
  }
621
621
 
622
- var ChatUtils$1 = {
622
+ var ChatUtils$2 = {
623
623
  isAskChat,
624
624
  isNewAnalyzerChat,
625
625
  isAnalyzeChat,
@@ -691,7 +691,7 @@ const COMMENT_STYLES$3 = {
691
691
  'vbscript': { type: 'apostrophe' }
692
692
  };
693
693
 
694
- var constants$2 = {
694
+ var constants$3 = {
695
695
  COMMENT_STYLES: COMMENT_STYLES$3
696
696
  };
697
697
 
@@ -710,7 +710,7 @@ var constants$2 = {
710
710
  * Generates a valid RFC 4122 UUID v4
711
711
  * @returns {string} A valid UUID v4
712
712
  */
713
- function generateUUID$3() {
713
+ function generateUUID$2() {
714
714
  return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
715
715
  const r = Math.random() * 16 | 0;
716
716
  const v = c === 'x' ? r : (r & 0x3 | 0x8);
@@ -729,7 +729,7 @@ function validateUUID$4(uuid) {
729
729
  if (!uuidPattern.test(uuid)) {
730
730
  return {
731
731
  "Block-UUID": "INVALID UUID",
732
- "Correct Block-UUID": generateUUID$3()
732
+ "Correct Block-UUID": generateUUID$2()
733
733
  };
734
734
  }
735
735
 
@@ -739,7 +739,7 @@ function validateUUID$4(uuid) {
739
739
  }
740
740
 
741
741
  var uuidUtils = {
742
- generateUUID: generateUUID$3,
742
+ generateUUID: generateUUID$2,
743
743
  validateUUID: validateUUID$4
744
744
  };
745
745
 
@@ -871,7 +871,7 @@ var lineNumberFormatter = {
871
871
  */
872
872
 
873
873
  // Dependencies from other modules within CodeBlockUtils
874
- const { COMMENT_STYLES: COMMENT_STYLES$2 } = constants$2;
874
+ const { COMMENT_STYLES: COMMENT_STYLES$2 } = constants$3;
875
875
  const { removeLineNumbers: removeLineNumbers$9 } = lineNumberFormatter; // Assuming this utility exists
876
876
  const { validateUUID: validateUUID$3 } = uuidUtils; // Assuming uuidUtils.js is in the same directory
877
877
 
@@ -1540,24 +1540,63 @@ function isValidVersion$1(version) {
1540
1540
  var versionUtils = {
1541
1541
  isValidVersion: isValidVersion$1};
1542
1542
 
1543
+ /**
1544
+ * Component: PatchUtils Constants
1545
+ * Block-UUID: c6747054-2c8b-461f-910d-0fd725d9a350
1546
+ * Parent-UUID: 32adc00e-7509-4219-8e40-6c1319371db9
1547
+ * Version: 2.1.0
1548
+ * Description: Contains shared constants and regular expressions used by the enhanced patch utilities.
1549
+ * Language: JavaScript
1550
+ * Created-at: 2025-05-14T16:55:00.000Z
1551
+ * Authors: Gemini 2.5 Flash Thinking (v1.0.0), Claude 3.7 Sonnet (v2.0.0), Qwen 3 Coder 480B - Cerebras (v2.1.0)
1552
+ */
1553
+
1554
+ // Traditional patch markers
1555
+ const PATCH_START_MARKER$1 = '# --- PATCH START MARKER ---';
1556
+ const PATCH_END_MARKER$1 = '# --- PATCH END MARKER ---';
1557
+
1558
+ // Abbreviated patch markers
1559
+ const ABBREVIATED_PATCH_START_MARKER$1 = '# --- ABBREVIATED PATCH START MARKER ---';
1560
+ const ABBREVIATED_PATCH_END_MARKER$1 = '# --- ABBREVIATED PATCH END MARKER ---';
1561
+
1562
+ // Patch metadata header
1563
+ const PATCH_METADATA_HEADER$1 = '# Patch Metadata';
1564
+
1565
+ // Diff file headers
1566
+ const ORIGINAL_FILE_HEADER$1 = '--- Original';
1567
+ const MODIFIED_FILE_HEADER$1 = '+++ Modified';
1568
+
1569
+ var constants$2 = {
1570
+ PATCH_START_MARKER: PATCH_START_MARKER$1,
1571
+ PATCH_END_MARKER: PATCH_END_MARKER$1,
1572
+ ABBREVIATED_PATCH_START_MARKER: ABBREVIATED_PATCH_START_MARKER$1,
1573
+ ABBREVIATED_PATCH_END_MARKER: ABBREVIATED_PATCH_END_MARKER$1,
1574
+ PATCH_METADATA_HEADER: PATCH_METADATA_HEADER$1,
1575
+ ORIGINAL_FILE_HEADER: ORIGINAL_FILE_HEADER$1,
1576
+ MODIFIED_FILE_HEADER: MODIFIED_FILE_HEADER$1
1577
+ };
1578
+
1543
1579
  /**
1544
1580
  * Component: PatchUtils Parser
1545
1581
  * Block-UUID: ce634df9-ff99-482a-a261-56ab34a2546e
1546
1582
  * Parent-UUID: fcc85ec1-0146-40bf-a937-6f7928258cbf
1547
- * Version: 1.1.0
1548
- * Description: Handles parsing, validation, extraction, and detection of traditional unified diff patches. Extracts metadata and raw diff content.
1583
+ * Version: 1.2.0
1584
+ * Description: Handles parsing, validation, extraction, and detection of traditional unified diff patches and abbreviated patches. Extracts metadata and raw diff content.
1549
1585
  * Language: JavaScript
1550
1586
  * Created-at: 2025-04-18T02:59:04.322Z
1551
- * Authors: Gemini 2.5 Pro (v1.0.0), Gemini 2.5 Pro (v1.1.0)
1587
+ * Authors: Gemini 2.5 Pro (v1.0.0), Gemini 2.5 Pro (v1.1.0), Qwen 3 Coder 480B - Cerebras (v1.2.0)
1552
1588
  */
1553
1589
 
1554
1590
  const { isValidVersion } = versionUtils; // Assuming SharedUtils is one level up
1591
+ const { PATCH_START_MARKER, PATCH_END_MARKER, PATCH_METADATA_HEADER,
1592
+ ORIGINAL_FILE_HEADER, MODIFIED_FILE_HEADER, ABBREVIATED_PATCH_START_MARKER,
1593
+ ABBREVIATED_PATCH_END_MARKER } = constants$2;
1555
1594
 
1556
1595
  /**
1557
1596
  * Determines the type of patch format used based on markers.
1558
- * Primarily looks for traditional unified diff markers after metadata.
1597
+ * Looks for traditional unified diff markers or abbreviated patch markers.
1559
1598
  * @param {string} patchText - The patch text
1560
- * @returns {string} 'traditional', or 'unknown'
1599
+ * @returns {string} 'traditional', 'abbreviated', or 'unknown'
1561
1600
  */
1562
1601
  function determinePatchFormat$1(patchText) {
1563
1602
  if (!patchText || typeof patchText !== 'string') {
@@ -1565,29 +1604,33 @@ function determinePatchFormat$1(patchText) {
1565
1604
  }
1566
1605
 
1567
1606
  // Check for required metadata header first
1568
- if (!patchText.includes('# Patch Metadata')) {
1607
+ if (!patchText.includes(PATCH_METADATA_HEADER)) {
1569
1608
  return 'unknown'; // Must have metadata header
1570
1609
  }
1571
1610
 
1572
1611
  // Check for traditional unified diff markers *after* potential metadata and start marker
1573
1612
  // Look for ---, +++, @@ after the start marker
1574
- const startMarkerIndex = patchText.indexOf('# --- PATCH START MARKER ---');
1575
- if (startMarkerIndex === -1) {
1576
- return 'unknown'; // Must have start marker
1577
- }
1613
+ const traditionalStartMarkerIndex = patchText.indexOf(PATCH_START_MARKER);
1614
+ if (traditionalStartMarkerIndex !== -1) {
1615
+ const contentAfterMarker = patchText.substring(traditionalStartMarkerIndex);
1578
1616
 
1579
- const contentAfterMarker = patchText.substring(startMarkerIndex);
1617
+ // Use regex for more reliable detection within the content part
1618
+ const traditionalMarkers = /^\s*--- Original\s*\n\s*\+\+\+ Modified\s*\n\s*@@ -\d+(?:,\d+)? \+\d+(?:,\d+)? @@/m;
1580
1619
 
1581
- // Use regex for more reliable detection within the content part
1582
- const traditionalMarkers = /^\s*--- Original\s*\n\s*\+\+\+ Modified\s*\n\s*@@ -\d+(?:,\d+)? \+\d+(?:,\d+)? @@/m;
1620
+ if (traditionalMarkers.test(contentAfterMarker)) {
1621
+ return 'traditional';
1622
+ }
1623
+ }
1583
1624
 
1584
- if (traditionalMarkers.test(contentAfterMarker)) {
1585
- return 'traditional';
1625
+ // Check for abbreviated patch markers
1626
+ const abbreviatedStartMarkerIndex = patchText.indexOf(ABBREVIATED_PATCH_START_MARKER);
1627
+ if (abbreviatedStartMarkerIndex !== -1) {
1628
+ return 'abbreviated';
1586
1629
  }
1587
1630
 
1588
1631
  // Removed check for obsolete '@=' context marker
1589
1632
 
1590
- return 'unknown'; // Format doesn't match expected traditional structure after markers
1633
+ return 'unknown'; // Format doesn't match expected structures after markers
1591
1634
  }
1592
1635
 
1593
1636
  /**
@@ -1608,7 +1651,8 @@ function extractPatchMetadata$1(patchText) {
1608
1651
  const trimmedLine = line.trim();
1609
1652
 
1610
1653
  // Stop processing metadata if we hit content markers or the start marker
1611
- if (trimmedLine === '# --- PATCH START MARKER ---' ||
1654
+ if (trimmedLine === PATCH_START_MARKER ||
1655
+ trimmedLine === ABBREVIATED_PATCH_START_MARKER ||
1612
1656
  line.startsWith('--- ') || // Standard diff header start
1613
1657
  line.startsWith('+++ ') || // Standard diff header start
1614
1658
  line.startsWith('@@ ')) // Standard diff hunk header start
@@ -1630,7 +1674,7 @@ function extractPatchMetadata$1(patchText) {
1630
1674
  }
1631
1675
  }
1632
1676
  // Allow lines that are just comments (e.g., # Description continued)
1633
- } else if (trimmedLine !== '' && !trimmedLine.startsWith('# Patch Metadata')) {
1677
+ } else if (trimmedLine !== '' && !trimmedLine.startsWith(PATCH_METADATA_HEADER)) {
1634
1678
  break;
1635
1679
  }
1636
1680
  }
@@ -1671,11 +1715,11 @@ function validatePatchMetadata$1(metadata) {
1671
1715
  }
1672
1716
 
1673
1717
  // Validate UUIDs are not the template string
1674
- if (metadata['Source-Block-UUID'] === '12d8a596-0620-4725-ba49-ae6fccdfa2ad') {
1675
- errors.push('Source-Block-UUID contains placeholder value "f016e2e4-f97f-4ffe-9f46-ea48f59a3eea".');
1718
+ if (metadata['Source-Block-UUID'] === '{{GS-UUID}}') {
1719
+ errors.push('Source-Block-UUID contains placeholder value "{{GS-UUID}}".');
1676
1720
  }
1677
- if (metadata['Target-Block-UUID'] === '5bfb717c-aaec-462e-a50e-d096aba0d63f') {
1678
- errors.push('Target-Block-UUID contains placeholder value "12e76392-f0ac-4c9d-a5be-c98401487352".');
1721
+ if (metadata['Target-Block-UUID'] === '{{GS-UUID}}') {
1722
+ errors.push('Target-Block-UUID contains placeholder value "{{GS-UUID}}".');
1679
1723
  }
1680
1724
 
1681
1725
  return errors;
@@ -1688,23 +1732,34 @@ function validatePatchMetadata$1(metadata) {
1688
1732
  * @param {string} format - Expected format (should be 'traditional').
1689
1733
  * @returns {string} Raw unified diff content, or empty string if not found/invalid.
1690
1734
  */
1691
- function extractPatchContent$1(patchText, format) {
1692
- if (!patchText || format !== 'traditional') {
1735
+ function extractPatchContent$1(patchText, format = 'traditional') {
1736
+ if (!patchText) {
1693
1737
  return "";
1694
1738
  }
1695
1739
 
1696
1740
  const lines = patchText.split('\n');
1697
-
1698
1741
  let contentLines = [];
1699
1742
  let inContentBlock = false;
1743
+ let startMarker, endMarker;
1744
+
1745
+ // Determine which markers to look for based on format
1746
+ if (format === 'traditional') {
1747
+ startMarker = PATCH_START_MARKER;
1748
+ endMarker = PATCH_END_MARKER;
1749
+ } else if (format === 'abbreviated') {
1750
+ startMarker = ABBREVIATED_PATCH_START_MARKER;
1751
+ endMarker = ABBREVIATED_PATCH_END_MARKER;
1752
+ } else {
1753
+ return ""; // Unsupported format
1754
+ }
1700
1755
 
1701
1756
  for (const line of lines) {
1702
- if (line.trim() === '# --- PATCH START MARKER ---') {
1757
+ if (line.trim() === startMarker) {
1703
1758
  inContentBlock = true;
1704
1759
  continue; // Skip the marker line itself
1705
1760
  }
1706
1761
 
1707
- if (line.trim() === '# --- PATCH END MARKER ---') {
1762
+ if (line.trim() === endMarker) {
1708
1763
  inContentBlock = false;
1709
1764
  break; // Stop processing once end marker is found
1710
1765
  }
@@ -1743,23 +1798,25 @@ function detectPatch(messageText) {
1743
1798
  }
1744
1799
 
1745
1800
  // Find the first code block that contains patch metadata
1746
- // Regex looks for ``` optionally followed by 'diff' language hint
1747
- const codeBlockRegex = /```(?:diff)?\s*\n([\s\S]*?)```/g;
1801
+ // Regex looks for ``` optionally followed by language hint
1802
+ const codeBlockRegex = /```([a-zA-Z0-9#+_-]*)?\s*\n([\s\S]*?)```/g;
1748
1803
  let match;
1749
1804
  while ((match = codeBlockRegex.exec(messageText)) !== null) {
1750
- const blockContent = match[1];
1805
+ const language = match[1] || '';
1806
+ const blockContent = match[2];
1751
1807
  if (isPatchBlock$1(blockContent)) { // Use isPatchBlock for check
1752
1808
  const metadata = extractPatchMetadata$1(blockContent); // Use extractPatchMetadata
1753
1809
  // Basic validation: Check for essential UUIDs
1754
1810
  if (metadata['Source-Block-UUID'] && metadata['Target-Block-UUID'] &&
1755
- metadata['Source-Block-UUID'] !== '26f930c9-2bc8-4906-bdf5-469700b2f857' && // Check placeholders
1756
- metadata['Target-Block-UUID'] !== '48a957eb-dd72-44bb-84a2-0df3c7cb16ed')
1811
+ metadata['Source-Block-UUID'] !== '{{GS-UUID}}' && // Check placeholders
1812
+ metadata['Target-Block-UUID'] !== '{{GS-UUID}}')
1757
1813
  {
1758
1814
  return {
1759
1815
  patchText: blockContent, // Return the content inside the fence
1760
1816
  metadata,
1761
1817
  sourceBlockUUID: metadata['Source-Block-UUID'],
1762
1818
  targetBlockUUID: metadata['Target-Block-UUID'],
1819
+ language: language,
1763
1820
  startIndex: match.index, // Store block position if needed
1764
1821
  endIndex: match.index + match[0].length
1765
1822
  };
@@ -1781,25 +1838,28 @@ function findAllPatches(messageText) {
1781
1838
  }
1782
1839
 
1783
1840
  const patches = [];
1784
- const codeBlockRegex = /```(?:diff)?\s*\n([\s\S]*?)```/g;
1841
+ // Find code blocks with any language identifier
1842
+ const codeBlockRegex = /```([a-zA-Z0-9#+_-]*)?\s*\n([\s\S]*?)```/g;
1785
1843
 
1786
1844
  let match;
1787
1845
  while ((match = codeBlockRegex.exec(messageText)) !== null) {
1788
- const blockContent = match[1];
1846
+ const language = match[1] || '';
1847
+ const blockContent = match[2];
1789
1848
 
1790
1849
  if (isPatchBlock$1(blockContent)) {
1791
1850
  const metadata = extractPatchMetadata$1(blockContent);
1792
1851
 
1793
1852
  // Basic validation: Check for essential UUIDs and non-placeholder values
1794
1853
  if (metadata['Source-Block-UUID'] && metadata['Target-Block-UUID'] &&
1795
- metadata['Source-Block-UUID'] !== 'e898f14f-14f6-4710-b35b-1cf3478fb5da' &&
1796
- metadata['Target-Block-UUID'] !== '0093be2a-fd84-4925-a0c1-13f8a8260cbf')
1854
+ metadata['Source-Block-UUID'] !== '{{GS-UUID}}' &&
1855
+ metadata['Target-Block-UUID'] !== '{{GS-UUID}}')
1797
1856
  {
1798
1857
  patches.push({
1799
1858
  patchText: blockContent,
1800
1859
  metadata,
1801
1860
  sourceBlockUUID: metadata['Source-Block-UUID'],
1802
1861
  targetBlockUUID: metadata['Target-Block-UUID'],
1862
+ language: language,
1803
1863
  startIndex: match.index,
1804
1864
  endIndex: match.index + match[0].length
1805
1865
  });
@@ -2267,7 +2327,7 @@ var verifyAndCorrectLineNumbers_1 = {
2267
2327
 
2268
2328
  /**
2269
2329
  * Component: PatchUtils Verify and Correct Hunk Headers
2270
- * Block-UUID: 61c74798-bddb-47e1-be48-4a102659933c
2330
+ * Block-UUID: 013f7f2d-0077-4cec-bd22-3bf2fc81f222
2271
2331
  * Parent-UUID: 2308ed72-91ff-48ba-bc80-310c23c01ff1
2272
2332
  * Version: 1.0.0
2273
2333
  * Description: Verifies and corrects the hunk headers within a patch based on the line number prefixes in the content lines.
@@ -2466,7 +2526,7 @@ var verifyAndCorrectHunkHeaders_1 = {
2466
2526
 
2467
2527
  /**
2468
2528
  * Component: PatchUtils Detect and Fix Redundant Changes
2469
- * Block-UUID: ffd6e8fc-399d-4254-827c-ef17e2f7d0d5
2529
+ * Block-UUID: bb354364-b497-412c-9570-b39355da8153
2470
2530
  * Parent-UUID: 2308ed72-91ff-48ba-bc80-310c23c01ff1
2471
2531
  * Version: 1.0.0
2472
2532
  * Description: Detects and optionally fixes redundant changes in a patch where content is deleted and re-added identically.
@@ -2869,7 +2929,7 @@ var detectAndFixRedundantChanges_1 = {
2869
2929
 
2870
2930
  /**
2871
2931
  * Component: PatchUtils Detect and Fix Overlapping Hunks
2872
- * Block-UUID: 0e1b2c3d-4e5f-6a7b-8c9d-0e1f2a3b4c5d
2932
+ * Block-UUID: 7324dab8-24ae-4a42-9dc3-766e69104ef6
2873
2933
  * Parent-UUID: 2308ed72-91ff-48ba-bc80-310c23c01ff1
2874
2934
  * Version: 1.0.0
2875
2935
  * Description: Detects and optionally fixes overlapping hunks in a patch file by merging them.
@@ -8746,6 +8806,8 @@ var PatchUtils$2 = {
8746
8806
  * Authors: Gemini 2.5 Pro (v1.0.0), Gemini 2.5 Flash (v1.1.0)
8747
8807
  */
8748
8808
 
8809
+ const GS_TOOL_BLOCK_TYPE = 'gs-tool';
8810
+
8749
8811
  /**
8750
8812
  * Checks if a given code block content represents a GitSense Chat Tool Block.
8751
8813
  * It verifies if the first non-empty line starts with "# GitSense Chat Tool".
@@ -8764,6 +8826,65 @@ function isToolBlock$1(content) {
8764
8826
  return false;
8765
8827
  }
8766
8828
 
8829
+
8830
+ // TODO: Add JSDocs
8831
+ function getToolBlocksByTool(content, tool, CodeBlockUtils) {
8832
+ const { blocks, warnings } = CodeBlockUtils.extractCodeBlocks(content, { silent: true });
8833
+
8834
+ return blocks.filter((block, index) => {
8835
+ if (block.type !== GS_TOOL_BLOCK_TYPE)
8836
+ return;
8837
+
8838
+ try {
8839
+ const data = parseToolBlock$1(block.content);
8840
+
8841
+ if (data.tool === tool) {
8842
+ block.index = index;
8843
+ return block;
8844
+ }
8845
+ } catch(error) {
8846
+ // FIXME: We need a more elegant way to identify examples.
8847
+ if (!content.includes(/Internal INTEGER IDs/)) {
8848
+ console.warn(`Invalid tool block JSON: ${error.message}`);
8849
+ }
8850
+ }
8851
+ });
8852
+ }
8853
+
8854
+ // TODO: Add JSDocs
8855
+ function getToolBlockElemsByTool(dom, tool) {
8856
+ const elems = dom.querySelectorAll('pre');
8857
+ const toolBockElems = [];
8858
+
8859
+ for ( let i = 0; i < elems.length; i++ ) {
8860
+ const elem = elems[i];
8861
+ let content = elem.textContent;
8862
+
8863
+ // We need to strip out the first two lines since this is not part of the actual code
8864
+ // block. These two lines are designed to make it easy to identify the language
8865
+ content = content.split('\n').slice(2).join('\n');
8866
+
8867
+ if (!isToolBlock$1(content))
8868
+ continue;
8869
+
8870
+ try {
8871
+ const data = parseToolBlock$1(content, { silent: true });
8872
+
8873
+ if (data.tool === tool)
8874
+ toolBockElems.push(elem);
8875
+ } catch(error) {
8876
+ // FIXME: We need a more elegant way to identify examples.
8877
+ if (!content.match(/Internal INTEGER IDs/)) {
8878
+ console.warn("getToolBlockElemsByTool: ",error.message);
8879
+ }
8880
+ continue;
8881
+ }
8882
+ }
8883
+
8884
+ return toolBockElems;
8885
+ }
8886
+
8887
+
8767
8888
  /**
8768
8889
  * Parses the content of a GitSense Chat Tool Block to extract the JSON payload.
8769
8890
  * It strips the marker line and any subsequent lines starting with '#' (comments)
@@ -8872,7 +8993,7 @@ function replaceToolBlock(markdownContent, toolName, newToolData, CodeBlockUtils
8872
8993
  // 2. Iterate through the processed blocks to find the target GitSense Chat Tool Block
8873
8994
  for (let i = 0; i < blocks.length; i++) {
8874
8995
  const block = blocks[i];
8875
- if (block.type === 'gs-tool' && block.toolData && block.toolData.tool === toolName) {
8996
+ if (block.type === GS_TOOL_BLOCK_TYPE && block.toolData && block.toolData.tool === toolName) {
8876
8997
  targetBlockIndex = i;
8877
8998
  break; // Found the first matching tool block
8878
8999
  }
@@ -9006,11 +9127,15 @@ function detectAndFormatUnfencedToolBlock(messageContent) {
9006
9127
  };
9007
9128
  }
9008
9129
 
9130
+
9131
+
9009
9132
  var GSToolBlockUtils$3 = {
9010
9133
  isToolBlock: isToolBlock$1,
9011
9134
  parseToolBlock: parseToolBlock$1,
9012
9135
  formatToolBlock,
9013
9136
  replaceToolBlock,
9137
+ getToolBlocksByTool,
9138
+ getToolBlockElemsByTool,
9014
9139
  detectAndFormatUnfencedToolBlock,
9015
9140
  };
9016
9141
 
@@ -9310,16 +9435,16 @@ var JsonUtils$2 = {
9310
9435
  * Component: CodeBlockUtils Block Processor
9311
9436
  * Block-UUID: 1d8a559b-4f7a-4b0b-9e0a-13786f94a74e
9312
9437
  * Parent-UUID: 082abf8b-079f-4c95-8464-0e66c0de45eb
9313
- * Version: 1.4.0
9438
+ * Version: 1.5.0
9314
9439
  * Description: Processes the content of identified code blocks, parsing headers or patch details, and provides utilities like fixing UUIDs within blocks.
9315
9440
  * Language: JavaScript
9316
9441
  * Created-at: 2025-07-21T00:33:22.312Z
9317
- * 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)
9442
+ * 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), Qwen 3 Coder 480B - Cerebras (v1.5.0)
9318
9443
  */
9319
9444
 
9320
9445
  const { findAllCodeFences: findAllCodeFences$3, matchFencesAndExtractBlocks: matchFencesAndExtractBlocks$3 } = blockExtractor;
9321
9446
  const { parseHeader: parseHeader$2 } = headerUtils;
9322
- const { validateUUID: validateUUID$2, generateUUID: generateUUID$2 } = uuidUtils;
9447
+ const { validateUUID: validateUUID$2 } = uuidUtils;
9323
9448
  const AnalysisBlockUtils$2 = AnalysisBlockUtils$3;
9324
9449
  const PatchUtils$1 = PatchUtils$2;
9325
9450
  const GSToolBlockUtils$2 = GSToolBlockUtils$3;
@@ -9435,7 +9560,7 @@ function processBlockContent(content, language, position, incomplete, options =
9435
9560
  if (PatchUtils$1.isPatchBlock(content)) {
9436
9561
  const patchFormat = PatchUtils$1.determinePatchFormat(content);
9437
9562
  const metadata = PatchUtils$1.extractPatchMetadata(content); // Extract metadata regardless of validation
9438
- const patchContent = PatchUtils$1.extractPatchContent(content); // Extract raw patch content
9563
+ const patchContent = PatchUtils$1.extractPatchContent(content, patchFormat); // Extract raw patch content
9439
9564
 
9440
9565
  let patchValidationResult = { valid: true, errors: [], patches: null }; // Default valid state
9441
9566
 
@@ -9452,36 +9577,16 @@ function processBlockContent(content, language, position, incomplete, options =
9452
9577
  // For now, just warn, but return the extracted (potentially invalid) metadata.
9453
9578
  }
9454
9579
 
9455
- // Optionally validate context patch structure if requested
9456
- if (patchFormat === 'context' && validatePatches) {
9457
- try {
9458
- const patches = PatchUtils$1.extractContextPatches(content);
9459
- if (!patches || patches.length === 0) {
9460
- patchValidationResult = { valid: false, errors: ['No valid context patch content found.'], patches: null };
9461
- warnings.push({
9462
- position: position,
9463
- type: 'patch_content_error',
9464
- message: 'No valid context patch content found.',
9465
- content: content,
9466
- });
9467
- } else {
9468
- // Basic validation passed (structure was parsable)
9469
- patchValidationResult = { valid: true, errors: [], patches: patches };
9470
- }
9471
- } catch (error) {
9472
- patchValidationResult = { valid: false, errors: [error.message], patches: null };
9473
- warnings.push({
9474
- position: position,
9475
- type: 'patch_parse_error',
9476
- message: `Error parsing context patch: ${error.message}`,
9477
- content: content,
9478
- });
9479
- }
9580
+ // Determine the correct language for the block
9581
+ let blockLanguage = language; // Default to the fence language
9582
+ if (patchFormat === 'traditional') {
9583
+ blockLanguage = 'diff'; // Enforce 'diff' for traditional patches
9480
9584
  }
9585
+ // For abbreviated patches, keep the original language (e.g., 'javascript', 'python')
9481
9586
 
9482
9587
  return {
9483
9588
  type: 'patch',
9484
- language: language === 'diff' ? 'diff' : language, // Use 'diff' if specified, else keep original
9589
+ language: blockLanguage, // Use determined language
9485
9590
  content: content, // Full original content within fences
9486
9591
  position: position,
9487
9592
  incomplete: incomplete,
@@ -9675,8 +9780,10 @@ function processBlockContents(text, completeBlocks, incompleteBlocks, options) {
9675
9780
  * @returns {Object} { blocks: Array, warnings: Array, hasIncompleteBlocks: boolean, lastIncompleteBlock: Object|null }
9676
9781
  */
9677
9782
  function processCodeBlocks$5(text, options = { silent: false, validatePatches: false }) {
9678
- if (typeof text !== "string") { // Allow empty strings
9679
- console.warn("Warning: Input must be a string.");
9783
+ if (typeof text !== "string") {
9784
+ if (text != null )
9785
+ console.warn("Warning: Input must be a string.");
9786
+
9680
9787
  return {
9681
9788
  blocks: [],
9682
9789
  warnings: [{ type: 'invalid_input', message: 'Input must be a string.' }],
@@ -10597,19 +10704,114 @@ var updateCodeBlock_1 = {
10597
10704
  updateCodeBlockInMessage: updateCodeBlockInMessage$1,
10598
10705
  deleteCodeBlockByIndex: deleteCodeBlockByIndex$2};
10599
10706
 
10707
+ /**
10708
+ * Component: CodeBlockUtils Lineage Tracer
10709
+ * Block-UUID: 13b08cce-6fd1-4ec9-a915-240714ee89b9
10710
+ * Parent-UUID: df1a629b-2cc0-4677-b410-3172ee0e0f9f
10711
+ * Version: 1.1.0
10712
+ * Description: Provides a utility function to trace the full lineage (patch-based and parent-based) of code blocks within a chat, including loop detection.
10713
+ * Language: JavaScript
10714
+ * Created-at: 2025-10-23T01:28:10.456Z
10715
+ * Authors: Qwen 3 Coder 480B - Cerebras (v1.0.0), Qwen 3 Coder 480B - Cerebras (v1.1.0)
10716
+ */
10717
+
10718
+ /**
10719
+ * Traces the full lineage of a code block or patch identified by a UUID.
10720
+ * The lineage includes the block itself and all its ancestors, tracing backwards
10721
+ * first through patch relationships (Source-Block-UUID -> Target-Block-UUID)
10722
+ * and then through Parent-UUID relationships.
10723
+ * Loop detection is included to prevent infinite tracing.
10724
+ *
10725
+ * @param {string} uuid - The UUID of the target code block or patch.
10726
+ * @param {Map<string, object>} blockMap - A map where keys are UUIDs (for code blocks)
10727
+ * or patch UUIDs (source:target) and values
10728
+ * are the corresponding block objects.
10729
+ * Block objects are expected to have
10730
+ * properties like `isPatch`, `metadata`, and `header`.
10731
+ * @returns {Array<object>} An array of block objects representing the lineage.
10732
+ * The first element is the block for the provided `uuid`.
10733
+ * Subsequent elements are its ancestors.
10734
+ * Returns an empty array if the initial uuid is not found.
10735
+ * If a loop is detected, the last element will have a property `loopDetected: true`.
10736
+ */
10737
+ function getLineage$2(uuid, blockMap) {
10738
+ if (!uuid || !blockMap) {
10739
+ return [];
10740
+ }
10741
+
10742
+ const lineage = [];
10743
+ const visitedUuids = new Set();
10744
+ let currentUuid = uuid;
10745
+
10746
+ while (currentUuid) {
10747
+ // --- Loop Detection ---
10748
+ if (visitedUuids.has(currentUuid)) {
10749
+ // Add a marker or the block itself to indicate the loop
10750
+ const loopBlock = blockMap.get(currentUuid) || { uuid: currentUuid, loopDetected: true, type: 'loop-marker' };
10751
+ loopBlock.loopDetected = true; // Ensure the property is set
10752
+ lineage.push(loopBlock);
10753
+ break; // Stop the trace
10754
+ }
10755
+ visitedUuids.add(currentUuid);
10756
+
10757
+ // --- 1. Try to find the block directly by its UUID ---
10758
+ let block = blockMap.get(currentUuid);
10759
+
10760
+ // --- 2. If not found directly, search patch keys ---
10761
+ if (!block) {
10762
+ for (const [key, value] of blockMap) {
10763
+ // Check if the map key is a patch key (contains ':') and ends with our target UUID
10764
+ if (key.includes(':') && key.endsWith(`:${currentUuid}`)) {
10765
+ block = value;
10766
+ break;
10767
+ }
10768
+ }
10769
+ }
10770
+
10771
+ // --- 3. If a block was found, add it to the lineage ---
10772
+ if (block) {
10773
+ lineage.push(block);
10774
+
10775
+ let nextUuid = null;
10776
+
10777
+ // --- 3a. If it's a patch, move to its source UUID ---
10778
+ if (block.isPatch && block.metadata && block.metadata['Source-Block-UUID']) {
10779
+ nextUuid = block.metadata['Source-Block-UUID'];
10780
+ }
10781
+ // --- 3b. If it's a code block, move to its Parent-UUID (if valid) ---
10782
+ else if (!block.isPatch && block.header && block.header['Parent-UUID'] && block.header['Parent-UUID'] !== 'N/A') {
10783
+ nextUuid = block.header['Parent-UUID'];
10784
+ }
10785
+ // If it's a code block with Parent-UUID: 0c104865-260a-4942-8cb6-93bebf40c450
10786
+
10787
+ currentUuid = nextUuid;
10788
+ } else {
10789
+ // If no block is found for currentUuid, the lineage is incomplete.
10790
+ // We could add a placeholder here if needed, but for now, we just stop.
10791
+ currentUuid = null;
10792
+ }
10793
+ }
10794
+
10795
+ return lineage;
10796
+ }
10797
+
10798
+ var lineageTracer = {
10799
+ getLineage: getLineage$2
10800
+ };
10801
+
10600
10802
  /**
10601
10803
  * Component: CodeBlockUtils Index
10602
- * Block-UUID: 7e9d3f8a-1b2c-4d5e-8f9a-0123456789ab
10603
- * Parent-UUID: a3b9c8d1-e0f2-4a1b-8c7d-5e6f0a9b1c2d
10604
- * Version: 1.4.0
10804
+ * Block-UUID: 98684aa5-d597-41b6-a7fd-cc16482aafdc
10805
+ * Parent-UUID: 7e9d3f8a-1b2c-4d5e-8f9a-0123456789ab
10806
+ * Version: 1.5.0
10605
10807
  * Description: Aggregates and exports all utilities related to code block processing from the CodeBlockUtils module. Serves as the main entry point for this module.
10606
10808
  * Language: JavaScript
10607
10809
  * Created-at: 2025-04-15T16:02:20.217Z
10608
- * Authors: Gemini 2.5 Pro (v1.0.0), Gemini 2.5 Pro (v1.1.0), Claude 3.7 Sonnet (v1.2.0), Gemini 2.5 Pro (v1.3.0), Gemini 2.5 Flash Thinking (v1.4.0)
10810
+ * Authors: Gemini 2.5 Pro (v1.0.0), Gemini 2.5 Pro (v1.1.0), Claude 3.7 Sonnet (v1.2.0), Gemini 2.5 Pro (v1.3.0), Gemini 2.5 Flash Thinking (v1.4.0), Qwen 3 Coder 480B - Cerebras (v1.5.0)
10609
10811
  */
10610
10812
 
10611
10813
  // Import from individual utility files
10612
- const { COMMENT_STYLES: COMMENT_STYLES$1 } = constants$2;
10814
+ const { COMMENT_STYLES: COMMENT_STYLES$1 } = constants$3;
10613
10815
  const { generateUUID: generateUUID$1, validateUUID: validateUUID$1 } = uuidUtils;
10614
10816
  const { isValidISOTimestamp: isValidISOTimestamp$1, parseHeader: parseHeader$1, getHeaderLineCount } = headerUtils;
10615
10817
  const { findAllCodeFences: findAllCodeFences$1, matchFencesAndExtractBlocks: matchFencesAndExtractBlocks$1, extractCodeBlocksWithUUIDs, findCodeBlockByUUID } = blockExtractor;
@@ -10621,9 +10823,10 @@ const { parseCodeBlocks: parseCommentDelimitedBlocks$1 } = headerParser;
10621
10823
  const { removeCodeBlockMarkers: removeCodeBlockMarkers$1 } = markerRemover;
10622
10824
  const { updateCodeBlockByIndex: updateCodeBlockByIndex$1, updateCodeBlockByUUID, updateCodeBlock, updateCodeBlockInMessage, deleteCodeBlockByIndex: deleteCodeBlockByIndex$1 } = updateCodeBlock_1;
10623
10825
  const { formatWithLineNumbers: formatWithLineNumbers$1, formatBlockWithLineNumbers: formatBlockWithLineNumbers$1, formatBlocksWithLineNumbers: formatBlocksWithLineNumbers$1, removeLineNumbers: removeLineNumbers$1 } = lineNumberFormatter;
10826
+ const { getLineage: getLineage$1 } = lineageTracer;
10624
10827
 
10625
10828
  // Export all imported items
10626
- var CodeBlockUtils$5 = {
10829
+ var CodeBlockUtils$7 = {
10627
10830
  // Constants
10628
10831
  COMMENT_STYLES: COMMENT_STYLES$1,
10629
10832
 
@@ -10675,22 +10878,23 @@ var CodeBlockUtils$5 = {
10675
10878
  // Line Number Formatting Utilities
10676
10879
  formatWithLineNumbers: formatWithLineNumbers$1,
10677
10880
  formatBlockWithLineNumbers: formatBlockWithLineNumbers$1,
10678
- formatBlocksWithLineNumbers: formatBlocksWithLineNumbers$1,
10881
+ formatBlocksWithLineNumbers: formatBlocksWithLineNumbers$1,
10882
+ getLineage: getLineage$1,
10679
10883
  removeLineNumbers: removeLineNumbers$1,
10680
10884
  };
10681
10885
 
10682
10886
  /*
10683
10887
  * Component: ContextUtils
10684
- * Block-UUID: a0b71292-b1cc-401a-8ce2-544a047b0fef
10685
- * Parent-UUID: c018b1f9-2291-4bc9-9c4b-ab53a5db745e
10686
- * Version: 1.3.0
10888
+ * Block-UUID: 534c348d-a11f-4de6-ad15-f33068d51fb8
10889
+ * Parent-UUID: a0b71292-b1cc-401a-8ce2-544a047b0fef
10890
+ * Version: 1.4.0
10687
10891
  * Description: Provides utility functions for parsing context message sections to extract file details and code blocks, and for formatting content for LLM context.
10688
10892
  * Language: JavaScript
10689
10893
  * Created-at: 2025-05-09T01:36:20.107Z
10690
- * 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)
10894
+ * 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)
10691
10895
  */
10692
10896
 
10693
- const CodeBlockUtils$4 = CodeBlockUtils$5;
10897
+ const CodeBlockUtils$6 = CodeBlockUtils$7;
10694
10898
  const MessageUtils$2 = MessageUtils$3;
10695
10899
 
10696
10900
  /**
@@ -10757,6 +10961,41 @@ function _createContextSummary(items, contentType) {
10757
10961
  return summary + "\n";
10758
10962
  }
10759
10963
 
10964
+ /**
10965
+ * Extracts all context files from a given chat object.
10966
+ * Iterates through chat messages, identifies context loader outputs,
10967
+ * and parses them into individual file objects.
10968
+ *
10969
+ * @param {object} chat - The GitSense chat object.
10970
+ * @returns {Array<object>} An array of parsed context file objects.
10971
+ * Returns an empty array if no context files are found or on error.
10972
+ */
10973
+ function getContextFiles$1(chat) {
10974
+ const ChatUtils = ChatUtils$2;
10975
+ const contextFiles = [];
10976
+ const messages = ChatUtils.getChatMessages(chat);
10977
+
10978
+ if (!Array.isArray(messages)) {
10979
+ console.warn("getContextFiles: Provided chat object does not contain a valid messages array.");
10980
+ return contextFiles;
10981
+ }
10982
+
10983
+ messages.forEach(message => {
10984
+ // Check if the message content is a context message
10985
+ if (MessageUtils$2.isContextMessage(message.message)) {
10986
+ try {
10987
+ // Extract all context sections from the message content
10988
+ const extractedFiles = extractContextSections$1(message.message);
10989
+ contextFiles.push(...extractedFiles);
10990
+ } catch (error) {
10991
+ console.warn(`getContextFiles: Failed to extract context sections from message ID ${message.id}:`, error);
10992
+ }
10993
+ }
10994
+ });
10995
+
10996
+ return contextFiles;
10997
+ }
10998
+
10760
10999
  /**
10761
11000
  * Escapes backticks in code blocks to prevent premature termination of LLM-generated code blocks.
10762
11001
  * @param {string} content - The content string to escape.
@@ -10829,7 +11068,7 @@ function parseContextSection$1(sectionText) {
10829
11068
  }
10830
11069
  });
10831
11070
 
10832
- const { blocks, warnings } = CodeBlockUtils$4.extractCodeBlocks(sectionText, { silent: true });
11071
+ const { blocks, warnings } = CodeBlockUtils$6.extractCodeBlocks(sectionText, { silent: true });
10833
11072
  const codeBlocks = blocks.filter(block => block.type === 'code');
10834
11073
 
10835
11074
  if (codeBlocks.length === 0) {
@@ -11031,6 +11270,7 @@ function formatContextContent$1(items, contentType, contentOption) {
11031
11270
  var ContextUtils$2 = {
11032
11271
  parseContextSection: parseContextSection$1,
11033
11272
  extractContextSections: extractContextSections$1,
11273
+ getContextFiles: getContextFiles$1,
11034
11274
  extractContextItemsOverviewTableRows: extractContextItemsOverviewTableRows$1,
11035
11275
  formatContextContent: formatContextContent$1,
11036
11276
  };
@@ -11088,7 +11328,7 @@ var contextMapper = {
11088
11328
 
11089
11329
  /*
11090
11330
  * Component: AnalyzerUtils Constants
11091
- * Block-UUID: 01b2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d
11331
+ * Block-UUID: fec5dcc4-a1d0-4ef7-8828-3e44a75892c4
11092
11332
  * Parent-UUID: N/A
11093
11333
  * Version: 1.0.0
11094
11334
  * Description: Defines constants specific to the AnalyzerUtils module.
@@ -11114,7 +11354,7 @@ var constants = {
11114
11354
  * Authors: Gemini 2.5 Flash (v1.0.0)
11115
11355
  */
11116
11356
 
11117
- const CodeBlockUtils$3 = CodeBlockUtils$5;
11357
+ const CodeBlockUtils$5 = CodeBlockUtils$7;
11118
11358
  const GSToolBlockUtils$1 = GSToolBlockUtils$3;
11119
11359
  const JsonUtils$1 = JsonUtils$2;
11120
11360
  const { ANALYZE_HEADER_PREFIX } = constants;
@@ -11131,7 +11371,7 @@ const { ANALYZE_HEADER_PREFIX } = constants;
11131
11371
  */
11132
11372
  function processLLMAnalysisResponse$2(messageContent, stoppedStreaming) {
11133
11373
  const silent = { silent: true };
11134
- const { blocks, warnings } = CodeBlockUtils$3.extractCodeBlocks(messageContent, silent);
11374
+ const { blocks, warnings } = CodeBlockUtils$5.extractCodeBlocks(messageContent, silent);
11135
11375
 
11136
11376
  const analysisBlocks = [];
11137
11377
  const analysisMetadataBlocks = [];
@@ -11316,7 +11556,7 @@ var dataValidator = {
11316
11556
 
11317
11557
  /*
11318
11558
  * Component: AnalyzerUtils Instruction Loader
11319
- * Block-UUID: 0a1b2c3d-4e5f-6a7b-8c9d-0e1f2a3b4c5e
11559
+ * Block-UUID: 3ecd422e-1dd8-482d-ae3f-90a5eae3ae44
11320
11560
  * Parent-UUID: N/A
11321
11561
  * Version: 1.0.0
11322
11562
  * Description: Provides utility functions for loading raw analyzer instruction content.
@@ -11325,19 +11565,19 @@ var dataValidator = {
11325
11565
  * Authors: Gemini 2.5 Flash (v1.0.0)
11326
11566
  */
11327
11567
 
11328
- const fs$7 = require$$0.promises;
11329
- const path$5 = require$$1;
11568
+ const fs$8 = require$$0.promises;
11569
+ const path$6 = require$$1;
11330
11570
 
11331
11571
  /**
11332
11572
  * Retrieves the raw Markdown content of the analyzer's '1.md' instruction file.
11333
11573
  *
11334
- * @param {string} analyzeMessagesBasePath - The absolute path to the base directory containing the analyzer message files (e.g., 'messages/analyze').
11574
+ * @param {string} analyzersBasePath - The absolute path to the base directory containing the analyzer message files (e.g., 'messages/analyze').
11335
11575
  * @param {string} analyzerId - The unique ID of the analyzer (format: 'analyzer_name::content_type::instructions_type').
11336
11576
  * @returns {Promise<string|null>} A promise that resolves with the full Markdown content of the '1.md' file, or null if not found/invalid.
11337
11577
  */
11338
- async function getAnalyzerInstructionsContent$3(analyzeMessagesBasePath, analyzerId) {
11339
- if (typeof analyzeMessagesBasePath !== 'string' || analyzeMessagesBasePath.trim() === '') {
11340
- console.error('Error: analyzeMessagesBasePath is required.');
11578
+ async function getAnalyzerInstructionsContent$3(analyzersBasePath, analyzerId) {
11579
+ if (typeof analyzersBasePath !== 'string' || analyzersBasePath.trim() === '') {
11580
+ console.error('Error: analyzersBasePath is required.');
11341
11581
  return null;
11342
11582
  }
11343
11583
  if (typeof analyzerId !== 'string' || analyzerId.trim() === '') {
@@ -11352,10 +11592,10 @@ async function getAnalyzerInstructionsContent$3(analyzeMessagesBasePath, analyze
11352
11592
  }
11353
11593
  const [analyzerName, contentType, instructionsType] = parts;
11354
11594
 
11355
- const instructionsFilePath = path$5.join(analyzeMessagesBasePath, analyzerName, contentType, instructionsType, '1.md');
11595
+ const instructionsFilePath = path$6.join(analyzersBasePath, analyzerName, contentType, instructionsType, '1.md');
11356
11596
 
11357
11597
  try {
11358
- const fileContent = await fs$7.readFile(instructionsFilePath, 'utf8');
11598
+ const fileContent = await fs$8.readFile(instructionsFilePath, 'utf8');
11359
11599
  const parts = fileContent.split('\n\n\n');
11360
11600
  parts.shift();
11361
11601
  return parts.join('\n\n\n');
@@ -11375,20 +11615,56 @@ var instructionLoader = {
11375
11615
  };
11376
11616
 
11377
11617
  /*
11618
+ * Component: AnalyzerUtils JSON Parser
11619
+ * Block-UUID: 4dd21efd-3ad3-43e1-adf0-d52d7c560970
11620
+ * Version: 1.0.0
11621
+ * Description: Provides utility functions for pre-processing JSON content from analyzer instructions.
11622
+ * Language: JavaScript
11623
+ * Created-at: 2025-11-23T05:38:15.725Z
11624
+ * Authors: GLM-4.6 (v1.0.0)
11625
+ */
11626
+
11627
+ /**
11628
+ * Pre-processes JSON content to quote unquoted template strings before parsing.
11629
+ * This handles cases where {{SYSTEM: ...}} and {{ANALYZER: ...}} placeholders
11630
+ * are not properly quoted in the JSON.
11631
+ *
11632
+ * @param {string} jsonString - The raw JSON string content.
11633
+ * @returns {string} The processed JSON string with template strings quoted.
11634
+ */
11635
+ function preprocessJsonForValidation$5(jsonString) {
11636
+ // Find all unquoted template strings and quote them with proper escaping
11637
+ // This regex looks for template strings that aren't already quoted
11638
+ return jsonString.replace(
11639
+ /(?<!")(\{\{(SYSTEM|ANALYZER):[^}]+\}\})(?!")/g,
11640
+ (match, template) => {
11641
+ // Escape any double quotes within the template string
11642
+ const escapedTemplate = template.replace(/"/g, '\\"');
11643
+ return `"${escapedTemplate}"`;
11644
+ }
11645
+ );
11646
+ }
11647
+
11648
+ var jsonParser = {
11649
+ preprocessJsonForValidation: preprocessJsonForValidation$5
11650
+ };
11651
+
11652
+ /**
11378
11653
  * Component: AnalyzerUtils Discovery
11379
- * Block-UUID: a87a86c4-69fc-4cbb-80a8-857f56122395
11380
- * Parent-UUID: 0b1c2d3e-4f5a-6b7c-8d9e-0f1a2b3c4d5f
11381
- * Version: 1.1.0
11382
- * Description: Provides utility functions for discovering available analyzers.
11654
+ * Block-UUID: 26a7df9b-ef29-4dcd-aab2-9cdc3a11133c
11655
+ * Parent-UUID: aa999515-84fb-43f6-91a7-cf79248d7286
11656
+ * Version: 1.3.1
11657
+ * Description: Provides utility functions for discovering available analyzers. Updated to include version and tags in analyzer objects.
11383
11658
  * Language: JavaScript
11384
- * Created-at: 2025-08-28T23:48:00.000Z
11385
- * Authors: Gemini 2.5 Flash (v1.0.0), Gemini 2.5 Flash (v1.1.0)
11659
+ * Created-at: 2025-11-27T14:45:30.978Z
11660
+ * Authors: Gemini 2.5 Flash (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)
11386
11661
  */
11387
11662
 
11388
- const fs$6 = require$$0.promises;
11389
- const path$4 = require$$1;
11663
+ const fs$7 = require$$0.promises;
11664
+ const path$5 = require$$1;
11390
11665
  const { getAnalyzerInstructionsContent: getAnalyzerInstructionsContent$2 } = instructionLoader;
11391
- const CodeBlockUtils$2 = CodeBlockUtils$5;
11666
+ const { preprocessJsonForValidation: preprocessJsonForValidation$4 } = jsonParser;
11667
+ const CodeBlockUtils$4 = CodeBlockUtils$7;
11392
11668
 
11393
11669
  /**
11394
11670
  * Reads and parses the config.json file in a directory.
@@ -11396,10 +11672,10 @@ const CodeBlockUtils$2 = CodeBlockUtils$5;
11396
11672
  * @returns {Promise<object|null>} A promise that resolves to the parsed config object
11397
11673
  * or null if the file doesn't exist or is invalid.
11398
11674
  */
11399
- async function readConfig$1(dirPath) {
11400
- const configPath = path$4.join(dirPath, 'config.json');
11675
+ async function readConfig$2(dirPath) {
11676
+ const configPath = path$5.join(dirPath, 'config.json');
11401
11677
  try {
11402
- const fileContent = await fs$6.readFile(configPath, 'utf8');
11678
+ const fileContent = await fs$7.readFile(configPath, 'utf8');
11403
11679
  return JSON.parse(fileContent);
11404
11680
  } catch (error) {
11405
11681
  if (error.code !== 'ENOENT') {
@@ -11411,12 +11687,12 @@ async function readConfig$1(dirPath) {
11411
11687
  }
11412
11688
 
11413
11689
  /**
11414
- * Checks if a directory name is valid based on the rules in messages/analyze/README.md.
11690
+ * Checks if a directory name is valid
11415
11691
  * Allowed: a-z, A-Z, 0-9, dash (-), underscore (_). Cannot start with underscore or contain dots.
11416
11692
  * @param {string} name - The directory name to check.
11417
11693
  * @returns {boolean} True if the name is valid, false otherwise.
11418
11694
  */
11419
- function isValidDirName(name) {
11695
+ function isValidDirName$2(name) {
11420
11696
  // Exclude names starting with underscore or containing dots
11421
11697
  if (name.startsWith('_') || name.includes('.')) {
11422
11698
  return false;
@@ -11429,51 +11705,53 @@ function isValidDirName(name) {
11429
11705
  * Discovers and lists all available analyzers by traversing the directory structure.
11430
11706
  * An analyzer is considered valid if a '1.md' file exists in the instructions directory.
11431
11707
  *
11432
- * @param {string} analyzeMessagesBasePath - The absolute or relative path to the base directory containing the analyzer message files (e.g., 'messages/analyze').
11708
+ * @param {string} analyzersBasePath - The absolute path to the base directory containing the analyzers (e.g., 'analyzers/analyzer-1').
11433
11709
  * @param {object} [options={}] - Optional configuration.
11434
- * @param {boolean} [options.includeDescription=false] - Whether to include the description of the analyzer.
11435
- * @returns {Promise<Array<{id: string, label: string, name: string, protected: boolean, description?: string}>>} A promise that resolves to an array of analyzer objects.
11710
+ * @returns {Promise<Array<{id: string, label: string, name: string, protected: boolean, description?: string, version?: string, tags?: Array<string>}>>} A promise that resolves to an array of analyzer objects.
11436
11711
  */
11437
- async function getAnalyzers$2(analyzeMessagesBasePath, options = {}) {
11438
- const { includeDescription = false } = options;
11712
+ async function getAnalyzers$3(analyzersBasePath, options = {}) {
11439
11713
  const analyzers = [];
11714
+ const demoAnalyzerPattern = /^demo-.+-\w{6}$/;
11440
11715
 
11441
11716
  try {
11442
- const analyzerEntries = await fs$6.readdir(analyzeMessagesBasePath, { withFileTypes: true });
11717
+ const analyzerEntries = await fs$7.readdir(analyzersBasePath, { withFileTypes: true });
11443
11718
 
11444
11719
  for (const analyzerEntry of analyzerEntries) {
11445
- if (analyzerEntry.isDirectory() && isValidDirName(analyzerEntry.name)) {
11720
+ if (analyzerEntry.isDirectory() && isValidDirName$2(analyzerEntry.name)) {
11446
11721
  const analyzerName = analyzerEntry.name;
11447
- const analyzerPath = path$4.join(analyzeMessagesBasePath, analyzerName);
11448
- const analyzerConfig = await readConfig$1(analyzerPath);
11722
+ const analyzerPath = path$5.join(analyzersBasePath, analyzerName);
11723
+ const analyzerConfig = await readConfig$2(analyzerPath);
11449
11724
  const analyzerLabel = analyzerConfig?.label || analyzerName;
11450
11725
 
11451
- const contentEntries = await fs$6.readdir(analyzerPath, { withFileTypes: true });
11726
+ const contentEntries = await fs$7.readdir(analyzerPath, { withFileTypes: true });
11452
11727
 
11453
11728
  for (const contentEntry of contentEntries) {
11454
- if (contentEntry.isDirectory() && isValidDirName(contentEntry.name)) {
11729
+ if (contentEntry.isDirectory() && isValidDirName$2(contentEntry.name)) {
11455
11730
  const contentType = contentEntry.name;
11456
- const contentPath = path$4.join(analyzerPath, contentType);
11457
- const contentConfig = await readConfig$1(contentPath);
11731
+ const contentPath = path$5.join(analyzerPath, contentType);
11732
+ const contentConfig = await readConfig$2(contentPath);
11458
11733
  const contentLabel = contentConfig?.label || contentType;
11459
11734
 
11460
- const instructionsEntries = await fs$6.readdir(contentPath, { withFileTypes: true });
11735
+ const instructionsEntries = await fs$7.readdir(contentPath, { withFileTypes: true });
11461
11736
 
11462
11737
  for (const instructionsEntry of instructionsEntries) {
11463
- if (instructionsEntry.isDirectory() && isValidDirName(instructionsEntry.name)) {
11738
+ if (instructionsEntry.isDirectory() && isValidDirName$2(instructionsEntry.name)) {
11464
11739
  const instructionsType = instructionsEntry.name;
11465
- const instructionsPath = path$4.join(contentPath, instructionsType);
11466
- const instructionsConfig = await readConfig$1(instructionsPath);
11740
+ const instructionsPath = path$5.join(contentPath, instructionsType);
11741
+ const instructionsConfig = await readConfig$2(instructionsPath);
11467
11742
  const instructionsLabel = instructionsConfig?.label || instructionsType;
11468
11743
 
11744
+ // Construct the analyzer ID and label
11745
+ const analyzerId = `${analyzerName}::${contentType}::${instructionsType}`;
11746
+
11469
11747
  // Check for the existence of 1.md to confirm a valid analyzer configuration
11470
- const instructionsFilePath = path$4.join(instructionsPath, '1.md');
11748
+ const instructionsFilePath = path$5.join(instructionsPath, '1.md');
11471
11749
  try {
11472
- await fs$6.access(instructionsFilePath); // Check if file exists and is accessible
11750
+ await fs$7.access(instructionsFilePath); // Check if file exists and is accessible
11473
11751
 
11474
11752
  // If analyzerName starts with 'tutorial-', check its last modified time.
11475
- if (analyzerName.startsWith('tutorial-')) {
11476
- const stats = await fs$6.stat(instructionsFilePath);
11753
+ if (analyzerName.startsWith('tutorial-') || demoAnalyzerPattern.test(analyzerName)) {
11754
+ const stats = await fs$7.stat(instructionsFilePath);
11477
11755
  const lastModified = stats.mtime.getTime(); // Get timestamp in milliseconds
11478
11756
  const sixtyMinutesAgo = Date.now() - (60 * 60 * 1000); // Current time - 60 minutes in ms
11479
11757
 
@@ -11482,37 +11760,43 @@ async function getAnalyzers$2(analyzeMessagesBasePath, options = {}) {
11482
11760
  continue;
11483
11761
  }
11484
11762
  }
11485
- // Construct the analyzer ID and label
11486
- const analyzerId = `${analyzerName}::${contentType}::${instructionsType}`;
11487
- const analyzerFullLabel = `${analyzerLabel} (${contentLabel} - ${instructionsLabel})`;
11763
+ // TODO: Decide if we should show the contentLabel and instructionsLabel. For now we will ignore them.
11764
+ const analyzerFullLabel = `${analyzerLabel}`; // (${contentLabel} - ${instructionsLabel})`;
11488
11765
 
11766
+ // Extract description, version, and tags from the JSON block in 1.md
11489
11767
  let description = null;
11490
- if (includeDescription) {
11491
- try {
11492
- const content = await getAnalyzerInstructionsContent$2(analyzeMessagesBasePath, analyzerId);
11493
- const { blocks, warnings } = CodeBlockUtils$2.extractCodeBlocks(content, { silent: true });
11494
- const jsonBlock = blocks.find(block => block.language === 'json');
11495
-
11496
- if (jsonBlock && jsonBlock.content) {
11497
- try {
11498
- const json = JSON.parse(jsonBlock.content);
11499
- description = json.description;
11500
- } catch(error) {
11501
- console.warn(`${analyzerId} contains an invalid JSON`);
11502
- }
11503
- }
11504
- } catch (descError) {
11505
- console.warn(`Warning: Could not load description for ${analyzerId}: ${descError.message}`);
11506
- descriptionContent = null;
11768
+ let version = null;
11769
+ let tags = [];
11770
+ let label = null;
11771
+ let requires_reference_files = null;
11772
+
11773
+ try {
11774
+ const content = await getAnalyzerInstructionsContent$2(analyzersBasePath, analyzerId);
11775
+ const { blocks, warnings } = CodeBlockUtils$4.extractCodeBlocks(content, { silent: true });
11776
+ const jsonBlock = blocks.find(block => block.language === 'json');
11777
+
11778
+ if (jsonBlock && jsonBlock.content) {
11779
+ const preprocessedContent = preprocessJsonForValidation$4(jsonBlock.content);
11780
+ const json = JSON.parse(preprocessedContent);
11781
+ description = json.description || null;
11782
+ label = json.label || null;
11783
+ requires_reference_files = json.requires_reference_files || false;
11784
+ version = json.version || null;
11785
+ tags = Array.isArray(json.tags) ? json.tags : [];
11507
11786
  }
11787
+ } catch (descError) {
11788
+ console.warn(`Warning: Could not load metadata for ${analyzerId}: ${descError.message}`);
11508
11789
  }
11509
11790
 
11510
11791
  analyzers.push({
11511
11792
  id: analyzerId,
11512
- label: analyzerFullLabel,
11513
11793
  name: analyzerName,
11794
+ description,
11795
+ label: label || analyzerFullLabel,
11796
+ requires_reference_files,
11514
11797
  protected: analyzerConfig?.protected || false,
11515
- description
11798
+ version,
11799
+ tags
11516
11800
  });
11517
11801
  } catch (error) {
11518
11802
  // If 1.md doesn't exist, this is not a complete analyzer configuration, skip.
@@ -11527,7 +11811,7 @@ async function getAnalyzers$2(analyzeMessagesBasePath, options = {}) {
11527
11811
  }
11528
11812
  }
11529
11813
  } catch (error) {
11530
- console.error(`Error traversing analyze messages directory ${analyzeMessagesBasePath}: ${error.message}`);
11814
+ console.error(`Error traversing analyzers directory ${analyzersBasePath}: ${error.message}`);
11531
11815
  // Depending on requirements, you might want to throw the error or return an empty array
11532
11816
  throw error; // Re-throw to indicate failure
11533
11817
  }
@@ -11536,8 +11820,10 @@ async function getAnalyzers$2(analyzeMessagesBasePath, options = {}) {
11536
11820
  }
11537
11821
 
11538
11822
  var discovery = {
11539
- getAnalyzers: getAnalyzers$2,
11540
- readConfig: readConfig$1};
11823
+ getAnalyzers: getAnalyzers$3,
11824
+ readConfig: readConfig$2,
11825
+ isValidDirName: isValidDirName$2,
11826
+ };
11541
11827
 
11542
11828
  /**
11543
11829
  * Component: Analyzer Saver Utility
@@ -11550,8 +11836,8 @@ var discovery = {
11550
11836
  * Authors: Gemini 2.5 Flash Thinking (v1.0.0), Gemini 2.5 Flash Thinking (v1.1.0)
11551
11837
  */
11552
11838
 
11553
- const fs$5 = require$$0.promises;
11554
- const path$3 = require$$1;
11839
+ const fs$6 = require$$0.promises;
11840
+ const path$4 = require$$1;
11555
11841
 
11556
11842
  /**
11557
11843
  * Saves or updates an analyzer configuration.
@@ -11561,19 +11847,19 @@ const path$3 = require$$1;
11561
11847
  * if necessary, saves the instructions to '1.md'. Optionally, it can
11562
11848
  * ensure config.json files exist with labels derived from directory names.
11563
11849
  *
11564
- * @param {string} analyzeMessagesBasePath - The absolute or relative path to the 'messages/analyze' directory.
11850
+ * @param {string} analyzersBasePath - The absolute path to the base directory containing the analyzers (e.g., 'analyzers/analyzer-1').
11565
11851
  * @param {string} analyzerId - The unique ID of the analyzer (format: 'analyzer_name::content_type::instructions_type').
11566
11852
  * @param {string} instructionsContent - The full content of the analyzer instructions message to be saved in '1.md'.
11567
11853
  * @param {object} [options={}] - Optional configuration options.
11568
11854
  * @param {boolean} [options.ensureConfigs=false] - If true, ensures config.json files exist in the analyzer, content, and instructions directories. Defaults to false.
11569
11855
  * @returns {Promise<{success: boolean, message?: string}>} A promise that resolves with a result object.
11570
11856
  */
11571
- async function saveConfiguration$1(analyzeMessagesBasePath, analyzerId, instructionsContent, options = {}) {
11857
+ async function saveConfiguration$2(analyzersBasePath, analyzerId, instructionsContent, options = {}) {
11572
11858
  const { ensureConfigs = false } = options;
11573
11859
 
11574
11860
  // 1. Validate inputs
11575
- if (typeof analyzeMessagesBasePath !== 'string' || analyzeMessagesBasePath.trim() === '') {
11576
- return { success: false, message: 'analyzeMessagesBasePath is required.' };
11861
+ if (typeof analyzersBasePath !== 'string' || analyzersBasePath.trim() === '') {
11862
+ return { success: false, message: 'analyzersBasePath is required.' };
11577
11863
  }
11578
11864
  if (typeof analyzerId !== 'string' || analyzerId.trim() === '') {
11579
11865
  return { success: false, message: 'analyzerId is required.' };
@@ -11606,18 +11892,18 @@ async function saveConfiguration$1(analyzeMessagesBasePath, analyzerId, instruct
11606
11892
  }
11607
11893
 
11608
11894
  // 3. Construct directory paths
11609
- const analyzerDir = path$3.join(analyzeMessagesBasePath, analyzerName);
11610
- const contentDir = path$3.join(analyzerDir, contentType);
11611
- const instructionsDir = path$3.join(contentDir, instructionsType);
11612
- const instructionsFilePath = path$3.join(instructionsDir, '1.md');
11895
+ const analyzerDir = path$4.join(analyzersBasePath, analyzerName);
11896
+ const contentDir = path$4.join(analyzerDir, contentType);
11897
+ const instructionsDir = path$4.join(contentDir, instructionsType);
11898
+ const instructionsFilePath = path$4.join(instructionsDir, '1.md');
11613
11899
 
11614
11900
  try {
11615
11901
  // 4. Create directories recursively
11616
- await fs$5.mkdir(instructionsDir, { recursive: true });
11902
+ await fs$6.mkdir(instructionsDir, { recursive: true });
11617
11903
 
11618
11904
  // 5. Save instructions content to 1.md
11619
11905
  const finalContent = `; role: assistant\n\n\n${instructionsContent}`;
11620
- await fs$5.writeFile(instructionsFilePath, finalContent, 'utf8');
11906
+ await fs$6.writeFile(instructionsFilePath, finalContent, 'utf8');
11621
11907
 
11622
11908
  // 6. Optionally create/Update config.json files
11623
11909
  if (ensureConfigs) {
@@ -11643,11 +11929,11 @@ async function saveConfiguration$1(analyzeMessagesBasePath, analyzerId, instruct
11643
11929
  * @param {string} label - The label to ensure is in the config.json.
11644
11930
  */
11645
11931
  async function ensureConfigJson(dirPath, label) {
11646
- const configPath = path$3.join(dirPath, 'config.json');
11932
+ const configPath = path$4.join(dirPath, 'config.json');
11647
11933
  let config = {};
11648
11934
 
11649
11935
  try {
11650
- const fileContent = await fs$5.readFile(configPath, 'utf8');
11936
+ const fileContent = await fs$6.readFile(configPath, 'utf8');
11651
11937
  config = JSON.parse(fileContent);
11652
11938
  } catch (error) {
11653
11939
  // If file doesn't exist or parsing fails, start with an empty config
@@ -11664,28 +11950,103 @@ async function ensureConfigJson(dirPath, label) {
11664
11950
  }
11665
11951
 
11666
11952
  // Write the updated config back to the file
11667
- await fs$5.writeFile(configPath, JSON.stringify(config, null, 4), 'utf8');
11953
+ await fs$6.writeFile(configPath, JSON.stringify(config, null, 4), 'utf8');
11668
11954
  }
11669
11955
 
11670
11956
 
11671
11957
  var saver = {
11672
- saveConfiguration: saveConfiguration$1,
11958
+ saveConfiguration: saveConfiguration$2,
11673
11959
  };
11674
11960
 
11675
- /*
11961
+ /**
11676
11962
  * Component: AnalyzerUtils Schema Loader
11677
- * Block-UUID: 0c1d2e3f-4a5b-6c7d-8e9f-0a1b2c3d4e5f
11678
- * Parent-UUID: N/A
11679
- * Version: 1.0.0
11963
+ * Block-UUID: bf25c501-f9e1-4e5b-ad22-9ed24ba50787
11964
+ * Parent-UUID: 66208fdc-2c80-4264-8500-06d5f4db103a
11965
+ * Version: 1.4.1
11680
11966
  * Description: Provides utility functions for retrieving and deducing JSON schemas for analyzers.
11681
11967
  * Language: JavaScript
11682
- * Created-at: 2025-08-28T23:48:00.000Z
11683
- * Authors: Gemini 2.5 Flash (v1.0.0)
11968
+ * Created-at: 2025-11-23T05:40:43.016Z
11969
+ * Authors: Gemini 2.5 Flash (v1.0.0), Gemini 2.5 Pro (v1.1.0), Claude Haiku 4.5 (v1.2.0), Claude Haiku 4.5 (v1.3.0), Qwen 3 Coder 480B - Cerebras (v1.4.0), GLM-4.6 (v1.4.1)
11684
11970
  */
11685
11971
 
11686
- const fs$4 = require$$0.promises;
11687
- const path$2 = require$$1;
11688
- const CodeBlockUtils$1 = CodeBlockUtils$5;
11972
+ /**
11973
+ * Parses the 'Custom Metadata Definitions' block from the markdown content.
11974
+ * Handles both formats:
11975
+ * - With backticks: * `fieldName` (type): description
11976
+ * - Without backticks: * fieldName (type): description
11977
+ *
11978
+ * @param {string} markdownContent - The full content of the 1.md file.
11979
+ * @returns {Map<string, {type: string, description: string}>} A map from field name to its type and description.
11980
+ */
11981
+ function parseMetadataDefinitions(markdownContent) {
11982
+ const definitions = new Map();
11983
+
11984
+ // Match the "### Custom Metadata Definitions" section until the next code block or section
11985
+ const sectionRegex = /### Custom Metadata Definitions\s*([\s\S]*?)(?=```|\n##|\n---)/;
11986
+ const match = markdownContent.match(sectionRegex);
11987
+
11988
+ if (!match || !match[1]) {
11989
+ return definitions;
11990
+ }
11991
+
11992
+ const definitionBlock = match[1];
11993
+
11994
+ // Regex to match both formats:
11995
+ // Format 1: * `fieldName` (type): description
11996
+ // Format 2: * fieldName (type): description
11997
+ // Capture groups:
11998
+ // Group 1: backtick-wrapped field name (if present)
11999
+ // Group 2: non-backtick field name (if present)
12000
+ // Group 3: field type
12001
+ // Group 4: description
12002
+ const lineRegex = /^\s*\*\s+(?:`([^`]+)`|([a-zA-Z0-9_-]+))\s+\(([^)]+)\):\s*(.+)/gm;
12003
+
12004
+ let lineMatch;
12005
+ while ((lineMatch = lineRegex.exec(definitionBlock)) !== null) {
12006
+ // Extract field name from either group 1 (backtick-wrapped) or group 2 (plain)
12007
+ const fieldName = (lineMatch[1] || lineMatch[2]).trim();
12008
+ const fieldType = lineMatch[3].trim();
12009
+ const description = lineMatch[4].trim();
12010
+
12011
+ if (fieldName && fieldType && description) {
12012
+ definitions.set(fieldName, { type: fieldType, description });
12013
+ }
12014
+ }
12015
+
12016
+ return definitions;
12017
+ }
12018
+
12019
+ const fs$5 = require$$0.promises;
12020
+ const path$3 = require$$1;
12021
+ const CodeBlockUtils$3 = CodeBlockUtils$7;
12022
+ const { preprocessJsonForValidation: preprocessJsonForValidation$3 } = jsonParser;
12023
+
12024
+ /**
12025
+ * Maps a simple type string (from the definitions block) to a JSON schema type object.
12026
+ * @param {string} simpleType - The type string (e.g., "number", "array of strings").
12027
+ * @returns {object} A JSON schema type definition.
12028
+ */
12029
+ function mapSimpleTypeToSchema(simpleType) {
12030
+ const lowerType = simpleType.toLowerCase().trim();
12031
+ switch (lowerType) {
12032
+ case 'string':
12033
+ return { type: 'string' };
12034
+ case 'number':
12035
+ case 'integer':
12036
+ return { type: 'number' };
12037
+ case 'boolean':
12038
+ return { type: 'boolean' };
12039
+ case 'array of strings':
12040
+ return { type: 'array', items: { type: 'string' } };
12041
+ case 'date':
12042
+ return { type: 'string', format: 'date' };
12043
+ case 'datetime':
12044
+ case 'iso 8601 timestamp':
12045
+ return { type: 'string', format: 'date-time' };
12046
+ default:
12047
+ return { type: 'string' }; // Default to string for unknown types
12048
+ }
12049
+ }
11689
12050
 
11690
12051
  /**
11691
12052
  * Deduces the JSON schema type and format/items from a string value pattern.
@@ -11698,6 +12059,15 @@ const CodeBlockUtils$1 = CodeBlockUtils$5;
11698
12059
  */
11699
12060
  function deduceSchemaType(value, fieldName) {
11700
12061
  const defaultSchema = { type: 'string' }; // Default fallback
12062
+
12063
+ // Handle new placeholder format {{SYSTEM: ...}} and {{ANALYZER: ...}}
12064
+ // These are valid placeholders and should not generate warnings
12065
+ if (typeof value === 'string') {
12066
+ const trimmedValue = value.trim();
12067
+ if (trimmedValue.startsWith('{{SYSTEM:') || trimmedValue.startsWith('{{ANALYZER:')) {
12068
+ return defaultSchema; // Return default string schema for system/analyzer placeholders
12069
+ }
12070
+ }
11701
12071
 
11702
12072
  if (typeof value !== 'string') {
11703
12073
  const jsType = typeof value;
@@ -11709,35 +12079,35 @@ function deduceSchemaType(value, fieldName) {
11709
12079
  }
11710
12080
 
11711
12081
  const trimmedValue = value.trim();
11712
-
12082
+
11713
12083
  if (/^\[string:.*\]$/.test(trimmedValue) || (/^\[[^:]+\]$/.test(trimmedValue) && !/^\[(number|datetime|date|<string>):.*\]$/.test(trimmedValue))) {
11714
12084
  return { type: 'string' };
11715
12085
  }
11716
-
12086
+
11717
12087
  if (/^\[number:.*\]$/.test(trimmedValue)) {
11718
12088
  return { type: 'number' };
11719
12089
  }
11720
-
12090
+
11721
12091
  if (/^\[boolean:.*\]$/.test(trimmedValue)) {
11722
12092
  return { type: 'boolean' };
11723
12093
  }
11724
-
12094
+
11725
12095
  if (/^\[date-*time:.*\]$/.test(trimmedValue)) {
11726
12096
  return { type: 'string', format: 'date-time' };
11727
12097
  }
11728
-
12098
+
11729
12099
  if (/^\[date:.*\]$/.test(trimmedValue)) {
11730
12100
  return { type: 'string', format: 'date' };
11731
12101
  }
11732
-
12102
+
11733
12103
  if (/^\[<string>:.*\]$/.test(trimmedValue) || trimmedValue.toLowerCase().includes('array of strings')) {
11734
12104
  return { type: 'array', items: { type: 'string' } };
11735
12105
  }
11736
-
12106
+
11737
12107
  if (trimmedValue.toLowerCase().includes("output 'true' or 'false'") || trimmedValue.toLowerCase().includes("determine if") && (trimmedValue.toLowerCase().includes("true") || trimmedValue.toLowerCase().includes("false"))) {
11738
12108
  return { type: 'boolean' };
11739
12109
  }
11740
-
12110
+
11741
12111
  console.warn(`Warning: Unknown metadata value pattern for field "${fieldName}". Defaulting to type 'string'. Value: "${value}"`);
11742
12112
  return defaultSchema;
11743
12113
  }
@@ -11748,14 +12118,14 @@ function deduceSchemaType(value, fieldName) {
11748
12118
  * Reads the corresponding '1.md' file, extracts the JSON block,
11749
12119
  * and deduces schema types from the string values.
11750
12120
  *
11751
- * @param {string} analyzeMessagesBasePath - The absolute or relative path to the base directory containing the analyzer message files (e.g., 'messages/analyze').
12121
+ * @param {string} analyzersBasePath - The absolute or relative path to the base directory containing the analyzer message files (e.g., 'messages/analyze').
11752
12122
  * @param {string} analyzerId - The unique ID of the analyzer (format: 'analyzer_name::content_type::instructions_type').
11753
12123
  * @returns {Promise<object|null>} A promise that resolves with the JSON schema object or null if the analyzer ID is invalid or the schema cannot be retrieved/parsed.
11754
12124
  * @throws {Error} If the 1.md file is found but does not contain exactly one JSON code block.
11755
12125
  */
11756
- async function getAnalyzerSchema$2(analyzeMessagesBasePath, analyzerId) {
11757
- if (typeof analyzeMessagesBasePath !== 'string' || analyzeMessagesBasePath.trim() === '') {
11758
- console.error('Error: analyzeMessagesBasePath is required.');
12126
+ async function getAnalyzerSchema$2(analyzersBasePath, analyzerId) {
12127
+ if (typeof analyzersBasePath !== 'string' || analyzersBasePath.trim() === '') {
12128
+ console.error('Error: analyzersBasePath is required.');
11759
12129
  return null;
11760
12130
  }
11761
12131
  if (typeof analyzerId !== 'string' || analyzerId.trim() === '') {
@@ -11770,11 +12140,14 @@ async function getAnalyzerSchema$2(analyzeMessagesBasePath, analyzerId) {
11770
12140
  }
11771
12141
  const [analyzerName, contentType, instructionsType] = parts;
11772
12142
 
11773
- const instructionsFilePath = path$2.join(analyzeMessagesBasePath, analyzerName, contentType, instructionsType, '1.md');
12143
+ const instructionsFilePath = path$3.join(analyzersBasePath, analyzerName, contentType, instructionsType, '1.md');
11774
12144
 
11775
12145
  try {
11776
- const fileContent = await fs$4.readFile(instructionsFilePath, 'utf8');
11777
- const { blocks } = CodeBlockUtils$1.extractCodeBlocks(fileContent, { silent: true });
12146
+ const fileContent = await fs$5.readFile(instructionsFilePath, 'utf8');
12147
+ // New: Parse the structured definitions block first
12148
+ const definedFields = parseMetadataDefinitions(fileContent);
12149
+
12150
+ const { blocks } = CodeBlockUtils$3.extractCodeBlocks(fileContent, { silent: true });
11778
12151
  const jsonBlocks = blocks.filter(block => block.type === 'code' && block.language === 'json');
11779
12152
 
11780
12153
  if (jsonBlocks.length !== 1) {
@@ -11782,19 +12155,24 @@ async function getAnalyzerSchema$2(analyzeMessagesBasePath, analyzerId) {
11782
12155
  }
11783
12156
 
11784
12157
  const jsonBlockContent = jsonBlocks[0].content;
12158
+ const preprocessedContent = preprocessJsonForValidation$3(jsonBlockContent);
11785
12159
  let rawJson = null;
11786
12160
  try {
11787
- rawJson = JSON.parse(jsonBlockContent);
12161
+ rawJson = JSON.parse(preprocessedContent);
11788
12162
  } catch (parseError) {
11789
12163
  console.error(`Error parsing JSON content from ${instructionsFilePath}: ${parseError.message}`);
11790
12164
  return null;
11791
12165
  }
11792
12166
 
12167
+ const startOfInstructions = fileContent.indexOf('# Analyze ');
12168
+
11793
12169
  const schema = {
11794
12170
  type: 'object',
11795
12171
  description: rawJson.description,
12172
+ version: rawJson.version,
11796
12173
  properties: {},
11797
- required: []
12174
+ required: [],
12175
+ instructions: fileContent.slice(startOfInstructions),
11798
12176
  };
11799
12177
 
11800
12178
  const metadataProperties = rawJson?.extracted_metadata;
@@ -11802,9 +12180,20 @@ async function getAnalyzerSchema$2(analyzeMessagesBasePath, analyzerId) {
11802
12180
  if (metadataProperties && typeof metadataProperties === 'object') {
11803
12181
  for (const fieldName in metadataProperties) {
11804
12182
  if (Object.hasOwnProperty.call(metadataProperties, fieldName)) {
11805
- const rawValue = metadataProperties[fieldName];
11806
- const fieldSchema = deduceSchemaType(rawValue, fieldName);
11807
- const description = rawValue.match(/^\[\w+: ([^\]]+)\]/)?.[1] || '';
12183
+ let fieldSchema;
12184
+ let description;
12185
+
12186
+ if (definedFields.has(fieldName)) {
12187
+ // Priority 1: Use the explicit definition
12188
+ const definition = definedFields.get(fieldName);
12189
+ fieldSchema = mapSimpleTypeToSchema(definition.type);
12190
+ description = definition.description;
12191
+ } else {
12192
+ // Priority 2: Fallback for system fields or backward compatibility
12193
+ const rawValue = metadataProperties[fieldName];
12194
+ fieldSchema = deduceSchemaType(rawValue, fieldName);
12195
+ description = rawValue.match(/^\[\w+: ([^\]]+)\]/)?.[1] || '';
12196
+ }
11808
12197
 
11809
12198
  schema.properties[fieldName] = {
11810
12199
  ...fieldSchema,
@@ -11818,7 +12207,6 @@ async function getAnalyzerSchema$2(analyzeMessagesBasePath, analyzerId) {
11818
12207
  }
11819
12208
 
11820
12209
  return schema;
11821
-
11822
12210
  } catch (error) {
11823
12211
  if (error.code === 'ENOENT') {
11824
12212
  console.warn(`Analyzer instructions file not found: ${instructionsFilePath}`);
@@ -11835,7 +12223,7 @@ var schemaLoader = {
11835
12223
 
11836
12224
  /*
11837
12225
  * Component: AnalyzerUtils Management
11838
- * Block-UUID: 0d1e2f3a-4b5c-6d7e-8f9a-0b1c2d3e4f5a
12226
+ * Block-UUID: 6241f381-512b-48a7-b70e-6b45683831fe
11839
12227
  * Parent-UUID: N/A
11840
12228
  * Version: 1.0.0
11841
12229
  * Description: Provides utility functions for managing (deleting) analyzer configurations.
@@ -11844,9 +12232,9 @@ var schemaLoader = {
11844
12232
  * Authors: Gemini 2.5 Flash (v1.0.0)
11845
12233
  */
11846
12234
 
11847
- const fs$3 = require$$0.promises;
11848
- const path$1 = require$$1;
11849
- const { readConfig } = discovery; // Import helper from discovery
12235
+ const fs$4 = require$$0.promises;
12236
+ const path$2 = require$$1;
12237
+ const { readConfig: readConfig$1 } = discovery; // Import helper from discovery
11850
12238
 
11851
12239
  /**
11852
12240
  * Checks if a directory is empty or only contains a config.json.
@@ -11855,7 +12243,7 @@ const { readConfig } = discovery; // Import helper from discovery
11855
12243
  */
11856
12244
  async function isDirectoryEmpty(dirPath) {
11857
12245
  try {
11858
- const files = await fs$3.readdir(dirPath);
12246
+ const files = await fs$4.readdir(dirPath);
11859
12247
  return files.length === 0 || (files.length === 1 && files[0] === 'config.json');
11860
12248
  } catch (error) {
11861
12249
  if (error.code === 'ENOENT') {
@@ -11868,13 +12256,13 @@ async function isDirectoryEmpty(dirPath) {
11868
12256
  /**
11869
12257
  * Deletes a specific analyzer configuration and intelligently cleans up empty directories.
11870
12258
  *
11871
- * @param {string} analyzeMessagesBasePath - The absolute or relative path to the base directory containing the analyzer message files (e.g., 'messages/analyze').
12259
+ * @param {string} analyzersBasePath - The absolute path to the base directory containing the analyzers (e.g., 'analyzers/analyzer-1').
11872
12260
  * @param {string} analyzerId - The unique ID of the analyzer to delete (format: 'analyzer_name::content_type::instructions_type').
11873
12261
  * @returns {Promise<{success: boolean, message: string}>} A promise that resolves with a result object indicating success or failure.
11874
12262
  */
11875
- async function deleteAnalyzer$2(analyzeMessagesBasePath, analyzerId) {
11876
- if (typeof analyzeMessagesBasePath !== 'string' || analyzeMessagesBasePath.trim() === '') {
11877
- return { success: false, message: 'analyzeMessagesBasePath is required.' };
12263
+ async function deleteAnalyzer$2(analyzersBasePath, analyzerId) {
12264
+ if (typeof analyzersBasePath !== 'string' || analyzersBasePath.trim() === '') {
12265
+ return { success: false, message: 'analyzersBasePath is required.' };
11878
12266
  }
11879
12267
  if (typeof analyzerId !== 'string' || analyzerId.trim() === '') {
11880
12268
  return { success: false, message: 'analyzerId is required.' };
@@ -11886,31 +12274,31 @@ async function deleteAnalyzer$2(analyzeMessagesBasePath, analyzerId) {
11886
12274
  }
11887
12275
  const [analyzerName, contentType, instructionsType] = parts;
11888
12276
 
11889
- const analyzerDir = path$1.join(analyzeMessagesBasePath, analyzerName);
11890
- const contentDir = path$1.join(analyzerDir, contentType);
11891
- const instructionsDir = path$1.join(contentDir, instructionsType);
11892
- const instructionsFilePath = path$1.join(instructionsDir, '1.md');
12277
+ const analyzerDir = path$2.join(analyzersBasePath, analyzerName);
12278
+ const contentDir = path$2.join(analyzerDir, contentType);
12279
+ const instructionsDir = path$2.join(contentDir, instructionsType);
12280
+ const instructionsFilePath = path$2.join(instructionsDir, '1.md');
11893
12281
 
11894
12282
  try {
11895
12283
  // 1. Check for protection at all levels
11896
- const analyzerConfig = await readConfig(analyzerDir);
12284
+ const analyzerConfig = await readConfig$1(analyzerDir);
11897
12285
  if (analyzerConfig?.protected) {
11898
12286
  return { success: false, message: `Analyzer '${analyzerName}' is protected and cannot be deleted.` };
11899
12287
  }
11900
12288
 
11901
- const contentConfig = await readConfig(contentDir);
12289
+ const contentConfig = await readConfig$1(contentDir);
11902
12290
  if (contentConfig?.protected) {
11903
12291
  return { success: false, message: `Content type '${contentType}' for analyzer '${analyzerName}' is protected and cannot be deleted.` };
11904
12292
  }
11905
12293
 
11906
- const instructionsConfig = await readConfig(instructionsDir);
12294
+ const instructionsConfig = await readConfig$1(instructionsDir);
11907
12295
  if (instructionsConfig?.protected) {
11908
12296
  return { success: false, message: `Instructions type '${instructionsType}' for content type '${contentType}' is protected and cannot be deleted.` };
11909
12297
  }
11910
12298
 
11911
12299
  // 2. Delete the 1.md file
11912
12300
  try {
11913
- await fs$3.unlink(instructionsFilePath);
12301
+ await fs$4.unlink(instructionsFilePath);
11914
12302
  } catch (error) {
11915
12303
  if (error.code === 'ENOENT') {
11916
12304
  return { success: false, message: `Analyzer instructions file not found: ${instructionsFilePath}. It may have already been deleted.` };
@@ -11924,7 +12312,7 @@ async function deleteAnalyzer$2(analyzeMessagesBasePath, analyzerId) {
11924
12312
  // Check and delete instructions directory
11925
12313
  if (await isDirectoryEmpty(instructionsDir)) {
11926
12314
  try {
11927
- await fs$3.rmdir(instructionsDir);
12315
+ await fs$4.rmdir(instructionsDir);
11928
12316
  deletedDirs.push(instructionsDir);
11929
12317
  } catch (error) {
11930
12318
  console.warn(`Warning: Could not remove empty instructions directory ${instructionsDir}: ${error.message}`);
@@ -11934,7 +12322,7 @@ async function deleteAnalyzer$2(analyzeMessagesBasePath, analyzerId) {
11934
12322
  // Check and delete content directory
11935
12323
  if (await isDirectoryEmpty(contentDir)) {
11936
12324
  try {
11937
- await fs$3.rmdir(contentDir);
12325
+ await fs$4.rmdir(contentDir);
11938
12326
  deletedDirs.push(contentDir);
11939
12327
  } catch (error) {
11940
12328
  console.warn(`Warning: Could not remove empty content directory ${contentDir}: ${error.message}`);
@@ -11944,7 +12332,7 @@ async function deleteAnalyzer$2(analyzeMessagesBasePath, analyzerId) {
11944
12332
  // Check and delete analyzer directory
11945
12333
  if (await isDirectoryEmpty(analyzerDir)) {
11946
12334
  try {
11947
- await fs$3.rmdir(analyzerDir);
12335
+ await fs$4.rmdir(analyzerDir);
11948
12336
  deletedDirs.push(analyzerDir);
11949
12337
  } catch (error) {
11950
12338
  console.warn(`Warning: Could not remove empty analyzer directory ${analyzerDir}: ${error.message}`);
@@ -11960,7 +12348,9 @@ async function deleteAnalyzer$2(analyzeMessagesBasePath, analyzerId) {
11960
12348
  }
11961
12349
 
11962
12350
  var management = {
11963
- deleteAnalyzer: deleteAnalyzer$2};
12351
+ deleteAnalyzer: deleteAnalyzer$2,
12352
+ isDirectoryEmpty,
12353
+ };
11964
12354
 
11965
12355
  /*
11966
12356
  * Component: AnalyzerUtils Default Prompt Loader
@@ -11973,25 +12363,25 @@ var management = {
11973
12363
  * Authors: Gemini 2.5 Flash (v1.0.0)
11974
12364
  */
11975
12365
 
11976
- const fs$2 = require$$0.promises;
11977
- const path = require$$1;
12366
+ const fs$3 = require$$0.promises;
12367
+ const path$1 = require$$1;
11978
12368
 
11979
12369
  /**
11980
12370
  * Retrieves the raw Markdown content of the shared system message ('_shared/system/1.md').
11981
12371
  *
11982
- * @param {string} analyzeMessagesBasePath - The absolute path to the base directory containing analyzer message files (e.g., 'messages/analyze').
12372
+ * @param {string} analyzersBasePath - The absolute path to the base directory containing the analyzers (e.g., 'analyzers/analyzer-1').
11983
12373
  * @returns {Promise<string|null>} A promise that resolves with the full Markdown content, or null if not found/invalid.
11984
12374
  */
11985
- async function getSystemMessageContent$2(analyzeMessagesBasePath) {
11986
- if (typeof analyzeMessagesBasePath !== 'string' || analyzeMessagesBasePath.trim() === '') {
11987
- console.error('Error: analyzeMessagesBasePath is required for getSystemMessageContent.');
12375
+ async function getSystemMessageContent$2(analyzersBasePath) {
12376
+ if (typeof analyzersBasePath !== 'string' || analyzersBasePath.trim() === '') {
12377
+ console.error('Error: analyzersBasePath is required for getSystemMessageContent.');
11988
12378
  return null;
11989
12379
  }
11990
12380
 
11991
- const systemMessageFilePath = path.join(analyzeMessagesBasePath, '_shared', 'system', '1.md');
12381
+ const systemMessageFilePath = path$1.join(analyzersBasePath, '_shared', 'system', '1.md');
11992
12382
 
11993
12383
  try {
11994
- const fileContent = await fs$2.readFile(systemMessageFilePath, 'utf8');
12384
+ const fileContent = await fs$3.readFile(systemMessageFilePath, 'utf8');
11995
12385
  const parts = fileContent.split('\n\n\n');
11996
12386
  parts.shift();
11997
12387
  return parts.join('\n\n\n');
@@ -12009,19 +12399,19 @@ async function getSystemMessageContent$2(analyzeMessagesBasePath) {
12009
12399
  /**
12010
12400
  * Retrieves the raw Markdown content of the shared start message ('_shared/start/1.md').
12011
12401
  *
12012
- * @param {string} analyzeMessagesBasePath - The absolute path to the base directory containing analyzer message files (e.g., 'messages/analyze').
12402
+ * @param {string} analyzersBasePath - The absolute path to the base directory containing the analyzers (e.g., 'analyzers/analyzer-1').
12013
12403
  * @returns {Promise<string|null>} A promise that resolves with the full Markdown content, or null if not found/invalid.
12014
12404
  */
12015
- async function getStartMessageContent$2(analyzeMessagesBasePath) {
12016
- if (typeof analyzeMessagesBasePath !== 'string' || analyzeMessagesBasePath.trim() === '') {
12017
- console.error('Error: analyzeMessagesBasePath is required for getStartMessageContent.');
12405
+ async function getStartMessageContent$2(analyzersBasePath) {
12406
+ if (typeof analyzersBasePath !== 'string' || analyzersBasePath.trim() === '') {
12407
+ console.error('Error: analyzersBasePath is required for getStartMessageContent.');
12018
12408
  return null;
12019
12409
  }
12020
12410
 
12021
- const startMessageFilePath = path.join(analyzeMessagesBasePath, '_shared', 'start', '1.md');
12411
+ const startMessageFilePath = path$1.join(analyzersBasePath, '_shared', 'start', '1.md');
12022
12412
 
12023
12413
  try {
12024
- const fileContent = await fs$2.readFile(startMessageFilePath, 'utf8');
12414
+ const fileContent = await fs$3.readFile(startMessageFilePath, 'utf8');
12025
12415
  const parts = fileContent.split('\n\n\n');
12026
12416
  parts.shift();
12027
12417
  return parts.join('\n\n\n');
@@ -12042,60 +12432,401 @@ var defaultPromptLoader = {
12042
12432
  };
12043
12433
 
12044
12434
  /*
12045
- * Component: AnalyzerUtils Index
12046
- * Block-UUID: b403b6a1-230b-4247-8cd6-2a3d068f4bbf
12047
- * Parent-UUID: N/A
12048
- * Version: 1.2.0
12049
- * Description: Aggregates and exports all utility functions from the AnalyzerUtils module.
12050
- * Language: JavaScript
12051
- * Created-at: 2025-08-28T15:56:40.319Z
12052
- * Authors: Gemini 2.5 Flash (v1.0.0), Gemini 2.5 Flash (v1.1.0), Gemini 2.5 Flash (v1.2.0)
12053
- */
12054
-
12055
- const { buildChatIdToPathMap: buildChatIdToPathMap$1 } = contextMapper;
12056
- const { processLLMAnalysisResponse: processLLMAnalysisResponse$1 } = responseProcessor;
12057
- const { validateLLMAnalysisData: validateLLMAnalysisData$1 } = dataValidator;
12058
- const { getAnalyzers: getAnalyzers$1 } = discovery;
12059
- const { saveConfiguration } = saver;
12060
- const { getAnalyzerSchema: getAnalyzerSchema$1 } = schemaLoader;
12061
- const { deleteAnalyzer: deleteAnalyzer$1 } = management;
12062
- const { getAnalyzerInstructionsContent: getAnalyzerInstructionsContent$1 } = instructionLoader;
12063
- const { getSystemMessageContent: getSystemMessageContent$1, getStartMessageContent: getStartMessageContent$1 } = defaultPromptLoader; // NEW: Import default prompt loaders
12064
-
12065
- var AnalyzerUtils$1 = {
12066
- buildChatIdToPathMap: buildChatIdToPathMap$1,
12067
- processLLMAnalysisResponse: processLLMAnalysisResponse$1,
12068
- validateLLMAnalysisData: validateLLMAnalysisData$1,
12069
- getAnalyzers: getAnalyzers$1,
12070
- getAnalyzerSchema: getAnalyzerSchema$1,
12071
- deleteAnalyzer: deleteAnalyzer$1,
12072
- getAnalyzerInstructionsContent: getAnalyzerInstructionsContent$1,
12073
- saveConfiguration,
12074
- getSystemMessageContent: getSystemMessageContent$1,
12075
- getStartMessageContent: getStartMessageContent$1
12076
- };
12077
-
12078
- /**
12079
- * Component: LLMUtils
12080
- * Block-UUID: a3106054-42f1-474f-96f3-182d66eb19a0
12435
+ * Component: AnalyzerUtils Updater
12436
+ * Block-UUID: aee32ada-81d8-4d44-b0a3-5613ba166d1e
12081
12437
  * Parent-UUID: N/A
12082
12438
  * Version: 1.0.0
12083
- * Description: Provides utility functions related to Large Language Model interactions, such as token estimation.
12439
+ * Description: Provides utility functions for updating analyzer configurations, including renaming and metadata updates.
12084
12440
  * Language: JavaScript
12085
- * Created-at: 2025-04-22T17:16:00.590Z
12086
- * Authors: Gemini 2.5 Pro (v1.0.0)
12441
+ * Created-at: 2025-12-25T19:55:00.000Z
12442
+ * Authors: GLM-4.6 (v1.0.0)
12087
12443
  */
12088
12444
 
12445
+ const fs$2 = require$$0.promises;
12446
+ const path = require$$1;
12447
+ const CodeBlockUtils$2 = CodeBlockUtils$7;
12448
+ const { preprocessJsonForValidation: preprocessJsonForValidation$2 } = jsonParser;
12449
+ const { isValidDirName: isValidDirName$1 } = discovery;
12450
+ const { readConfig } = discovery;
12451
+
12089
12452
  /**
12090
- * Estimates the number of tokens in a given text string.
12091
- * This is a basic estimation using the common heuristic of ~4 characters per token.
12092
- * Actual token count can vary significantly based on the specific tokenizer used by an LLM.
12453
+ * Updates an analyzer configuration, including renaming the analyzer if needed.
12093
12454
  *
12094
- * @param {string} text - The text to estimate tokens for.
12095
- * @returns {number} An estimated token count. Returns 0 if input is not a non-empty string.
12455
+ * @param {string} analyzersBasePath - The absolute path to the base directory containing the analyzers.
12456
+ * @param {string} analyzerId - The current unique ID of the analyzer (format: 'analyzer_name::content_type::instructions_type').
12457
+ * @param {object} updates - The updates to apply to the analyzer.
12458
+ * @param {string} [updates.name] - The new analyzer name (for renaming the directory).
12459
+ * @param {string} [updates.label] - The new label for the analyzer.
12460
+ * @param {string} [updates.description] - The new description for the analyzer.
12461
+ * @param {string} [updates.version] - The new version for the analyzer.
12462
+ * @param {Array<string>} [updates.tags] - The new tags for the analyzer.
12463
+ * @returns {Promise<{success: boolean, message: string, newAnalyzerId?: string}>} A promise that resolves with a result object.
12096
12464
  */
12097
- function estimateTokens$1(text) {
12098
- if (typeof text !== 'string' || text.length === 0) {
12465
+ async function updateAnalyzer$1(analyzersBasePath, analyzerId, updates) {
12466
+ // 1. Validate inputs
12467
+ if (typeof analyzersBasePath !== 'string' || analyzersBasePath.trim() === '') {
12468
+ return { success: false, message: 'analyzersBasePath is required.' };
12469
+ }
12470
+ if (typeof analyzerId !== 'string' || analyzerId.trim() === '') {
12471
+ return { success: false, message: 'analyzerId is required.' };
12472
+ }
12473
+ if (!updates || typeof updates !== 'object') {
12474
+ return { success: false, message: 'updates object is required.' };
12475
+ }
12476
+
12477
+ // 2.0 Parse current analyzerId
12478
+ const parts = analyzerId.split('::');
12479
+ if (parts.length !== 3) {
12480
+ return { success: false, message: `Invalid analyzerId format. Expected 'analyzer_name::content_type::instructions_type', but got '${analyzerId}'.` };
12481
+ }
12482
+ const [analyzerName, contentType, instructionsType] = parts;
12483
+
12484
+ // 2.1 Unset name if it has been provided but is the same as the analyzerName
12485
+ if (updates.name && analyzerName === updates.name) {
12486
+ delete updates.name;
12487
+ }
12488
+
12489
+ // 3. Validate new name if provided
12490
+ if (updates.name && !isValidDirName$1(updates.name)) {
12491
+ return { success: false, message: `Invalid analyzer name '${updates.name}'. Names must be alphanumeric, dash, or underscore, cannot start with underscore, and cannot contain dots.` };
12492
+ }
12493
+
12494
+ // 4. Check if analyzer is protected
12495
+ const analyzerDir = path.join(analyzersBasePath, analyzerName);
12496
+ const analyzerConfig = await readConfig(analyzerDir);
12497
+ if (analyzerConfig?.protected) {
12498
+ return { success: false, message: `Analyzer '${analyzerName}' is protected and cannot be updated.` };
12499
+ }
12500
+
12501
+ // 5. Get current instructions content
12502
+ const instructionsFilePath = path.join(analyzerDir, contentType, instructionsType, '1.md');
12503
+ let instructionsContent;
12504
+ try {
12505
+ instructionsContent = await fs$2.readFile(instructionsFilePath, 'utf8');
12506
+ } catch (error) {
12507
+ return { success: false, message: `Failed to read analyzer instructions: ${error.message}` };
12508
+ }
12509
+
12510
+ // 6. Extract and update JSON block
12511
+ const { blocks } = CodeBlockUtils$2.extractCodeBlocks(instructionsContent, { silent: true });
12512
+ const jsonBlock = blocks.find(block => block.language === 'json');
12513
+
12514
+ if (!jsonBlock) {
12515
+ return { success: false, message: 'No JSON block found in analyzer instructions.' };
12516
+ }
12517
+
12518
+ let jsonData;
12519
+ try {
12520
+ const preprocessedContent = preprocessJsonForValidation$2(jsonBlock.content);
12521
+ jsonData = JSON.parse(preprocessedContent);
12522
+ } catch (error) {
12523
+ return { success: false, message: `Failed to parse JSON block: ${error.message}` };
12524
+ }
12525
+
12526
+ // 7. Update JSON data with provided values
12527
+ if (updates.label !== undefined) jsonData.label = updates.label;
12528
+ if (updates.description !== undefined) jsonData.description = updates.description;
12529
+ if (updates.version !== undefined) jsonData.version = updates.version;
12530
+ if (updates.tags !== undefined) jsonData.tags = updates.tags;
12531
+
12532
+ // 8. Rebuild the instructions content with updated JSON
12533
+ const updatedJsonContent = JSON.stringify(jsonData, null, 2);
12534
+ const updatedInstructionsContent = instructionsContent.replace(
12535
+ jsonBlock.content,
12536
+ updatedJsonContent
12537
+ );
12538
+
12539
+ // 9. Handle directory renaming if needed
12540
+ let newAnalyzerId = analyzerId;
12541
+ if (updates.name && updates.name !== analyzerName) {
12542
+ // Create new directory structure
12543
+ const newAnalyzerDir = path.join(analyzersBasePath, updates.name);
12544
+ const newContentDir = path.join(newAnalyzerDir, contentType);
12545
+ const newInstructionsDir = path.join(newContentDir, instructionsType);
12546
+ const newInstructionsFilePath = path.join(newInstructionsDir, '1.md');
12547
+
12548
+ try {
12549
+ // Create new directories
12550
+ await fs$2.mkdir(newInstructionsDir, { recursive: true });
12551
+
12552
+ // Copy config.json if it exists
12553
+ const configPath = path.join(analyzerDir, 'config.json');
12554
+ const newConfigPath = path.join(newAnalyzerDir, 'config.json');
12555
+ try {
12556
+ const configContent = await fs$2.readFile(configPath, 'utf8');
12557
+ await fs$2.writeFile(newConfigPath, configContent, 'utf8');
12558
+ } catch (error) {
12559
+ // Config file might not exist, that's okay
12560
+ }
12561
+
12562
+ // Copy content config.json if it exists
12563
+ const contentConfigPath = path.join(analyzerDir, contentType, 'config.json');
12564
+ const newContentConfigPath = path.join(newContentDir, 'config.json');
12565
+ try {
12566
+ const contentConfigContent = await fs$2.readFile(contentConfigPath, 'utf8');
12567
+ await fs$2.writeFile(newContentConfigPath, contentConfigContent, 'utf8');
12568
+ } catch (error) {
12569
+ // Config file might not exist, that's okay
12570
+ }
12571
+
12572
+ // Copy instructions config.json if it exists
12573
+ const instructionsConfigPath = path.join(analyzerDir, contentType, instructionsType, 'config.json');
12574
+ const newInstructionsConfigPath = path.join(newInstructionsDir, 'config.json');
12575
+ try {
12576
+ const instructionsConfigContent = await fs$2.readFile(instructionsConfigPath, 'utf8');
12577
+ await fs$2.writeFile(newInstructionsConfigPath, instructionsConfigContent, 'utf8');
12578
+ } catch (error) {
12579
+ // Config file might not exist, that's okay
12580
+ }
12581
+
12582
+ // Write updated instructions to new location
12583
+ const finalContent = `; role: assistant\n\n\n${updatedInstructionsContent}`;
12584
+ await fs$2.writeFile(newInstructionsFilePath, finalContent, 'utf8');
12585
+
12586
+ // Update the analyzer ID
12587
+ newAnalyzerId = `${updates.name}::${contentType}::${instructionsType}`;
12588
+
12589
+ // Delete old analyzer directory (will be done after successful save)
12590
+ } catch (error) {
12591
+ return { success: false, message: `Failed to create new analyzer directory: ${error.message}` };
12592
+ }
12593
+ } else {
12594
+ // Just update the existing file
12595
+ const finalContent = `; role: assistant\n\n\n${updatedInstructionsContent}`;
12596
+ await fs$2.writeFile(instructionsFilePath, finalContent, 'utf8');
12597
+ }
12598
+
12599
+ // 10. Clean up old directory if renamed
12600
+ if (updates.name && updates.name !== analyzerName) {
12601
+ try {
12602
+ // Use the deleteAnalyzer function to clean up the old directory
12603
+ const { deleteAnalyzer } = management;
12604
+ await deleteAnalyzer(analyzersBasePath, analyzerId);
12605
+ } catch (error) {
12606
+ console.warn(`Warning: Could not clean up old analyzer directory: ${error.message}`);
12607
+ }
12608
+ }
12609
+
12610
+ return {
12611
+ success: true,
12612
+ message: `Analyzer '${analyzerId}' updated successfully.`,
12613
+ newAnalyzerId
12614
+ };
12615
+ }
12616
+
12617
+ var updater = {
12618
+ updateAnalyzer: updateAnalyzer$1
12619
+ };
12620
+
12621
+ /*
12622
+ * Component: AnalyzerUtils Cloner
12623
+ * Block-UUID: 56b8c9f4-97ce-406c-a598-22a838ebefda
12624
+ * Parent-UUID: N/A
12625
+ * Version: 1.0.0
12626
+ * Description: Provides utility functions for cloning analyzer configurations.
12627
+ * Language: JavaScript
12628
+ * Created-at: 2025-12-26T16:59:39.782Z
12629
+ * Authors: GLM-4.6 (v1.0.0)
12630
+ */
12631
+
12632
+ require$$0.promises;
12633
+ const CodeBlockUtils$1 = CodeBlockUtils$7;
12634
+ const { preprocessJsonForValidation: preprocessJsonForValidation$1 } = jsonParser;
12635
+ const { isValidDirName } = discovery;
12636
+ const { saveConfiguration: saveConfiguration$1 } = saver;
12637
+ const { getAnalyzers: getAnalyzers$2 } = discovery;
12638
+
12639
+ /**
12640
+ * Clones an existing analyzer with a new name.
12641
+ *
12642
+ * @param {string} analyzersBasePath - The absolute path to the base directory containing the analyzers.
12643
+ * @param {string} originalAnalyzerId - The ID of the analyzer to clone (format: 'analyzer_name::content_type::instructions_type').
12644
+ * @param {string} newAnalyzerName - The new name for the cloned analyzer.
12645
+ * @param {object} [options={}] - Optional configuration options.
12646
+ * @param {string} [options.label] - The new label for the cloned analyzer (optional, defaults to newAnalyzerName).
12647
+ * @param {string} [options.description] - The new description for the cloned analyzer (optional, keeps original if not provided).
12648
+ * @returns {Promise<{success: boolean, message: string, newAnalyzerId?: string}>} A promise that resolves with a result object.
12649
+ */
12650
+ async function cloneAnalyzer$1(analyzersBasePath, originalAnalyzerId, newAnalyzerName, options = {}) {
12651
+ // 1. Validate inputs
12652
+ if (typeof analyzersBasePath !== 'string' || analyzersBasePath.trim() === '') {
12653
+ return { success: false, message: 'analyzersBasePath is required.' };
12654
+ }
12655
+ if (typeof originalAnalyzerId !== 'string' || originalAnalyzerId.trim() === '') {
12656
+ return { success: false, message: 'originalAnalyzerId is required.' };
12657
+ }
12658
+ if (typeof newAnalyzerName !== 'string' || newAnalyzerName.trim() === '') {
12659
+ return { success: false, message: 'newAnalyzerName is required.' };
12660
+ }
12661
+
12662
+ // 2. Parse original analyzerId
12663
+ const parts = originalAnalyzerId.split('::');
12664
+ if (parts.length !== 3) {
12665
+ return { success: false, message: `Invalid originalAnalyzerId format. Expected 'analyzer_name::content_type::instructions_type', but got '${originalAnalyzerId}'.` };
12666
+ }
12667
+ const [originalAnalyzerName, contentType, instructionsType] = parts;
12668
+
12669
+ // 3. Validate new analyzer name
12670
+ if (!isValidDirName(newAnalyzerName)) {
12671
+ return { success: false, message: `Invalid analyzer name '${newAnalyzerName}'. Names must be alphanumeric, dash, or underscore, cannot start with underscore, and cannot contain dots.` };
12672
+ }
12673
+
12674
+ // 4. Check if new analyzer name already exists
12675
+ try {
12676
+ const existingAnalyzers = await getAnalyzers$2(analyzersBasePath);
12677
+ const nameExists = existingAnalyzers.some(analyzer => analyzer.name === newAnalyzerName);
12678
+ if (nameExists) {
12679
+ return { success: false, message: `An analyzer with name '${newAnalyzerName}' already exists. Please choose a different name.` };
12680
+ }
12681
+ } catch (error) {
12682
+ return { success: false, message: `Failed to check for existing analyzers: ${error.message}` };
12683
+ }
12684
+
12685
+ // 5. Get original analyzer's instructions content
12686
+ const { getAnalyzerInstructionsContent } = instructionLoader;
12687
+ let instructionsContent;
12688
+ try {
12689
+ instructionsContent = await getAnalyzerInstructionsContent(analyzersBasePath, originalAnalyzerId);
12690
+ if (!instructionsContent) {
12691
+ return { success: false, message: `Could not find original analyzer '${originalAnalyzerId}'.` };
12692
+ }
12693
+ } catch (error) {
12694
+ return { success: false, message: `Failed to read original analyzer instructions: ${error.message}` };
12695
+ }
12696
+
12697
+ // 6. Extract and update JSON block
12698
+ const { blocks } = CodeBlockUtils$1.extractCodeBlocks(instructionsContent, { silent: true });
12699
+ const jsonBlocks = blocks.filter((block, index) => {
12700
+ if (block.language === 'json') {
12701
+ block.index = index;
12702
+ return block;
12703
+ }
12704
+ });
12705
+ const jsonBlock = jsonBlocks[jsonBlocks.length - 1];
12706
+
12707
+ if (!jsonBlock) {
12708
+ return { success: false, message: 'No JSON block found in original analyzer instructions.' };
12709
+ }
12710
+
12711
+ let jsonData;
12712
+ try {
12713
+ const preprocessedContent = preprocessJsonForValidation$1(jsonBlock.content);
12714
+ jsonData = JSON.parse(preprocessedContent);
12715
+ } catch (error) {
12716
+ return { success: false, message: `Failed to parse JSON block: ${error.message}` };
12717
+ }
12718
+
12719
+ // 7. Update JSON data with new values
12720
+ if (options.label !== undefined) {
12721
+ jsonData.label = options.label;
12722
+ }
12723
+ if (options.description !== undefined) {
12724
+ jsonData.description = options.description;
12725
+ }
12726
+ // Reset version for cloned analyzer
12727
+ jsonData.version = '1.0.0';
12728
+
12729
+ // 8. Rebuild the instructions content with updated JSON
12730
+ let updatedInstructionsContent = CodeBlockUtils$1.updateCodeBlockByIndex(
12731
+ instructionsContent,
12732
+ jsonBlock.index,
12733
+ JSON.stringify(jsonData, null, 2),
12734
+ 'json'
12735
+ );
12736
+
12737
+ // 9. Create the new analyzer ID
12738
+ const newAnalyzerId = `${newAnalyzerName}::${contentType}::${instructionsType}`;
12739
+
12740
+ // 10. Update the analyzer id in the instructions
12741
+ updatedInstructionsContent = updatedInstructionsContent.replaceAll(originalAnalyzerId, newAnalyzerId);
12742
+
12743
+ // 11. Save the new analyzer
12744
+ try {
12745
+ const saveResult = await saveConfiguration$1(
12746
+ analyzersBasePath,
12747
+ newAnalyzerId,
12748
+ updatedInstructionsContent
12749
+ );
12750
+
12751
+ if (saveResult.success) {
12752
+ return {
12753
+ success: true,
12754
+ message: `Analyzer '${originalAnalyzerName}' cloned successfully as '${newAnalyzerName}'.`,
12755
+ newAnalyzerId
12756
+ };
12757
+ } else {
12758
+ return { success: false, error: saveResult.message };
12759
+ }
12760
+ } catch (error) {
12761
+ return { success: false, message: `Failed to save cloned analyzer: ${error.message}` };
12762
+ }
12763
+ }
12764
+
12765
+ var cloner = {
12766
+ cloneAnalyzer: cloneAnalyzer$1
12767
+ };
12768
+
12769
+ /*
12770
+ * Component: AnalyzerUtils Index
12771
+ * Block-UUID: 780e17b0-0c1e-4d77-bf6e-302951b341bf
12772
+ * Parent-UUID: b403b6a1-230b-4247-8cd6-2a3d068f4bbf
12773
+ * Version: 1.5.0
12774
+ * Description: Aggregates and exports all utility functions from the AnalyzerUtils module. Added cloneAnalyzer method.
12775
+ * Language: JavaScript
12776
+ * Created-at: 2025-08-28T15:56:40.319Z
12777
+ * Authors: Gemini 2.5 Flash (v1.0.0), Gemini 2.5 Flash (v1.1.0), Gemini 2.5 Flash (v1.2.0), GLM-4.6 (v1.3.0), GLM-4.6 (v1.4.0), GLM-4.6 (v1.5.0)
12778
+ */
12779
+
12780
+ const { buildChatIdToPathMap: buildChatIdToPathMap$1 } = contextMapper;
12781
+ const { processLLMAnalysisResponse: processLLMAnalysisResponse$1 } = responseProcessor;
12782
+ const { validateLLMAnalysisData: validateLLMAnalysisData$1 } = dataValidator;
12783
+ const { getAnalyzers: getAnalyzers$1 } = discovery;
12784
+ const { saveConfiguration } = saver;
12785
+ const { getAnalyzerSchema: getAnalyzerSchema$1 } = schemaLoader;
12786
+ const { deleteAnalyzer: deleteAnalyzer$1 } = management;
12787
+ const { getAnalyzerInstructionsContent: getAnalyzerInstructionsContent$1 } = instructionLoader;
12788
+ const { getSystemMessageContent: getSystemMessageContent$1, getStartMessageContent: getStartMessageContent$1 } = defaultPromptLoader;
12789
+ const { preprocessJsonForValidation } = jsonParser;
12790
+ const { updateAnalyzer } = updater;
12791
+ const { cloneAnalyzer } = cloner;
12792
+
12793
+ var AnalyzerUtils$1 = {
12794
+ buildChatIdToPathMap: buildChatIdToPathMap$1,
12795
+ processLLMAnalysisResponse: processLLMAnalysisResponse$1,
12796
+ validateLLMAnalysisData: validateLLMAnalysisData$1,
12797
+ getAnalyzers: getAnalyzers$1,
12798
+ getAnalyzerSchema: getAnalyzerSchema$1,
12799
+ deleteAnalyzer: deleteAnalyzer$1,
12800
+ getAnalyzerInstructionsContent: getAnalyzerInstructionsContent$1,
12801
+ saveConfiguration,
12802
+ getSystemMessageContent: getSystemMessageContent$1,
12803
+ getStartMessageContent: getStartMessageContent$1,
12804
+ preprocessJsonForValidation,
12805
+ updateAnalyzer,
12806
+ cloneAnalyzer
12807
+ };
12808
+
12809
+ /**
12810
+ * Component: LLMUtils
12811
+ * Block-UUID: b8536c11-ed0b-4538-b3ff-35da94e67012
12812
+ * Parent-UUID: N/A
12813
+ * Version: 1.0.0
12814
+ * Description: Provides utility functions related to Large Language Model interactions, such as token estimation.
12815
+ * Language: JavaScript
12816
+ * Created-at: 2025-04-22T17:16:00.590Z
12817
+ * Authors: Gemini 2.5 Pro (v1.0.0)
12818
+ */
12819
+
12820
+ /**
12821
+ * Estimates the number of tokens in a given text string.
12822
+ * This is a basic estimation using the common heuristic of ~4 characters per token.
12823
+ * Actual token count can vary significantly based on the specific tokenizer used by an LLM.
12824
+ *
12825
+ * @param {string} text - The text to estimate tokens for.
12826
+ * @returns {number} An estimated token count. Returns 0 if input is not a non-empty string.
12827
+ */
12828
+ function estimateTokens$1(text) {
12829
+ if (typeof text !== 'string' || text.length === 0) {
12099
12830
  return 0;
12100
12831
  }
12101
12832
  // Basic heuristic: average token length is around 4 characters
@@ -12221,7 +12952,7 @@ var DateUtils$1 = {
12221
12952
 
12222
12953
  /**
12223
12954
  * Component: Formatter Utilities
12224
- * Block-UUID: 9b1e07bf-e92c-43c1-9ac0-5dacdb7ae618
12955
+ * Block-UUID: 8512c809-87b0-4f90-af9d-b3dca204c4de
12225
12956
  * Parent-UUID: 9c07d12f-5a05-4402-99d2-85d872d7b2f7
12226
12957
  * Version: 1.0.0
12227
12958
  * Description: Utility functions for formatting content in GitSense Chat
@@ -12276,13 +13007,13 @@ var FormatterUtils$1 = {
12276
13007
 
12277
13008
  /**
12278
13009
  * Component: DomUtils Helper Functions
12279
- * Block-UUID: e85d4e1d-f45e-450b-a0f2-941af362c2be
12280
- * Parent-UUID: fe462d17-e1c8-45d6-9fb0-f53322c9cda9
12281
- * Version: 1.1.0
13010
+ * Block-UUID: 9a8e8e8e-8b15-4346-bbfa-740e6a5d2d79
13011
+ * Parent-UUID: 8a528728-ce54-4445-946d-1743fb4b16be
13012
+ * Version: 1.3.1
12282
13013
  * Description: Provides helper functions for creating and manipulating DOM elements.
12283
13014
  * Language: JavaScript
12284
- * Created-at: 2025-09-11T19:03:50.026Z
12285
- * Authors: Gemini 2.5 Pro (v1.0.0), Gemini 2.5 Flash (v1.1.0)
13015
+ * Created-at: 2025-12-14T04:18:40.180Z
13016
+ * 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)
12286
13017
  */
12287
13018
 
12288
13019
  function createElement(type, params) {
@@ -12294,6 +13025,7 @@ function createElement(type, params) {
12294
13025
  let {
12295
13026
  id,
12296
13027
  ariaLabel,
13028
+ xmlns,
12297
13029
  role,
12298
13030
  html,
12299
13031
  text,
@@ -12316,11 +13048,15 @@ function createElement(type, params) {
12316
13048
  "data-message-role": dmr,
12317
13049
  attrs = {},
12318
13050
  dataset,
13051
+ rows
12319
13052
  } = params;
12320
13053
 
12321
13054
  // Set standard attributes
12322
13055
  if (id != null) elem.id = id;
12323
13056
  if (ariaLabel) elem.setAttribute("aria-label", ariaLabel);
13057
+ if (xmlns) elem.setAttribute("xmlns", xmlns);
13058
+ if (params.viewBox) elem.setAttribute("viewBox", params.viewBox);
13059
+ if (params.fill) elem.setAttribute("fill", params.fill);
12324
13060
  if (role) elem.setAttribute("role", role);
12325
13061
  if (placeholder) elem.setAttribute("placeholder", placeholder);
12326
13062
  if (cls || className) elem.setAttribute("class", cls || className);
@@ -12328,6 +13064,9 @@ function createElement(type, params) {
12328
13064
  if (title != null) elem.setAttribute("title", title);
12329
13065
  if (inputType) elem.setAttribute("type", inputType);
12330
13066
  if (name != null) elem.setAttribute("name", name);
13067
+ if (params.width) elem.setAttribute("width", params.width);
13068
+ if (params.height) elem.setAttribute("height", params.height);
13069
+ if (params.stroke) elem.setAttribute("stroke", params.stroke);
12331
13070
  if (value != null) elem.value = value;
12332
13071
  if (checked != null && checked) elem.checked = true; // Use property for checked
12333
13072
  if (selected != null && selected) elem.selected = true; // Use property for selected
@@ -12335,13 +13074,21 @@ function createElement(type, params) {
12335
13074
  if (dmi != null) elem.setAttribute("data-message-id", dmi);
12336
13075
  if (dmr != null) elem.setAttribute("data-message-role", dmr);
12337
13076
  if (_for != null ) elem.setAttribute("for", _for);
13077
+ if (params.strokeWidth) elem.setAttribute("stroke-width", params.strokeWidth);
13078
+ if (params.strokeLinecap) elem.setAttribute("stroke-linecap", params.strokeLinecap);
13079
+ if (params.strokeLinejoin) elem.setAttribute("stroke-linejoin", params.strokeLinejoin);
12338
13080
  if (colSpan != null) elem.colSpan = colSpan;
13081
+ if (rows) elem.setAttribute("rows", params.rows);
12339
13082
 
12340
13083
  // Set content
12341
13084
  if (html != null) {
12342
13085
  if (typeof html === "object" && html instanceof Node) { // Ensure html is a Node
12343
13086
  elem.appendChild(html);
12344
13087
  } else if (typeof html === "string") {
13088
+ // For SVG elements, use innerHTML to support path elements
13089
+ if (type === 'svg' && params.innerHTML) {
13090
+ elem.innerHTML = params.innerHTML;
13091
+ }
12345
13092
  elem.innerHTML = html;
12346
13093
  }
12347
13094
  } else if (text != null) {
@@ -12367,12 +13114,12 @@ function createElement(type, params) {
12367
13114
  });
12368
13115
  }
12369
13116
 
12370
- // NEW: Set additional attributes from attrs object
13117
+ // Set additional attributes from attrs object
12371
13118
  if (attrs && typeof attrs === 'object') {
12372
13119
  for (const [key, value] of Object.entries(attrs)) {
12373
13120
  if (value !== undefined && value !== null) {
12374
13121
  // Skip if already set by standard properties or handled directly
12375
- if (!['id', 'class', 'role', 'aria-label', 'title', 'style', 'checked', 'selected', 'disabled', 'value', 'name', 'type', 'for'].includes(key) && !key.startsWith('data-')) {
13122
+ if (!['id', 'class', 'role', 'aria-label', 'title', 'style', 'selected', 'disabled', 'value', 'name', 'type', 'for'].includes(key) && !key.startsWith('data-')) {
12376
13123
  elem.setAttribute(key, value);
12377
13124
  }
12378
13125
  }
@@ -12485,14 +13232,17 @@ const h$1 = {
12485
13232
  },
12486
13233
  createLink: (params) => {
12487
13234
  const link = createElement("a", params);
12488
- // href and target are handled by createElement
12489
- if (params?.href) link.href = params.href; // Ensure href is set if provided
13235
+ if (params?.href) link.href = params.href;
13236
+ if (params?.target) link.target = params.target;
12490
13237
  return link;
12491
13238
  },
12492
13239
  createNav: (params) => {
12493
13240
  return createElement("nav", params);
12494
13241
  },
12495
- createOL: (params) => { // Added
13242
+ createOl: (params) => {
13243
+ return createElement("ol", params);
13244
+ },
13245
+ createOL: (params) => {
12496
13246
  return createElement("ol", params);
12497
13247
  },
12498
13248
  createOption: (params) => { // Added
@@ -12508,13 +13258,49 @@ const h$1 = {
12508
13258
  createPre: (params) => {
12509
13259
  return createElement("pre", params);
12510
13260
  },
12511
- createSelect: (params) => { // Added
12512
- // name, disabled etc handled by createElement
12513
- return createElement("select", params);
13261
+ createSelect: (params) => {
13262
+ // Extract value before creating the select element
13263
+ const { value, ...selectParams } = params || {};
13264
+
13265
+ // Create select element without the value initially
13266
+ const select = createElement("select", selectParams);
13267
+
13268
+ // Handle options array if provided
13269
+ if (params && params.options && Array.isArray(params.options)) {
13270
+ params.options.forEach(option => {
13271
+ if (option && typeof option === 'object') {
13272
+ // Check if this option should be selected based on the value
13273
+ const isSelected = (value !== undefined && value !== null) &&
13274
+ (option.value === value ||
13275
+ (value === '' && option.value === '') ||
13276
+ (value === null && option.value === null));
13277
+
13278
+ const optionElement = createElement("option", {
13279
+ value: option.value,
13280
+ text: option.text,
13281
+ selected: isSelected || option.selected
13282
+ });
13283
+ select.appendChild(optionElement);
13284
+ }
13285
+ });
13286
+ }
13287
+
13288
+ // Set the value after options are added to ensure proper selection
13289
+ if (value !== undefined && value !== null) {
13290
+ select.value = value;
13291
+ }
13292
+
13293
+ return select;
12514
13294
  },
12515
13295
  createSpan: (params) => {
12516
13296
  return createElement("span", params);
12517
13297
  },
13298
+ createSvg: (params) => {
13299
+ // Create SVG element with xmlns attribute
13300
+ const svgParams = { ...params, xmlns: "http://www.w3.org/2000/svg" };
13301
+ const svg = createElement("svg", svgParams);
13302
+ return svg;
13303
+ },
12518
13304
  createStrong: (params) => { // Added
12519
13305
  return createElement("strong", params);
12520
13306
  },
@@ -12527,6 +13313,10 @@ const h$1 = {
12527
13313
  createText: (text) => {
12528
13314
  return document.createTextNode(text);
12529
13315
  },
13316
+ createTextarea: (params) => {
13317
+ const textArea = createElement("textarea", params);
13318
+ return textArea;
13319
+ },
12530
13320
  createTextArea: (params) => {
12531
13321
  const textArea = createElement("textarea", params);
12532
13322
  return textArea;
@@ -12725,6 +13515,35 @@ const h$1 = {
12725
13515
  })
12726
13516
  .join('');
12727
13517
  },
13518
+
13519
+ /**
13520
+ * Injects CSS styles into the document head.
13521
+ * @param {string} cssString - The CSS string to inject.
13522
+ * @param {string} [id] - Optional ID for the style element to prevent duplicates.
13523
+ * @returns {HTMLStyleElement} The created or existing style element.
13524
+ */
13525
+ injectStyles: (cssString, id) => {
13526
+ // If an ID is provided, check if styles with this ID already exist
13527
+ if (id) {
13528
+ const existingStyle = document.getElementById(id);
13529
+ if (existingStyle) {
13530
+ // Styles already injected, return the existing element
13531
+ return existingStyle;
13532
+ }
13533
+ }
13534
+
13535
+ // Create a new style element
13536
+ const styleElement = document.createElement('style');
13537
+ if (id) {
13538
+ styleElement.id = id;
13539
+ }
13540
+ styleElement.textContent = cssString;
13541
+
13542
+ // Append to the document head
13543
+ document.head.appendChild(styleElement);
13544
+
13545
+ return styleElement;
13546
+ },
12728
13547
 
12729
13548
  /**
12730
13549
  * Calculates the distance between an element's edge and the viewport's corresponding edge
@@ -22357,7 +23176,10 @@ function createMarkdownRenderer$1(hljs, hstyle = {}, nhstyle = {}) {
22357
23176
  // Create the inner code element with the highlighted content
22358
23177
  // and the language label prepended.
22359
23178
  const codeElement = h.createCode({
22360
- html: `<span class='gs-chat-lang'>${lang}</span>\n\n${highlightedCode}`
23179
+ html: `<span class='gs-chat-lang'>${lang}</span>\n\n${highlightedCode}`,
23180
+ style: {
23181
+ fontSize: '13px'
23182
+ }
22361
23183
  });
22362
23184
 
22363
23185
  // Wrap the code element in a pre tag with styling
@@ -22758,7 +23580,7 @@ let SVGUtils$1 = class SVGUtils {
22758
23580
  * @returns {Element} SVG element
22759
23581
  */
22760
23582
  static archive(params) {
22761
- const xml = `<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' width='16' height='16'><path fill-rule='evenodd' d='M1.75 2.5a.25.25 0 00-.25.25v1.5c0 .138.112.25.25.25h12.5a.25.25 0 00.25-.25v-1.5a.25.25 0 00-.25-.25H1.75zM0 2.75C0 1.784.784 1 1.75 1h12.5c.966 0 1.75.784 1.75 1.75v1.5A1.75 1.75 0 0114.25 6H1.75A1.75 1.75 0 010 4.25v-1.5zM1.75 7a.75.75 0 01.75.75v5.5c0 .138.112.25.25.25h10.5a.25.25 0 00.25-.25v-5.5a.75.75 0 111.5 0v5.5A1.75 1.75 0 0113.25 15H2.75A1.75 1.75 0 011 13.25v-5.5A.75.75 0 011.75 7zm4.5 1a.75.75 0 000 1.5h3.5a.75.75 0 100-1.5h-3.5z'></path></svg>`;
23583
+ const xml = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path d="M0 2.75C0 1.784.784 1 1.75 1h12.5c.966 0 1.75.784 1.75 1.75v1.5A1.75 1.75 0 0 1 14.25 6H1.75A1.75 1.75 0 0 1 0 4.25ZM1.75 7a.75.75 0 0 1 .75.75v5.5c0 .138.112.25.25.25h10.5a.25.25 0 0 0 .25-.25v-5.5a.75.75 0 0 1 1.5 0v5.5A1.75 1.75 0 0 1 13.25 15H2.75A1.75 1.75 0 0 1 1 13.25v-5.5A.75.75 0 0 1 1.75 7Zm0-4.5a.25.25 0 0 0-.25.25v1.5c0 .138.112.25.25.25h12.5a.25.25 0 0 0 .25-.25v-1.5a.25.25 0 0 0-.25-.25ZM6.25 8h3.5a.75.75 0 0 1 0 1.5h-3.5a.75.75 0 0 1 0-1.5Z"></path></svg>`;
22762
23584
  return this.create(xml, params);
22763
23585
  }
22764
23586
 
@@ -23084,6 +23906,16 @@ let SVGUtils$1 = class SVGUtils {
23084
23906
  return this.create(xml, params);
23085
23907
  }
23086
23908
 
23909
+ /**
23910
+ * Generate commentDiscussions24 SVG icon
23911
+ * @param {Object} params - Configuration parameters
23912
+ * @returns {Element} SVG element
23913
+ */
23914
+ static commentDiscussions24(params) {
23915
+ const xml = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path d="M1.75 1h12.5c.966 0 1.75.784 1.75 1.75v9.5A1.75 1.75 0 0 1 14.25 14H8.061l-2.574 2.573A1.458 1.458 0 0 1 3 15.543V14H1.75A1.75 1.75 0 0 1 0 12.25v-9.5C0 1.784.784 1 1.75 1ZM1.5 2.75v9.5c0 .138.112.25.25.25h2a.75.75 0 0 1 .75.75v2.19l2.72-2.72a.749.749 0 0 1 .53-.22h6.5a.25.25 0 0 0 .25-.25v-9.5a.25.25 0 0 0-.25-.25H1.75a.25.25 0 0 0-.25.25Z"></path><path d="M22.5 8.75a.25.25 0 0 0-.25-.25h-3.5a.75.75 0 0 1 0-1.5h3.5c.966 0 1.75.784 1.75 1.75v9.5A1.75 1.75 0 0 1 22.25 20H21v1.543a1.457 1.457 0 0 1-2.487 1.03L15.939 20H10.75A1.75 1.75 0 0 1 9 18.25v-1.465a.75.75 0 0 1 1.5 0v1.465c0 .138.112.25.25.25h5.5a.75.75 0 0 1 .53.22l2.72 2.72v-2.19a.75.75 0 0 1 .75-.75h2a.25.25 0 0 0 .25-.25v-9.5Z"></path></svg>`;
23916
+ return this.create(xml, params);
23917
+ }
23918
+
23087
23919
  /**
23088
23920
  * Generate commit SVG icon
23089
23921
  * @param {Object} params - Configuration parameters
@@ -23094,6 +23926,16 @@ let SVGUtils$1 = class SVGUtils {
23094
23926
  return this.create(xml, params);
23095
23927
  }
23096
23928
 
23929
+ /**
23930
+ * Generate commit SVG icon
23931
+ * @param {Object} params - Configuration parameters
23932
+ * @returns {Element} SVG element
23933
+ */
23934
+ static compose24(params) {
23935
+ const xml = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path d="M1 21.25V4a1.75 1.75 0 0 1 1.75-1.75h7.51a.75.75 0 0 1 0 1.5H2.75A.25.25 0 0 0 2.5 4v17.25a.25.25 0 0 0 .25.25H20a.25.25 0 0 0 .25-.25V13a.75.75 0 0 1 1.5 0v8.25c0 .464-.184.909-.513 1.237A1.746 1.746 0 0 1 20 23H2.75A1.75 1.75 0 0 1 1 21.25Z"></path><path d="M19.013 1.427a1.75 1.75 0 0 1 2.474 0l1.086 1.086a1.75 1.75 0 0 1 0 2.474l-8.61 8.61c-.21.21-.47.364-.756.445l-3.25.929a.751.751 0 0 1-.928-.927l.929-3.25c.082-.287.235-.547.445-.758l8.61-8.61Zm-7.55 9.67a.241.241 0 0 0-.063.108l-.558 1.953 1.953-.558a.253.253 0 0 0 .108-.064L19.19 6.25l-1.44-1.44Zm8.964-8.61a.245.245 0 0 0-.177-.073.245.245 0 0 0-.177.073L18.811 3.75l1.439 1.44 1.263-1.263a.245.245 0 0 0 .073-.177.245.245 0 0 0-.073-.177Z"></path></svg>`;
23936
+ return this.create(xml, params);
23937
+ }
23938
+
23097
23939
  /**
23098
23940
  * Generate container SVG icon
23099
23941
  * @param {Object} params - Configuration parameters
@@ -23124,6 +23966,16 @@ let SVGUtils$1 = class SVGUtils {
23124
23966
  return this.create(xml, params);
23125
23967
  }
23126
23968
 
23969
+ /**
23970
+ * Generate crosshairs SVG icon
23971
+ * @param {Object} params - Configuration parameters
23972
+ * @returns {Element} SVG element
23973
+ */
23974
+ static crosshairs(params) {
23975
+ const xml = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path d="M14 8A6 6 0 1 1 2 8a6 6 0 0 1 12 0Zm-1.5 0a4.5 4.5 0 1 0-9 0 4.5 4.5 0 0 0 9 0Z"></path><path d="M5 7.25a.75.75 0 0 1 0 1.5H1a.75.75 0 0 1 0-1.5Zm3-7a.75.75 0 0 1 .75.75v4a.75.75 0 0 1-1.5 0V1A.75.75 0 0 1 8 .25Zm7 7a.75.75 0 0 1 0 1.5h-4a.75.75 0 0 1 0-1.5Zm-7 3a.75.75 0 0 1 .75.75v4a.75.75 0 0 1-1.5 0v-4a.75.75 0 0 1 .75-.75Z"></path></svg>`;
23976
+ return this.create(xml, params);
23977
+ }
23978
+
23127
23979
  /**
23128
23980
  * Generate deepSeek SVG icon
23129
23981
  * @param {Object} params - Configuration parameters
@@ -23165,6 +24017,16 @@ let SVGUtils$1 = class SVGUtils {
23165
24017
  return this.create(xml, params);
23166
24018
  }
23167
24019
 
24020
+ /**
24021
+ * Generate dropZone SVG icon
24022
+ * @param {Object} params - Configuration parameters
24023
+ * @returns {Element} SVG element
24024
+ */
24025
+ static dropZone(params) {
24026
+ const xml = `<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='64' height='64'><path d="M7 18C4.23858 18 2 15.7614 2 13C2 10.2386 4.23858 8 7 8C7.55228 8 8 8.44772 8 9C8 9.55228 7.55228 10 7 10C5.34315 10 4 11.3431 4 13C4 14.6569 5.34315 16 7 16H17C18.6569 16 20 14.6569 20 13C20 11.3431 18.6569 10 17 10C16.4477 10 16 9.55228 16 9C16 8.44772 16.4477 8 17 8C19.7614 8 22 10.2386 22 13C22 15.7614 19.7614 18 17 18H7Z" fill="currentColor"/><path d="M12 2L12 14M12 14L9 11M12 14L15 11" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>`;
24027
+ return this.create(xml, params);
24028
+ }
24029
+
23168
24030
  /**
23169
24031
  * Generate dot SVG icon
23170
24032
  * @param {Object} params - Configuration parameters
@@ -23205,6 +24067,26 @@ let SVGUtils$1 = class SVGUtils {
23205
24067
  return this.create(xml, params);
23206
24068
  }
23207
24069
 
24070
+ /**
24071
+ * Generate eye SVG icon
24072
+ * @param {Object} params - Configuration parameters
24073
+ * @returns {Element} SVG element
24074
+ */
24075
+ static eye(params) {
24076
+ const xml = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path d="M8 2c1.981 0 3.671.992 4.933 2.078 1.27 1.091 2.187 2.345 2.637 3.023a1.62 1.62 0 0 1 0 1.798c-.45.678-1.367 1.932-2.637 3.023C11.67 13.008 9.981 14 8 14c-1.981 0-3.671-.992-4.933-2.078C1.797 10.83.88 9.576.43 8.898a1.62 1.62 0 0 1 0-1.798c.45-.677 1.367-1.931 2.637-3.022C4.33 2.992 6.019 2 8 2ZM1.679 7.932a.12.12 0 0 0 0 .136c.411.622 1.241 1.75 2.366 2.717C5.176 11.758 6.527 12.5 8 12.5c1.473 0 2.825-.742 3.955-1.715 1.124-.967 1.954-2.096 2.366-2.717a.12.12 0 0 0 0-.136c-.412-.621-1.242-1.75-2.366-2.717C10.824 4.242 9.473 3.5 8 3.5c-1.473 0-2.825.742-3.955 1.715-1.124.967-1.954 2.096-2.366 2.717ZM8 10a2 2 0 1 1-.001-3.999A2 2 0 0 1 8 10Z"></path></svg>`;
24077
+ return this.create(xml, params);
24078
+ }
24079
+
24080
+ /**
24081
+ * Generate eye closed SVG icon
24082
+ * @param {Object} params - Configuration parameters
24083
+ * @returns {Element} SVG element
24084
+ */
24085
+ static eyeClosed(params) {
24086
+ const xml = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path d="M.143 2.31a.75.75 0 0 1 1.047-.167l14.5 10.5a.75.75 0 1 1-.88 1.214l-2.248-1.628C11.346 13.19 9.792 14 8 14c-1.981 0-3.67-.992-4.933-2.078C1.797 10.832.88 9.577.43 8.9a1.619 1.619 0 0 1 0-1.797c.353-.533.995-1.42 1.868-2.305L.31 3.357A.75.75 0 0 1 .143 2.31Zm1.536 5.622A.12.12 0 0 0 1.657 8c0 .021.006.045.022.068.412.621 1.242 1.75 2.366 2.717C5.175 11.758 6.527 12.5 8 12.5c1.195 0 2.31-.488 3.29-1.191L9.063 9.695A2 2 0 0 1 6.058 7.52L3.529 5.688a14.207 14.207 0 0 0-1.85 2.244ZM8 3.5c-.516 0-1.017.09-1.499.251a.75.75 0 1 1-.473-1.423A6.207 6.207 0 0 1 8 2c1.981 0 3.67.992 4.933 2.078 1.27 1.091 2.187 2.345 2.637 3.023a1.62 1.62 0 0 1 0 1.798c-.11.166-.248.365-.41.587a.75.75 0 1 1-1.21-.887c.148-.201.272-.382.371-.53a.119.119 0 0 0 0-.137c-.412-.621-1.242-1.75-2.366-2.717C10.825 4.242 9.473 3.5 8 3.5Z"></path></svg>`;
24087
+ return this.create(xml, params);
24088
+ }
24089
+
23208
24090
  /**
23209
24091
  * Generate file SVG icon
23210
24092
  * @param {Object} params - Configuration parameters
@@ -23497,6 +24379,16 @@ let SVGUtils$1 = class SVGUtils {
23497
24379
  return this.create(xml, params);
23498
24380
  }
23499
24381
 
24382
+ /**
24383
+ * Generate lock SVG icon
24384
+ * @param {Object} params - Configuration parameters
24385
+ * @returns {Element} SVG element
24386
+ */
24387
+ static lock(params) {
24388
+ const xml = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path d="M4 4a4 4 0 0 1 8 0v2h.25c.966 0 1.75.784 1.75 1.75v5.5A1.75 1.75 0 0 1 12.25 15h-8.5A1.75 1.75 0 0 1 2 13.25v-5.5C2 6.784 2.784 6 3.75 6H4Zm8.25 3.5h-8.5a.25.25 0 0 0-.25.25v5.5c0 .138.112.25.25.25h8.5a.25.25 0 0 0 .25-.25v-5.5a.25.25 0 0 0-.25-.25ZM10.5 6V4a2.5 2.5 0 1 0-5 0v2Z"></path></svg>`;
24389
+ return this.create(xml, params);
24390
+ }
24391
+
23500
24392
  /**
23501
24393
  * Generate lightBulb SVG icon
23502
24394
  * @param {Object} params - Configuration parameters
@@ -23550,17 +24442,27 @@ let SVGUtils$1 = class SVGUtils {
23550
24442
  }
23551
24443
 
23552
24444
  /**
23553
- * Generate northStar SVG icon
24445
+ * Generate moveToEnd SVG icon
23554
24446
  * @param {Object} params - Configuration parameters
23555
24447
  * @returns {Element} SVG element
23556
24448
  */
23557
- static northStar(params) {
23558
- const xml = `<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' width='16' height='16'><path d='M8.5.75a.75.75 0 00-1.5 0v5.19L4.391 3.33a.75.75 0 10-1.06 1.061L5.939 7H.75a.75.75 0 000 1.5h5.19l-2.61 2.609a.75.75 0 101.061 1.06L7 9.561v5.189a.75.75 0 001.5 0V9.56l2.609 2.61a.75.75 0 101.06-1.061L9.561 8.5h5.189a.75.75 0 000-1.5H9.56l2.61-2.609a.75.75 0 00-1.061-1.06L8.5 5.939V.75z'></path></svg>`;
24449
+ static moveToEnd(params) {
24450
+ const xml = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path d="m10.78 8.53-3.75 3.75a.749.749 0 1 1-1.06-1.06l2.469-2.47H1.75a.75.75 0 0 1 0-1.5h6.689L5.97 4.78a.749.749 0 1 1 1.06-1.06l3.75 3.75a.749.749 0 0 1 0 1.06ZM13 12.25v-8.5a.75.75 0 0 1 1.5 0v8.5a.75.75 0 0 1-1.5 0Z"></path></svg>`;
23559
24451
  return this.create(xml, params);
23560
24452
  }
23561
24453
 
23562
24454
  /**
23563
- * Generate note SVG icon
24455
+ * Generate northStar SVG icon
24456
+ * @param {Object} params - Configuration parameters
24457
+ * @returns {Element} SVG element
24458
+ */
24459
+ static northStar(params) {
24460
+ const xml = `<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' width='16' height='16'><path d='M8.5.75a.75.75 0 00-1.5 0v5.19L4.391 3.33a.75.75 0 10-1.06 1.061L5.939 7H.75a.75.75 0 000 1.5h5.19l-2.61 2.609a.75.75 0 101.061 1.06L7 9.561v5.189a.75.75 0 001.5 0V9.56l2.609 2.61a.75.75 0 101.06-1.061L9.561 8.5h5.189a.75.75 0 000-1.5H9.56l2.61-2.609a.75.75 0 00-1.061-1.06L8.5 5.939V.75z'></path></svg>`;
24461
+ return this.create(xml, params);
24462
+ }
24463
+
24464
+ /**
24465
+ * Generate note SVG icon
23564
24466
  * @param {Object} params - Configuration parameters
23565
24467
  * @returns {Element} SVG element
23566
24468
  */
@@ -23569,6 +24471,16 @@ let SVGUtils$1 = class SVGUtils {
23569
24471
  return this.create(xml, params);
23570
24472
  }
23571
24473
 
24474
+ /**
24475
+ * Generate note24 SVG icon
24476
+ * @param {Object} params - Configuration parameters
24477
+ * @returns {Element} SVG element
24478
+ */
24479
+ static note24(params) {
24480
+ const xml = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path d="M0 4.75C0 3.784.784 3 1.75 3h20.5c.966 0 1.75.784 1.75 1.75v14.5A1.75 1.75 0 0 1 22.25 21H1.75A1.75 1.75 0 0 1 0 19.25Zm1.75-.25a.25.25 0 0 0-.25.25v14.5c0 .138.112.25.25.25h20.5a.25.25 0 0 0 .25-.25V4.75a.25.25 0 0 0-.25-.25Z"></path><path d="M5 8.75A.75.75 0 0 1 5.75 8h11.5a.75.75 0 0 1 0 1.5H5.75A.75.75 0 0 1 5 8.75Zm0 4a.75.75 0 0 1 .75-.75h5.5a.75.75 0 0 1 0 1.5h-5.5a.75.75 0 0 1-.75-.75Z"></path></svg>`;
24481
+ return this.create(xml, params);
24482
+ }
24483
+
23572
24484
  /**
23573
24485
  * Generate openAI SVG icon
23574
24486
  * @param {Object} params - Configuration parameters
@@ -23635,7 +24547,17 @@ let SVGUtils$1 = class SVGUtils {
23635
24547
  * @returns {Element} SVG element
23636
24548
  */
23637
24549
  static pin(params) {
23638
- const xml = `<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' width='16' height='16'><path fill-rule='evenodd' d='M4.456.734a1.75 1.75 0 012.826.504l.613 1.327a3.081 3.081 0 002.084 1.707l2.454.584c1.332.317 1.8 1.972.832 2.94L11.06 10l3.72 3.72a.75.75 0 11-1.061 1.06L10 11.06l-2.204 2.205c-.968.968-2.623.5-2.94-.832l-.584-2.454a3.081 3.081 0 00-1.707-2.084l-1.327-.613a1.75 1.75 0 01-.504-2.826L4.456.734zM5.92 1.866a.25.25 0 00-.404-.072L1.794 5.516a.25.25 0 00.072.404l1.328.613A4.582 4.582 0 015.73 9.63l.584 2.454a.25.25 0 00.42.12l5.47-5.47a.25.25 0 00-.12-.42L9.63 5.73a4.581 4.581 0 01-3.098-2.537L5.92 1.866z'></path></svg>`;
24550
+ const xml = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path d="m11.294.984 3.722 3.722a1.75 1.75 0 0 1-.504 2.826l-1.327.613a3.089 3.089 0 0 0-1.707 2.084l-.584 2.454c-.317 1.332-1.972 1.8-2.94.832L5.75 11.311 1.78 15.28a.749.749 0 1 1-1.06-1.06l3.969-3.97-2.204-2.204c-.968-.968-.5-2.623.832-2.94l2.454-.584a3.08 3.08 0 0 0 2.084-1.707l.613-1.327a1.75 1.75 0 0 1 2.826-.504ZM6.283 9.723l2.732 2.731a.25.25 0 0 0 .42-.119l.584-2.454a4.586 4.586 0 0 1 2.537-3.098l1.328-.613a.25.25 0 0 0 .072-.404l-3.722-3.722a.25.25 0 0 0-.404.072l-.613 1.328a4.584 4.584 0 0 1-3.098 2.537l-2.454.584a.25.25 0 0 0-.119.42l2.731 2.732Z"></path></svg>`;
24551
+ return this.create(xml, params);
24552
+ }
24553
+
24554
+ /**
24555
+ * Generate pin24 SVG icon
24556
+ * @param {Object} params - Configuration parameters
24557
+ * @returns {Element} SVG element
24558
+ */
24559
+ static pin24(params) {
24560
+ const xml = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path d="m16.114 1.553 6.333 6.333a1.75 1.75 0 0 1-.603 2.869l-1.63.633a5.67 5.67 0 0 0-3.395 3.725l-1.131 3.959a1.75 1.75 0 0 1-2.92.757L9 16.061l-5.595 5.594a.749.749 0 1 1-1.06-1.06L7.939 15l-3.768-3.768a1.75 1.75 0 0 1 .757-2.92l3.959-1.131a5.666 5.666 0 0 0 3.725-3.395l.633-1.63a1.75 1.75 0 0 1 2.869-.603ZM5.232 10.171l8.597 8.597a.25.25 0 0 0 .417-.108l1.131-3.959A7.17 7.17 0 0 1 19.67 9.99l1.63-.634a.25.25 0 0 0 .086-.409l-6.333-6.333a.25.25 0 0 0-.409.086l-.634 1.63a7.17 7.17 0 0 1-4.711 4.293L5.34 9.754a.25.25 0 0 0-.108.417Z"></path></svg>`;
23639
24561
  return this.create(xml, params);
23640
24562
  }
23641
24563
 
@@ -23751,6 +24673,16 @@ let SVGUtils$1 = class SVGUtils {
23751
24673
  return this.create(xml, params);
23752
24674
  }
23753
24675
 
24676
+ /**
24677
+ * Generate sparkle SVG icon
24678
+ * @param {Object} params - Configuration parameters
24679
+ * @returns {Element} SVG element
24680
+ */
24681
+ static sparkle(params) {
24682
+ const xml = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path d="M7.198.57c.275-.752 1.34-.752 1.615 0l.849 2.317a5.819 5.819 0 0 0 3.462 3.463l2.317.848c.753.275.753 1.34 0 1.615l-2.317.849a5.815 5.815 0 0 0-3.462 3.462l-.849 2.317c-.275.753-1.34.753-1.615 0l-.848-2.317a5.819 5.819 0 0 0-3.463-3.462L.57 8.813c-.752-.275-.752-1.34 0-1.615l2.317-.848A5.823 5.823 0 0 0 6.35 2.887L7.198.57Zm.562 2.833A7.323 7.323 0 0 1 3.403 7.76l-.673.246.673.246a7.324 7.324 0 0 1 4.357 4.356l.246.673.246-.673a7.322 7.322 0 0 1 4.356-4.356l.673-.246-.673-.246a7.324 7.324 0 0 1-4.356-4.357l-.246-.673-.246.673Z"></path></svg>`;
24683
+ return this.create(xml, params);
24684
+ }
24685
+
23754
24686
  /**
23755
24687
  * Generate square SVG icon
23756
24688
  * @param {Object} params - Configuration parameters
@@ -23761,6 +24693,16 @@ let SVGUtils$1 = class SVGUtils {
23761
24693
  return this.create(xml, params);
23762
24694
  }
23763
24695
 
24696
+ /**
24697
+ * Generate squarCircle SVG icon
24698
+ * @param {Object} params - Configuration parameters
24699
+ * @returns {Element} SVG element
24700
+ */
24701
+ static squareCircle(params) {
24702
+ const xml = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path d="M8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16Zm0-1.5a6.5 6.5 0 1 0 0-13 6.5 6.5 0 0 0 0 13Z"></path><path d="M5 5.75A.75.75 0 0 1 5.75 5h4.5a.75.75 0 0 1 .75.75v4.5a.75.75 0 0 1-.75.75h-4.5a.75.75 0 0 1-.75-.75Z"></path></svg>`;
24703
+ return this.create(xml, params);
24704
+ }
24705
+
23764
24706
  /**
23765
24707
  * Generate stack SVG icon
23766
24708
  * @param {Object} params - Configuration parameters
@@ -24051,6 +24993,16 @@ let SVGUtils$1 = class SVGUtils {
24051
24993
  return this.create(xml, params);
24052
24994
  }
24053
24995
 
24996
+ /**
24997
+ * Generate verticalEllipsis SVG icon
24998
+ * @param {Object} params - Configuration parameters
24999
+ * @returns {Element} SVG element
25000
+ */
25001
+ static verticalEllipsis(params) {
25002
+ const xml = `<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' width='16' height='16' transform='rotate(90)'><path d='M8 9a1.5 1.5 0 100-3 1.5 1.5 0 000 3zM1.5 9a1.5 1.5 0 100-3 1.5 1.5 0 000 3zm13 0a1.5 1.5 0 100-3 1.5 1.5 0 000 3z'></path></svg>`;
25003
+ return this.create(xml, params);
25004
+ }
25005
+
24054
25006
  /**
24055
25007
  * Generate x SVG icon
24056
25008
  * @param {Object} params - Configuration parameters
@@ -24080,10 +25032,318 @@ let SVGUtils$1 = class SVGUtils {
24080
25032
  const xml = `<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' width='16' height='16'><path fill-rule='evenodd' d='M2.343 13.657A8 8 0 1113.657 2.343 8 8 0 012.343 13.657zM6.03 4.97a.75.75 0 00-1.06 1.06L6.94 8 4.97 9.97a.75.75 0 101.06 1.06L8 9.06l1.97 1.97a.75.75 0 101.06-1.06L9.06 8l1.97-1.97a.75.75 0 10-1.06-1.06L8 6.94 6.03 4.97z'></path></svg>`;
24081
25033
  return this.create(xml, params);
24082
25034
  }
25035
+
25036
+ static zai(params) {
25037
+ const xml = `<svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" fill-rule="evenodd" height="1em" style="flex:none;line-height:1" viewBox="0 0 24 24" width="1em"><title>Z.ai</title><path d="M12.105 2L9.927 4.953H.653L2.83 2h9.276zM23.254 19.048L21.078 22h-9.242l2.174-2.952h9.244zM24 2L9.264 22H0L14.736 2H24z"/></svg>`;
25038
+ return this.create(xml, params);
25039
+ }
24083
25040
  };
24084
25041
 
24085
25042
  var SVGUtils_1 = SVGUtils$1;
24086
25043
 
25044
+ /*
25045
+ * Component: StringUtils
25046
+ * Block-UUID: 7069f2b4-f0b3-4dcd-9eae-5cecaddcdd66
25047
+ * Version: 1.0.0
25048
+ * Description: A collection of utility functions for common string manipulation and processing tasks within GitSense Chat.
25049
+ * Language: JavaScript
25050
+ * Created-at: 2025-10-30T22:06:47.000Z
25051
+ * Authors: Qwen 3 Coder 480B - Cerebras (v1.0.0)
25052
+ */
25053
+
25054
+ /**
25055
+ * Splits a string into an array of substrings based on a delimiter, up to a specified limit.
25056
+ * This mimics behavior similar to Python's `str.split(sep, maxsplit)` or Java's `String.split(regex, limit)`.
25057
+ *
25058
+ * @param {string} text - The string to split.
25059
+ * @param {string|RegExp} delimiter - The delimiter to split the string by.
25060
+ * @param {number} [limit=Infinity] - The maximum number of parts to return. The final part will contain the remainder of the string.
25061
+ * @returns {string[]} An array of substrings.
25062
+ */
25063
+ function splitWithLimit$1(text, delimiter, limit = Infinity) {
25064
+ if (limit === 0) {
25065
+ return [];
25066
+ }
25067
+ if (limit === 1) {
25068
+ return [text];
25069
+ }
25070
+
25071
+ const parts = [];
25072
+ let remainingText = text;
25073
+ let count = 0;
25074
+
25075
+ // Handle RegExp delimiter
25076
+ if (delimiter instanceof RegExp) {
25077
+ // For RegExp, we need to manually find matches and slice the string
25078
+ // This is a simplified approach and might not cover all edge cases of global regex matching
25079
+ // especially with capture groups or zero-width matches.
25080
+ const flags = delimiter.flags.includes('g') ? delimiter.flags : delimiter.flags + 'g';
25081
+ const globalDelimiter = new RegExp(delimiter.source, flags);
25082
+ let match;
25083
+ let lastIndex = 0;
25084
+ let matchCount = 0;
25085
+
25086
+ // We need (limit - 1) matches to create `limit` parts
25087
+ while ((match = globalDelimiter.exec(remainingText)) !== null && matchCount < limit - 1) {
25088
+ // Add the part before the match
25089
+ parts.push(remainingText.substring(lastIndex, match.index));
25090
+ // Add the matched delimiter part itself if it's non-empty (important for zero-width matches)
25091
+ // This part is tricky; typically, the delimiter itself is not part of the returned array.
25092
+ // The logic below follows the standard split behavior where the delimiter is not included.
25093
+ lastIndex = match.index + match[0].length;
25094
+ matchCount++;
25095
+ // Avoid infinite loop for zero-length matches
25096
+ if (match.index === globalDelimiter.lastIndex) {
25097
+ globalDelimiter.lastIndex++;
25098
+ }
25099
+ }
25100
+ // Add the final part
25101
+ parts.push(remainingText.substring(lastIndex));
25102
+ } else {
25103
+ // Handle string delimiter
25104
+ const delimLength = delimiter.length;
25105
+ if (delimLength === 0) {
25106
+ // If delimiter is an empty string, split by characters
25107
+ // This is consistent with JS String.split('')
25108
+ return text.split('').slice(0, limit);
25109
+ }
25110
+ let index;
25111
+ while (count < limit - 1 && (index = remainingText.indexOf(delimiter)) !== -1) {
25112
+ parts.push(remainingText.substring(0, index));
25113
+ remainingText = remainingText.substring(index + delimLength);
25114
+ count++;
25115
+ }
25116
+ // Add the final part (the rest of the string)
25117
+ parts.push(remainingText);
25118
+ }
25119
+
25120
+ return parts;
25121
+ }
25122
+
25123
+ /**
25124
+ * Capitalizes the first character of a string.
25125
+ *
25126
+ * @param {string} text - The string to capitalize.
25127
+ * @returns {string} The string with its first character capitalized.
25128
+ */
25129
+ function capitalize$1(text) {
25130
+ if (!text || typeof text !== 'string') {
25131
+ return text;
25132
+ }
25133
+ return text.charAt(0).toUpperCase() + text.slice(1);
25134
+ }
25135
+
25136
+ /**
25137
+ * Converts a string to title case (capitalizing the first letter of each word).
25138
+ * Words are sequences of characters separated by space, tab, or newline.
25139
+ *
25140
+ * @param {string} text - The string to convert.
25141
+ * @returns {string} The string in title case.
25142
+ */
25143
+ function titleCase$1(text) {
25144
+ if (!text || typeof text !== 'string') {
25145
+ return text;
25146
+ }
25147
+ // Split by one or more whitespace characters
25148
+ return text.split(/\s+/).map(word => capitalize$1(word)).join(' ');
25149
+ }
25150
+
25151
+ /**
25152
+ * Converts a string to camelCase.
25153
+ *
25154
+ * @param {string} text - The string to convert.
25155
+ * @returns {string} The string in camelCase.
25156
+ */
25157
+ function camelCase$1(text) {
25158
+ if (!text || typeof text !== 'string') {
25159
+ return text;
25160
+ }
25161
+ return text
25162
+ .split(/\W+/) // Split on non-word characters
25163
+ .map((word, index) => {
25164
+ if (index === 0) {
25165
+ // First word is lowercase
25166
+ return word.toLowerCase();
25167
+ }
25168
+ // Subsequent words have their first letter capitalized
25169
+ return capitalize$1(word.toLowerCase());
25170
+ })
25171
+ .join('');
25172
+ }
25173
+
25174
+ /**
25175
+ * Converts a string to PascalCase.
25176
+ *
25177
+ * @param {string} text - The string to convert.
25178
+ * @returns {string} The string in PascalCase.
25179
+ */
25180
+ function pascalCase$1(text) {
25181
+ if (!text || typeof text !== 'string') {
25182
+ return text;
25183
+ }
25184
+ return text
25185
+ .split(/\W+/) // Split on non-word characters
25186
+ .map(word => capitalize$1(word.toLowerCase()))
25187
+ .join('');
25188
+ }
25189
+
25190
+ /**
25191
+ * Converts a string to kebab-case.
25192
+ *
25193
+ * @param {string} text - The string to convert.
25194
+ * @returns {string} The string in kebab-case.
25195
+ */
25196
+ function kebabCase$1(text) {
25197
+ if (!text || typeof text !== 'string') {
25198
+ return text;
25199
+ }
25200
+ return text
25201
+ .split(/\W+/) // Split on non-word characters
25202
+ .map(word => word.toLowerCase())
25203
+ .filter(word => word.length > 0) // Remove empty strings from split
25204
+ .join('-');
25205
+ }
25206
+
25207
+ /**
25208
+ * Converts a string to snake_case.
25209
+ *
25210
+ * @param {string} text - The string to convert.
25211
+ * @returns {string} The string in snake_case.
25212
+ */
25213
+ function snakeCase$1(text) {
25214
+ if (!text || typeof text !== 'string') {
25215
+ return text;
25216
+ }
25217
+ return text
25218
+ .split(/\W+/) // Split on non-word characters
25219
+ .map(word => word.toLowerCase())
25220
+ .filter(word => word.length > 0) // Remove empty strings from split
25221
+ .join('_');
25222
+ }
25223
+
25224
+ /**
25225
+ * Converts a string to CONSTANT_CASE.
25226
+ *
25227
+ * @param {string} text - The string to convert.
25228
+ * @returns {string} The string in CONSTANT_CASE.
25229
+ */
25230
+ function constantCase$1(text) {
25231
+ if (!text || typeof text !== 'string') {
25232
+ return text;
25233
+ }
25234
+ return text
25235
+ .split(/\W+/) // Split on non-word characters
25236
+ .map(word => word.toUpperCase())
25237
+ .filter(word => word.length > 0) // Remove empty strings from split
25238
+ .join('_');
25239
+ }
25240
+
25241
+ /**
25242
+ * Trims leading and trailing whitespace and normalizes internal whitespace
25243
+ * (replacing sequences of whitespace characters with a single space).
25244
+ *
25245
+ * @param {string} text - The string to trim and normalize.
25246
+ * @returns {string} The trimmed and normalized string.
25247
+ */
25248
+ function trimWhitespace$1(text) {
25249
+ if (!text || typeof text !== 'string') {
25250
+ return text;
25251
+ }
25252
+ return text.trim().replace(/\s+/g, ' ');
25253
+ }
25254
+
25255
+ /**
25256
+ * Truncates a string to a specified maximum length and appends a suffix if truncated.
25257
+ *
25258
+ * @param {string} text - The string to truncate.
25259
+ * @param {number} maxLength - The maximum length of the string.
25260
+ * @param {string} [suffix='...'] - The suffix to append if the string is truncated.
25261
+ * @returns {string} The truncated string with suffix, or the original string if not truncated.
25262
+ */
25263
+ function truncate$1(text, maxLength, suffix = '...') {
25264
+ if (!text || typeof text !== 'string' || maxLength <= 0) {
25265
+ return '';
25266
+ }
25267
+ if (text.length <= maxLength) {
25268
+ return text;
25269
+ }
25270
+ // Ensure we don't cut the suffix itself if maxLength is very small
25271
+ const suffixLength = suffix.length;
25272
+ if (maxLength <= suffixLength) {
25273
+ // If max length is less than or equal to suffix, return a truncated suffix
25274
+ // This is a bit of an edge case, but handles it gracefully.
25275
+ return suffix.substring(0, maxLength);
25276
+ }
25277
+ return text.substring(0, maxLength - suffixLength) + suffix;
25278
+ }
25279
+
25280
+ /**
25281
+ * Escapes HTML characters in a string to prevent them from being interpreted as HTML.
25282
+ *
25283
+ * @param {string} text - The string to escape.
25284
+ * @returns {string} The escaped string.
25285
+ */
25286
+ function escapeHtml(text) {
25287
+ if (!text || typeof text !== 'string') {
25288
+ return text;
25289
+ }
25290
+ const htmlEscapes = {
25291
+ '&': '&amp;',
25292
+ '<': '&lt;',
25293
+ '>': '&gt;',
25294
+ '"': '&quot;',
25295
+ "'": '&#39;',
25296
+ };
25297
+ return text.replace(/[&<>"']/g, match => htmlEscapes[match]);
25298
+ }
25299
+
25300
+ /**
25301
+ * Removes HTML tags from a string.
25302
+ *
25303
+ * @param {string} text - The string to strip tags from.
25304
+ * @returns {string} The string without HTML tags.
25305
+ */
25306
+ function stripHtml(text) {
25307
+ if (!text || typeof text !== 'string') {
25308
+ return text;
25309
+ }
25310
+ return text.replace(/<[^>]*>/g, '');
25311
+ }
25312
+
25313
+ /**
25314
+ * Performs a basic check to see if a string looks like a valid email address.
25315
+ * This is a simple validation and should not be used for robust security checks.
25316
+ *
25317
+ * @param {string} email - The email string to validate.
25318
+ * @returns {boolean} True if the string looks like a valid email, false otherwise.
25319
+ */
25320
+ function isValidEmail(email) {
25321
+ if (!email || typeof email !== 'string') {
25322
+ return false;
25323
+ }
25324
+ // A basic regex for email validation. Note: Fully validating email addresses
25325
+ // is extremely complex. This covers common cases.
25326
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
25327
+ return emailRegex.test(email);
25328
+ }
25329
+
25330
+
25331
+ var stringUtils = {
25332
+ splitWithLimit: splitWithLimit$1,
25333
+ capitalize: capitalize$1,
25334
+ titleCase: titleCase$1,
25335
+ camelCase: camelCase$1,
25336
+ kebabCase: kebabCase$1,
25337
+ snakeCase: snakeCase$1,
25338
+ constantCase: constantCase$1,
25339
+ pascalCase: pascalCase$1,
25340
+ trimWhitespace: trimWhitespace$1,
25341
+ truncate: truncate$1,
25342
+ escapeHtml,
25343
+ stripHtml,
25344
+ isValidEmail,
25345
+ };
25346
+
24087
25347
  /*
24088
25348
  * Component: Language Name Utils
24089
25349
  * Block-UUID: 313abeab-2bb3-49d1-8693-e9e88aa97113
@@ -24255,19 +25515,670 @@ var LanguageNameUtils$1 = {
24255
25515
  getLanguageNameMap
24256
25516
  };
24257
25517
 
25518
+ /**
25519
+ * Component: MetaRawResultUtils
25520
+ * Block-UUID: 8a4d235b-a9d3-44b8-9c08-47b3678ba1c0
25521
+ * Parent-UUID: N/A
25522
+ * Version: 1.0.0
25523
+ * Description: Utility for parsing and extracting mappings from meta-raw-result messages
25524
+ * Language: JavaScript
25525
+ * Created-at: 2025-11-15T19:15:00.000Z
25526
+ * Authors: GLM-4.6 (v1.0.0)
25527
+ */
25528
+
25529
+ const ChatUtils$1 = ChatUtils$2;
25530
+
25531
+ /**
25532
+ * Extracts and parses all meta-raw-result messages from a chat
25533
+ * @param {Object} chat - The chat object containing messages
25534
+ * @param {string} model - The model name (optional, for message filtering)
25535
+ * @returns {Map<number, Object>} Map of chatId to file metadata
25536
+ */
25537
+ function extractMetaRawResultMappings$1(chat, model = null) {
25538
+ const mappings = new Map();
25539
+
25540
+ try {
25541
+ // Get all messages from the chat
25542
+ const allMessages = ChatUtils$1.getChatMessages(chat, model);
25543
+
25544
+ // Filter for meta-raw-result messages
25545
+ const metaRawMessages = allMessages.filter(msg => isMetaRawResultMessage$1(msg));
25546
+
25547
+ // Parse each meta-raw-result message
25548
+ for (const message of metaRawMessages) {
25549
+ const messageMappings = parseMetaRawResultContent$1(message.message);
25550
+
25551
+ // Merge mappings into the main map
25552
+ for (const [chatId, fileInfo] of messageMappings) {
25553
+ mappings.set(chatId, fileInfo);
25554
+ }
25555
+ }
25556
+
25557
+ return mappings;
25558
+ } catch (error) {
25559
+ console.error('Error extracting meta-raw-result mappings:', error);
25560
+ return mappings;
25561
+ }
25562
+ }
25563
+
25564
+ /**
25565
+ * Validates if a message is a meta-raw-result message
25566
+ * @param {Object} message - The message object to validate
25567
+ * @returns {boolean} True if message is meta-raw-result type
25568
+ */
25569
+ function isMetaRawResultMessage$1(message) {
25570
+ if (!message || !message.message) {
25571
+ return false;
25572
+ }
25573
+
25574
+ // Check if message type is meta-raw-result
25575
+ if (message.type === 'meta-raw-result') {
25576
+ return true;
25577
+ }
25578
+
25579
+ return false;
25580
+ }
25581
+
25582
+ /**
25583
+ * Parses a single meta-raw-result message content
25584
+ * @param {string} messageContent - The message content to parse
25585
+ * @returns {Map<number, Object>} Map of chatId to file metadata
25586
+ */
25587
+ function parseMetaRawResultContent$1(messageContent) {
25588
+ const mappings = new Map();
25589
+
25590
+ try {
25591
+ // Split content into lines
25592
+ const lines = messageContent.split('\n');
25593
+
25594
+ // Find the data section
25595
+ let dataSectionStart = -1;
25596
+ for (let i = 0; i < lines.length; i++) {
25597
+ if (lines[i].trimStart().startsWith('## Data ')) {
25598
+ dataSectionStart = i;
25599
+ break;
25600
+ }
25601
+ }
25602
+
25603
+ if (dataSectionStart === -1) {
25604
+ return mappings; // No data section found
25605
+ }
25606
+
25607
+ // Find the table header (line with |)
25608
+ let headerLine = -1;
25609
+ for (let i = dataSectionStart; i < lines.length; i++) {
25610
+ if (lines[i].includes('|') && lines[i].includes('Chat ID')) {
25611
+ headerLine = i;
25612
+ break;
25613
+ }
25614
+ }
25615
+
25616
+ if (headerLine === -1) {
25617
+ return mappings; // No table header found
25618
+ }
25619
+
25620
+ // Find the separator line (|---|---|...)
25621
+ let separatorLine = -1;
25622
+ for (let i = headerLine + 1; i < lines.length; i++) {
25623
+ if (lines[i].match(/^\|[\s\-\|]*\|$/)) {
25624
+ separatorLine = i;
25625
+ break;
25626
+ }
25627
+ }
25628
+
25629
+ if (separatorLine === -1) {
25630
+ return mappings; // No separator found
25631
+ }
25632
+
25633
+ // Parse data rows (everything after the separator)
25634
+ for (let i = separatorLine + 1; i < lines.length; i++) {
25635
+ const line = lines[i].trim();
25636
+
25637
+ // Skip empty lines or separator lines
25638
+ if (!line || line.match(/^\|[\s\-\|]*\|$/) || !line.includes('|')) {
25639
+ continue;
25640
+ }
25641
+
25642
+ // Parse the table row
25643
+ const fileInfo = parseTableRow$1(line);
25644
+ if (fileInfo && fileInfo.id) {
25645
+ mappings.set(fileInfo.id, fileInfo);
25646
+ }
25647
+ }
25648
+
25649
+ return mappings;
25650
+ } catch (error) {
25651
+ console.error('Error parsing meta-raw-result content:', error);
25652
+ return mappings;
25653
+ }
25654
+ }
25655
+
25656
+ /**
25657
+ * Parses a single table row from meta-raw-result content
25658
+ * @param {string} rowLine - The table row line to parse
25659
+ * @returns {Object|null} Parsed file information object or null if parsing failed
25660
+ */
25661
+ function parseTableRow$1(rowLine) {
25662
+ try {
25663
+ // Split the row by | and clean up
25664
+ const cells = rowLine.split('|').map(cell => cell.trim()).filter(cell => cell);
25665
+
25666
+ if (cells.length < 6) {
25667
+ return null; // Not enough columns
25668
+ }
25669
+
25670
+ // Extract basic information from the table cells
25671
+ const repo = cells[0];
25672
+ const branch = cells[1];
25673
+ const filePath = cells[2];
25674
+ const language = cells[3];
25675
+ const chatId = parseInt(cells[4], 10);
25676
+
25677
+ // Parse file path to extract name and path
25678
+ const pathParts = filePath.split('/');
25679
+ const name = pathParts[pathParts.length - 1];
25680
+ const path = filePath;
25681
+
25682
+ // Create full path
25683
+ const fullPath = `${repo}/${path}`;
25684
+
25685
+ return {
25686
+ id: chatId,
25687
+ name: name,
25688
+ path: path,
25689
+ repo: repo,
25690
+ branch: branch,
25691
+ language: language,
25692
+ fullPath: fullPath
25693
+ };
25694
+ } catch (error) {
25695
+ console.error('Error parsing table row:', error);
25696
+ return null;
25697
+ }
25698
+ }
25699
+
25700
+ /**
25701
+ * Parses size string to bytes
25702
+ * @param {string} sizeStr - Size string (e.g., "1.2 KB", "500 B")
25703
+ * @returns {number} Size in bytes
25704
+ */
25705
+ function parseSize$1(sizeStr) {
25706
+ if (!sizeStr || sizeStr === 'N/A') {
25707
+ return 0;
25708
+ }
25709
+
25710
+ const match = sizeStr.match(/^([\d.]+)\s*(B|KB|MB|GB)?$/i);
25711
+ if (!match) {
25712
+ return 0;
25713
+ }
25714
+
25715
+ const value = parseFloat(match[1]);
25716
+ const unit = (match[2] || 'B').toUpperCase();
25717
+
25718
+ switch (unit) {
25719
+ case 'B': return Math.round(value);
25720
+ case 'KB': return Math.round(value * 1024);
25721
+ case 'MB': return Math.round(value * 1024 * 1024);
25722
+ case 'GB': return Math.round(value * 1024 * 1024 * 1024);
25723
+ default: return 0;
25724
+ }
25725
+ }
25726
+
25727
+ /**
25728
+ * Parses tokens string to number
25729
+ * @param {string} tokensStr - Tokens string (e.g., "1.2k", "500")
25730
+ * @returns {number} Token count
25731
+ */
25732
+ function parseTokens$1(tokensStr) {
25733
+ if (!tokensStr || tokensStr === 'N/A') {
25734
+ return 0;
25735
+ }
25736
+
25737
+ const match = tokensStr.match(/^([\d.]+)(k|m|b)?$/i);
25738
+ if (!match) {
25739
+ return 0;
25740
+ }
25741
+
25742
+ const value = parseFloat(match[1]);
25743
+ const suffix = (match[2] || '').toLowerCase();
25744
+
25745
+ switch (suffix) {
25746
+ case 'k': return Math.round(value * 1000);
25747
+ case 'm': return Math.round(value * 1000000);
25748
+ case 'b': return Math.round(value * 1000000000);
25749
+ default: return Math.round(value);
25750
+ }
25751
+ }
25752
+
25753
+ var MetaRawResultUtils$1 = {
25754
+ extractMetaRawResultMappings: extractMetaRawResultMappings$1,
25755
+ isMetaRawResultMessage: isMetaRawResultMessage$1,
25756
+ parseMetaRawResultContent: parseMetaRawResultContent$1,
25757
+ parseTableRow: parseTableRow$1,
25758
+ parseSize: parseSize$1,
25759
+ parseTokens: parseTokens$1
25760
+ };
25761
+
25762
+ /**
25763
+ * Component: ReferenceMessageUtils
25764
+ * Block-UUID: 2870551a-4311-4d64-9491-c9cc62f276e8
25765
+ * Parent-UUID: N/A
25766
+ * Description: Utility for parsing reference messages in the compact message workflow, extracting session data, metadata, and message sections.
25767
+ * Language: JavaScript
25768
+ * Created-at: 2025-12-07T00:08:42.573Z
25769
+ * Authors: GLM-4.6 (v1.0.0)
25770
+ */
25771
+
25772
+ let ReferenceMessageUtils$1 = class ReferenceMessageUtils {
25773
+ /**
25774
+ * Extracts all data from a reference message including session data, metadata, and message sections
25775
+ * @param {string} referenceMessage - The reference message content to parse
25776
+ * @returns {Object} Parsed data with messages to compact and keep, metadata, and session information
25777
+ */
25778
+ static extractReferenceMessageData(referenceMessage) {
25779
+ if (!referenceMessage) {
25780
+ return {
25781
+ messagesToCompact: [],
25782
+ messagesToKeep: [],
25783
+ originalChatUuid: null,
25784
+ validationHash: null,
25785
+ sessionId: null,
25786
+ range: null,
25787
+ from: null,
25788
+ to: null
25789
+ };
25790
+ }
25791
+
25792
+ // Extract Original Chat UUID
25793
+ const originalChatUuidMatch = referenceMessage.match(/Original Chat UUID:\s*([a-f0-9-]+)/i);
25794
+ const originalChatUuid = originalChatUuidMatch ? originalChatUuidMatch[1] : null;
25795
+
25796
+ // Extract Validation Hash
25797
+ const validationHashMatch = referenceMessage.match(/Validation Hash:\s*([a-f0-9]+)/i);
25798
+ const validationHash = validationHashMatch ? validationHashMatch[1] : null;
25799
+
25800
+ // Extract Session ID
25801
+ const sessionIdMatch = referenceMessage.match(/Session ID:\s*(\d+)/i);
25802
+ const sessionId = sessionIdMatch ? sessionIdMatch[1] : null;
25803
+
25804
+ // Extract Range
25805
+ const rangeMatch = referenceMessage.match(/Range:\s*(\d+-\d+)/i);
25806
+ const range = rangeMatch ? rangeMatch[1] : null;
25807
+
25808
+ // Extract From and To values
25809
+ const fromMatch = referenceMessage.match(/From:\s*(\d+)/i);
25810
+ const from = fromMatch ? parseInt(fromMatch[1], 10) : null;
25811
+
25812
+ const toMatch = referenceMessage.match(/To:\s*(\d+)/i);
25813
+ const to = toMatch ? parseInt(toMatch[1], 10) : null;
25814
+
25815
+ // If range is not found but from and to are available, construct the range
25816
+ if (!range && from !== null && to !== null) {
25817
+ range = `${from}-${to}`;
25818
+ }
25819
+
25820
+ // Extract Messages to Compact section
25821
+ const compactSectionMatch = referenceMessage.match(/# Messages to Compact\n([\s\S]*?)(?=\n# |\n$|$)/);
25822
+ const compactSection = compactSectionMatch ? compactSectionMatch[1] : '';
25823
+
25824
+ // Extract Messages to Keep section
25825
+ const keepSectionMatch = referenceMessage.match(/# Messages to Keep \(Context\)\n([\s\S]*?)(?=\n# |\n$|$)/);
25826
+ const keepSection = keepSectionMatch ? keepSectionMatch[1] : '';
25827
+
25828
+ // Parse messages from each section
25829
+ const messagesToCompact = this.parseMessageSection(compactSection);
25830
+ const messagesToKeep = this.parseMessageSection(keepSection);
25831
+
25832
+ return {
25833
+ messagesToCompact,
25834
+ messagesToKeep,
25835
+ originalChatUuid,
25836
+ validationHash,
25837
+ sessionId,
25838
+ range,
25839
+ from,
25840
+ to
25841
+ };
25842
+ }
25843
+
25844
+ /**
25845
+ * Parses a message section to extract individual messages
25846
+ * @param {string} section - The section content to parse
25847
+ * @returns {Array} Array of parsed message objects
25848
+ */
25849
+ static parseMessageSection(section) {
25850
+ if (!section) return [];
25851
+
25852
+ const messages = [];
25853
+ const messageRegex = /<(\w+) message number (\d+)>\n([\s\S]*?)\n<\/\1 message number \2>/g;
25854
+ let match;
25855
+
25856
+ while ((match = messageRegex.exec(section)) !== null) {
25857
+ const [, role, positionStr, content] = match;
25858
+ const position = parseInt(positionStr, 10);
25859
+
25860
+ messages.push({
25861
+ role,
25862
+ position,
25863
+ content: content.trim()
25864
+ });
25865
+ }
25866
+
25867
+ return messages;
25868
+ }
25869
+
25870
+ /**
25871
+ * Extracts just the metadata from a reference message without the message sections
25872
+ * @param {string} referenceMessage - The reference message content to parse
25873
+ * @returns {Object} Metadata object with original chat UUID, validation hash, session ID, and range information
25874
+ */
25875
+ static extractReferenceMessageMetadata(referenceMessage) {
25876
+ const data = this.extractReferenceMessageData(referenceMessage);
25877
+
25878
+ return {
25879
+ originalChatUuid: data.originalChatUuid,
25880
+ validationHash: data.validationHash,
25881
+ sessionId: data.sessionId,
25882
+ range: data.range,
25883
+ from: data.from,
25884
+ to: data.to
25885
+ };
25886
+ }
25887
+
25888
+ /**
25889
+ * Checks if a message is a reference message
25890
+ * @param {string} message - The message content to check
25891
+ * @returns {boolean} True if the message is a reference message
25892
+ */
25893
+ static isReferenceMessage(message) {
25894
+ if (!message) return false;
25895
+
25896
+ // Check for key indicators of a reference message
25897
+ return message.includes('# Compact Messages Reference') &&
25898
+ message.includes('Original Chat UUID:') &&
25899
+ message.includes('Session ID:');
25900
+ }
25901
+ };
25902
+
25903
+ var ReferenceMessageUtils_1 = { ReferenceMessageUtils: ReferenceMessageUtils$1 };
25904
+
25905
+ /**
25906
+ * Component: CompactedMessageUtils
25907
+ * Block-UUID: b4111118-ec11-49d6-9a6e-402ca0b6df5b
25908
+ * Parent-UUID: N/A
25909
+ * Version: 1.0.1
25910
+ * Description: Utility for formatting, parsing, and validating compacted messages with standardized metadata headers.
25911
+ * Language: JavaScript
25912
+ * Created-at: 2025-12-07T00:09:15.842Z
25913
+ * Authors: GLM-4.6 (v1.0.0), GLM-4.6 (v1.0.1)
25914
+ */
25915
+
25916
+ let CompactedMessageUtils$1 = class CompactedMessageUtils {
25917
+ /**
25918
+ * Formats a compacted message with standardized metadata headers
25919
+ * @param {string} content - The compacted message content
25920
+ * @param {string} originalChatUuid - UUID of the original chat
25921
+ * @param {string} messageRange - Message range that was compacted (e.g., "3-6")
25922
+ * @param {Object} options - Additional options
25923
+ * @param {Date} options.compactedAt - Custom timestamp for when the message was compacted
25924
+ * @returns {string} Formatted compacted message with metadata
25925
+ */
25926
+ static formatCompactedMessage(content, originalChatUuid, messageRange, options = {}) {
25927
+ if (!content) {
25928
+ throw new Error('Content is required for formatting a compacted message');
25929
+ }
25930
+
25931
+ if (!originalChatUuid) {
25932
+ throw new Error('Original chat UUID is required for formatting a compacted message');
25933
+ }
25934
+
25935
+ if (!messageRange) {
25936
+ throw new Error('Message range is required for formatting a compacted message');
25937
+ }
25938
+
25939
+ // Use provided timestamp or generate current one
25940
+ const compactedAt = options.compactedAt || new Date().toISOString();
25941
+
25942
+ return `## Compacted Message
25943
+
25944
+ - **Original Chat:** [${originalChatUuid}](/?chat=${originalChatUuid})
25945
+ - **Message Range:** ${messageRange}
25946
+ - **Compacted At:** ${compactedAt}
25947
+
25948
+ ${content}`;
25949
+ }
25950
+
25951
+ /**
25952
+ * Extracts metadata from a compacted message
25953
+ * @param {string} compactedMessage - The compacted message to parse
25954
+ * @returns {Object|null} Metadata object or null if format is invalid
25955
+ */
25956
+ static extractCompactedMessageMetadata(compactedMessage) {
25957
+ if (!this.isCompactedMessage(compactedMessage)) {
25958
+ return null;
25959
+ }
25960
+
25961
+ // Extract Original Chat UUID
25962
+ const originalChatMatch = compactedMessage.match(/\*\*Original Chat:\*\*\s*([a-f0-9-]+)/i);
25963
+ const originalChatUuid = originalChatMatch ? originalChatMatch[1] : null;
25964
+
25965
+ // Extract Message Range
25966
+ const rangeMatch = compactedMessage.match(/\*\*Message Range:\*\*\s*(\d+-\d+)/i);
25967
+ const messageRange = rangeMatch ? rangeMatch[1] : null;
25968
+
25969
+ // Extract Compacted At timestamp
25970
+ const compactedAtMatch = compactedMessage.match(/\*\*Compacted At:\*\*\s*([0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}Z)/i);
25971
+ const compactedAt = compactedAtMatch ? compactedAtMatch[1] : null;
25972
+
25973
+ return {
25974
+ originalChatUuid,
25975
+ messageRange,
25976
+ compactedAt
25977
+ };
25978
+ }
25979
+
25980
+ /**
25981
+ * Extracts just the content portion from a compacted message (without metadata)
25982
+ * @param {string} compactedMessage - The compacted message to parse
25983
+ * @returns {string|null} Content portion or null if format is invalid
25984
+ */
25985
+ static extractCompactedMessageContent(compactedMessage) {
25986
+ if (!this.isCompactedMessage(compactedMessage)) {
25987
+ return null;
25988
+ }
25989
+
25990
+ // Find the start and end boundaries
25991
+ const startMarker = "## Compacted Message";
25992
+ const endMarker = "## End Compacted Message";
25993
+
25994
+ const startIndex = compactedMessage.indexOf(startMarker);
25995
+ const endIndex = compactedMessage.indexOf(endMarker);
25996
+
25997
+ // If start marker is not found, return null
25998
+ if (startIndex === -1) {
25999
+ return null;
26000
+ }
26001
+
26002
+ // If end marker is not found, extract everything after start marker
26003
+ if (endIndex === -1) {
26004
+ return compactedMessage.substring(startIndex + startMarker.length).trim();
26005
+ }
26006
+
26007
+ // Extract content between the markers
26008
+ const contentStart = startIndex + startMarker.length;
26009
+ return compactedMessage.substring(contentStart, endIndex).trim();
26010
+ }
26011
+
26012
+ /**
26013
+ * Validates that a compacted message follows the expected format
26014
+ * @param {string} compactedMessage - The compacted message to validate
26015
+ * @returns {Object} Validation result with isValid flag and any errors/warnings
26016
+ */
26017
+ static validateCompactedMessageFormat(compactedMessage) {
26018
+ const result = {
26019
+ isValid: true,
26020
+ errors: [],
26021
+ warnings: []
26022
+ };
26023
+
26024
+ if (!compactedMessage) {
26025
+ result.isValid = false;
26026
+ result.errors.push('Compacted message is empty or null');
26027
+ return result;
26028
+ }
26029
+
26030
+ // Check for required header
26031
+ if (!compactedMessage.includes('## Compacted Message')) {
26032
+ result.isValid = false;
26033
+ result.errors.push('Missing required "## Compacted Message" header');
26034
+ }
26035
+
26036
+ // Check for required metadata fields
26037
+ const metadata = this.extractCompactedMessageMetadata(compactedMessage);
26038
+
26039
+ if (!metadata) {
26040
+ result.isValid = false;
26041
+ result.errors.push('Could not extract metadata from compacted message');
26042
+ return result;
26043
+ }
26044
+
26045
+ if (!metadata.originalChatUuid) {
26046
+ result.isValid = false;
26047
+ result.errors.push('Missing or invalid Original Chat UUID');
26048
+ }
26049
+
26050
+ if (!metadata.messageRange) {
26051
+ result.isValid = false;
26052
+ result.errors.push('Missing or invalid Message Range');
26053
+ }
26054
+
26055
+ if (!metadata.compactedAt) {
26056
+ result.warnings.push('Missing or invalid Compacted At timestamp');
26057
+ }
26058
+
26059
+ // Validate UUID format
26060
+ if (metadata.originalChatUuid && !this.isValidUUID(metadata.originalChatUuid)) {
26061
+ result.isValid = false;
26062
+ result.errors.push('Original Chat UUID is not in valid UUID format');
26063
+ }
26064
+
26065
+ // Validate message range format
26066
+ if (metadata.messageRange && !this.isValidMessageRange(metadata.messageRange)) {
26067
+ result.isValid = false;
26068
+ result.errors.push('Message Range is not in valid format (expected "X-Y")');
26069
+ }
26070
+
26071
+ // Validate timestamp format
26072
+ if (metadata.compactedAt && !this.isValidISOTimestamp(metadata.compactedAt)) {
26073
+ result.warnings.push('Compacted At timestamp is not in valid ISO 8601 format');
26074
+ }
26075
+
26076
+ return result;
26077
+ }
26078
+
26079
+ /**
26080
+ * Checks if a message is a compacted message
26081
+ * @param {string} message - The message content to check
26082
+ * @returns {boolean} True if the message is a compacted message
26083
+ */
26084
+ static isCompactedMessage(message) {
26085
+ if (!message) return false;
26086
+
26087
+ // Check for the compacted message header
26088
+ return message.includes('## Compacted Message');
26089
+ }
26090
+
26091
+ /**
26092
+ * Validates if a string is a valid UUID v4
26093
+ * @param {string} uuid - The UUID string to validate
26094
+ * @returns {boolean} True if valid UUID v4
26095
+ */
26096
+ static isValidUUID(uuid) {
26097
+ const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
26098
+ return uuidRegex.test(uuid);
26099
+ }
26100
+
26101
+ /**
26102
+ * Validates if a string is a valid message range (e.g., "3-6")
26103
+ * @param {string} range - The range string to validate
26104
+ * @returns {boolean} True if valid range format
26105
+ */
26106
+ static isValidMessageRange(range) {
26107
+ const rangeRegex = /^\d+-\d+$/;
26108
+ if (!rangeRegex.test(range)) return false;
26109
+
26110
+ const [from, to] = range.split('-').map(Number);
26111
+ return !isNaN(from) && !isNaN(to) && from <= to;
26112
+ }
26113
+
26114
+ /**
26115
+ * Validates if a string is a valid ISO 8601 timestamp
26116
+ * @param {string} timestamp - The timestamp string to validate
26117
+ * @returns {boolean} True if valid ISO 8601 timestamp
26118
+ */
26119
+ static isValidISOTimestamp(timestamp) {
26120
+ if (!timestamp) return false;
26121
+
26122
+ const date = new Date(timestamp);
26123
+ return !isNaN(date.getTime()) && timestamp === date.toISOString();
26124
+ }
26125
+ };
26126
+
26127
+ var CompactedMessageUtils_1 = { CompactedMessageUtils: CompactedMessageUtils$1 };
26128
+
26129
+ /**
26130
+ * Component: CompactChatUtils Index
26131
+ * Block-UUID: cd0a6091-edb6-4e92-9439-63dd8f6a6796
26132
+ * Parent-UUID: 5ffa0338-b4fb-4968-908c-f23a26d4923b
26133
+ * Version: 2.0.0
26134
+ * Description: Entry point for CompactChatUtils that exports both ReferenceMessageUtils and CompactedMessageUtils methods directly for easier access.
26135
+ * Language: JavaScript
26136
+ * Created-at: 2025-12-07T00:10:05.123Z
26137
+ * Authors: GLM-4.6 (v1.0.0), GLM-4.6 (v2.0.0)
26138
+ */
26139
+
26140
+ const { ReferenceMessageUtils } = ReferenceMessageUtils_1;
26141
+ const { CompactedMessageUtils } = CompactedMessageUtils_1;
26142
+
26143
+ /**
26144
+ * CompactChatUtils provides utilities for working with compacted messages in GitSense Chat.
26145
+ * It includes functionality for parsing reference messages and formatting/parsing compacted messages.
26146
+ */
26147
+ var CompactChatUtils$1 = {
26148
+ // Export the classes for those who want to access them directly
26149
+ ReferenceMessageUtils,
26150
+ CompactedMessageUtils,
26151
+
26152
+ // Export all methods from ReferenceMessageUtils directly
26153
+ extractReferenceMessageData: ReferenceMessageUtils.extractReferenceMessageData,
26154
+ parseMessageSection: ReferenceMessageUtils.parseMessageSection,
26155
+ extractReferenceMessageMetadata: ReferenceMessageUtils.extractReferenceMessageMetadata,
26156
+ isReferenceMessage: ReferenceMessageUtils.isReferenceMessage,
26157
+
26158
+ // Export all methods from CompactedMessageUtils directly
26159
+ formatCompactedMessage: CompactedMessageUtils.formatCompactedMessage,
26160
+ extractCompactedMessageMetadata: CompactedMessageUtils.extractCompactedMessageMetadata,
26161
+ extractCompactedMessageContent: CompactedMessageUtils.extractCompactedMessageContent,
26162
+ validateCompactedMessageFormat: CompactedMessageUtils.validateCompactedMessageFormat,
26163
+ isCompactedMessage: CompactedMessageUtils.isCompactedMessage,
26164
+ isValidUUID: CompactedMessageUtils.isValidUUID,
26165
+ isValidMessageRange: CompactedMessageUtils.isValidMessageRange,
26166
+ isValidISOTimestamp: CompactedMessageUtils.isValidISOTimestamp
26167
+ };
26168
+
24258
26169
  /*
24259
26170
  * Component: GitSenseChatUtils
24260
- * Block-UUID: 1793b3a8-4881-4306-bfdf-673eef503679
24261
- * Parent-UUID: eaeabebb-04b6-4881-9b29-88798c7f21b6
24262
- * Version: 2.4.0
24263
- * Description: Interface class for GitSense Chat utilities providing a unified API for code block parsing (markdown), extraction, and patch operations. Integrates functionalities from CodeBlockUtils and PatchUtils modules, and now includes ConfigUtils, EnvUtils, and ObjectUtils.
26171
+ * Block-UUID: c1b6c4d3-7959-4eb6-8022-6cd7aa59240d
26172
+ * Parent-UUID: 1793b3a8-4881-4306-bfdf-673eef503679
26173
+ * Version: 2.7.0
26174
+ * Description: Interface class for GitSense Chat utilities providing a unified API for code block parsing (markdown), extraction, and patch operations. Integrates functionalities from CodeBlockUtils and PatchUtils modules, and now includes ConfigUtils, EnvUtils, ObjectUtils, MetaRawResultUtils, and CompactChatUtils.
24264
26175
  * Language: JavaScript
24265
26176
  * Created-at: 2025-10-17T17:13:04.663Z
24266
- * Authors: Claude 3.7 Sonnet (v1.0.0), Gemini 2.5 Pro (v2.0.0), Gemini 2.5 Pro (v2.1.0), Gemini 2.5 Pro (v2.1.1), Gemini 2.5 Flash (v2.1.2), Gemini 2.5 Flash (v2.1.3), Gemini 2.5 Flash (v2.1.4), Qwen 3 Coder 480B - Cerebras (v2.1.5), Qwen 3 Coder 480B - Cerebras (v2.2.0), Qwen 3 Coder 480B - Cerebras (v2.2.1), Claude Haiku 4.5 (v2.3.0), Qwen 3 Coder 480B - Cerebras (v2.4.0)
26177
+ * Authors: Claude 3.7 Sonnet (v1.0.0), Gemini 2.5 Pro (v2.0.0), Gemini 2.5 Pro (v2.1.0), Gemini 2.5 Pro (v2.1.1), Gemini 2.5 Flash (v2.1.2), Gemini 2.5 Flash (v2.1.3), Gemini 2.5 Flash (v2.1.4), Qwen 3 Coder 480B - Cerebras (v2.1.5), Qwen 3 Coder 480B - Cerebras (v2.2.0), Qwen 3 Coder 480B - Cerebras (v2.2.1), Claude Haiku 4.5 (v2.3.0), Qwen 3 Coder 480B - Cerebras (v2.4.0), Qwen 3 Coder 480B - Cerebras (v2.5.0), GLM-4.6 (v2.6.0), GLM-4.6 (v2.7.0)
24267
26178
  */
24268
26179
 
24269
- const ChatUtils = ChatUtils$1;
24270
- const CodeBlockUtils = CodeBlockUtils$5;
26180
+ const ChatUtils = ChatUtils$2;
26181
+ const CodeBlockUtils = CodeBlockUtils$7;
24271
26182
  const ContextUtils = ContextUtils$2;
24272
26183
  const MessageUtils = MessageUtils$3;
24273
26184
  const AnalysisBlockUtils = AnalysisBlockUtils$3;
@@ -24284,7 +26195,10 @@ const EnvUtils = EnvUtils$1;
24284
26195
  const DomUtils = DomUtils$1;
24285
26196
  const ObjectUtils = ObjectUtils$1;
24286
26197
  const SVGUtils = SVGUtils_1;
26198
+ const StringUtils = stringUtils;
24287
26199
  const LanguageNameUtils = LanguageNameUtils$1;
26200
+ const MetaRawResultUtils = MetaRawResultUtils$1;
26201
+ const CompactChatUtils = CompactChatUtils$1;
24288
26202
 
24289
26203
  const {
24290
26204
  normalizeLanguageName,
@@ -24370,6 +26284,7 @@ const {
24370
26284
  removeCodeBlockMarkers,
24371
26285
  updateCodeBlockByIndex,
24372
26286
  deleteCodeBlockByIndex,
26287
+ getLineage,
24373
26288
  } = CodeBlockUtils;
24374
26289
 
24375
26290
  const {
@@ -24415,6 +26330,7 @@ const {
24415
26330
  parseContextSection,
24416
26331
  extractContextSections,
24417
26332
  extractContextItemsOverviewTableRows,
26333
+ getContextFiles,
24418
26334
  formatContextContent,
24419
26335
  } = ContextUtils;
24420
26336
 
@@ -24422,6 +26338,28 @@ const {
24422
26338
  trimObjectStrings
24423
26339
  } = ObjectUtils;
24424
26340
 
26341
+ const {
26342
+ splitWithLimit,
26343
+ capitalize,
26344
+ titleCase,
26345
+ camelCase,
26346
+ kebabCase,
26347
+ snakeCase,
26348
+ constantCase,
26349
+ pascalCase,
26350
+ trimWhitespace,
26351
+ truncate,
26352
+ } = StringUtils;
26353
+
26354
+ const {
26355
+ extractMetaRawResultMappings,
26356
+ isMetaRawResultMessage,
26357
+ parseMetaRawResultContent,
26358
+ parseTableRow,
26359
+ parseSize,
26360
+ parseTokens
26361
+ } = MetaRawResultUtils;
26362
+
24425
26363
 
24426
26364
  /**
24427
26365
  * GitSenseChatUtils class provides a unified interface to code block and patch utilities.
@@ -24662,7 +26600,12 @@ var GitSenseChatUtils_1 = {
24662
26600
  EnvUtils,
24663
26601
  DomUtils,
24664
26602
  ObjectUtils,
26603
+ StringUtils,
24665
26604
  SVGUtils,
26605
+ MetaRawResultUtils,
26606
+ CompactChatUtils,
26607
+ ReferenceMessageUtils: CompactChatUtils.ReferenceMessageUtils,
26608
+ CompactedMessageUtils: CompactChatUtils.CompactedMessageUtils,
24666
26609
 
24667
26610
  // --- Individual Function Exports (sourced correctly) ---
24668
26611
 
@@ -24755,6 +26698,18 @@ var GitSenseChatUtils_1 = {
24755
26698
  // Object Utils
24756
26699
  trimObjectStrings,
24757
26700
 
26701
+ // String Utils
26702
+ splitWithLimit,
26703
+ capitalize,
26704
+ titleCase,
26705
+ camelCase,
26706
+ kebabCase,
26707
+ snakeCase,
26708
+ constantCase,
26709
+ pascalCase,
26710
+ trimWhitespace,
26711
+ truncate,
26712
+
24758
26713
  // Markdown Utils
24759
26714
  createMarkdownRenderer,
24760
26715
  removeSignature,
@@ -24774,10 +26729,19 @@ var GitSenseChatUtils_1 = {
24774
26729
  getApiKey,
24775
26730
 
24776
26731
  // Context Utils
26732
+ getContextFiles,
24777
26733
  parseContextSection,
24778
26734
  extractContextSections,
24779
26735
  extractContextItemsOverviewTableRows,
24780
26736
  formatContextContent,
26737
+
26738
+ // MetaRawResult Utils
26739
+ extractMetaRawResultMappings,
26740
+ isMetaRawResultMessage,
26741
+ parseMetaRawResultContent,
26742
+ parseTableRow,
26743
+ parseSize,
26744
+ parseTokens,
24781
26745
  };
24782
26746
 
24783
26747
  var GitSenseChatUtils$1 = /*@__PURE__*/getDefaultExportFromCjs(GitSenseChatUtils_1);