@gitsense/gsc-utils 0.2.25 → 0.2.27

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 (35) hide show
  1. package/README.md +380 -62
  2. package/dist/gsc-utils.cjs.js +2203 -331
  3. package/dist/gsc-utils.esm.js +2203 -331
  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 +86 -12
  25. package/src/GSToolBlockUtils.js +66 -1
  26. package/src/GitSenseChatUtils.js +58 -5
  27. package/src/MarkdownUtils.js +4 -1
  28. package/src/MessageUtils.js +1 -1
  29. package/src/MetaRawResultUtils.js +244 -0
  30. package/src/PatchUtils/constants.js +9 -3
  31. package/src/PatchUtils/patchParser.js +60 -36
  32. package/src/PatchUtils/patchVerifier/detectAndFixOverlappingHunks.js +1 -1
  33. package/src/SVGUtils.js +57 -0
  34. package/src/SharedUtils/stringUtils.js +303 -0
  35. package/src/CodeBlockUtils/blockProcessor.js.rej +0 -8
@@ -43,8 +43,8 @@ function getAugmentedNamespace(n) {
43
43
  * Authors: Claude 3.7 Sonnet (v1.0.0), Gemini 2.5 Pro (v1.1.0), Gemini 2.5 Flash Thinking (v1.2.0)
44
44
  */
45
45
 
46
- const fs$8 = require$$0;
47
- const path$6 = require$$1;
46
+ const fs$9 = require$$0;
47
+ const path$7 = require$$1;
48
48
  const ANALYZE_MESSAGE_REGEXP = /^# Analyze - `([^`]+)`\n/;
49
49
 
50
50
  /**
@@ -78,12 +78,12 @@ function unescapeCodeBlocks(content) {
78
78
  * @returns {Array} An array of message objects with role and content properties
79
79
  */
80
80
  function getChatTemplateMessages$1(dirname, messageType) {
81
- const messagesDir = messageType ? path$6.join(dirname, messageType) : dirname;
81
+ const messagesDir = messageType ? path$7.join(dirname, messageType) : dirname;
82
82
  const messages = [];
83
83
 
84
84
  try {
85
85
  // Read all files in the directory
86
- const files = fs$8.readdirSync(messagesDir);
86
+ const files = fs$9.readdirSync(messagesDir);
87
87
 
88
88
  // Sort files numerically (1.md, 2.md, etc.)
89
89
  const sortedFiles = files.sort((a, b) => {
@@ -94,9 +94,9 @@ function getChatTemplateMessages$1(dirname, messageType) {
94
94
 
95
95
  // Process each file
96
96
  for (const file of sortedFiles) {
97
- if (path$6.extname(file) === '.md') {
98
- const filePath = path$6.join(messagesDir, file);
99
- const fileContent = fs$8.readFileSync(filePath, 'utf8');
97
+ if (path$7.extname(file) === '.md') {
98
+ const filePath = path$7.join(messagesDir, file);
99
+ const fileContent = fs$9.readFileSync(filePath, 'utf8');
100
100
 
101
101
  // Split by triple newline to separate metadata from content
102
102
  const parts = fileContent.split('\n\n\n');
@@ -396,7 +396,7 @@ function getMessageContentType$1(messageContent) {
396
396
  return 'regular'; // Handle non-string input gracefully
397
397
  }
398
398
  const trimmedContent = messageContent.trimStart(); // Check from the beginning, ignoring leading whitespace
399
- if (trimmedContent.startsWith('## FILE CONTENT')) {
399
+ if (trimmedContent.startsWith('## FILE CONTENT') || trimmedContent.startsWith('## REFERENCE FILE CONTENT')) {
400
400
  return 'file-content-context';
401
401
  } else if (trimmedContent.startsWith('## OVERVIEW')) {
402
402
  return 'overview-context';
@@ -617,7 +617,7 @@ function getChatMessages$1(chat, model) {
617
617
  return getMessagesBeforeId$1(model || chat.main_model, chat.messages[0], null);
618
618
  }
619
619
 
620
- var ChatUtils$1 = {
620
+ var ChatUtils$2 = {
621
621
  isAskChat,
622
622
  isNewAnalyzerChat,
623
623
  isAnalyzeChat,
@@ -689,7 +689,7 @@ const COMMENT_STYLES$3 = {
689
689
  'vbscript': { type: 'apostrophe' }
690
690
  };
691
691
 
692
- var constants$2 = {
692
+ var constants$3 = {
693
693
  COMMENT_STYLES: COMMENT_STYLES$3
694
694
  };
695
695
 
@@ -708,7 +708,7 @@ var constants$2 = {
708
708
  * Generates a valid RFC 4122 UUID v4
709
709
  * @returns {string} A valid UUID v4
710
710
  */
711
- function generateUUID$3() {
711
+ function generateUUID$2() {
712
712
  return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
713
713
  const r = Math.random() * 16 | 0;
714
714
  const v = c === 'x' ? r : (r & 0x3 | 0x8);
@@ -727,7 +727,7 @@ function validateUUID$4(uuid) {
727
727
  if (!uuidPattern.test(uuid)) {
728
728
  return {
729
729
  "Block-UUID": "INVALID UUID",
730
- "Correct Block-UUID": generateUUID$3()
730
+ "Correct Block-UUID": generateUUID$2()
731
731
  };
732
732
  }
733
733
 
@@ -737,7 +737,7 @@ function validateUUID$4(uuid) {
737
737
  }
738
738
 
739
739
  var uuidUtils = {
740
- generateUUID: generateUUID$3,
740
+ generateUUID: generateUUID$2,
741
741
  validateUUID: validateUUID$4
742
742
  };
743
743
 
@@ -869,7 +869,7 @@ var lineNumberFormatter = {
869
869
  */
870
870
 
871
871
  // Dependencies from other modules within CodeBlockUtils
872
- const { COMMENT_STYLES: COMMENT_STYLES$2 } = constants$2;
872
+ const { COMMENT_STYLES: COMMENT_STYLES$2 } = constants$3;
873
873
  const { removeLineNumbers: removeLineNumbers$9 } = lineNumberFormatter; // Assuming this utility exists
874
874
  const { validateUUID: validateUUID$3 } = uuidUtils; // Assuming uuidUtils.js is in the same directory
875
875
 
@@ -1538,24 +1538,63 @@ function isValidVersion$1(version) {
1538
1538
  var versionUtils = {
1539
1539
  isValidVersion: isValidVersion$1};
1540
1540
 
1541
+ /**
1542
+ * Component: PatchUtils Constants
1543
+ * Block-UUID: c6747054-2c8b-461f-910d-0fd725d9a350
1544
+ * Parent-UUID: 32adc00e-7509-4219-8e40-6c1319371db9
1545
+ * Version: 2.1.0
1546
+ * Description: Contains shared constants and regular expressions used by the enhanced patch utilities.
1547
+ * Language: JavaScript
1548
+ * Created-at: 2025-05-14T16:55:00.000Z
1549
+ * Authors: Gemini 2.5 Flash Thinking (v1.0.0), Claude 3.7 Sonnet (v2.0.0), Qwen 3 Coder 480B - Cerebras (v2.1.0)
1550
+ */
1551
+
1552
+ // Traditional patch markers
1553
+ const PATCH_START_MARKER$1 = '# --- PATCH START MARKER ---';
1554
+ const PATCH_END_MARKER$1 = '# --- PATCH END MARKER ---';
1555
+
1556
+ // Abbreviated patch markers
1557
+ const ABBREVIATED_PATCH_START_MARKER$1 = '# --- ABBREVIATED PATCH START MARKER ---';
1558
+ const ABBREVIATED_PATCH_END_MARKER$1 = '# --- ABBREVIATED PATCH END MARKER ---';
1559
+
1560
+ // Patch metadata header
1561
+ const PATCH_METADATA_HEADER$1 = '# Patch Metadata';
1562
+
1563
+ // Diff file headers
1564
+ const ORIGINAL_FILE_HEADER$1 = '--- Original';
1565
+ const MODIFIED_FILE_HEADER$1 = '+++ Modified';
1566
+
1567
+ var constants$2 = {
1568
+ PATCH_START_MARKER: PATCH_START_MARKER$1,
1569
+ PATCH_END_MARKER: PATCH_END_MARKER$1,
1570
+ ABBREVIATED_PATCH_START_MARKER: ABBREVIATED_PATCH_START_MARKER$1,
1571
+ ABBREVIATED_PATCH_END_MARKER: ABBREVIATED_PATCH_END_MARKER$1,
1572
+ PATCH_METADATA_HEADER: PATCH_METADATA_HEADER$1,
1573
+ ORIGINAL_FILE_HEADER: ORIGINAL_FILE_HEADER$1,
1574
+ MODIFIED_FILE_HEADER: MODIFIED_FILE_HEADER$1
1575
+ };
1576
+
1541
1577
  /**
1542
1578
  * Component: PatchUtils Parser
1543
1579
  * Block-UUID: ce634df9-ff99-482a-a261-56ab34a2546e
1544
1580
  * Parent-UUID: fcc85ec1-0146-40bf-a937-6f7928258cbf
1545
- * Version: 1.1.0
1546
- * Description: Handles parsing, validation, extraction, and detection of traditional unified diff patches. Extracts metadata and raw diff content.
1581
+ * Version: 1.2.0
1582
+ * Description: Handles parsing, validation, extraction, and detection of traditional unified diff patches and abbreviated patches. Extracts metadata and raw diff content.
1547
1583
  * Language: JavaScript
1548
1584
  * Created-at: 2025-04-18T02:59:04.322Z
1549
- * Authors: Gemini 2.5 Pro (v1.0.0), Gemini 2.5 Pro (v1.1.0)
1585
+ * Authors: Gemini 2.5 Pro (v1.0.0), Gemini 2.5 Pro (v1.1.0), Qwen 3 Coder 480B - Cerebras (v1.2.0)
1550
1586
  */
1551
1587
 
1552
1588
  const { isValidVersion } = versionUtils; // Assuming SharedUtils is one level up
1589
+ const { PATCH_START_MARKER, PATCH_END_MARKER, PATCH_METADATA_HEADER,
1590
+ ORIGINAL_FILE_HEADER, MODIFIED_FILE_HEADER, ABBREVIATED_PATCH_START_MARKER,
1591
+ ABBREVIATED_PATCH_END_MARKER } = constants$2;
1553
1592
 
1554
1593
  /**
1555
1594
  * Determines the type of patch format used based on markers.
1556
- * Primarily looks for traditional unified diff markers after metadata.
1595
+ * Looks for traditional unified diff markers or abbreviated patch markers.
1557
1596
  * @param {string} patchText - The patch text
1558
- * @returns {string} 'traditional', or 'unknown'
1597
+ * @returns {string} 'traditional', 'abbreviated', or 'unknown'
1559
1598
  */
1560
1599
  function determinePatchFormat$1(patchText) {
1561
1600
  if (!patchText || typeof patchText !== 'string') {
@@ -1563,29 +1602,33 @@ function determinePatchFormat$1(patchText) {
1563
1602
  }
1564
1603
 
1565
1604
  // Check for required metadata header first
1566
- if (!patchText.includes('# Patch Metadata')) {
1605
+ if (!patchText.includes(PATCH_METADATA_HEADER)) {
1567
1606
  return 'unknown'; // Must have metadata header
1568
1607
  }
1569
1608
 
1570
1609
  // Check for traditional unified diff markers *after* potential metadata and start marker
1571
1610
  // Look for ---, +++, @@ after the start marker
1572
- const startMarkerIndex = patchText.indexOf('# --- PATCH START MARKER ---');
1573
- if (startMarkerIndex === -1) {
1574
- return 'unknown'; // Must have start marker
1575
- }
1611
+ const traditionalStartMarkerIndex = patchText.indexOf(PATCH_START_MARKER);
1612
+ if (traditionalStartMarkerIndex !== -1) {
1613
+ const contentAfterMarker = patchText.substring(traditionalStartMarkerIndex);
1576
1614
 
1577
- const contentAfterMarker = patchText.substring(startMarkerIndex);
1615
+ // Use regex for more reliable detection within the content part
1616
+ const traditionalMarkers = /^\s*--- Original\s*\n\s*\+\+\+ Modified\s*\n\s*@@ -\d+(?:,\d+)? \+\d+(?:,\d+)? @@/m;
1578
1617
 
1579
- // Use regex for more reliable detection within the content part
1580
- const traditionalMarkers = /^\s*--- Original\s*\n\s*\+\+\+ Modified\s*\n\s*@@ -\d+(?:,\d+)? \+\d+(?:,\d+)? @@/m;
1618
+ if (traditionalMarkers.test(contentAfterMarker)) {
1619
+ return 'traditional';
1620
+ }
1621
+ }
1581
1622
 
1582
- if (traditionalMarkers.test(contentAfterMarker)) {
1583
- return 'traditional';
1623
+ // Check for abbreviated patch markers
1624
+ const abbreviatedStartMarkerIndex = patchText.indexOf(ABBREVIATED_PATCH_START_MARKER);
1625
+ if (abbreviatedStartMarkerIndex !== -1) {
1626
+ return 'abbreviated';
1584
1627
  }
1585
1628
 
1586
1629
  // Removed check for obsolete '@=' context marker
1587
1630
 
1588
- return 'unknown'; // Format doesn't match expected traditional structure after markers
1631
+ return 'unknown'; // Format doesn't match expected structures after markers
1589
1632
  }
1590
1633
 
1591
1634
  /**
@@ -1606,7 +1649,8 @@ function extractPatchMetadata$1(patchText) {
1606
1649
  const trimmedLine = line.trim();
1607
1650
 
1608
1651
  // Stop processing metadata if we hit content markers or the start marker
1609
- if (trimmedLine === '# --- PATCH START MARKER ---' ||
1652
+ if (trimmedLine === PATCH_START_MARKER ||
1653
+ trimmedLine === ABBREVIATED_PATCH_START_MARKER ||
1610
1654
  line.startsWith('--- ') || // Standard diff header start
1611
1655
  line.startsWith('+++ ') || // Standard diff header start
1612
1656
  line.startsWith('@@ ')) // Standard diff hunk header start
@@ -1628,7 +1672,7 @@ function extractPatchMetadata$1(patchText) {
1628
1672
  }
1629
1673
  }
1630
1674
  // Allow lines that are just comments (e.g., # Description continued)
1631
- } else if (trimmedLine !== '' && !trimmedLine.startsWith('# Patch Metadata')) {
1675
+ } else if (trimmedLine !== '' && !trimmedLine.startsWith(PATCH_METADATA_HEADER)) {
1632
1676
  break;
1633
1677
  }
1634
1678
  }
@@ -1669,11 +1713,11 @@ function validatePatchMetadata$1(metadata) {
1669
1713
  }
1670
1714
 
1671
1715
  // Validate UUIDs are not the template string
1672
- if (metadata['Source-Block-UUID'] === '12d8a596-0620-4725-ba49-ae6fccdfa2ad') {
1673
- errors.push('Source-Block-UUID contains placeholder value "f016e2e4-f97f-4ffe-9f46-ea48f59a3eea".');
1716
+ if (metadata['Source-Block-UUID'] === '{{GS-UUID}}') {
1717
+ errors.push('Source-Block-UUID contains placeholder value "{{GS-UUID}}".');
1674
1718
  }
1675
- if (metadata['Target-Block-UUID'] === '5bfb717c-aaec-462e-a50e-d096aba0d63f') {
1676
- errors.push('Target-Block-UUID contains placeholder value "12e76392-f0ac-4c9d-a5be-c98401487352".');
1719
+ if (metadata['Target-Block-UUID'] === '{{GS-UUID}}') {
1720
+ errors.push('Target-Block-UUID contains placeholder value "{{GS-UUID}}".');
1677
1721
  }
1678
1722
 
1679
1723
  return errors;
@@ -1686,23 +1730,34 @@ function validatePatchMetadata$1(metadata) {
1686
1730
  * @param {string} format - Expected format (should be 'traditional').
1687
1731
  * @returns {string} Raw unified diff content, or empty string if not found/invalid.
1688
1732
  */
1689
- function extractPatchContent$1(patchText, format) {
1690
- if (!patchText || format !== 'traditional') {
1733
+ function extractPatchContent$1(patchText, format = 'traditional') {
1734
+ if (!patchText) {
1691
1735
  return "";
1692
1736
  }
1693
1737
 
1694
1738
  const lines = patchText.split('\n');
1695
-
1696
1739
  let contentLines = [];
1697
1740
  let inContentBlock = false;
1741
+ let startMarker, endMarker;
1742
+
1743
+ // Determine which markers to look for based on format
1744
+ if (format === 'traditional') {
1745
+ startMarker = PATCH_START_MARKER;
1746
+ endMarker = PATCH_END_MARKER;
1747
+ } else if (format === 'abbreviated') {
1748
+ startMarker = ABBREVIATED_PATCH_START_MARKER;
1749
+ endMarker = ABBREVIATED_PATCH_END_MARKER;
1750
+ } else {
1751
+ return ""; // Unsupported format
1752
+ }
1698
1753
 
1699
1754
  for (const line of lines) {
1700
- if (line.trim() === '# --- PATCH START MARKER ---') {
1755
+ if (line.trim() === startMarker) {
1701
1756
  inContentBlock = true;
1702
1757
  continue; // Skip the marker line itself
1703
1758
  }
1704
1759
 
1705
- if (line.trim() === '# --- PATCH END MARKER ---') {
1760
+ if (line.trim() === endMarker) {
1706
1761
  inContentBlock = false;
1707
1762
  break; // Stop processing once end marker is found
1708
1763
  }
@@ -1741,23 +1796,25 @@ function detectPatch(messageText) {
1741
1796
  }
1742
1797
 
1743
1798
  // Find the first code block that contains patch metadata
1744
- // Regex looks for ``` optionally followed by 'diff' language hint
1745
- const codeBlockRegex = /```(?:diff)?\s*\n([\s\S]*?)```/g;
1799
+ // Regex looks for ``` optionally followed by language hint
1800
+ const codeBlockRegex = /```([a-zA-Z0-9#+_-]*)?\s*\n([\s\S]*?)```/g;
1746
1801
  let match;
1747
1802
  while ((match = codeBlockRegex.exec(messageText)) !== null) {
1748
- const blockContent = match[1];
1803
+ const language = match[1] || '';
1804
+ const blockContent = match[2];
1749
1805
  if (isPatchBlock$1(blockContent)) { // Use isPatchBlock for check
1750
1806
  const metadata = extractPatchMetadata$1(blockContent); // Use extractPatchMetadata
1751
1807
  // Basic validation: Check for essential UUIDs
1752
1808
  if (metadata['Source-Block-UUID'] && metadata['Target-Block-UUID'] &&
1753
- metadata['Source-Block-UUID'] !== '26f930c9-2bc8-4906-bdf5-469700b2f857' && // Check placeholders
1754
- metadata['Target-Block-UUID'] !== '48a957eb-dd72-44bb-84a2-0df3c7cb16ed')
1809
+ metadata['Source-Block-UUID'] !== '{{GS-UUID}}' && // Check placeholders
1810
+ metadata['Target-Block-UUID'] !== '{{GS-UUID}}')
1755
1811
  {
1756
1812
  return {
1757
1813
  patchText: blockContent, // Return the content inside the fence
1758
1814
  metadata,
1759
1815
  sourceBlockUUID: metadata['Source-Block-UUID'],
1760
1816
  targetBlockUUID: metadata['Target-Block-UUID'],
1817
+ language: language,
1761
1818
  startIndex: match.index, // Store block position if needed
1762
1819
  endIndex: match.index + match[0].length
1763
1820
  };
@@ -1779,25 +1836,28 @@ function findAllPatches(messageText) {
1779
1836
  }
1780
1837
 
1781
1838
  const patches = [];
1782
- const codeBlockRegex = /```(?:diff)?\s*\n([\s\S]*?)```/g;
1839
+ // Find code blocks with any language identifier
1840
+ const codeBlockRegex = /```([a-zA-Z0-9#+_-]*)?\s*\n([\s\S]*?)```/g;
1783
1841
 
1784
1842
  let match;
1785
1843
  while ((match = codeBlockRegex.exec(messageText)) !== null) {
1786
- const blockContent = match[1];
1844
+ const language = match[1] || '';
1845
+ const blockContent = match[2];
1787
1846
 
1788
1847
  if (isPatchBlock$1(blockContent)) {
1789
1848
  const metadata = extractPatchMetadata$1(blockContent);
1790
1849
 
1791
1850
  // Basic validation: Check for essential UUIDs and non-placeholder values
1792
1851
  if (metadata['Source-Block-UUID'] && metadata['Target-Block-UUID'] &&
1793
- metadata['Source-Block-UUID'] !== 'e898f14f-14f6-4710-b35b-1cf3478fb5da' &&
1794
- metadata['Target-Block-UUID'] !== '0093be2a-fd84-4925-a0c1-13f8a8260cbf')
1852
+ metadata['Source-Block-UUID'] !== '{{GS-UUID}}' &&
1853
+ metadata['Target-Block-UUID'] !== '{{GS-UUID}}')
1795
1854
  {
1796
1855
  patches.push({
1797
1856
  patchText: blockContent,
1798
1857
  metadata,
1799
1858
  sourceBlockUUID: metadata['Source-Block-UUID'],
1800
1859
  targetBlockUUID: metadata['Target-Block-UUID'],
1860
+ language: language,
1801
1861
  startIndex: match.index,
1802
1862
  endIndex: match.index + match[0].length
1803
1863
  });
@@ -2867,7 +2927,7 @@ var detectAndFixRedundantChanges_1 = {
2867
2927
 
2868
2928
  /**
2869
2929
  * Component: PatchUtils Detect and Fix Overlapping Hunks
2870
- * Block-UUID: 0e1b2c3d-4e5f-6a7b-8c9d-0e1f2a3b4c5d
2930
+ * Block-UUID: 7324dab8-24ae-4a42-9dc3-766e69104ef6
2871
2931
  * Parent-UUID: 2308ed72-91ff-48ba-bc80-310c23c01ff1
2872
2932
  * Version: 1.0.0
2873
2933
  * Description: Detects and optionally fixes overlapping hunks in a patch file by merging them.
@@ -8744,6 +8804,8 @@ var PatchUtils$2 = {
8744
8804
  * Authors: Gemini 2.5 Pro (v1.0.0), Gemini 2.5 Flash (v1.1.0)
8745
8805
  */
8746
8806
 
8807
+ const GS_TOOL_BLOCK_TYPE = 'gs-tool';
8808
+
8747
8809
  /**
8748
8810
  * Checks if a given code block content represents a GitSense Chat Tool Block.
8749
8811
  * It verifies if the first non-empty line starts with "# GitSense Chat Tool".
@@ -8762,6 +8824,65 @@ function isToolBlock$1(content) {
8762
8824
  return false;
8763
8825
  }
8764
8826
 
8827
+
8828
+ // TODO: Add JSDocs
8829
+ function getToolBlocksByTool(content, tool, CodeBlockUtils) {
8830
+ const { blocks, warnings } = CodeBlockUtils.extractCodeBlocks(content, { silent: true });
8831
+
8832
+ return blocks.filter((block, index) => {
8833
+ if (block.type !== GS_TOOL_BLOCK_TYPE)
8834
+ return;
8835
+
8836
+ try {
8837
+ const data = parseToolBlock$1(block.content);
8838
+
8839
+ if (data.tool === tool) {
8840
+ block.index = index;
8841
+ return block;
8842
+ }
8843
+ } catch(error) {
8844
+ // FIXME: We need a more elegant way to identify examples.
8845
+ if (!content.includes(/Internal INTEGER IDs/)) {
8846
+ console.warn(`Invalid tool block JSON: ${error.message}`);
8847
+ }
8848
+ }
8849
+ });
8850
+ }
8851
+
8852
+ // TODO: Add JSDocs
8853
+ function getToolBlockElemsByTool(dom, tool) {
8854
+ const elems = dom.querySelectorAll('pre');
8855
+ const toolBockElems = [];
8856
+
8857
+ for ( let i = 0; i < elems.length; i++ ) {
8858
+ const elem = elems[i];
8859
+ let content = elem.textContent;
8860
+
8861
+ // We need to strip out the first two lines since this is not part of the actual code
8862
+ // block. These two lines are designed to make it easy to identify the language
8863
+ content = content.split('\n').slice(2).join('\n');
8864
+
8865
+ if (!isToolBlock$1(content))
8866
+ continue;
8867
+
8868
+ try {
8869
+ const data = parseToolBlock$1(content, { silent: true });
8870
+
8871
+ if (data.tool === tool)
8872
+ toolBockElems.push(elem);
8873
+ } catch(error) {
8874
+ // FIXME: We need a more elegant way to identify examples.
8875
+ if (!content.match(/Internal INTEGER IDs/)) {
8876
+ console.warn("getToolBlockElemsByTool: ",error.message);
8877
+ }
8878
+ continue;
8879
+ }
8880
+ }
8881
+
8882
+ return toolBockElems;
8883
+ }
8884
+
8885
+
8765
8886
  /**
8766
8887
  * Parses the content of a GitSense Chat Tool Block to extract the JSON payload.
8767
8888
  * It strips the marker line and any subsequent lines starting with '#' (comments)
@@ -8870,7 +8991,7 @@ function replaceToolBlock(markdownContent, toolName, newToolData, CodeBlockUtils
8870
8991
  // 2. Iterate through the processed blocks to find the target GitSense Chat Tool Block
8871
8992
  for (let i = 0; i < blocks.length; i++) {
8872
8993
  const block = blocks[i];
8873
- if (block.type === 'gs-tool' && block.toolData && block.toolData.tool === toolName) {
8994
+ if (block.type === GS_TOOL_BLOCK_TYPE && block.toolData && block.toolData.tool === toolName) {
8874
8995
  targetBlockIndex = i;
8875
8996
  break; // Found the first matching tool block
8876
8997
  }
@@ -9004,11 +9125,15 @@ function detectAndFormatUnfencedToolBlock(messageContent) {
9004
9125
  };
9005
9126
  }
9006
9127
 
9128
+
9129
+
9007
9130
  var GSToolBlockUtils$3 = {
9008
9131
  isToolBlock: isToolBlock$1,
9009
9132
  parseToolBlock: parseToolBlock$1,
9010
9133
  formatToolBlock,
9011
9134
  replaceToolBlock,
9135
+ getToolBlocksByTool,
9136
+ getToolBlockElemsByTool,
9012
9137
  detectAndFormatUnfencedToolBlock,
9013
9138
  };
9014
9139
 
@@ -9308,16 +9433,16 @@ var JsonUtils$2 = {
9308
9433
  * Component: CodeBlockUtils Block Processor
9309
9434
  * Block-UUID: 1d8a559b-4f7a-4b0b-9e0a-13786f94a74e
9310
9435
  * Parent-UUID: 082abf8b-079f-4c95-8464-0e66c0de45eb
9311
- * Version: 1.4.0
9436
+ * Version: 1.5.0
9312
9437
  * Description: Processes the content of identified code blocks, parsing headers or patch details, and provides utilities like fixing UUIDs within blocks.
9313
9438
  * Language: JavaScript
9314
9439
  * Created-at: 2025-07-21T00:33:22.312Z
9315
- * 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)
9440
+ * 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)
9316
9441
  */
9317
9442
 
9318
9443
  const { findAllCodeFences: findAllCodeFences$3, matchFencesAndExtractBlocks: matchFencesAndExtractBlocks$3 } = blockExtractor;
9319
9444
  const { parseHeader: parseHeader$2 } = headerUtils;
9320
- const { validateUUID: validateUUID$2, generateUUID: generateUUID$2 } = uuidUtils;
9445
+ const { validateUUID: validateUUID$2 } = uuidUtils;
9321
9446
  const AnalysisBlockUtils$2 = AnalysisBlockUtils$3;
9322
9447
  const PatchUtils$1 = PatchUtils$2;
9323
9448
  const GSToolBlockUtils$2 = GSToolBlockUtils$3;
@@ -9433,7 +9558,7 @@ function processBlockContent(content, language, position, incomplete, options =
9433
9558
  if (PatchUtils$1.isPatchBlock(content)) {
9434
9559
  const patchFormat = PatchUtils$1.determinePatchFormat(content);
9435
9560
  const metadata = PatchUtils$1.extractPatchMetadata(content); // Extract metadata regardless of validation
9436
- const patchContent = PatchUtils$1.extractPatchContent(content); // Extract raw patch content
9561
+ const patchContent = PatchUtils$1.extractPatchContent(content, patchFormat); // Extract raw patch content
9437
9562
 
9438
9563
  let patchValidationResult = { valid: true, errors: [], patches: null }; // Default valid state
9439
9564
 
@@ -9450,36 +9575,16 @@ function processBlockContent(content, language, position, incomplete, options =
9450
9575
  // For now, just warn, but return the extracted (potentially invalid) metadata.
9451
9576
  }
9452
9577
 
9453
- // Optionally validate context patch structure if requested
9454
- if (patchFormat === 'context' && validatePatches) {
9455
- try {
9456
- const patches = PatchUtils$1.extractContextPatches(content);
9457
- if (!patches || patches.length === 0) {
9458
- patchValidationResult = { valid: false, errors: ['No valid context patch content found.'], patches: null };
9459
- warnings.push({
9460
- position: position,
9461
- type: 'patch_content_error',
9462
- message: 'No valid context patch content found.',
9463
- content: content,
9464
- });
9465
- } else {
9466
- // Basic validation passed (structure was parsable)
9467
- patchValidationResult = { valid: true, errors: [], patches: patches };
9468
- }
9469
- } catch (error) {
9470
- patchValidationResult = { valid: false, errors: [error.message], patches: null };
9471
- warnings.push({
9472
- position: position,
9473
- type: 'patch_parse_error',
9474
- message: `Error parsing context patch: ${error.message}`,
9475
- content: content,
9476
- });
9477
- }
9578
+ // Determine the correct language for the block
9579
+ let blockLanguage = language; // Default to the fence language
9580
+ if (patchFormat === 'traditional') {
9581
+ blockLanguage = 'diff'; // Enforce 'diff' for traditional patches
9478
9582
  }
9583
+ // For abbreviated patches, keep the original language (e.g., 'javascript', 'python')
9479
9584
 
9480
9585
  return {
9481
9586
  type: 'patch',
9482
- language: language === 'diff' ? 'diff' : language, // Use 'diff' if specified, else keep original
9587
+ language: blockLanguage, // Use determined language
9483
9588
  content: content, // Full original content within fences
9484
9589
  position: position,
9485
9590
  incomplete: incomplete,
@@ -9673,8 +9778,10 @@ function processBlockContents(text, completeBlocks, incompleteBlocks, options) {
9673
9778
  * @returns {Object} { blocks: Array, warnings: Array, hasIncompleteBlocks: boolean, lastIncompleteBlock: Object|null }
9674
9779
  */
9675
9780
  function processCodeBlocks$5(text, options = { silent: false, validatePatches: false }) {
9676
- if (typeof text !== "string") { // Allow empty strings
9677
- console.warn("Warning: Input must be a string.");
9781
+ if (typeof text !== "string") {
9782
+ if (text != null )
9783
+ console.warn("Warning: Input must be a string.");
9784
+
9678
9785
  return {
9679
9786
  blocks: [],
9680
9787
  warnings: [{ type: 'invalid_input', message: 'Input must be a string.' }],
@@ -10595,19 +10702,114 @@ var updateCodeBlock_1 = {
10595
10702
  updateCodeBlockInMessage: updateCodeBlockInMessage$1,
10596
10703
  deleteCodeBlockByIndex: deleteCodeBlockByIndex$2};
10597
10704
 
10705
+ /**
10706
+ * Component: CodeBlockUtils Lineage Tracer
10707
+ * Block-UUID: 13b08cce-6fd1-4ec9-a915-240714ee89b9
10708
+ * Parent-UUID: df1a629b-2cc0-4677-b410-3172ee0e0f9f
10709
+ * Version: 1.1.0
10710
+ * Description: Provides a utility function to trace the full lineage (patch-based and parent-based) of code blocks within a chat, including loop detection.
10711
+ * Language: JavaScript
10712
+ * Created-at: 2025-10-23T01:28:10.456Z
10713
+ * Authors: Qwen 3 Coder 480B - Cerebras (v1.0.0), Qwen 3 Coder 480B - Cerebras (v1.1.0)
10714
+ */
10715
+
10716
+ /**
10717
+ * Traces the full lineage of a code block or patch identified by a UUID.
10718
+ * The lineage includes the block itself and all its ancestors, tracing backwards
10719
+ * first through patch relationships (Source-Block-UUID -> Target-Block-UUID)
10720
+ * and then through Parent-UUID relationships.
10721
+ * Loop detection is included to prevent infinite tracing.
10722
+ *
10723
+ * @param {string} uuid - The UUID of the target code block or patch.
10724
+ * @param {Map<string, object>} blockMap - A map where keys are UUIDs (for code blocks)
10725
+ * or patch UUIDs (source:target) and values
10726
+ * are the corresponding block objects.
10727
+ * Block objects are expected to have
10728
+ * properties like `isPatch`, `metadata`, and `header`.
10729
+ * @returns {Array<object>} An array of block objects representing the lineage.
10730
+ * The first element is the block for the provided `uuid`.
10731
+ * Subsequent elements are its ancestors.
10732
+ * Returns an empty array if the initial uuid is not found.
10733
+ * If a loop is detected, the last element will have a property `loopDetected: true`.
10734
+ */
10735
+ function getLineage$2(uuid, blockMap) {
10736
+ if (!uuid || !blockMap) {
10737
+ return [];
10738
+ }
10739
+
10740
+ const lineage = [];
10741
+ const visitedUuids = new Set();
10742
+ let currentUuid = uuid;
10743
+
10744
+ while (currentUuid) {
10745
+ // --- Loop Detection ---
10746
+ if (visitedUuids.has(currentUuid)) {
10747
+ // Add a marker or the block itself to indicate the loop
10748
+ const loopBlock = blockMap.get(currentUuid) || { uuid: currentUuid, loopDetected: true, type: 'loop-marker' };
10749
+ loopBlock.loopDetected = true; // Ensure the property is set
10750
+ lineage.push(loopBlock);
10751
+ break; // Stop the trace
10752
+ }
10753
+ visitedUuids.add(currentUuid);
10754
+
10755
+ // --- 1. Try to find the block directly by its UUID ---
10756
+ let block = blockMap.get(currentUuid);
10757
+
10758
+ // --- 2. If not found directly, search patch keys ---
10759
+ if (!block) {
10760
+ for (const [key, value] of blockMap) {
10761
+ // Check if the map key is a patch key (contains ':') and ends with our target UUID
10762
+ if (key.includes(':') && key.endsWith(`:${currentUuid}`)) {
10763
+ block = value;
10764
+ break;
10765
+ }
10766
+ }
10767
+ }
10768
+
10769
+ // --- 3. If a block was found, add it to the lineage ---
10770
+ if (block) {
10771
+ lineage.push(block);
10772
+
10773
+ let nextUuid = null;
10774
+
10775
+ // --- 3a. If it's a patch, move to its source UUID ---
10776
+ if (block.isPatch && block.metadata && block.metadata['Source-Block-UUID']) {
10777
+ nextUuid = block.metadata['Source-Block-UUID'];
10778
+ }
10779
+ // --- 3b. If it's a code block, move to its Parent-UUID (if valid) ---
10780
+ else if (!block.isPatch && block.header && block.header['Parent-UUID'] && block.header['Parent-UUID'] !== 'N/A') {
10781
+ nextUuid = block.header['Parent-UUID'];
10782
+ }
10783
+ // If it's a code block with Parent-UUID: 0c104865-260a-4942-8cb6-93bebf40c450
10784
+
10785
+ currentUuid = nextUuid;
10786
+ } else {
10787
+ // If no block is found for currentUuid, the lineage is incomplete.
10788
+ // We could add a placeholder here if needed, but for now, we just stop.
10789
+ currentUuid = null;
10790
+ }
10791
+ }
10792
+
10793
+ return lineage;
10794
+ }
10795
+
10796
+ var lineageTracer = {
10797
+ getLineage: getLineage$2
10798
+ };
10799
+
10598
10800
  /**
10599
10801
  * Component: CodeBlockUtils Index
10600
- * Block-UUID: 7e9d3f8a-1b2c-4d5e-8f9a-0123456789ab
10601
- * Parent-UUID: a3b9c8d1-e0f2-4a1b-8c7d-5e6f0a9b1c2d
10602
- * Version: 1.4.0
10802
+ * Block-UUID: 98684aa5-d597-41b6-a7fd-cc16482aafdc
10803
+ * Parent-UUID: 7e9d3f8a-1b2c-4d5e-8f9a-0123456789ab
10804
+ * Version: 1.5.0
10603
10805
  * Description: Aggregates and exports all utilities related to code block processing from the CodeBlockUtils module. Serves as the main entry point for this module.
10604
10806
  * Language: JavaScript
10605
10807
  * Created-at: 2025-04-15T16:02:20.217Z
10606
- * 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)
10808
+ * 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)
10607
10809
  */
10608
10810
 
10609
10811
  // Import from individual utility files
10610
- const { COMMENT_STYLES: COMMENT_STYLES$1 } = constants$2;
10812
+ const { COMMENT_STYLES: COMMENT_STYLES$1 } = constants$3;
10611
10813
  const { generateUUID: generateUUID$1, validateUUID: validateUUID$1 } = uuidUtils;
10612
10814
  const { isValidISOTimestamp: isValidISOTimestamp$1, parseHeader: parseHeader$1, getHeaderLineCount } = headerUtils;
10613
10815
  const { findAllCodeFences: findAllCodeFences$1, matchFencesAndExtractBlocks: matchFencesAndExtractBlocks$1, extractCodeBlocksWithUUIDs, findCodeBlockByUUID } = blockExtractor;
@@ -10619,9 +10821,10 @@ const { parseCodeBlocks: parseCommentDelimitedBlocks$1 } = headerParser;
10619
10821
  const { removeCodeBlockMarkers: removeCodeBlockMarkers$1 } = markerRemover;
10620
10822
  const { updateCodeBlockByIndex: updateCodeBlockByIndex$1, updateCodeBlockByUUID, updateCodeBlock, updateCodeBlockInMessage, deleteCodeBlockByIndex: deleteCodeBlockByIndex$1 } = updateCodeBlock_1;
10621
10823
  const { formatWithLineNumbers: formatWithLineNumbers$1, formatBlockWithLineNumbers: formatBlockWithLineNumbers$1, formatBlocksWithLineNumbers: formatBlocksWithLineNumbers$1, removeLineNumbers: removeLineNumbers$1 } = lineNumberFormatter;
10824
+ const { getLineage: getLineage$1 } = lineageTracer;
10622
10825
 
10623
10826
  // Export all imported items
10624
- var CodeBlockUtils$5 = {
10827
+ var CodeBlockUtils$7 = {
10625
10828
  // Constants
10626
10829
  COMMENT_STYLES: COMMENT_STYLES$1,
10627
10830
 
@@ -10673,22 +10876,23 @@ var CodeBlockUtils$5 = {
10673
10876
  // Line Number Formatting Utilities
10674
10877
  formatWithLineNumbers: formatWithLineNumbers$1,
10675
10878
  formatBlockWithLineNumbers: formatBlockWithLineNumbers$1,
10676
- formatBlocksWithLineNumbers: formatBlocksWithLineNumbers$1,
10879
+ formatBlocksWithLineNumbers: formatBlocksWithLineNumbers$1,
10880
+ getLineage: getLineage$1,
10677
10881
  removeLineNumbers: removeLineNumbers$1,
10678
10882
  };
10679
10883
 
10680
10884
  /*
10681
10885
  * Component: ContextUtils
10682
- * Block-UUID: a0b71292-b1cc-401a-8ce2-544a047b0fef
10683
- * Parent-UUID: c018b1f9-2291-4bc9-9c4b-ab53a5db745e
10684
- * Version: 1.3.0
10886
+ * Block-UUID: 534c348d-a11f-4de6-ad15-f33068d51fb8
10887
+ * Parent-UUID: a0b71292-b1cc-401a-8ce2-544a047b0fef
10888
+ * Version: 1.4.0
10685
10889
  * Description: Provides utility functions for parsing context message sections to extract file details and code blocks, and for formatting content for LLM context.
10686
10890
  * Language: JavaScript
10687
10891
  * Created-at: 2025-05-09T01:36:20.107Z
10688
- * 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)
10892
+ * 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)
10689
10893
  */
10690
10894
 
10691
- const CodeBlockUtils$4 = CodeBlockUtils$5;
10895
+ const CodeBlockUtils$6 = CodeBlockUtils$7;
10692
10896
  const MessageUtils$2 = MessageUtils$3;
10693
10897
 
10694
10898
  /**
@@ -10755,6 +10959,41 @@ function _createContextSummary(items, contentType) {
10755
10959
  return summary + "\n";
10756
10960
  }
10757
10961
 
10962
+ /**
10963
+ * Extracts all context files from a given chat object.
10964
+ * Iterates through chat messages, identifies context loader outputs,
10965
+ * and parses them into individual file objects.
10966
+ *
10967
+ * @param {object} chat - The GitSense chat object.
10968
+ * @returns {Array<object>} An array of parsed context file objects.
10969
+ * Returns an empty array if no context files are found or on error.
10970
+ */
10971
+ function getContextFiles$1(chat) {
10972
+ const ChatUtils = ChatUtils$2;
10973
+ const contextFiles = [];
10974
+ const messages = ChatUtils.getChatMessages(chat);
10975
+
10976
+ if (!Array.isArray(messages)) {
10977
+ console.warn("getContextFiles: Provided chat object does not contain a valid messages array.");
10978
+ return contextFiles;
10979
+ }
10980
+
10981
+ messages.forEach(message => {
10982
+ // Check if the message content is a context message
10983
+ if (MessageUtils$2.isContextMessage(message.message)) {
10984
+ try {
10985
+ // Extract all context sections from the message content
10986
+ const extractedFiles = extractContextSections$1(message.message);
10987
+ contextFiles.push(...extractedFiles);
10988
+ } catch (error) {
10989
+ console.warn(`getContextFiles: Failed to extract context sections from message ID ${message.id}:`, error);
10990
+ }
10991
+ }
10992
+ });
10993
+
10994
+ return contextFiles;
10995
+ }
10996
+
10758
10997
  /**
10759
10998
  * Escapes backticks in code blocks to prevent premature termination of LLM-generated code blocks.
10760
10999
  * @param {string} content - The content string to escape.
@@ -10827,7 +11066,7 @@ function parseContextSection$1(sectionText) {
10827
11066
  }
10828
11067
  });
10829
11068
 
10830
- const { blocks, warnings } = CodeBlockUtils$4.extractCodeBlocks(sectionText, { silent: true });
11069
+ const { blocks, warnings } = CodeBlockUtils$6.extractCodeBlocks(sectionText, { silent: true });
10831
11070
  const codeBlocks = blocks.filter(block => block.type === 'code');
10832
11071
 
10833
11072
  if (codeBlocks.length === 0) {
@@ -11029,6 +11268,7 @@ function formatContextContent$1(items, contentType, contentOption) {
11029
11268
  var ContextUtils$2 = {
11030
11269
  parseContextSection: parseContextSection$1,
11031
11270
  extractContextSections: extractContextSections$1,
11271
+ getContextFiles: getContextFiles$1,
11032
11272
  extractContextItemsOverviewTableRows: extractContextItemsOverviewTableRows$1,
11033
11273
  formatContextContent: formatContextContent$1,
11034
11274
  };
@@ -11086,7 +11326,7 @@ var contextMapper = {
11086
11326
 
11087
11327
  /*
11088
11328
  * Component: AnalyzerUtils Constants
11089
- * Block-UUID: 01b2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d
11329
+ * Block-UUID: fec5dcc4-a1d0-4ef7-8828-3e44a75892c4
11090
11330
  * Parent-UUID: N/A
11091
11331
  * Version: 1.0.0
11092
11332
  * Description: Defines constants specific to the AnalyzerUtils module.
@@ -11112,7 +11352,7 @@ var constants = {
11112
11352
  * Authors: Gemini 2.5 Flash (v1.0.0)
11113
11353
  */
11114
11354
 
11115
- const CodeBlockUtils$3 = CodeBlockUtils$5;
11355
+ const CodeBlockUtils$5 = CodeBlockUtils$7;
11116
11356
  const GSToolBlockUtils$1 = GSToolBlockUtils$3;
11117
11357
  const JsonUtils$1 = JsonUtils$2;
11118
11358
  const { ANALYZE_HEADER_PREFIX } = constants;
@@ -11129,7 +11369,7 @@ const { ANALYZE_HEADER_PREFIX } = constants;
11129
11369
  */
11130
11370
  function processLLMAnalysisResponse$2(messageContent, stoppedStreaming) {
11131
11371
  const silent = { silent: true };
11132
- const { blocks, warnings } = CodeBlockUtils$3.extractCodeBlocks(messageContent, silent);
11372
+ const { blocks, warnings } = CodeBlockUtils$5.extractCodeBlocks(messageContent, silent);
11133
11373
 
11134
11374
  const analysisBlocks = [];
11135
11375
  const analysisMetadataBlocks = [];
@@ -11314,7 +11554,7 @@ var dataValidator = {
11314
11554
 
11315
11555
  /*
11316
11556
  * Component: AnalyzerUtils Instruction Loader
11317
- * Block-UUID: 0a1b2c3d-4e5f-6a7b-8c9d-0e1f2a3b4c5e
11557
+ * Block-UUID: 3ecd422e-1dd8-482d-ae3f-90a5eae3ae44
11318
11558
  * Parent-UUID: N/A
11319
11559
  * Version: 1.0.0
11320
11560
  * Description: Provides utility functions for loading raw analyzer instruction content.
@@ -11323,19 +11563,19 @@ var dataValidator = {
11323
11563
  * Authors: Gemini 2.5 Flash (v1.0.0)
11324
11564
  */
11325
11565
 
11326
- const fs$7 = require$$0.promises;
11327
- const path$5 = require$$1;
11566
+ const fs$8 = require$$0.promises;
11567
+ const path$6 = require$$1;
11328
11568
 
11329
11569
  /**
11330
11570
  * Retrieves the raw Markdown content of the analyzer's '1.md' instruction file.
11331
11571
  *
11332
- * @param {string} analyzeMessagesBasePath - The absolute path to the base directory containing the analyzer message files (e.g., 'messages/analyze').
11572
+ * @param {string} analyzersBasePath - The absolute path to the base directory containing the analyzer message files (e.g., 'messages/analyze').
11333
11573
  * @param {string} analyzerId - The unique ID of the analyzer (format: 'analyzer_name::content_type::instructions_type').
11334
11574
  * @returns {Promise<string|null>} A promise that resolves with the full Markdown content of the '1.md' file, or null if not found/invalid.
11335
11575
  */
11336
- async function getAnalyzerInstructionsContent$3(analyzeMessagesBasePath, analyzerId) {
11337
- if (typeof analyzeMessagesBasePath !== 'string' || analyzeMessagesBasePath.trim() === '') {
11338
- console.error('Error: analyzeMessagesBasePath is required.');
11576
+ async function getAnalyzerInstructionsContent$3(analyzersBasePath, analyzerId) {
11577
+ if (typeof analyzersBasePath !== 'string' || analyzersBasePath.trim() === '') {
11578
+ console.error('Error: analyzersBasePath is required.');
11339
11579
  return null;
11340
11580
  }
11341
11581
  if (typeof analyzerId !== 'string' || analyzerId.trim() === '') {
@@ -11350,10 +11590,10 @@ async function getAnalyzerInstructionsContent$3(analyzeMessagesBasePath, analyze
11350
11590
  }
11351
11591
  const [analyzerName, contentType, instructionsType] = parts;
11352
11592
 
11353
- const instructionsFilePath = path$5.join(analyzeMessagesBasePath, analyzerName, contentType, instructionsType, '1.md');
11593
+ const instructionsFilePath = path$6.join(analyzersBasePath, analyzerName, contentType, instructionsType, '1.md');
11354
11594
 
11355
11595
  try {
11356
- const fileContent = await fs$7.readFile(instructionsFilePath, 'utf8');
11596
+ const fileContent = await fs$8.readFile(instructionsFilePath, 'utf8');
11357
11597
  const parts = fileContent.split('\n\n\n');
11358
11598
  parts.shift();
11359
11599
  return parts.join('\n\n\n');
@@ -11373,20 +11613,56 @@ var instructionLoader = {
11373
11613
  };
11374
11614
 
11375
11615
  /*
11616
+ * Component: AnalyzerUtils JSON Parser
11617
+ * Block-UUID: 4dd21efd-3ad3-43e1-adf0-d52d7c560970
11618
+ * Version: 1.0.0
11619
+ * Description: Provides utility functions for pre-processing JSON content from analyzer instructions.
11620
+ * Language: JavaScript
11621
+ * Created-at: 2025-11-23T05:38:15.725Z
11622
+ * Authors: GLM-4.6 (v1.0.0)
11623
+ */
11624
+
11625
+ /**
11626
+ * Pre-processes JSON content to quote unquoted template strings before parsing.
11627
+ * This handles cases where {{SYSTEM: ...}} and {{ANALYZER: ...}} placeholders
11628
+ * are not properly quoted in the JSON.
11629
+ *
11630
+ * @param {string} jsonString - The raw JSON string content.
11631
+ * @returns {string} The processed JSON string with template strings quoted.
11632
+ */
11633
+ function preprocessJsonForValidation$5(jsonString) {
11634
+ // Find all unquoted template strings and quote them with proper escaping
11635
+ // This regex looks for template strings that aren't already quoted
11636
+ return jsonString.replace(
11637
+ /(?<!")(\{\{(SYSTEM|ANALYZER):[^}]+\}\})(?!")/g,
11638
+ (match, template) => {
11639
+ // Escape any double quotes within the template string
11640
+ const escapedTemplate = template.replace(/"/g, '\\"');
11641
+ return `"${escapedTemplate}"`;
11642
+ }
11643
+ );
11644
+ }
11645
+
11646
+ var jsonParser = {
11647
+ preprocessJsonForValidation: preprocessJsonForValidation$5
11648
+ };
11649
+
11650
+ /**
11376
11651
  * Component: AnalyzerUtils Discovery
11377
- * Block-UUID: a87a86c4-69fc-4cbb-80a8-857f56122395
11378
- * Parent-UUID: 0b1c2d3e-4f5a-6b7c-8d9e-0f1a2b3c4d5f
11379
- * Version: 1.1.0
11380
- * Description: Provides utility functions for discovering available analyzers.
11652
+ * Block-UUID: 26a7df9b-ef29-4dcd-aab2-9cdc3a11133c
11653
+ * Parent-UUID: aa999515-84fb-43f6-91a7-cf79248d7286
11654
+ * Version: 1.3.1
11655
+ * Description: Provides utility functions for discovering available analyzers. Updated to include version and tags in analyzer objects.
11381
11656
  * Language: JavaScript
11382
- * Created-at: 2025-08-28T23:48:00.000Z
11383
- * Authors: Gemini 2.5 Flash (v1.0.0), Gemini 2.5 Flash (v1.1.0)
11657
+ * Created-at: 2025-11-27T14:45:30.978Z
11658
+ * 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)
11384
11659
  */
11385
11660
 
11386
- const fs$6 = require$$0.promises;
11387
- const path$4 = require$$1;
11661
+ const fs$7 = require$$0.promises;
11662
+ const path$5 = require$$1;
11388
11663
  const { getAnalyzerInstructionsContent: getAnalyzerInstructionsContent$2 } = instructionLoader;
11389
- const CodeBlockUtils$2 = CodeBlockUtils$5;
11664
+ const { preprocessJsonForValidation: preprocessJsonForValidation$4 } = jsonParser;
11665
+ const CodeBlockUtils$4 = CodeBlockUtils$7;
11390
11666
 
11391
11667
  /**
11392
11668
  * Reads and parses the config.json file in a directory.
@@ -11394,10 +11670,10 @@ const CodeBlockUtils$2 = CodeBlockUtils$5;
11394
11670
  * @returns {Promise<object|null>} A promise that resolves to the parsed config object
11395
11671
  * or null if the file doesn't exist or is invalid.
11396
11672
  */
11397
- async function readConfig$1(dirPath) {
11398
- const configPath = path$4.join(dirPath, 'config.json');
11673
+ async function readConfig$2(dirPath) {
11674
+ const configPath = path$5.join(dirPath, 'config.json');
11399
11675
  try {
11400
- const fileContent = await fs$6.readFile(configPath, 'utf8');
11676
+ const fileContent = await fs$7.readFile(configPath, 'utf8');
11401
11677
  return JSON.parse(fileContent);
11402
11678
  } catch (error) {
11403
11679
  if (error.code !== 'ENOENT') {
@@ -11409,12 +11685,12 @@ async function readConfig$1(dirPath) {
11409
11685
  }
11410
11686
 
11411
11687
  /**
11412
- * Checks if a directory name is valid based on the rules in messages/analyze/README.md.
11688
+ * Checks if a directory name is valid
11413
11689
  * Allowed: a-z, A-Z, 0-9, dash (-), underscore (_). Cannot start with underscore or contain dots.
11414
11690
  * @param {string} name - The directory name to check.
11415
11691
  * @returns {boolean} True if the name is valid, false otherwise.
11416
11692
  */
11417
- function isValidDirName(name) {
11693
+ function isValidDirName$2(name) {
11418
11694
  // Exclude names starting with underscore or containing dots
11419
11695
  if (name.startsWith('_') || name.includes('.')) {
11420
11696
  return false;
@@ -11427,51 +11703,53 @@ function isValidDirName(name) {
11427
11703
  * Discovers and lists all available analyzers by traversing the directory structure.
11428
11704
  * An analyzer is considered valid if a '1.md' file exists in the instructions directory.
11429
11705
  *
11430
- * @param {string} analyzeMessagesBasePath - The absolute or relative path to the base directory containing the analyzer message files (e.g., 'messages/analyze').
11706
+ * @param {string} analyzersBasePath - The absolute path to the base directory containing the analyzers (e.g., 'analyzers/analyzer-1').
11431
11707
  * @param {object} [options={}] - Optional configuration.
11432
- * @param {boolean} [options.includeDescription=false] - Whether to include the description of the analyzer.
11433
- * @returns {Promise<Array<{id: string, label: string, name: string, protected: boolean, description?: string}>>} A promise that resolves to an array of analyzer objects.
11708
+ * @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.
11434
11709
  */
11435
- async function getAnalyzers$2(analyzeMessagesBasePath, options = {}) {
11436
- const { includeDescription = false } = options;
11710
+ async function getAnalyzers$3(analyzersBasePath, options = {}) {
11437
11711
  const analyzers = [];
11712
+ const demoAnalyzerPattern = /^demo-.+-\w{6}$/;
11438
11713
 
11439
11714
  try {
11440
- const analyzerEntries = await fs$6.readdir(analyzeMessagesBasePath, { withFileTypes: true });
11715
+ const analyzerEntries = await fs$7.readdir(analyzersBasePath, { withFileTypes: true });
11441
11716
 
11442
11717
  for (const analyzerEntry of analyzerEntries) {
11443
- if (analyzerEntry.isDirectory() && isValidDirName(analyzerEntry.name)) {
11718
+ if (analyzerEntry.isDirectory() && isValidDirName$2(analyzerEntry.name)) {
11444
11719
  const analyzerName = analyzerEntry.name;
11445
- const analyzerPath = path$4.join(analyzeMessagesBasePath, analyzerName);
11446
- const analyzerConfig = await readConfig$1(analyzerPath);
11720
+ const analyzerPath = path$5.join(analyzersBasePath, analyzerName);
11721
+ const analyzerConfig = await readConfig$2(analyzerPath);
11447
11722
  const analyzerLabel = analyzerConfig?.label || analyzerName;
11448
11723
 
11449
- const contentEntries = await fs$6.readdir(analyzerPath, { withFileTypes: true });
11724
+ const contentEntries = await fs$7.readdir(analyzerPath, { withFileTypes: true });
11450
11725
 
11451
11726
  for (const contentEntry of contentEntries) {
11452
- if (contentEntry.isDirectory() && isValidDirName(contentEntry.name)) {
11727
+ if (contentEntry.isDirectory() && isValidDirName$2(contentEntry.name)) {
11453
11728
  const contentType = contentEntry.name;
11454
- const contentPath = path$4.join(analyzerPath, contentType);
11455
- const contentConfig = await readConfig$1(contentPath);
11729
+ const contentPath = path$5.join(analyzerPath, contentType);
11730
+ const contentConfig = await readConfig$2(contentPath);
11456
11731
  const contentLabel = contentConfig?.label || contentType;
11457
11732
 
11458
- const instructionsEntries = await fs$6.readdir(contentPath, { withFileTypes: true });
11733
+ const instructionsEntries = await fs$7.readdir(contentPath, { withFileTypes: true });
11459
11734
 
11460
11735
  for (const instructionsEntry of instructionsEntries) {
11461
- if (instructionsEntry.isDirectory() && isValidDirName(instructionsEntry.name)) {
11736
+ if (instructionsEntry.isDirectory() && isValidDirName$2(instructionsEntry.name)) {
11462
11737
  const instructionsType = instructionsEntry.name;
11463
- const instructionsPath = path$4.join(contentPath, instructionsType);
11464
- const instructionsConfig = await readConfig$1(instructionsPath);
11738
+ const instructionsPath = path$5.join(contentPath, instructionsType);
11739
+ const instructionsConfig = await readConfig$2(instructionsPath);
11465
11740
  const instructionsLabel = instructionsConfig?.label || instructionsType;
11466
11741
 
11742
+ // Construct the analyzer ID and label
11743
+ const analyzerId = `${analyzerName}::${contentType}::${instructionsType}`;
11744
+
11467
11745
  // Check for the existence of 1.md to confirm a valid analyzer configuration
11468
- const instructionsFilePath = path$4.join(instructionsPath, '1.md');
11746
+ const instructionsFilePath = path$5.join(instructionsPath, '1.md');
11469
11747
  try {
11470
- await fs$6.access(instructionsFilePath); // Check if file exists and is accessible
11748
+ await fs$7.access(instructionsFilePath); // Check if file exists and is accessible
11471
11749
 
11472
11750
  // If analyzerName starts with 'tutorial-', check its last modified time.
11473
- if (analyzerName.startsWith('tutorial-')) {
11474
- const stats = await fs$6.stat(instructionsFilePath);
11751
+ if (analyzerName.startsWith('tutorial-') || demoAnalyzerPattern.test(analyzerName)) {
11752
+ const stats = await fs$7.stat(instructionsFilePath);
11475
11753
  const lastModified = stats.mtime.getTime(); // Get timestamp in milliseconds
11476
11754
  const sixtyMinutesAgo = Date.now() - (60 * 60 * 1000); // Current time - 60 minutes in ms
11477
11755
 
@@ -11480,37 +11758,43 @@ async function getAnalyzers$2(analyzeMessagesBasePath, options = {}) {
11480
11758
  continue;
11481
11759
  }
11482
11760
  }
11483
- // Construct the analyzer ID and label
11484
- const analyzerId = `${analyzerName}::${contentType}::${instructionsType}`;
11485
- const analyzerFullLabel = `${analyzerLabel} (${contentLabel} - ${instructionsLabel})`;
11761
+ // TODO: Decide if we should show the contentLabel and instructionsLabel. For now we will ignore them.
11762
+ const analyzerFullLabel = `${analyzerLabel}`; // (${contentLabel} - ${instructionsLabel})`;
11486
11763
 
11764
+ // Extract description, version, and tags from the JSON block in 1.md
11487
11765
  let description = null;
11488
- if (includeDescription) {
11489
- try {
11490
- const content = await getAnalyzerInstructionsContent$2(analyzeMessagesBasePath, analyzerId);
11491
- const { blocks, warnings } = CodeBlockUtils$2.extractCodeBlocks(content, { silent: true });
11492
- const jsonBlock = blocks.find(block => block.language === 'json');
11493
-
11494
- if (jsonBlock && jsonBlock.content) {
11495
- try {
11496
- const json = JSON.parse(jsonBlock.content);
11497
- description = json.description;
11498
- } catch(error) {
11499
- console.warn(`${analyzerId} contains an invalid JSON`);
11500
- }
11501
- }
11502
- } catch (descError) {
11503
- console.warn(`Warning: Could not load description for ${analyzerId}: ${descError.message}`);
11504
- descriptionContent = null;
11766
+ let version = null;
11767
+ let tags = [];
11768
+ let label = null;
11769
+ let requires_reference_files = null;
11770
+
11771
+ try {
11772
+ const content = await getAnalyzerInstructionsContent$2(analyzersBasePath, analyzerId);
11773
+ const { blocks, warnings } = CodeBlockUtils$4.extractCodeBlocks(content, { silent: true });
11774
+ const jsonBlock = blocks.find(block => block.language === 'json');
11775
+
11776
+ if (jsonBlock && jsonBlock.content) {
11777
+ const preprocessedContent = preprocessJsonForValidation$4(jsonBlock.content);
11778
+ const json = JSON.parse(preprocessedContent);
11779
+ description = json.description || null;
11780
+ label = json.label || null;
11781
+ requires_reference_files = json.requires_reference_files || false;
11782
+ version = json.version || null;
11783
+ tags = Array.isArray(json.tags) ? json.tags : [];
11505
11784
  }
11785
+ } catch (descError) {
11786
+ console.warn(`Warning: Could not load metadata for ${analyzerId}: ${descError.message}`);
11506
11787
  }
11507
11788
 
11508
11789
  analyzers.push({
11509
11790
  id: analyzerId,
11510
- label: analyzerFullLabel,
11511
11791
  name: analyzerName,
11792
+ description,
11793
+ label: label || analyzerFullLabel,
11794
+ requires_reference_files,
11512
11795
  protected: analyzerConfig?.protected || false,
11513
- description
11796
+ version,
11797
+ tags
11514
11798
  });
11515
11799
  } catch (error) {
11516
11800
  // If 1.md doesn't exist, this is not a complete analyzer configuration, skip.
@@ -11525,7 +11809,7 @@ async function getAnalyzers$2(analyzeMessagesBasePath, options = {}) {
11525
11809
  }
11526
11810
  }
11527
11811
  } catch (error) {
11528
- console.error(`Error traversing analyze messages directory ${analyzeMessagesBasePath}: ${error.message}`);
11812
+ console.error(`Error traversing analyzers directory ${analyzersBasePath}: ${error.message}`);
11529
11813
  // Depending on requirements, you might want to throw the error or return an empty array
11530
11814
  throw error; // Re-throw to indicate failure
11531
11815
  }
@@ -11534,8 +11818,10 @@ async function getAnalyzers$2(analyzeMessagesBasePath, options = {}) {
11534
11818
  }
11535
11819
 
11536
11820
  var discovery = {
11537
- getAnalyzers: getAnalyzers$2,
11538
- readConfig: readConfig$1};
11821
+ getAnalyzers: getAnalyzers$3,
11822
+ readConfig: readConfig$2,
11823
+ isValidDirName: isValidDirName$2,
11824
+ };
11539
11825
 
11540
11826
  /**
11541
11827
  * Component: Analyzer Saver Utility
@@ -11548,8 +11834,8 @@ var discovery = {
11548
11834
  * Authors: Gemini 2.5 Flash Thinking (v1.0.0), Gemini 2.5 Flash Thinking (v1.1.0)
11549
11835
  */
11550
11836
 
11551
- const fs$5 = require$$0.promises;
11552
- const path$3 = require$$1;
11837
+ const fs$6 = require$$0.promises;
11838
+ const path$4 = require$$1;
11553
11839
 
11554
11840
  /**
11555
11841
  * Saves or updates an analyzer configuration.
@@ -11559,19 +11845,19 @@ const path$3 = require$$1;
11559
11845
  * if necessary, saves the instructions to '1.md'. Optionally, it can
11560
11846
  * ensure config.json files exist with labels derived from directory names.
11561
11847
  *
11562
- * @param {string} analyzeMessagesBasePath - The absolute or relative path to the 'messages/analyze' directory.
11848
+ * @param {string} analyzersBasePath - The absolute path to the base directory containing the analyzers (e.g., 'analyzers/analyzer-1').
11563
11849
  * @param {string} analyzerId - The unique ID of the analyzer (format: 'analyzer_name::content_type::instructions_type').
11564
11850
  * @param {string} instructionsContent - The full content of the analyzer instructions message to be saved in '1.md'.
11565
11851
  * @param {object} [options={}] - Optional configuration options.
11566
11852
  * @param {boolean} [options.ensureConfigs=false] - If true, ensures config.json files exist in the analyzer, content, and instructions directories. Defaults to false.
11567
11853
  * @returns {Promise<{success: boolean, message?: string}>} A promise that resolves with a result object.
11568
11854
  */
11569
- async function saveConfiguration$1(analyzeMessagesBasePath, analyzerId, instructionsContent, options = {}) {
11855
+ async function saveConfiguration$2(analyzersBasePath, analyzerId, instructionsContent, options = {}) {
11570
11856
  const { ensureConfigs = false } = options;
11571
11857
 
11572
11858
  // 1. Validate inputs
11573
- if (typeof analyzeMessagesBasePath !== 'string' || analyzeMessagesBasePath.trim() === '') {
11574
- return { success: false, message: 'analyzeMessagesBasePath is required.' };
11859
+ if (typeof analyzersBasePath !== 'string' || analyzersBasePath.trim() === '') {
11860
+ return { success: false, message: 'analyzersBasePath is required.' };
11575
11861
  }
11576
11862
  if (typeof analyzerId !== 'string' || analyzerId.trim() === '') {
11577
11863
  return { success: false, message: 'analyzerId is required.' };
@@ -11604,18 +11890,18 @@ async function saveConfiguration$1(analyzeMessagesBasePath, analyzerId, instruct
11604
11890
  }
11605
11891
 
11606
11892
  // 3. Construct directory paths
11607
- const analyzerDir = path$3.join(analyzeMessagesBasePath, analyzerName);
11608
- const contentDir = path$3.join(analyzerDir, contentType);
11609
- const instructionsDir = path$3.join(contentDir, instructionsType);
11610
- const instructionsFilePath = path$3.join(instructionsDir, '1.md');
11893
+ const analyzerDir = path$4.join(analyzersBasePath, analyzerName);
11894
+ const contentDir = path$4.join(analyzerDir, contentType);
11895
+ const instructionsDir = path$4.join(contentDir, instructionsType);
11896
+ const instructionsFilePath = path$4.join(instructionsDir, '1.md');
11611
11897
 
11612
11898
  try {
11613
11899
  // 4. Create directories recursively
11614
- await fs$5.mkdir(instructionsDir, { recursive: true });
11900
+ await fs$6.mkdir(instructionsDir, { recursive: true });
11615
11901
 
11616
11902
  // 5. Save instructions content to 1.md
11617
11903
  const finalContent = `; role: assistant\n\n\n${instructionsContent}`;
11618
- await fs$5.writeFile(instructionsFilePath, finalContent, 'utf8');
11904
+ await fs$6.writeFile(instructionsFilePath, finalContent, 'utf8');
11619
11905
 
11620
11906
  // 6. Optionally create/Update config.json files
11621
11907
  if (ensureConfigs) {
@@ -11641,11 +11927,11 @@ async function saveConfiguration$1(analyzeMessagesBasePath, analyzerId, instruct
11641
11927
  * @param {string} label - The label to ensure is in the config.json.
11642
11928
  */
11643
11929
  async function ensureConfigJson(dirPath, label) {
11644
- const configPath = path$3.join(dirPath, 'config.json');
11930
+ const configPath = path$4.join(dirPath, 'config.json');
11645
11931
  let config = {};
11646
11932
 
11647
11933
  try {
11648
- const fileContent = await fs$5.readFile(configPath, 'utf8');
11934
+ const fileContent = await fs$6.readFile(configPath, 'utf8');
11649
11935
  config = JSON.parse(fileContent);
11650
11936
  } catch (error) {
11651
11937
  // If file doesn't exist or parsing fails, start with an empty config
@@ -11662,28 +11948,103 @@ async function ensureConfigJson(dirPath, label) {
11662
11948
  }
11663
11949
 
11664
11950
  // Write the updated config back to the file
11665
- await fs$5.writeFile(configPath, JSON.stringify(config, null, 4), 'utf8');
11951
+ await fs$6.writeFile(configPath, JSON.stringify(config, null, 4), 'utf8');
11666
11952
  }
11667
11953
 
11668
11954
 
11669
11955
  var saver = {
11670
- saveConfiguration: saveConfiguration$1,
11956
+ saveConfiguration: saveConfiguration$2,
11671
11957
  };
11672
11958
 
11673
- /*
11959
+ /**
11674
11960
  * Component: AnalyzerUtils Schema Loader
11675
- * Block-UUID: 0c1d2e3f-4a5b-6c7d-8e9f-0a1b2c3d4e5f
11676
- * Parent-UUID: N/A
11677
- * Version: 1.0.0
11961
+ * Block-UUID: bf25c501-f9e1-4e5b-ad22-9ed24ba50787
11962
+ * Parent-UUID: 66208fdc-2c80-4264-8500-06d5f4db103a
11963
+ * Version: 1.4.1
11678
11964
  * Description: Provides utility functions for retrieving and deducing JSON schemas for analyzers.
11679
11965
  * Language: JavaScript
11680
- * Created-at: 2025-08-28T23:48:00.000Z
11681
- * Authors: Gemini 2.5 Flash (v1.0.0)
11966
+ * Created-at: 2025-11-23T05:40:43.016Z
11967
+ * 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)
11682
11968
  */
11683
11969
 
11684
- const fs$4 = require$$0.promises;
11685
- const path$2 = require$$1;
11686
- const CodeBlockUtils$1 = CodeBlockUtils$5;
11970
+ /**
11971
+ * Parses the 'Custom Metadata Definitions' block from the markdown content.
11972
+ * Handles both formats:
11973
+ * - With backticks: * `fieldName` (type): description
11974
+ * - Without backticks: * fieldName (type): description
11975
+ *
11976
+ * @param {string} markdownContent - The full content of the 1.md file.
11977
+ * @returns {Map<string, {type: string, description: string}>} A map from field name to its type and description.
11978
+ */
11979
+ function parseMetadataDefinitions(markdownContent) {
11980
+ const definitions = new Map();
11981
+
11982
+ // Match the "### Custom Metadata Definitions" section until the next code block or section
11983
+ const sectionRegex = /### Custom Metadata Definitions\s*([\s\S]*?)(?=```|\n##|\n---)/;
11984
+ const match = markdownContent.match(sectionRegex);
11985
+
11986
+ if (!match || !match[1]) {
11987
+ return definitions;
11988
+ }
11989
+
11990
+ const definitionBlock = match[1];
11991
+
11992
+ // Regex to match both formats:
11993
+ // Format 1: * `fieldName` (type): description
11994
+ // Format 2: * fieldName (type): description
11995
+ // Capture groups:
11996
+ // Group 1: backtick-wrapped field name (if present)
11997
+ // Group 2: non-backtick field name (if present)
11998
+ // Group 3: field type
11999
+ // Group 4: description
12000
+ const lineRegex = /^\s*\*\s+(?:`([^`]+)`|([a-zA-Z0-9_-]+))\s+\(([^)]+)\):\s*(.+)/gm;
12001
+
12002
+ let lineMatch;
12003
+ while ((lineMatch = lineRegex.exec(definitionBlock)) !== null) {
12004
+ // Extract field name from either group 1 (backtick-wrapped) or group 2 (plain)
12005
+ const fieldName = (lineMatch[1] || lineMatch[2]).trim();
12006
+ const fieldType = lineMatch[3].trim();
12007
+ const description = lineMatch[4].trim();
12008
+
12009
+ if (fieldName && fieldType && description) {
12010
+ definitions.set(fieldName, { type: fieldType, description });
12011
+ }
12012
+ }
12013
+
12014
+ return definitions;
12015
+ }
12016
+
12017
+ const fs$5 = require$$0.promises;
12018
+ const path$3 = require$$1;
12019
+ const CodeBlockUtils$3 = CodeBlockUtils$7;
12020
+ const { preprocessJsonForValidation: preprocessJsonForValidation$3 } = jsonParser;
12021
+
12022
+ /**
12023
+ * Maps a simple type string (from the definitions block) to a JSON schema type object.
12024
+ * @param {string} simpleType - The type string (e.g., "number", "array of strings").
12025
+ * @returns {object} A JSON schema type definition.
12026
+ */
12027
+ function mapSimpleTypeToSchema(simpleType) {
12028
+ const lowerType = simpleType.toLowerCase().trim();
12029
+ switch (lowerType) {
12030
+ case 'string':
12031
+ return { type: 'string' };
12032
+ case 'number':
12033
+ case 'integer':
12034
+ return { type: 'number' };
12035
+ case 'boolean':
12036
+ return { type: 'boolean' };
12037
+ case 'array of strings':
12038
+ return { type: 'array', items: { type: 'string' } };
12039
+ case 'date':
12040
+ return { type: 'string', format: 'date' };
12041
+ case 'datetime':
12042
+ case 'iso 8601 timestamp':
12043
+ return { type: 'string', format: 'date-time' };
12044
+ default:
12045
+ return { type: 'string' }; // Default to string for unknown types
12046
+ }
12047
+ }
11687
12048
 
11688
12049
  /**
11689
12050
  * Deduces the JSON schema type and format/items from a string value pattern.
@@ -11696,6 +12057,15 @@ const CodeBlockUtils$1 = CodeBlockUtils$5;
11696
12057
  */
11697
12058
  function deduceSchemaType(value, fieldName) {
11698
12059
  const defaultSchema = { type: 'string' }; // Default fallback
12060
+
12061
+ // Handle new placeholder format {{SYSTEM: ...}} and {{ANALYZER: ...}}
12062
+ // These are valid placeholders and should not generate warnings
12063
+ if (typeof value === 'string') {
12064
+ const trimmedValue = value.trim();
12065
+ if (trimmedValue.startsWith('{{SYSTEM:') || trimmedValue.startsWith('{{ANALYZER:')) {
12066
+ return defaultSchema; // Return default string schema for system/analyzer placeholders
12067
+ }
12068
+ }
11699
12069
 
11700
12070
  if (typeof value !== 'string') {
11701
12071
  const jsType = typeof value;
@@ -11707,35 +12077,35 @@ function deduceSchemaType(value, fieldName) {
11707
12077
  }
11708
12078
 
11709
12079
  const trimmedValue = value.trim();
11710
-
12080
+
11711
12081
  if (/^\[string:.*\]$/.test(trimmedValue) || (/^\[[^:]+\]$/.test(trimmedValue) && !/^\[(number|datetime|date|<string>):.*\]$/.test(trimmedValue))) {
11712
12082
  return { type: 'string' };
11713
12083
  }
11714
-
12084
+
11715
12085
  if (/^\[number:.*\]$/.test(trimmedValue)) {
11716
12086
  return { type: 'number' };
11717
12087
  }
11718
-
12088
+
11719
12089
  if (/^\[boolean:.*\]$/.test(trimmedValue)) {
11720
12090
  return { type: 'boolean' };
11721
12091
  }
11722
-
12092
+
11723
12093
  if (/^\[date-*time:.*\]$/.test(trimmedValue)) {
11724
12094
  return { type: 'string', format: 'date-time' };
11725
12095
  }
11726
-
12096
+
11727
12097
  if (/^\[date:.*\]$/.test(trimmedValue)) {
11728
12098
  return { type: 'string', format: 'date' };
11729
12099
  }
11730
-
12100
+
11731
12101
  if (/^\[<string>:.*\]$/.test(trimmedValue) || trimmedValue.toLowerCase().includes('array of strings')) {
11732
12102
  return { type: 'array', items: { type: 'string' } };
11733
12103
  }
11734
-
12104
+
11735
12105
  if (trimmedValue.toLowerCase().includes("output 'true' or 'false'") || trimmedValue.toLowerCase().includes("determine if") && (trimmedValue.toLowerCase().includes("true") || trimmedValue.toLowerCase().includes("false"))) {
11736
12106
  return { type: 'boolean' };
11737
12107
  }
11738
-
12108
+
11739
12109
  console.warn(`Warning: Unknown metadata value pattern for field "${fieldName}". Defaulting to type 'string'. Value: "${value}"`);
11740
12110
  return defaultSchema;
11741
12111
  }
@@ -11746,14 +12116,14 @@ function deduceSchemaType(value, fieldName) {
11746
12116
  * Reads the corresponding '1.md' file, extracts the JSON block,
11747
12117
  * and deduces schema types from the string values.
11748
12118
  *
11749
- * @param {string} analyzeMessagesBasePath - The absolute or relative path to the base directory containing the analyzer message files (e.g., 'messages/analyze').
12119
+ * @param {string} analyzersBasePath - The absolute or relative path to the base directory containing the analyzer message files (e.g., 'messages/analyze').
11750
12120
  * @param {string} analyzerId - The unique ID of the analyzer (format: 'analyzer_name::content_type::instructions_type').
11751
12121
  * @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.
11752
12122
  * @throws {Error} If the 1.md file is found but does not contain exactly one JSON code block.
11753
12123
  */
11754
- async function getAnalyzerSchema$2(analyzeMessagesBasePath, analyzerId) {
11755
- if (typeof analyzeMessagesBasePath !== 'string' || analyzeMessagesBasePath.trim() === '') {
11756
- console.error('Error: analyzeMessagesBasePath is required.');
12124
+ async function getAnalyzerSchema$2(analyzersBasePath, analyzerId) {
12125
+ if (typeof analyzersBasePath !== 'string' || analyzersBasePath.trim() === '') {
12126
+ console.error('Error: analyzersBasePath is required.');
11757
12127
  return null;
11758
12128
  }
11759
12129
  if (typeof analyzerId !== 'string' || analyzerId.trim() === '') {
@@ -11768,11 +12138,14 @@ async function getAnalyzerSchema$2(analyzeMessagesBasePath, analyzerId) {
11768
12138
  }
11769
12139
  const [analyzerName, contentType, instructionsType] = parts;
11770
12140
 
11771
- const instructionsFilePath = path$2.join(analyzeMessagesBasePath, analyzerName, contentType, instructionsType, '1.md');
12141
+ const instructionsFilePath = path$3.join(analyzersBasePath, analyzerName, contentType, instructionsType, '1.md');
11772
12142
 
11773
12143
  try {
11774
- const fileContent = await fs$4.readFile(instructionsFilePath, 'utf8');
11775
- const { blocks } = CodeBlockUtils$1.extractCodeBlocks(fileContent, { silent: true });
12144
+ const fileContent = await fs$5.readFile(instructionsFilePath, 'utf8');
12145
+ // New: Parse the structured definitions block first
12146
+ const definedFields = parseMetadataDefinitions(fileContent);
12147
+
12148
+ const { blocks } = CodeBlockUtils$3.extractCodeBlocks(fileContent, { silent: true });
11776
12149
  const jsonBlocks = blocks.filter(block => block.type === 'code' && block.language === 'json');
11777
12150
 
11778
12151
  if (jsonBlocks.length !== 1) {
@@ -11780,19 +12153,24 @@ async function getAnalyzerSchema$2(analyzeMessagesBasePath, analyzerId) {
11780
12153
  }
11781
12154
 
11782
12155
  const jsonBlockContent = jsonBlocks[0].content;
12156
+ const preprocessedContent = preprocessJsonForValidation$3(jsonBlockContent);
11783
12157
  let rawJson = null;
11784
12158
  try {
11785
- rawJson = JSON.parse(jsonBlockContent);
12159
+ rawJson = JSON.parse(preprocessedContent);
11786
12160
  } catch (parseError) {
11787
12161
  console.error(`Error parsing JSON content from ${instructionsFilePath}: ${parseError.message}`);
11788
12162
  return null;
11789
12163
  }
11790
12164
 
12165
+ const startOfInstructions = fileContent.indexOf('# Analyze ');
12166
+
11791
12167
  const schema = {
11792
12168
  type: 'object',
11793
12169
  description: rawJson.description,
12170
+ version: rawJson.version,
11794
12171
  properties: {},
11795
- required: []
12172
+ required: [],
12173
+ instructions: fileContent.slice(startOfInstructions),
11796
12174
  };
11797
12175
 
11798
12176
  const metadataProperties = rawJson?.extracted_metadata;
@@ -11800,9 +12178,20 @@ async function getAnalyzerSchema$2(analyzeMessagesBasePath, analyzerId) {
11800
12178
  if (metadataProperties && typeof metadataProperties === 'object') {
11801
12179
  for (const fieldName in metadataProperties) {
11802
12180
  if (Object.hasOwnProperty.call(metadataProperties, fieldName)) {
11803
- const rawValue = metadataProperties[fieldName];
11804
- const fieldSchema = deduceSchemaType(rawValue, fieldName);
11805
- const description = rawValue.match(/^\[\w+: ([^\]]+)\]/)?.[1] || '';
12181
+ let fieldSchema;
12182
+ let description;
12183
+
12184
+ if (definedFields.has(fieldName)) {
12185
+ // Priority 1: Use the explicit definition
12186
+ const definition = definedFields.get(fieldName);
12187
+ fieldSchema = mapSimpleTypeToSchema(definition.type);
12188
+ description = definition.description;
12189
+ } else {
12190
+ // Priority 2: Fallback for system fields or backward compatibility
12191
+ const rawValue = metadataProperties[fieldName];
12192
+ fieldSchema = deduceSchemaType(rawValue, fieldName);
12193
+ description = rawValue.match(/^\[\w+: ([^\]]+)\]/)?.[1] || '';
12194
+ }
11806
12195
 
11807
12196
  schema.properties[fieldName] = {
11808
12197
  ...fieldSchema,
@@ -11816,7 +12205,6 @@ async function getAnalyzerSchema$2(analyzeMessagesBasePath, analyzerId) {
11816
12205
  }
11817
12206
 
11818
12207
  return schema;
11819
-
11820
12208
  } catch (error) {
11821
12209
  if (error.code === 'ENOENT') {
11822
12210
  console.warn(`Analyzer instructions file not found: ${instructionsFilePath}`);
@@ -11833,7 +12221,7 @@ var schemaLoader = {
11833
12221
 
11834
12222
  /*
11835
12223
  * Component: AnalyzerUtils Management
11836
- * Block-UUID: 0d1e2f3a-4b5c-6d7e-8f9a-0b1c2d3e4f5a
12224
+ * Block-UUID: 6241f381-512b-48a7-b70e-6b45683831fe
11837
12225
  * Parent-UUID: N/A
11838
12226
  * Version: 1.0.0
11839
12227
  * Description: Provides utility functions for managing (deleting) analyzer configurations.
@@ -11842,9 +12230,9 @@ var schemaLoader = {
11842
12230
  * Authors: Gemini 2.5 Flash (v1.0.0)
11843
12231
  */
11844
12232
 
11845
- const fs$3 = require$$0.promises;
11846
- const path$1 = require$$1;
11847
- const { readConfig } = discovery; // Import helper from discovery
12233
+ const fs$4 = require$$0.promises;
12234
+ const path$2 = require$$1;
12235
+ const { readConfig: readConfig$1 } = discovery; // Import helper from discovery
11848
12236
 
11849
12237
  /**
11850
12238
  * Checks if a directory is empty or only contains a config.json.
@@ -11853,7 +12241,7 @@ const { readConfig } = discovery; // Import helper from discovery
11853
12241
  */
11854
12242
  async function isDirectoryEmpty(dirPath) {
11855
12243
  try {
11856
- const files = await fs$3.readdir(dirPath);
12244
+ const files = await fs$4.readdir(dirPath);
11857
12245
  return files.length === 0 || (files.length === 1 && files[0] === 'config.json');
11858
12246
  } catch (error) {
11859
12247
  if (error.code === 'ENOENT') {
@@ -11866,13 +12254,13 @@ async function isDirectoryEmpty(dirPath) {
11866
12254
  /**
11867
12255
  * Deletes a specific analyzer configuration and intelligently cleans up empty directories.
11868
12256
  *
11869
- * @param {string} analyzeMessagesBasePath - The absolute or relative path to the base directory containing the analyzer message files (e.g., 'messages/analyze').
12257
+ * @param {string} analyzersBasePath - The absolute path to the base directory containing the analyzers (e.g., 'analyzers/analyzer-1').
11870
12258
  * @param {string} analyzerId - The unique ID of the analyzer to delete (format: 'analyzer_name::content_type::instructions_type').
11871
12259
  * @returns {Promise<{success: boolean, message: string}>} A promise that resolves with a result object indicating success or failure.
11872
12260
  */
11873
- async function deleteAnalyzer$2(analyzeMessagesBasePath, analyzerId) {
11874
- if (typeof analyzeMessagesBasePath !== 'string' || analyzeMessagesBasePath.trim() === '') {
11875
- return { success: false, message: 'analyzeMessagesBasePath is required.' };
12261
+ async function deleteAnalyzer$2(analyzersBasePath, analyzerId) {
12262
+ if (typeof analyzersBasePath !== 'string' || analyzersBasePath.trim() === '') {
12263
+ return { success: false, message: 'analyzersBasePath is required.' };
11876
12264
  }
11877
12265
  if (typeof analyzerId !== 'string' || analyzerId.trim() === '') {
11878
12266
  return { success: false, message: 'analyzerId is required.' };
@@ -11884,31 +12272,31 @@ async function deleteAnalyzer$2(analyzeMessagesBasePath, analyzerId) {
11884
12272
  }
11885
12273
  const [analyzerName, contentType, instructionsType] = parts;
11886
12274
 
11887
- const analyzerDir = path$1.join(analyzeMessagesBasePath, analyzerName);
11888
- const contentDir = path$1.join(analyzerDir, contentType);
11889
- const instructionsDir = path$1.join(contentDir, instructionsType);
11890
- const instructionsFilePath = path$1.join(instructionsDir, '1.md');
12275
+ const analyzerDir = path$2.join(analyzersBasePath, analyzerName);
12276
+ const contentDir = path$2.join(analyzerDir, contentType);
12277
+ const instructionsDir = path$2.join(contentDir, instructionsType);
12278
+ const instructionsFilePath = path$2.join(instructionsDir, '1.md');
11891
12279
 
11892
12280
  try {
11893
12281
  // 1. Check for protection at all levels
11894
- const analyzerConfig = await readConfig(analyzerDir);
12282
+ const analyzerConfig = await readConfig$1(analyzerDir);
11895
12283
  if (analyzerConfig?.protected) {
11896
12284
  return { success: false, message: `Analyzer '${analyzerName}' is protected and cannot be deleted.` };
11897
12285
  }
11898
12286
 
11899
- const contentConfig = await readConfig(contentDir);
12287
+ const contentConfig = await readConfig$1(contentDir);
11900
12288
  if (contentConfig?.protected) {
11901
12289
  return { success: false, message: `Content type '${contentType}' for analyzer '${analyzerName}' is protected and cannot be deleted.` };
11902
12290
  }
11903
12291
 
11904
- const instructionsConfig = await readConfig(instructionsDir);
12292
+ const instructionsConfig = await readConfig$1(instructionsDir);
11905
12293
  if (instructionsConfig?.protected) {
11906
12294
  return { success: false, message: `Instructions type '${instructionsType}' for content type '${contentType}' is protected and cannot be deleted.` };
11907
12295
  }
11908
12296
 
11909
12297
  // 2. Delete the 1.md file
11910
12298
  try {
11911
- await fs$3.unlink(instructionsFilePath);
12299
+ await fs$4.unlink(instructionsFilePath);
11912
12300
  } catch (error) {
11913
12301
  if (error.code === 'ENOENT') {
11914
12302
  return { success: false, message: `Analyzer instructions file not found: ${instructionsFilePath}. It may have already been deleted.` };
@@ -11922,7 +12310,7 @@ async function deleteAnalyzer$2(analyzeMessagesBasePath, analyzerId) {
11922
12310
  // Check and delete instructions directory
11923
12311
  if (await isDirectoryEmpty(instructionsDir)) {
11924
12312
  try {
11925
- await fs$3.rmdir(instructionsDir);
12313
+ await fs$4.rmdir(instructionsDir);
11926
12314
  deletedDirs.push(instructionsDir);
11927
12315
  } catch (error) {
11928
12316
  console.warn(`Warning: Could not remove empty instructions directory ${instructionsDir}: ${error.message}`);
@@ -11932,7 +12320,7 @@ async function deleteAnalyzer$2(analyzeMessagesBasePath, analyzerId) {
11932
12320
  // Check and delete content directory
11933
12321
  if (await isDirectoryEmpty(contentDir)) {
11934
12322
  try {
11935
- await fs$3.rmdir(contentDir);
12323
+ await fs$4.rmdir(contentDir);
11936
12324
  deletedDirs.push(contentDir);
11937
12325
  } catch (error) {
11938
12326
  console.warn(`Warning: Could not remove empty content directory ${contentDir}: ${error.message}`);
@@ -11942,7 +12330,7 @@ async function deleteAnalyzer$2(analyzeMessagesBasePath, analyzerId) {
11942
12330
  // Check and delete analyzer directory
11943
12331
  if (await isDirectoryEmpty(analyzerDir)) {
11944
12332
  try {
11945
- await fs$3.rmdir(analyzerDir);
12333
+ await fs$4.rmdir(analyzerDir);
11946
12334
  deletedDirs.push(analyzerDir);
11947
12335
  } catch (error) {
11948
12336
  console.warn(`Warning: Could not remove empty analyzer directory ${analyzerDir}: ${error.message}`);
@@ -11958,7 +12346,9 @@ async function deleteAnalyzer$2(analyzeMessagesBasePath, analyzerId) {
11958
12346
  }
11959
12347
 
11960
12348
  var management = {
11961
- deleteAnalyzer: deleteAnalyzer$2};
12349
+ deleteAnalyzer: deleteAnalyzer$2,
12350
+ isDirectoryEmpty,
12351
+ };
11962
12352
 
11963
12353
  /*
11964
12354
  * Component: AnalyzerUtils Default Prompt Loader
@@ -11971,25 +12361,25 @@ var management = {
11971
12361
  * Authors: Gemini 2.5 Flash (v1.0.0)
11972
12362
  */
11973
12363
 
11974
- const fs$2 = require$$0.promises;
11975
- const path = require$$1;
12364
+ const fs$3 = require$$0.promises;
12365
+ const path$1 = require$$1;
11976
12366
 
11977
12367
  /**
11978
12368
  * Retrieves the raw Markdown content of the shared system message ('_shared/system/1.md').
11979
12369
  *
11980
- * @param {string} analyzeMessagesBasePath - The absolute path to the base directory containing analyzer message files (e.g., 'messages/analyze').
12370
+ * @param {string} analyzersBasePath - The absolute path to the base directory containing the analyzers (e.g., 'analyzers/analyzer-1').
11981
12371
  * @returns {Promise<string|null>} A promise that resolves with the full Markdown content, or null if not found/invalid.
11982
12372
  */
11983
- async function getSystemMessageContent$2(analyzeMessagesBasePath) {
11984
- if (typeof analyzeMessagesBasePath !== 'string' || analyzeMessagesBasePath.trim() === '') {
11985
- console.error('Error: analyzeMessagesBasePath is required for getSystemMessageContent.');
12373
+ async function getSystemMessageContent$2(analyzersBasePath) {
12374
+ if (typeof analyzersBasePath !== 'string' || analyzersBasePath.trim() === '') {
12375
+ console.error('Error: analyzersBasePath is required for getSystemMessageContent.');
11986
12376
  return null;
11987
12377
  }
11988
12378
 
11989
- const systemMessageFilePath = path.join(analyzeMessagesBasePath, '_shared', 'system', '1.md');
12379
+ const systemMessageFilePath = path$1.join(analyzersBasePath, '_shared', 'system', '1.md');
11990
12380
 
11991
12381
  try {
11992
- const fileContent = await fs$2.readFile(systemMessageFilePath, 'utf8');
12382
+ const fileContent = await fs$3.readFile(systemMessageFilePath, 'utf8');
11993
12383
  const parts = fileContent.split('\n\n\n');
11994
12384
  parts.shift();
11995
12385
  return parts.join('\n\n\n');
@@ -12007,19 +12397,19 @@ async function getSystemMessageContent$2(analyzeMessagesBasePath) {
12007
12397
  /**
12008
12398
  * Retrieves the raw Markdown content of the shared start message ('_shared/start/1.md').
12009
12399
  *
12010
- * @param {string} analyzeMessagesBasePath - The absolute path to the base directory containing analyzer message files (e.g., 'messages/analyze').
12400
+ * @param {string} analyzersBasePath - The absolute path to the base directory containing the analyzers (e.g., 'analyzers/analyzer-1').
12011
12401
  * @returns {Promise<string|null>} A promise that resolves with the full Markdown content, or null if not found/invalid.
12012
12402
  */
12013
- async function getStartMessageContent$2(analyzeMessagesBasePath) {
12014
- if (typeof analyzeMessagesBasePath !== 'string' || analyzeMessagesBasePath.trim() === '') {
12015
- console.error('Error: analyzeMessagesBasePath is required for getStartMessageContent.');
12403
+ async function getStartMessageContent$2(analyzersBasePath) {
12404
+ if (typeof analyzersBasePath !== 'string' || analyzersBasePath.trim() === '') {
12405
+ console.error('Error: analyzersBasePath is required for getStartMessageContent.');
12016
12406
  return null;
12017
12407
  }
12018
12408
 
12019
- const startMessageFilePath = path.join(analyzeMessagesBasePath, '_shared', 'start', '1.md');
12409
+ const startMessageFilePath = path$1.join(analyzersBasePath, '_shared', 'start', '1.md');
12020
12410
 
12021
12411
  try {
12022
- const fileContent = await fs$2.readFile(startMessageFilePath, 'utf8');
12412
+ const fileContent = await fs$3.readFile(startMessageFilePath, 'utf8');
12023
12413
  const parts = fileContent.split('\n\n\n');
12024
12414
  parts.shift();
12025
12415
  return parts.join('\n\n\n');
@@ -12040,59 +12430,400 @@ var defaultPromptLoader = {
12040
12430
  };
12041
12431
 
12042
12432
  /*
12043
- * Component: AnalyzerUtils Index
12044
- * Block-UUID: b403b6a1-230b-4247-8cd6-2a3d068f4bbf
12045
- * Parent-UUID: N/A
12046
- * Version: 1.2.0
12047
- * Description: Aggregates and exports all utility functions from the AnalyzerUtils module.
12048
- * Language: JavaScript
12049
- * Created-at: 2025-08-28T15:56:40.319Z
12050
- * Authors: Gemini 2.5 Flash (v1.0.0), Gemini 2.5 Flash (v1.1.0), Gemini 2.5 Flash (v1.2.0)
12051
- */
12052
-
12053
- const { buildChatIdToPathMap: buildChatIdToPathMap$1 } = contextMapper;
12054
- const { processLLMAnalysisResponse: processLLMAnalysisResponse$1 } = responseProcessor;
12055
- const { validateLLMAnalysisData: validateLLMAnalysisData$1 } = dataValidator;
12056
- const { getAnalyzers: getAnalyzers$1 } = discovery;
12057
- const { saveConfiguration } = saver;
12058
- const { getAnalyzerSchema: getAnalyzerSchema$1 } = schemaLoader;
12059
- const { deleteAnalyzer: deleteAnalyzer$1 } = management;
12060
- const { getAnalyzerInstructionsContent: getAnalyzerInstructionsContent$1 } = instructionLoader;
12061
- const { getSystemMessageContent: getSystemMessageContent$1, getStartMessageContent: getStartMessageContent$1 } = defaultPromptLoader; // NEW: Import default prompt loaders
12062
-
12063
- var AnalyzerUtils$1 = {
12064
- buildChatIdToPathMap: buildChatIdToPathMap$1,
12065
- processLLMAnalysisResponse: processLLMAnalysisResponse$1,
12066
- validateLLMAnalysisData: validateLLMAnalysisData$1,
12067
- getAnalyzers: getAnalyzers$1,
12068
- getAnalyzerSchema: getAnalyzerSchema$1,
12069
- deleteAnalyzer: deleteAnalyzer$1,
12070
- getAnalyzerInstructionsContent: getAnalyzerInstructionsContent$1,
12071
- saveConfiguration,
12072
- getSystemMessageContent: getSystemMessageContent$1,
12073
- getStartMessageContent: getStartMessageContent$1
12074
- };
12075
-
12076
- /**
12077
- * Component: LLMUtils
12078
- * Block-UUID: a3106054-42f1-474f-96f3-182d66eb19a0
12433
+ * Component: AnalyzerUtils Updater
12434
+ * Block-UUID: 8a7b6c5d-4e3f-2a1b-9c8d-7e6f5a4b3c2d
12079
12435
  * Parent-UUID: N/A
12080
12436
  * Version: 1.0.0
12081
- * Description: Provides utility functions related to Large Language Model interactions, such as token estimation.
12437
+ * Description: Provides utility functions for updating analyzer configurations, including renaming and metadata updates.
12082
12438
  * Language: JavaScript
12083
- * Created-at: 2025-04-22T17:16:00.590Z
12084
- * Authors: Gemini 2.5 Pro (v1.0.0)
12439
+ * Created-at: 2025-12-25T19:55:00.000Z
12440
+ * Authors: GLM-4.6 (v1.0.0)
12085
12441
  */
12086
12442
 
12443
+ const fs$2 = require$$0.promises;
12444
+ const path = require$$1;
12445
+ const CodeBlockUtils$2 = CodeBlockUtils$7;
12446
+ const { preprocessJsonForValidation: preprocessJsonForValidation$2 } = jsonParser;
12447
+ const { isValidDirName: isValidDirName$1 } = discovery;
12448
+ const { readConfig } = discovery;
12449
+
12087
12450
  /**
12088
- * Estimates the number of tokens in a given text string.
12089
- * This is a basic estimation using the common heuristic of ~4 characters per token.
12090
- * Actual token count can vary significantly based on the specific tokenizer used by an LLM.
12451
+ * Updates an analyzer configuration, including renaming the analyzer if needed.
12091
12452
  *
12092
- * @param {string} text - The text to estimate tokens for.
12093
- * @returns {number} An estimated token count. Returns 0 if input is not a non-empty string.
12453
+ * @param {string} analyzersBasePath - The absolute path to the base directory containing the analyzers.
12454
+ * @param {string} analyzerId - The current unique ID of the analyzer (format: 'analyzer_name::content_type::instructions_type').
12455
+ * @param {object} updates - The updates to apply to the analyzer.
12456
+ * @param {string} [updates.name] - The new analyzer name (for renaming the directory).
12457
+ * @param {string} [updates.label] - The new label for the analyzer.
12458
+ * @param {string} [updates.description] - The new description for the analyzer.
12459
+ * @param {string} [updates.version] - The new version for the analyzer.
12460
+ * @param {Array<string>} [updates.tags] - The new tags for the analyzer.
12461
+ * @returns {Promise<{success: boolean, message: string, newAnalyzerId?: string}>} A promise that resolves with a result object.
12094
12462
  */
12095
- function estimateTokens$1(text) {
12463
+ async function updateAnalyzer$1(analyzersBasePath, analyzerId, updates) {
12464
+ // 1. Validate inputs
12465
+ if (typeof analyzersBasePath !== 'string' || analyzersBasePath.trim() === '') {
12466
+ return { success: false, message: 'analyzersBasePath is required.' };
12467
+ }
12468
+ if (typeof analyzerId !== 'string' || analyzerId.trim() === '') {
12469
+ return { success: false, message: 'analyzerId is required.' };
12470
+ }
12471
+ if (!updates || typeof updates !== 'object') {
12472
+ return { success: false, message: 'updates object is required.' };
12473
+ }
12474
+
12475
+ // 2.0 Parse current analyzerId
12476
+ const parts = analyzerId.split('::');
12477
+ if (parts.length !== 3) {
12478
+ return { success: false, message: `Invalid analyzerId format. Expected 'analyzer_name::content_type::instructions_type', but got '${analyzerId}'.` };
12479
+ }
12480
+ const [analyzerName, contentType, instructionsType] = parts;
12481
+
12482
+ // 2.1 Unset name if it has been provided but is the same as the analyzerName
12483
+ if (updates.name && analyzerName === updates.name) {
12484
+ delete updates.name;
12485
+ }
12486
+
12487
+ // 3. Validate new name if provided
12488
+ if (updates.name && !isValidDirName$1(updates.name)) {
12489
+ return { success: false, message: `Invalid analyzer name '${updates.name}'. Names must be alphanumeric, dash, or underscore, cannot start with underscore, and cannot contain dots.` };
12490
+ }
12491
+
12492
+ // 4. Check if analyzer is protected
12493
+ const analyzerDir = path.join(analyzersBasePath, analyzerName);
12494
+ const analyzerConfig = await readConfig(analyzerDir);
12495
+ if (analyzerConfig?.protected) {
12496
+ return { success: false, message: `Analyzer '${analyzerName}' is protected and cannot be updated.` };
12497
+ }
12498
+
12499
+ // 5. Get current instructions content
12500
+ const instructionsFilePath = path.join(analyzerDir, contentType, instructionsType, '1.md');
12501
+ let instructionsContent;
12502
+ try {
12503
+ instructionsContent = await fs$2.readFile(instructionsFilePath, 'utf8');
12504
+ } catch (error) {
12505
+ return { success: false, message: `Failed to read analyzer instructions: ${error.message}` };
12506
+ }
12507
+
12508
+ // 6. Extract and update JSON block
12509
+ const { blocks } = CodeBlockUtils$2.extractCodeBlocks(instructionsContent, { silent: true });
12510
+ const jsonBlock = blocks.find(block => block.language === 'json');
12511
+
12512
+ if (!jsonBlock) {
12513
+ return { success: false, message: 'No JSON block found in analyzer instructions.' };
12514
+ }
12515
+
12516
+ let jsonData;
12517
+ try {
12518
+ const preprocessedContent = preprocessJsonForValidation$2(jsonBlock.content);
12519
+ jsonData = JSON.parse(preprocessedContent);
12520
+ } catch (error) {
12521
+ return { success: false, message: `Failed to parse JSON block: ${error.message}` };
12522
+ }
12523
+
12524
+ // 7. Update JSON data with provided values
12525
+ if (updates.label !== undefined) jsonData.label = updates.label;
12526
+ if (updates.description !== undefined) jsonData.description = updates.description;
12527
+ if (updates.version !== undefined) jsonData.version = updates.version;
12528
+ if (updates.tags !== undefined) jsonData.tags = updates.tags;
12529
+
12530
+ // 8. Rebuild the instructions content with updated JSON
12531
+ const updatedJsonContent = JSON.stringify(jsonData, null, 2);
12532
+ const updatedInstructionsContent = instructionsContent.replace(
12533
+ jsonBlock.content,
12534
+ updatedJsonContent
12535
+ );
12536
+
12537
+ // 9. Handle directory renaming if needed
12538
+ let newAnalyzerId = analyzerId;
12539
+ if (updates.name && updates.name !== analyzerName) {
12540
+ // Create new directory structure
12541
+ const newAnalyzerDir = path.join(analyzersBasePath, updates.name);
12542
+ const newContentDir = path.join(newAnalyzerDir, contentType);
12543
+ const newInstructionsDir = path.join(newContentDir, instructionsType);
12544
+ const newInstructionsFilePath = path.join(newInstructionsDir, '1.md');
12545
+
12546
+ try {
12547
+ // Create new directories
12548
+ await fs$2.mkdir(newInstructionsDir, { recursive: true });
12549
+
12550
+ // Copy config.json if it exists
12551
+ const configPath = path.join(analyzerDir, 'config.json');
12552
+ const newConfigPath = path.join(newAnalyzerDir, 'config.json');
12553
+ try {
12554
+ const configContent = await fs$2.readFile(configPath, 'utf8');
12555
+ await fs$2.writeFile(newConfigPath, configContent, 'utf8');
12556
+ } catch (error) {
12557
+ // Config file might not exist, that's okay
12558
+ }
12559
+
12560
+ // Copy content config.json if it exists
12561
+ const contentConfigPath = path.join(analyzerDir, contentType, 'config.json');
12562
+ const newContentConfigPath = path.join(newContentDir, 'config.json');
12563
+ try {
12564
+ const contentConfigContent = await fs$2.readFile(contentConfigPath, 'utf8');
12565
+ await fs$2.writeFile(newContentConfigPath, contentConfigContent, 'utf8');
12566
+ } catch (error) {
12567
+ // Config file might not exist, that's okay
12568
+ }
12569
+
12570
+ // Copy instructions config.json if it exists
12571
+ const instructionsConfigPath = path.join(analyzerDir, contentType, instructionsType, 'config.json');
12572
+ const newInstructionsConfigPath = path.join(newInstructionsDir, 'config.json');
12573
+ try {
12574
+ const instructionsConfigContent = await fs$2.readFile(instructionsConfigPath, 'utf8');
12575
+ await fs$2.writeFile(newInstructionsConfigPath, instructionsConfigContent, 'utf8');
12576
+ } catch (error) {
12577
+ // Config file might not exist, that's okay
12578
+ }
12579
+
12580
+ // Write updated instructions to new location
12581
+ const finalContent = `; role: assistant\n\n\n${updatedInstructionsContent}`;
12582
+ await fs$2.writeFile(newInstructionsFilePath, finalContent, 'utf8');
12583
+
12584
+ // Update the analyzer ID
12585
+ newAnalyzerId = `${updates.name}::${contentType}::${instructionsType}`;
12586
+
12587
+ // Delete old analyzer directory (will be done after successful save)
12588
+ } catch (error) {
12589
+ return { success: false, message: `Failed to create new analyzer directory: ${error.message}` };
12590
+ }
12591
+ } else {
12592
+ // Just update the existing file
12593
+ const finalContent = `; role: assistant\n\n\n${updatedInstructionsContent}`;
12594
+ await fs$2.writeFile(instructionsFilePath, finalContent, 'utf8');
12595
+ }
12596
+
12597
+ // 10. Clean up old directory if renamed
12598
+ if (updates.name && updates.name !== analyzerName) {
12599
+ try {
12600
+ // Use the deleteAnalyzer function to clean up the old directory
12601
+ const { deleteAnalyzer } = management;
12602
+ await deleteAnalyzer(analyzersBasePath, analyzerId);
12603
+ } catch (error) {
12604
+ console.warn(`Warning: Could not clean up old analyzer directory: ${error.message}`);
12605
+ }
12606
+ }
12607
+
12608
+ return {
12609
+ success: true,
12610
+ message: `Analyzer '${analyzerId}' updated successfully.`,
12611
+ newAnalyzerId
12612
+ };
12613
+ }
12614
+
12615
+ var updater = {
12616
+ updateAnalyzer: updateAnalyzer$1
12617
+ };
12618
+
12619
+ /*
12620
+ * Component: AnalyzerUtils Cloner
12621
+ * Block-UUID: 56b8c9f4-97ce-406c-a598-22a838ebefda
12622
+ * Parent-UUID: N/A
12623
+ * Version: 1.0.0
12624
+ * Description: Provides utility functions for cloning analyzer configurations.
12625
+ * Language: JavaScript
12626
+ * Created-at: 2025-12-26T16:59:39.782Z
12627
+ * Authors: GLM-4.6 (v1.0.0)
12628
+ */
12629
+
12630
+ require$$0.promises;
12631
+ const CodeBlockUtils$1 = CodeBlockUtils$7;
12632
+ const { preprocessJsonForValidation: preprocessJsonForValidation$1 } = jsonParser;
12633
+ const { isValidDirName } = discovery;
12634
+ const { saveConfiguration: saveConfiguration$1 } = saver;
12635
+ const { getAnalyzers: getAnalyzers$2 } = discovery;
12636
+
12637
+ /**
12638
+ * Clones an existing analyzer with a new name.
12639
+ *
12640
+ * @param {string} analyzersBasePath - The absolute path to the base directory containing the analyzers.
12641
+ * @param {string} originalAnalyzerId - The ID of the analyzer to clone (format: 'analyzer_name::content_type::instructions_type').
12642
+ * @param {string} newAnalyzerName - The new name for the cloned analyzer.
12643
+ * @param {object} [options={}] - Optional configuration options.
12644
+ * @param {string} [options.label] - The new label for the cloned analyzer (optional, defaults to newAnalyzerName).
12645
+ * @param {string} [options.description] - The new description for the cloned analyzer (optional, keeps original if not provided).
12646
+ * @returns {Promise<{success: boolean, message: string, newAnalyzerId?: string}>} A promise that resolves with a result object.
12647
+ */
12648
+ async function cloneAnalyzer$1(analyzersBasePath, originalAnalyzerId, newAnalyzerName, options = {}) {
12649
+ // 1. Validate inputs
12650
+ if (typeof analyzersBasePath !== 'string' || analyzersBasePath.trim() === '') {
12651
+ return { success: false, message: 'analyzersBasePath is required.' };
12652
+ }
12653
+ if (typeof originalAnalyzerId !== 'string' || originalAnalyzerId.trim() === '') {
12654
+ return { success: false, message: 'originalAnalyzerId is required.' };
12655
+ }
12656
+ if (typeof newAnalyzerName !== 'string' || newAnalyzerName.trim() === '') {
12657
+ return { success: false, message: 'newAnalyzerName is required.' };
12658
+ }
12659
+
12660
+ // 2. Parse original analyzerId
12661
+ const parts = originalAnalyzerId.split('::');
12662
+ if (parts.length !== 3) {
12663
+ return { success: false, message: `Invalid originalAnalyzerId format. Expected 'analyzer_name::content_type::instructions_type', but got '${originalAnalyzerId}'.` };
12664
+ }
12665
+ const [originalAnalyzerName, contentType, instructionsType] = parts;
12666
+
12667
+ // 3. Validate new analyzer name
12668
+ if (!isValidDirName(newAnalyzerName)) {
12669
+ return { success: false, message: `Invalid analyzer name '${newAnalyzerName}'. Names must be alphanumeric, dash, or underscore, cannot start with underscore, and cannot contain dots.` };
12670
+ }
12671
+
12672
+ // 4. Check if new analyzer name already exists
12673
+ try {
12674
+ const existingAnalyzers = await getAnalyzers$2(analyzersBasePath);
12675
+ const nameExists = existingAnalyzers.some(analyzer => analyzer.name === newAnalyzerName);
12676
+ if (nameExists) {
12677
+ return { success: false, message: `An analyzer with name '${newAnalyzerName}' already exists. Please choose a different name.` };
12678
+ }
12679
+ } catch (error) {
12680
+ return { success: false, message: `Failed to check for existing analyzers: ${error.message}` };
12681
+ }
12682
+
12683
+ // 5. Get original analyzer's instructions content
12684
+ const { getAnalyzerInstructionsContent } = instructionLoader;
12685
+ let instructionsContent;
12686
+ try {
12687
+ instructionsContent = await getAnalyzerInstructionsContent(analyzersBasePath, originalAnalyzerId);
12688
+ if (!instructionsContent) {
12689
+ return { success: false, message: `Could not find original analyzer '${originalAnalyzerId}'.` };
12690
+ }
12691
+ } catch (error) {
12692
+ return { success: false, message: `Failed to read original analyzer instructions: ${error.message}` };
12693
+ }
12694
+
12695
+ // 6. Extract and update JSON block
12696
+ const { blocks } = CodeBlockUtils$1.extractCodeBlocks(instructionsContent, { silent: true });
12697
+ const jsonBlocks = blocks.filter((block, index) => {
12698
+ if (block.language === 'json') {
12699
+ block.index = index;
12700
+ return block;
12701
+ }
12702
+ });
12703
+ const jsonBlock = jsonBlocks[jsonBlocks.length - 1];
12704
+
12705
+ if (!jsonBlock) {
12706
+ return { success: false, message: 'No JSON block found in original analyzer instructions.' };
12707
+ }
12708
+
12709
+ let jsonData;
12710
+ try {
12711
+ const preprocessedContent = preprocessJsonForValidation$1(jsonBlock.content);
12712
+ jsonData = JSON.parse(preprocessedContent);
12713
+ } catch (error) {
12714
+ return { success: false, message: `Failed to parse JSON block: ${error.message}` };
12715
+ }
12716
+
12717
+ // 7. Update JSON data with new values
12718
+ if (options.label !== undefined) {
12719
+ jsonData.label = options.label;
12720
+ }
12721
+ if (options.description !== undefined) {
12722
+ jsonData.description = options.description;
12723
+ }
12724
+ // Reset version for cloned analyzer
12725
+ jsonData.version = '1.0.0';
12726
+
12727
+ // 8. Rebuild the instructions content with updated JSON
12728
+ let updatedInstructionsContent = CodeBlockUtils$1.updateCodeBlockByIndex(
12729
+ instructionsContent,
12730
+ jsonBlock.index,
12731
+ JSON.stringify(jsonData, null, 2),
12732
+ 'json'
12733
+ );
12734
+
12735
+ // 9. Create the new analyzer ID
12736
+ const newAnalyzerId = `${newAnalyzerName}::${contentType}::${instructionsType}`;
12737
+
12738
+ // 10. Update the analyzer id in the instructions
12739
+ updatedInstructionsContent = updatedInstructionsContent.replaceAll(originalAnalyzerId, newAnalyzerId);
12740
+
12741
+ // 11. Save the new analyzer
12742
+ try {
12743
+ const saveResult = await saveConfiguration$1(
12744
+ analyzersBasePath,
12745
+ newAnalyzerId,
12746
+ updatedInstructionsContent
12747
+ );
12748
+
12749
+ if (saveResult.success) {
12750
+ return {
12751
+ success: true,
12752
+ message: `Analyzer '${originalAnalyzerName}' cloned successfully as '${newAnalyzerName}'.`,
12753
+ newAnalyzerId
12754
+ };
12755
+ } else {
12756
+ return { success: false, error: saveResult.message };
12757
+ }
12758
+ } catch (error) {
12759
+ return { success: false, message: `Failed to save cloned analyzer: ${error.message}` };
12760
+ }
12761
+ }
12762
+
12763
+ var cloner = {
12764
+ cloneAnalyzer: cloneAnalyzer$1
12765
+ };
12766
+
12767
+ /*
12768
+ * Component: AnalyzerUtils Index
12769
+ * Block-UUID: 780e17b0-0c1e-4d77-bf6e-302951b341bf
12770
+ * Parent-UUID: b403b6a1-230b-4247-8cd6-2a3d068f4bbf
12771
+ * Version: 1.5.0
12772
+ * Description: Aggregates and exports all utility functions from the AnalyzerUtils module. Added cloneAnalyzer method.
12773
+ * Language: JavaScript
12774
+ * Created-at: 2025-08-28T15:56:40.319Z
12775
+ * 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)
12776
+ */
12777
+
12778
+ const { buildChatIdToPathMap: buildChatIdToPathMap$1 } = contextMapper;
12779
+ const { processLLMAnalysisResponse: processLLMAnalysisResponse$1 } = responseProcessor;
12780
+ const { validateLLMAnalysisData: validateLLMAnalysisData$1 } = dataValidator;
12781
+ const { getAnalyzers: getAnalyzers$1 } = discovery;
12782
+ const { saveConfiguration } = saver;
12783
+ const { getAnalyzerSchema: getAnalyzerSchema$1 } = schemaLoader;
12784
+ const { deleteAnalyzer: deleteAnalyzer$1 } = management;
12785
+ const { getAnalyzerInstructionsContent: getAnalyzerInstructionsContent$1 } = instructionLoader;
12786
+ const { getSystemMessageContent: getSystemMessageContent$1, getStartMessageContent: getStartMessageContent$1 } = defaultPromptLoader;
12787
+ const { preprocessJsonForValidation } = jsonParser;
12788
+ const { updateAnalyzer } = updater;
12789
+ const { cloneAnalyzer } = cloner;
12790
+
12791
+ var AnalyzerUtils$1 = {
12792
+ buildChatIdToPathMap: buildChatIdToPathMap$1,
12793
+ processLLMAnalysisResponse: processLLMAnalysisResponse$1,
12794
+ validateLLMAnalysisData: validateLLMAnalysisData$1,
12795
+ getAnalyzers: getAnalyzers$1,
12796
+ getAnalyzerSchema: getAnalyzerSchema$1,
12797
+ deleteAnalyzer: deleteAnalyzer$1,
12798
+ getAnalyzerInstructionsContent: getAnalyzerInstructionsContent$1,
12799
+ saveConfiguration,
12800
+ getSystemMessageContent: getSystemMessageContent$1,
12801
+ getStartMessageContent: getStartMessageContent$1,
12802
+ preprocessJsonForValidation,
12803
+ updateAnalyzer,
12804
+ cloneAnalyzer
12805
+ };
12806
+
12807
+ /**
12808
+ * Component: LLMUtils
12809
+ * Block-UUID: a3106054-42f1-474f-96f3-182d66eb19a0
12810
+ * Parent-UUID: N/A
12811
+ * Version: 1.0.0
12812
+ * Description: Provides utility functions related to Large Language Model interactions, such as token estimation.
12813
+ * Language: JavaScript
12814
+ * Created-at: 2025-04-22T17:16:00.590Z
12815
+ * Authors: Gemini 2.5 Pro (v1.0.0)
12816
+ */
12817
+
12818
+ /**
12819
+ * Estimates the number of tokens in a given text string.
12820
+ * This is a basic estimation using the common heuristic of ~4 characters per token.
12821
+ * Actual token count can vary significantly based on the specific tokenizer used by an LLM.
12822
+ *
12823
+ * @param {string} text - The text to estimate tokens for.
12824
+ * @returns {number} An estimated token count. Returns 0 if input is not a non-empty string.
12825
+ */
12826
+ function estimateTokens$1(text) {
12096
12827
  if (typeof text !== 'string' || text.length === 0) {
12097
12828
  return 0;
12098
12829
  }
@@ -12274,13 +13005,13 @@ var FormatterUtils$1 = {
12274
13005
 
12275
13006
  /**
12276
13007
  * Component: DomUtils Helper Functions
12277
- * Block-UUID: e85d4e1d-f45e-450b-a0f2-941af362c2be
12278
- * Parent-UUID: fe462d17-e1c8-45d6-9fb0-f53322c9cda9
12279
- * Version: 1.1.0
13008
+ * Block-UUID: 9a8e8e8e-8b15-4346-bbfa-740e6a5d2d79
13009
+ * Parent-UUID: 8a528728-ce54-4445-946d-1743fb4b16be
13010
+ * Version: 1.3.0
12280
13011
  * Description: Provides helper functions for creating and manipulating DOM elements.
12281
13012
  * Language: JavaScript
12282
- * Created-at: 2025-09-11T19:03:50.026Z
12283
- * Authors: Gemini 2.5 Pro (v1.0.0), Gemini 2.5 Flash (v1.1.0)
13013
+ * Created-at: 2025-12-14T04:18:40.180Z
13014
+ * 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)
12284
13015
  */
12285
13016
 
12286
13017
  function createElement(type, params) {
@@ -12292,6 +13023,7 @@ function createElement(type, params) {
12292
13023
  let {
12293
13024
  id,
12294
13025
  ariaLabel,
13026
+ xmlns,
12295
13027
  role,
12296
13028
  html,
12297
13029
  text,
@@ -12314,11 +13046,15 @@ function createElement(type, params) {
12314
13046
  "data-message-role": dmr,
12315
13047
  attrs = {},
12316
13048
  dataset,
13049
+ rows
12317
13050
  } = params;
12318
13051
 
12319
13052
  // Set standard attributes
12320
13053
  if (id != null) elem.id = id;
12321
13054
  if (ariaLabel) elem.setAttribute("aria-label", ariaLabel);
13055
+ if (xmlns) elem.setAttribute("xmlns", xmlns);
13056
+ if (params.viewBox) elem.setAttribute("viewBox", params.viewBox);
13057
+ if (params.fill) elem.setAttribute("fill", params.fill);
12322
13058
  if (role) elem.setAttribute("role", role);
12323
13059
  if (placeholder) elem.setAttribute("placeholder", placeholder);
12324
13060
  if (cls || className) elem.setAttribute("class", cls || className);
@@ -12326,6 +13062,9 @@ function createElement(type, params) {
12326
13062
  if (title != null) elem.setAttribute("title", title);
12327
13063
  if (inputType) elem.setAttribute("type", inputType);
12328
13064
  if (name != null) elem.setAttribute("name", name);
13065
+ if (params.width) elem.setAttribute("width", params.width);
13066
+ if (params.height) elem.setAttribute("height", params.height);
13067
+ if (params.stroke) elem.setAttribute("stroke", params.stroke);
12329
13068
  if (value != null) elem.value = value;
12330
13069
  if (checked != null && checked) elem.checked = true; // Use property for checked
12331
13070
  if (selected != null && selected) elem.selected = true; // Use property for selected
@@ -12333,13 +13072,21 @@ function createElement(type, params) {
12333
13072
  if (dmi != null) elem.setAttribute("data-message-id", dmi);
12334
13073
  if (dmr != null) elem.setAttribute("data-message-role", dmr);
12335
13074
  if (_for != null ) elem.setAttribute("for", _for);
13075
+ if (params.strokeWidth) elem.setAttribute("stroke-width", params.strokeWidth);
13076
+ if (params.strokeLinecap) elem.setAttribute("stroke-linecap", params.strokeLinecap);
13077
+ if (params.strokeLinejoin) elem.setAttribute("stroke-linejoin", params.strokeLinejoin);
12336
13078
  if (colSpan != null) elem.colSpan = colSpan;
13079
+ if (rows) elem.setAttribute("rows", params.rows);
12337
13080
 
12338
13081
  // Set content
12339
13082
  if (html != null) {
12340
13083
  if (typeof html === "object" && html instanceof Node) { // Ensure html is a Node
12341
13084
  elem.appendChild(html);
12342
13085
  } else if (typeof html === "string") {
13086
+ // For SVG elements, use innerHTML to support path elements
13087
+ if (type === 'svg' && params.innerHTML) {
13088
+ elem.innerHTML = params.innerHTML;
13089
+ }
12343
13090
  elem.innerHTML = html;
12344
13091
  }
12345
13092
  } else if (text != null) {
@@ -12365,12 +13112,12 @@ function createElement(type, params) {
12365
13112
  });
12366
13113
  }
12367
13114
 
12368
- // NEW: Set additional attributes from attrs object
13115
+ // Set additional attributes from attrs object
12369
13116
  if (attrs && typeof attrs === 'object') {
12370
13117
  for (const [key, value] of Object.entries(attrs)) {
12371
13118
  if (value !== undefined && value !== null) {
12372
13119
  // Skip if already set by standard properties or handled directly
12373
- if (!['id', 'class', 'role', 'aria-label', 'title', 'style', 'checked', 'selected', 'disabled', 'value', 'name', 'type', 'for'].includes(key) && !key.startsWith('data-')) {
13120
+ if (!['id', 'class', 'role', 'aria-label', 'title', 'style', 'selected', 'disabled', 'value', 'name', 'type', 'for'].includes(key) && !key.startsWith('data-')) {
12374
13121
  elem.setAttribute(key, value);
12375
13122
  }
12376
13123
  }
@@ -12483,14 +13230,17 @@ const h$1 = {
12483
13230
  },
12484
13231
  createLink: (params) => {
12485
13232
  const link = createElement("a", params);
12486
- // href and target are handled by createElement
12487
- if (params?.href) link.href = params.href; // Ensure href is set if provided
13233
+ if (params?.href) link.href = params.href;
13234
+ if (params?.target) link.target = params.target;
12488
13235
  return link;
12489
13236
  },
12490
13237
  createNav: (params) => {
12491
13238
  return createElement("nav", params);
12492
13239
  },
12493
- createOL: (params) => { // Added
13240
+ createOl: (params) => {
13241
+ return createElement("ol", params);
13242
+ },
13243
+ createOL: (params) => {
12494
13244
  return createElement("ol", params);
12495
13245
  },
12496
13246
  createOption: (params) => { // Added
@@ -12508,11 +13258,33 @@ const h$1 = {
12508
13258
  },
12509
13259
  createSelect: (params) => { // Added
12510
13260
  // name, disabled etc handled by createElement
12511
- return createElement("select", params);
13261
+ const select = createElement("select", params);
13262
+
13263
+ // Handle options array if provided
13264
+ if (params && params.options && Array.isArray(params.options)) {
13265
+ params.options.forEach(option => {
13266
+ if (option && typeof option === 'object') {
13267
+ const optionElement = createElement("option", {
13268
+ value: option.value,
13269
+ text: option.text,
13270
+ selected: option.selected
13271
+ });
13272
+ select.appendChild(optionElement);
13273
+ }
13274
+ });
13275
+ }
13276
+
13277
+ return select;
12512
13278
  },
12513
13279
  createSpan: (params) => {
12514
13280
  return createElement("span", params);
12515
13281
  },
13282
+ createSvg: (params) => {
13283
+ // Create SVG element with xmlns attribute
13284
+ const svgParams = { ...params, xmlns: "http://www.w3.org/2000/svg" };
13285
+ const svg = createElement("svg", svgParams);
13286
+ return svg;
13287
+ },
12516
13288
  createStrong: (params) => { // Added
12517
13289
  return createElement("strong", params);
12518
13290
  },
@@ -12525,6 +13297,10 @@ const h$1 = {
12525
13297
  createText: (text) => {
12526
13298
  return document.createTextNode(text);
12527
13299
  },
13300
+ createTextarea: (params) => {
13301
+ const textArea = createElement("textarea", params);
13302
+ return textArea;
13303
+ },
12528
13304
  createTextArea: (params) => {
12529
13305
  const textArea = createElement("textarea", params);
12530
13306
  return textArea;
@@ -12723,6 +13499,35 @@ const h$1 = {
12723
13499
  })
12724
13500
  .join('');
12725
13501
  },
13502
+
13503
+ /**
13504
+ * Injects CSS styles into the document head.
13505
+ * @param {string} cssString - The CSS string to inject.
13506
+ * @param {string} [id] - Optional ID for the style element to prevent duplicates.
13507
+ * @returns {HTMLStyleElement} The created or existing style element.
13508
+ */
13509
+ injectStyles: (cssString, id) => {
13510
+ // If an ID is provided, check if styles with this ID already exist
13511
+ if (id) {
13512
+ const existingStyle = document.getElementById(id);
13513
+ if (existingStyle) {
13514
+ // Styles already injected, return the existing element
13515
+ return existingStyle;
13516
+ }
13517
+ }
13518
+
13519
+ // Create a new style element
13520
+ const styleElement = document.createElement('style');
13521
+ if (id) {
13522
+ styleElement.id = id;
13523
+ }
13524
+ styleElement.textContent = cssString;
13525
+
13526
+ // Append to the document head
13527
+ document.head.appendChild(styleElement);
13528
+
13529
+ return styleElement;
13530
+ },
12726
13531
 
12727
13532
  /**
12728
13533
  * Calculates the distance between an element's edge and the viewport's corresponding edge
@@ -22355,7 +23160,10 @@ function createMarkdownRenderer$1(hljs, hstyle = {}, nhstyle = {}) {
22355
23160
  // Create the inner code element with the highlighted content
22356
23161
  // and the language label prepended.
22357
23162
  const codeElement = h.createCode({
22358
- html: `<span class='gs-chat-lang'>${lang}</span>\n\n${highlightedCode}`
23163
+ html: `<span class='gs-chat-lang'>${lang}</span>\n\n${highlightedCode}`,
23164
+ style: {
23165
+ fontSize: '13px'
23166
+ }
22359
23167
  });
22360
23168
 
22361
23169
  // Wrap the code element in a pre tag with styling
@@ -23163,6 +23971,18 @@ let SVGUtils$1 = class SVGUtils {
23163
23971
  return this.create(xml, params);
23164
23972
  }
23165
23973
 
23974
+ /**
23975
+ * Generate dropZone SVG icon
23976
+ * @param {Object} params - Configuration parameters
23977
+ * @returns {Element} SVG element
23978
+ */
23979
+ static dropZone(params) {
23980
+ 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>`;
23981
+ return this.create(xml, params);
23982
+ }
23983
+
23984
+
23985
+
23166
23986
  /**
23167
23987
  * Generate dot SVG icon
23168
23988
  * @param {Object} params - Configuration parameters
@@ -23203,6 +24023,26 @@ let SVGUtils$1 = class SVGUtils {
23203
24023
  return this.create(xml, params);
23204
24024
  }
23205
24025
 
24026
+ /**
24027
+ * Generate eye SVG icon
24028
+ * @param {Object} params - Configuration parameters
24029
+ * @returns {Element} SVG element
24030
+ */
24031
+ static eye(params) {
24032
+ 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>`;
24033
+ return this.create(xml, params);
24034
+ }
24035
+
24036
+ /**
24037
+ * Generate eye closed SVG icon
24038
+ * @param {Object} params - Configuration parameters
24039
+ * @returns {Element} SVG element
24040
+ */
24041
+ static eyeClosed(params) {
24042
+ 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>`;
24043
+ return this.create(xml, params);
24044
+ }
24045
+
23206
24046
  /**
23207
24047
  * Generate file SVG icon
23208
24048
  * @param {Object} params - Configuration parameters
@@ -23759,6 +24599,16 @@ let SVGUtils$1 = class SVGUtils {
23759
24599
  return this.create(xml, params);
23760
24600
  }
23761
24601
 
24602
+ /**
24603
+ * Generate squarCircle SVG icon
24604
+ * @param {Object} params - Configuration parameters
24605
+ * @returns {Element} SVG element
24606
+ */
24607
+ static squareCircle(params) {
24608
+ 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>`;
24609
+ return this.create(xml, params);
24610
+ }
24611
+
23762
24612
  /**
23763
24613
  * Generate stack SVG icon
23764
24614
  * @param {Object} params - Configuration parameters
@@ -24049,6 +24899,16 @@ let SVGUtils$1 = class SVGUtils {
24049
24899
  return this.create(xml, params);
24050
24900
  }
24051
24901
 
24902
+ /**
24903
+ * Generate verticalEllipsis SVG icon
24904
+ * @param {Object} params - Configuration parameters
24905
+ * @returns {Element} SVG element
24906
+ */
24907
+ static verticalEllipsis(params) {
24908
+ 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>`;
24909
+ return this.create(xml, params);
24910
+ }
24911
+
24052
24912
  /**
24053
24913
  * Generate x SVG icon
24054
24914
  * @param {Object} params - Configuration parameters
@@ -24078,35 +24938,343 @@ let SVGUtils$1 = class SVGUtils {
24078
24938
  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>`;
24079
24939
  return this.create(xml, params);
24080
24940
  }
24941
+
24942
+ static zai(params) {
24943
+ 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>`;
24944
+ return this.create(xml, params);
24945
+ }
24081
24946
  };
24082
24947
 
24083
24948
  var SVGUtils_1 = SVGUtils$1;
24084
24949
 
24085
24950
  /*
24086
- * Component: Language Name Utils
24087
- * Block-UUID: 313abeab-2bb3-49d1-8693-e9e88aa97113
24088
- * Parent-UUID: N/A
24951
+ * Component: StringUtils
24952
+ * Block-UUID: 7069f2b4-f0b3-4dcd-9eae-5cecaddcdd66
24089
24953
  * Version: 1.0.0
24090
- * Description: Utility for converting common lowercase language names and aliases to their proper capitalization
24954
+ * Description: A collection of utility functions for common string manipulation and processing tasks within GitSense Chat.
24091
24955
  * Language: JavaScript
24092
- * Created-at: 2025-10-19T23:25:09.231Z
24956
+ * Created-at: 2025-10-30T22:06:47.000Z
24093
24957
  * Authors: Qwen 3 Coder 480B - Cerebras (v1.0.0)
24094
24958
  */
24095
24959
 
24096
24960
  /**
24097
- * A mapping of language aliases to their properly capitalized names
24961
+ * Splits a string into an array of substrings based on a delimiter, up to a specified limit.
24962
+ * This mimics behavior similar to Python's `str.split(sep, maxsplit)` or Java's `String.split(regex, limit)`.
24963
+ *
24964
+ * @param {string} text - The string to split.
24965
+ * @param {string|RegExp} delimiter - The delimiter to split the string by.
24966
+ * @param {number} [limit=Infinity] - The maximum number of parts to return. The final part will contain the remainder of the string.
24967
+ * @returns {string[]} An array of substrings.
24098
24968
  */
24099
- const LANGUAGE_NAME_MAP = {
24100
- // Core Programming Languages
24101
- 'javascript': 'JavaScript',
24102
- 'js': 'JavaScript',
24103
- 'typescript': 'TypeScript',
24104
- 'ts': 'TypeScript',
24105
- 'python': 'Python',
24106
- 'py': 'Python',
24107
- 'java': 'Java',
24108
- 'c': 'C',
24109
- 'c++': 'C++',
24969
+ function splitWithLimit$1(text, delimiter, limit = Infinity) {
24970
+ if (limit === 0) {
24971
+ return [];
24972
+ }
24973
+ if (limit === 1) {
24974
+ return [text];
24975
+ }
24976
+
24977
+ const parts = [];
24978
+ let remainingText = text;
24979
+ let count = 0;
24980
+
24981
+ // Handle RegExp delimiter
24982
+ if (delimiter instanceof RegExp) {
24983
+ // For RegExp, we need to manually find matches and slice the string
24984
+ // This is a simplified approach and might not cover all edge cases of global regex matching
24985
+ // especially with capture groups or zero-width matches.
24986
+ const flags = delimiter.flags.includes('g') ? delimiter.flags : delimiter.flags + 'g';
24987
+ const globalDelimiter = new RegExp(delimiter.source, flags);
24988
+ let match;
24989
+ let lastIndex = 0;
24990
+ let matchCount = 0;
24991
+
24992
+ // We need (limit - 1) matches to create `limit` parts
24993
+ while ((match = globalDelimiter.exec(remainingText)) !== null && matchCount < limit - 1) {
24994
+ // Add the part before the match
24995
+ parts.push(remainingText.substring(lastIndex, match.index));
24996
+ // Add the matched delimiter part itself if it's non-empty (important for zero-width matches)
24997
+ // This part is tricky; typically, the delimiter itself is not part of the returned array.
24998
+ // The logic below follows the standard split behavior where the delimiter is not included.
24999
+ lastIndex = match.index + match[0].length;
25000
+ matchCount++;
25001
+ // Avoid infinite loop for zero-length matches
25002
+ if (match.index === globalDelimiter.lastIndex) {
25003
+ globalDelimiter.lastIndex++;
25004
+ }
25005
+ }
25006
+ // Add the final part
25007
+ parts.push(remainingText.substring(lastIndex));
25008
+ } else {
25009
+ // Handle string delimiter
25010
+ const delimLength = delimiter.length;
25011
+ if (delimLength === 0) {
25012
+ // If delimiter is an empty string, split by characters
25013
+ // This is consistent with JS String.split('')
25014
+ return text.split('').slice(0, limit);
25015
+ }
25016
+ let index;
25017
+ while (count < limit - 1 && (index = remainingText.indexOf(delimiter)) !== -1) {
25018
+ parts.push(remainingText.substring(0, index));
25019
+ remainingText = remainingText.substring(index + delimLength);
25020
+ count++;
25021
+ }
25022
+ // Add the final part (the rest of the string)
25023
+ parts.push(remainingText);
25024
+ }
25025
+
25026
+ return parts;
25027
+ }
25028
+
25029
+ /**
25030
+ * Capitalizes the first character of a string.
25031
+ *
25032
+ * @param {string} text - The string to capitalize.
25033
+ * @returns {string} The string with its first character capitalized.
25034
+ */
25035
+ function capitalize$1(text) {
25036
+ if (!text || typeof text !== 'string') {
25037
+ return text;
25038
+ }
25039
+ return text.charAt(0).toUpperCase() + text.slice(1);
25040
+ }
25041
+
25042
+ /**
25043
+ * Converts a string to title case (capitalizing the first letter of each word).
25044
+ * Words are sequences of characters separated by space, tab, or newline.
25045
+ *
25046
+ * @param {string} text - The string to convert.
25047
+ * @returns {string} The string in title case.
25048
+ */
25049
+ function titleCase$1(text) {
25050
+ if (!text || typeof text !== 'string') {
25051
+ return text;
25052
+ }
25053
+ // Split by one or more whitespace characters
25054
+ return text.split(/\s+/).map(word => capitalize$1(word)).join(' ');
25055
+ }
25056
+
25057
+ /**
25058
+ * Converts a string to camelCase.
25059
+ *
25060
+ * @param {string} text - The string to convert.
25061
+ * @returns {string} The string in camelCase.
25062
+ */
25063
+ function camelCase$1(text) {
25064
+ if (!text || typeof text !== 'string') {
25065
+ return text;
25066
+ }
25067
+ return text
25068
+ .split(/\W+/) // Split on non-word characters
25069
+ .map((word, index) => {
25070
+ if (index === 0) {
25071
+ // First word is lowercase
25072
+ return word.toLowerCase();
25073
+ }
25074
+ // Subsequent words have their first letter capitalized
25075
+ return capitalize$1(word.toLowerCase());
25076
+ })
25077
+ .join('');
25078
+ }
25079
+
25080
+ /**
25081
+ * Converts a string to PascalCase.
25082
+ *
25083
+ * @param {string} text - The string to convert.
25084
+ * @returns {string} The string in PascalCase.
25085
+ */
25086
+ function pascalCase$1(text) {
25087
+ if (!text || typeof text !== 'string') {
25088
+ return text;
25089
+ }
25090
+ return text
25091
+ .split(/\W+/) // Split on non-word characters
25092
+ .map(word => capitalize$1(word.toLowerCase()))
25093
+ .join('');
25094
+ }
25095
+
25096
+ /**
25097
+ * Converts a string to kebab-case.
25098
+ *
25099
+ * @param {string} text - The string to convert.
25100
+ * @returns {string} The string in kebab-case.
25101
+ */
25102
+ function kebabCase$1(text) {
25103
+ if (!text || typeof text !== 'string') {
25104
+ return text;
25105
+ }
25106
+ return text
25107
+ .split(/\W+/) // Split on non-word characters
25108
+ .map(word => word.toLowerCase())
25109
+ .filter(word => word.length > 0) // Remove empty strings from split
25110
+ .join('-');
25111
+ }
25112
+
25113
+ /**
25114
+ * Converts a string to snake_case.
25115
+ *
25116
+ * @param {string} text - The string to convert.
25117
+ * @returns {string} The string in snake_case.
25118
+ */
25119
+ function snakeCase$1(text) {
25120
+ if (!text || typeof text !== 'string') {
25121
+ return text;
25122
+ }
25123
+ return text
25124
+ .split(/\W+/) // Split on non-word characters
25125
+ .map(word => word.toLowerCase())
25126
+ .filter(word => word.length > 0) // Remove empty strings from split
25127
+ .join('_');
25128
+ }
25129
+
25130
+ /**
25131
+ * Converts a string to CONSTANT_CASE.
25132
+ *
25133
+ * @param {string} text - The string to convert.
25134
+ * @returns {string} The string in CONSTANT_CASE.
25135
+ */
25136
+ function constantCase$1(text) {
25137
+ if (!text || typeof text !== 'string') {
25138
+ return text;
25139
+ }
25140
+ return text
25141
+ .split(/\W+/) // Split on non-word characters
25142
+ .map(word => word.toUpperCase())
25143
+ .filter(word => word.length > 0) // Remove empty strings from split
25144
+ .join('_');
25145
+ }
25146
+
25147
+ /**
25148
+ * Trims leading and trailing whitespace and normalizes internal whitespace
25149
+ * (replacing sequences of whitespace characters with a single space).
25150
+ *
25151
+ * @param {string} text - The string to trim and normalize.
25152
+ * @returns {string} The trimmed and normalized string.
25153
+ */
25154
+ function trimWhitespace$1(text) {
25155
+ if (!text || typeof text !== 'string') {
25156
+ return text;
25157
+ }
25158
+ return text.trim().replace(/\s+/g, ' ');
25159
+ }
25160
+
25161
+ /**
25162
+ * Truncates a string to a specified maximum length and appends a suffix if truncated.
25163
+ *
25164
+ * @param {string} text - The string to truncate.
25165
+ * @param {number} maxLength - The maximum length of the string.
25166
+ * @param {string} [suffix='...'] - The suffix to append if the string is truncated.
25167
+ * @returns {string} The truncated string with suffix, or the original string if not truncated.
25168
+ */
25169
+ function truncate$1(text, maxLength, suffix = '...') {
25170
+ if (!text || typeof text !== 'string' || maxLength <= 0) {
25171
+ return '';
25172
+ }
25173
+ if (text.length <= maxLength) {
25174
+ return text;
25175
+ }
25176
+ // Ensure we don't cut the suffix itself if maxLength is very small
25177
+ const suffixLength = suffix.length;
25178
+ if (maxLength <= suffixLength) {
25179
+ // If max length is less than or equal to suffix, return a truncated suffix
25180
+ // This is a bit of an edge case, but handles it gracefully.
25181
+ return suffix.substring(0, maxLength);
25182
+ }
25183
+ return text.substring(0, maxLength - suffixLength) + suffix;
25184
+ }
25185
+
25186
+ /**
25187
+ * Escapes HTML characters in a string to prevent them from being interpreted as HTML.
25188
+ *
25189
+ * @param {string} text - The string to escape.
25190
+ * @returns {string} The escaped string.
25191
+ */
25192
+ function escapeHtml(text) {
25193
+ if (!text || typeof text !== 'string') {
25194
+ return text;
25195
+ }
25196
+ const htmlEscapes = {
25197
+ '&': '&amp;',
25198
+ '<': '&lt;',
25199
+ '>': '&gt;',
25200
+ '"': '&quot;',
25201
+ "'": '&#39;',
25202
+ };
25203
+ return text.replace(/[&<>"']/g, match => htmlEscapes[match]);
25204
+ }
25205
+
25206
+ /**
25207
+ * Removes HTML tags from a string.
25208
+ *
25209
+ * @param {string} text - The string to strip tags from.
25210
+ * @returns {string} The string without HTML tags.
25211
+ */
25212
+ function stripHtml(text) {
25213
+ if (!text || typeof text !== 'string') {
25214
+ return text;
25215
+ }
25216
+ return text.replace(/<[^>]*>/g, '');
25217
+ }
25218
+
25219
+ /**
25220
+ * Performs a basic check to see if a string looks like a valid email address.
25221
+ * This is a simple validation and should not be used for robust security checks.
25222
+ *
25223
+ * @param {string} email - The email string to validate.
25224
+ * @returns {boolean} True if the string looks like a valid email, false otherwise.
25225
+ */
25226
+ function isValidEmail(email) {
25227
+ if (!email || typeof email !== 'string') {
25228
+ return false;
25229
+ }
25230
+ // A basic regex for email validation. Note: Fully validating email addresses
25231
+ // is extremely complex. This covers common cases.
25232
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
25233
+ return emailRegex.test(email);
25234
+ }
25235
+
25236
+
25237
+ var stringUtils = {
25238
+ splitWithLimit: splitWithLimit$1,
25239
+ capitalize: capitalize$1,
25240
+ titleCase: titleCase$1,
25241
+ camelCase: camelCase$1,
25242
+ kebabCase: kebabCase$1,
25243
+ snakeCase: snakeCase$1,
25244
+ constantCase: constantCase$1,
25245
+ pascalCase: pascalCase$1,
25246
+ trimWhitespace: trimWhitespace$1,
25247
+ truncate: truncate$1,
25248
+ escapeHtml,
25249
+ stripHtml,
25250
+ isValidEmail,
25251
+ };
25252
+
25253
+ /*
25254
+ * Component: Language Name Utils
25255
+ * Block-UUID: 313abeab-2bb3-49d1-8693-e9e88aa97113
25256
+ * Parent-UUID: N/A
25257
+ * Version: 1.0.0
25258
+ * Description: Utility for converting common lowercase language names and aliases to their proper capitalization
25259
+ * Language: JavaScript
25260
+ * Created-at: 2025-10-19T23:25:09.231Z
25261
+ * Authors: Qwen 3 Coder 480B - Cerebras (v1.0.0)
25262
+ */
25263
+
25264
+ /**
25265
+ * A mapping of language aliases to their properly capitalized names
25266
+ */
25267
+ const LANGUAGE_NAME_MAP = {
25268
+ // Core Programming Languages
25269
+ 'javascript': 'JavaScript',
25270
+ 'js': 'JavaScript',
25271
+ 'typescript': 'TypeScript',
25272
+ 'ts': 'TypeScript',
25273
+ 'python': 'Python',
25274
+ 'py': 'Python',
25275
+ 'java': 'Java',
25276
+ 'c': 'C',
25277
+ 'c++': 'C++',
24110
25278
  'cpp': 'C++',
24111
25279
  'c#': 'C#',
24112
25280
  'csharp': 'C#',
@@ -24253,19 +25421,670 @@ var LanguageNameUtils$1 = {
24253
25421
  getLanguageNameMap
24254
25422
  };
24255
25423
 
25424
+ /**
25425
+ * Component: MetaRawResultUtils
25426
+ * Block-UUID: 8a4d235b-a9d3-44b8-9c08-47b3678ba1c0
25427
+ * Parent-UUID: N/A
25428
+ * Version: 1.0.0
25429
+ * Description: Utility for parsing and extracting mappings from meta-raw-result messages
25430
+ * Language: JavaScript
25431
+ * Created-at: 2025-11-15T19:15:00.000Z
25432
+ * Authors: GLM-4.6 (v1.0.0)
25433
+ */
25434
+
25435
+ const ChatUtils$1 = ChatUtils$2;
25436
+
25437
+ /**
25438
+ * Extracts and parses all meta-raw-result messages from a chat
25439
+ * @param {Object} chat - The chat object containing messages
25440
+ * @param {string} model - The model name (optional, for message filtering)
25441
+ * @returns {Map<number, Object>} Map of chatId to file metadata
25442
+ */
25443
+ function extractMetaRawResultMappings$1(chat, model = null) {
25444
+ const mappings = new Map();
25445
+
25446
+ try {
25447
+ // Get all messages from the chat
25448
+ const allMessages = ChatUtils$1.getChatMessages(chat, model);
25449
+
25450
+ // Filter for meta-raw-result messages
25451
+ const metaRawMessages = allMessages.filter(msg => isMetaRawResultMessage$1(msg));
25452
+
25453
+ // Parse each meta-raw-result message
25454
+ for (const message of metaRawMessages) {
25455
+ const messageMappings = parseMetaRawResultContent$1(message.message);
25456
+
25457
+ // Merge mappings into the main map
25458
+ for (const [chatId, fileInfo] of messageMappings) {
25459
+ mappings.set(chatId, fileInfo);
25460
+ }
25461
+ }
25462
+
25463
+ return mappings;
25464
+ } catch (error) {
25465
+ console.error('Error extracting meta-raw-result mappings:', error);
25466
+ return mappings;
25467
+ }
25468
+ }
25469
+
25470
+ /**
25471
+ * Validates if a message is a meta-raw-result message
25472
+ * @param {Object} message - The message object to validate
25473
+ * @returns {boolean} True if message is meta-raw-result type
25474
+ */
25475
+ function isMetaRawResultMessage$1(message) {
25476
+ if (!message || !message.message) {
25477
+ return false;
25478
+ }
25479
+
25480
+ // Check if message type is meta-raw-result
25481
+ if (message.type === 'meta-raw-result') {
25482
+ return true;
25483
+ }
25484
+
25485
+ return false;
25486
+ }
25487
+
25488
+ /**
25489
+ * Parses a single meta-raw-result message content
25490
+ * @param {string} messageContent - The message content to parse
25491
+ * @returns {Map<number, Object>} Map of chatId to file metadata
25492
+ */
25493
+ function parseMetaRawResultContent$1(messageContent) {
25494
+ const mappings = new Map();
25495
+
25496
+ try {
25497
+ // Split content into lines
25498
+ const lines = messageContent.split('\n');
25499
+
25500
+ // Find the data section
25501
+ let dataSectionStart = -1;
25502
+ for (let i = 0; i < lines.length; i++) {
25503
+ if (lines[i].trimStart().startsWith('## Data ')) {
25504
+ dataSectionStart = i;
25505
+ break;
25506
+ }
25507
+ }
25508
+
25509
+ if (dataSectionStart === -1) {
25510
+ return mappings; // No data section found
25511
+ }
25512
+
25513
+ // Find the table header (line with |)
25514
+ let headerLine = -1;
25515
+ for (let i = dataSectionStart; i < lines.length; i++) {
25516
+ if (lines[i].includes('|') && lines[i].includes('Chat ID')) {
25517
+ headerLine = i;
25518
+ break;
25519
+ }
25520
+ }
25521
+
25522
+ if (headerLine === -1) {
25523
+ return mappings; // No table header found
25524
+ }
25525
+
25526
+ // Find the separator line (|---|---|...)
25527
+ let separatorLine = -1;
25528
+ for (let i = headerLine + 1; i < lines.length; i++) {
25529
+ if (lines[i].match(/^\|[\s\-\|]*\|$/)) {
25530
+ separatorLine = i;
25531
+ break;
25532
+ }
25533
+ }
25534
+
25535
+ if (separatorLine === -1) {
25536
+ return mappings; // No separator found
25537
+ }
25538
+
25539
+ // Parse data rows (everything after the separator)
25540
+ for (let i = separatorLine + 1; i < lines.length; i++) {
25541
+ const line = lines[i].trim();
25542
+
25543
+ // Skip empty lines or separator lines
25544
+ if (!line || line.match(/^\|[\s\-\|]*\|$/) || !line.includes('|')) {
25545
+ continue;
25546
+ }
25547
+
25548
+ // Parse the table row
25549
+ const fileInfo = parseTableRow$1(line);
25550
+ if (fileInfo && fileInfo.id) {
25551
+ mappings.set(fileInfo.id, fileInfo);
25552
+ }
25553
+ }
25554
+
25555
+ return mappings;
25556
+ } catch (error) {
25557
+ console.error('Error parsing meta-raw-result content:', error);
25558
+ return mappings;
25559
+ }
25560
+ }
25561
+
25562
+ /**
25563
+ * Parses a single table row from meta-raw-result content
25564
+ * @param {string} rowLine - The table row line to parse
25565
+ * @returns {Object|null} Parsed file information object or null if parsing failed
25566
+ */
25567
+ function parseTableRow$1(rowLine) {
25568
+ try {
25569
+ // Split the row by | and clean up
25570
+ const cells = rowLine.split('|').map(cell => cell.trim()).filter(cell => cell);
25571
+
25572
+ if (cells.length < 6) {
25573
+ return null; // Not enough columns
25574
+ }
25575
+
25576
+ // Extract basic information from the table cells
25577
+ const repo = cells[0];
25578
+ const branch = cells[1];
25579
+ const filePath = cells[2];
25580
+ const language = cells[3];
25581
+ const chatId = parseInt(cells[4], 10);
25582
+
25583
+ // Parse file path to extract name and path
25584
+ const pathParts = filePath.split('/');
25585
+ const name = pathParts[pathParts.length - 1];
25586
+ const path = filePath;
25587
+
25588
+ // Create full path
25589
+ const fullPath = `${repo}/${path}`;
25590
+
25591
+ return {
25592
+ id: chatId,
25593
+ name: name,
25594
+ path: path,
25595
+ repo: repo,
25596
+ branch: branch,
25597
+ language: language,
25598
+ fullPath: fullPath
25599
+ };
25600
+ } catch (error) {
25601
+ console.error('Error parsing table row:', error);
25602
+ return null;
25603
+ }
25604
+ }
25605
+
25606
+ /**
25607
+ * Parses size string to bytes
25608
+ * @param {string} sizeStr - Size string (e.g., "1.2 KB", "500 B")
25609
+ * @returns {number} Size in bytes
25610
+ */
25611
+ function parseSize$1(sizeStr) {
25612
+ if (!sizeStr || sizeStr === 'N/A') {
25613
+ return 0;
25614
+ }
25615
+
25616
+ const match = sizeStr.match(/^([\d.]+)\s*(B|KB|MB|GB)?$/i);
25617
+ if (!match) {
25618
+ return 0;
25619
+ }
25620
+
25621
+ const value = parseFloat(match[1]);
25622
+ const unit = (match[2] || 'B').toUpperCase();
25623
+
25624
+ switch (unit) {
25625
+ case 'B': return Math.round(value);
25626
+ case 'KB': return Math.round(value * 1024);
25627
+ case 'MB': return Math.round(value * 1024 * 1024);
25628
+ case 'GB': return Math.round(value * 1024 * 1024 * 1024);
25629
+ default: return 0;
25630
+ }
25631
+ }
25632
+
25633
+ /**
25634
+ * Parses tokens string to number
25635
+ * @param {string} tokensStr - Tokens string (e.g., "1.2k", "500")
25636
+ * @returns {number} Token count
25637
+ */
25638
+ function parseTokens$1(tokensStr) {
25639
+ if (!tokensStr || tokensStr === 'N/A') {
25640
+ return 0;
25641
+ }
25642
+
25643
+ const match = tokensStr.match(/^([\d.]+)(k|m|b)?$/i);
25644
+ if (!match) {
25645
+ return 0;
25646
+ }
25647
+
25648
+ const value = parseFloat(match[1]);
25649
+ const suffix = (match[2] || '').toLowerCase();
25650
+
25651
+ switch (suffix) {
25652
+ case 'k': return Math.round(value * 1000);
25653
+ case 'm': return Math.round(value * 1000000);
25654
+ case 'b': return Math.round(value * 1000000000);
25655
+ default: return Math.round(value);
25656
+ }
25657
+ }
25658
+
25659
+ var MetaRawResultUtils$1 = {
25660
+ extractMetaRawResultMappings: extractMetaRawResultMappings$1,
25661
+ isMetaRawResultMessage: isMetaRawResultMessage$1,
25662
+ parseMetaRawResultContent: parseMetaRawResultContent$1,
25663
+ parseTableRow: parseTableRow$1,
25664
+ parseSize: parseSize$1,
25665
+ parseTokens: parseTokens$1
25666
+ };
25667
+
25668
+ /**
25669
+ * Component: ReferenceMessageUtils
25670
+ * Block-UUID: 2870551a-4311-4d64-9491-c9cc62f276e8
25671
+ * Parent-UUID: N/A
25672
+ * Description: Utility for parsing reference messages in the compact message workflow, extracting session data, metadata, and message sections.
25673
+ * Language: JavaScript
25674
+ * Created-at: 2025-12-07T00:08:42.573Z
25675
+ * Authors: GLM-4.6 (v1.0.0)
25676
+ */
25677
+
25678
+ let ReferenceMessageUtils$1 = class ReferenceMessageUtils {
25679
+ /**
25680
+ * Extracts all data from a reference message including session data, metadata, and message sections
25681
+ * @param {string} referenceMessage - The reference message content to parse
25682
+ * @returns {Object} Parsed data with messages to compact and keep, metadata, and session information
25683
+ */
25684
+ static extractReferenceMessageData(referenceMessage) {
25685
+ if (!referenceMessage) {
25686
+ return {
25687
+ messagesToCompact: [],
25688
+ messagesToKeep: [],
25689
+ originalChatUuid: null,
25690
+ validationHash: null,
25691
+ sessionId: null,
25692
+ range: null,
25693
+ from: null,
25694
+ to: null
25695
+ };
25696
+ }
25697
+
25698
+ // Extract Original Chat UUID
25699
+ const originalChatUuidMatch = referenceMessage.match(/Original Chat UUID:\s*([a-f0-9-]+)/i);
25700
+ const originalChatUuid = originalChatUuidMatch ? originalChatUuidMatch[1] : null;
25701
+
25702
+ // Extract Validation Hash
25703
+ const validationHashMatch = referenceMessage.match(/Validation Hash:\s*([a-f0-9]+)/i);
25704
+ const validationHash = validationHashMatch ? validationHashMatch[1] : null;
25705
+
25706
+ // Extract Session ID
25707
+ const sessionIdMatch = referenceMessage.match(/Session ID:\s*(\d+)/i);
25708
+ const sessionId = sessionIdMatch ? sessionIdMatch[1] : null;
25709
+
25710
+ // Extract Range
25711
+ const rangeMatch = referenceMessage.match(/Range:\s*(\d+-\d+)/i);
25712
+ const range = rangeMatch ? rangeMatch[1] : null;
25713
+
25714
+ // Extract From and To values
25715
+ const fromMatch = referenceMessage.match(/From:\s*(\d+)/i);
25716
+ const from = fromMatch ? parseInt(fromMatch[1], 10) : null;
25717
+
25718
+ const toMatch = referenceMessage.match(/To:\s*(\d+)/i);
25719
+ const to = toMatch ? parseInt(toMatch[1], 10) : null;
25720
+
25721
+ // If range is not found but from and to are available, construct the range
25722
+ if (!range && from !== null && to !== null) {
25723
+ range = `${from}-${to}`;
25724
+ }
25725
+
25726
+ // Extract Messages to Compact section
25727
+ const compactSectionMatch = referenceMessage.match(/# Messages to Compact\n([\s\S]*?)(?=\n# |\n$|$)/);
25728
+ const compactSection = compactSectionMatch ? compactSectionMatch[1] : '';
25729
+
25730
+ // Extract Messages to Keep section
25731
+ const keepSectionMatch = referenceMessage.match(/# Messages to Keep \(Context\)\n([\s\S]*?)(?=\n# |\n$|$)/);
25732
+ const keepSection = keepSectionMatch ? keepSectionMatch[1] : '';
25733
+
25734
+ // Parse messages from each section
25735
+ const messagesToCompact = this.parseMessageSection(compactSection);
25736
+ const messagesToKeep = this.parseMessageSection(keepSection);
25737
+
25738
+ return {
25739
+ messagesToCompact,
25740
+ messagesToKeep,
25741
+ originalChatUuid,
25742
+ validationHash,
25743
+ sessionId,
25744
+ range,
25745
+ from,
25746
+ to
25747
+ };
25748
+ }
25749
+
25750
+ /**
25751
+ * Parses a message section to extract individual messages
25752
+ * @param {string} section - The section content to parse
25753
+ * @returns {Array} Array of parsed message objects
25754
+ */
25755
+ static parseMessageSection(section) {
25756
+ if (!section) return [];
25757
+
25758
+ const messages = [];
25759
+ const messageRegex = /<(\w+) message number (\d+)>\n([\s\S]*?)\n<\/\1 message number \2>/g;
25760
+ let match;
25761
+
25762
+ while ((match = messageRegex.exec(section)) !== null) {
25763
+ const [, role, positionStr, content] = match;
25764
+ const position = parseInt(positionStr, 10);
25765
+
25766
+ messages.push({
25767
+ role,
25768
+ position,
25769
+ content: content.trim()
25770
+ });
25771
+ }
25772
+
25773
+ return messages;
25774
+ }
25775
+
25776
+ /**
25777
+ * Extracts just the metadata from a reference message without the message sections
25778
+ * @param {string} referenceMessage - The reference message content to parse
25779
+ * @returns {Object} Metadata object with original chat UUID, validation hash, session ID, and range information
25780
+ */
25781
+ static extractReferenceMessageMetadata(referenceMessage) {
25782
+ const data = this.extractReferenceMessageData(referenceMessage);
25783
+
25784
+ return {
25785
+ originalChatUuid: data.originalChatUuid,
25786
+ validationHash: data.validationHash,
25787
+ sessionId: data.sessionId,
25788
+ range: data.range,
25789
+ from: data.from,
25790
+ to: data.to
25791
+ };
25792
+ }
25793
+
25794
+ /**
25795
+ * Checks if a message is a reference message
25796
+ * @param {string} message - The message content to check
25797
+ * @returns {boolean} True if the message is a reference message
25798
+ */
25799
+ static isReferenceMessage(message) {
25800
+ if (!message) return false;
25801
+
25802
+ // Check for key indicators of a reference message
25803
+ return message.includes('# Compact Messages Reference') &&
25804
+ message.includes('Original Chat UUID:') &&
25805
+ message.includes('Session ID:');
25806
+ }
25807
+ };
25808
+
25809
+ var ReferenceMessageUtils_1 = { ReferenceMessageUtils: ReferenceMessageUtils$1 };
25810
+
25811
+ /**
25812
+ * Component: CompactedMessageUtils
25813
+ * Block-UUID: b4111118-ec11-49d6-9a6e-402ca0b6df5b
25814
+ * Parent-UUID: N/A
25815
+ * Version: 1.0.1
25816
+ * Description: Utility for formatting, parsing, and validating compacted messages with standardized metadata headers.
25817
+ * Language: JavaScript
25818
+ * Created-at: 2025-12-07T00:09:15.842Z
25819
+ * Authors: GLM-4.6 (v1.0.0), GLM-4.6 (v1.0.1)
25820
+ */
25821
+
25822
+ let CompactedMessageUtils$1 = class CompactedMessageUtils {
25823
+ /**
25824
+ * Formats a compacted message with standardized metadata headers
25825
+ * @param {string} content - The compacted message content
25826
+ * @param {string} originalChatUuid - UUID of the original chat
25827
+ * @param {string} messageRange - Message range that was compacted (e.g., "3-6")
25828
+ * @param {Object} options - Additional options
25829
+ * @param {Date} options.compactedAt - Custom timestamp for when the message was compacted
25830
+ * @returns {string} Formatted compacted message with metadata
25831
+ */
25832
+ static formatCompactedMessage(content, originalChatUuid, messageRange, options = {}) {
25833
+ if (!content) {
25834
+ throw new Error('Content is required for formatting a compacted message');
25835
+ }
25836
+
25837
+ if (!originalChatUuid) {
25838
+ throw new Error('Original chat UUID is required for formatting a compacted message');
25839
+ }
25840
+
25841
+ if (!messageRange) {
25842
+ throw new Error('Message range is required for formatting a compacted message');
25843
+ }
25844
+
25845
+ // Use provided timestamp or generate current one
25846
+ const compactedAt = options.compactedAt || new Date().toISOString();
25847
+
25848
+ return `## Compacted Message
25849
+
25850
+ - **Original Chat:** [${originalChatUuid}](/?chat=${originalChatUuid})
25851
+ - **Message Range:** ${messageRange}
25852
+ - **Compacted At:** ${compactedAt}
25853
+
25854
+ ${content}`;
25855
+ }
25856
+
25857
+ /**
25858
+ * Extracts metadata from a compacted message
25859
+ * @param {string} compactedMessage - The compacted message to parse
25860
+ * @returns {Object|null} Metadata object or null if format is invalid
25861
+ */
25862
+ static extractCompactedMessageMetadata(compactedMessage) {
25863
+ if (!this.isCompactedMessage(compactedMessage)) {
25864
+ return null;
25865
+ }
25866
+
25867
+ // Extract Original Chat UUID
25868
+ const originalChatMatch = compactedMessage.match(/\*\*Original Chat:\*\*\s*([a-f0-9-]+)/i);
25869
+ const originalChatUuid = originalChatMatch ? originalChatMatch[1] : null;
25870
+
25871
+ // Extract Message Range
25872
+ const rangeMatch = compactedMessage.match(/\*\*Message Range:\*\*\s*(\d+-\d+)/i);
25873
+ const messageRange = rangeMatch ? rangeMatch[1] : null;
25874
+
25875
+ // Extract Compacted At timestamp
25876
+ 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);
25877
+ const compactedAt = compactedAtMatch ? compactedAtMatch[1] : null;
25878
+
25879
+ return {
25880
+ originalChatUuid,
25881
+ messageRange,
25882
+ compactedAt
25883
+ };
25884
+ }
25885
+
25886
+ /**
25887
+ * Extracts just the content portion from a compacted message (without metadata)
25888
+ * @param {string} compactedMessage - The compacted message to parse
25889
+ * @returns {string|null} Content portion or null if format is invalid
25890
+ */
25891
+ static extractCompactedMessageContent(compactedMessage) {
25892
+ if (!this.isCompactedMessage(compactedMessage)) {
25893
+ return null;
25894
+ }
25895
+
25896
+ // Find the start and end boundaries
25897
+ const startMarker = "## Compacted Message";
25898
+ const endMarker = "## End Compacted Message";
25899
+
25900
+ const startIndex = compactedMessage.indexOf(startMarker);
25901
+ const endIndex = compactedMessage.indexOf(endMarker);
25902
+
25903
+ // If start marker is not found, return null
25904
+ if (startIndex === -1) {
25905
+ return null;
25906
+ }
25907
+
25908
+ // If end marker is not found, extract everything after start marker
25909
+ if (endIndex === -1) {
25910
+ return compactedMessage.substring(startIndex + startMarker.length).trim();
25911
+ }
25912
+
25913
+ // Extract content between the markers
25914
+ const contentStart = startIndex + startMarker.length;
25915
+ return compactedMessage.substring(contentStart, endIndex).trim();
25916
+ }
25917
+
25918
+ /**
25919
+ * Validates that a compacted message follows the expected format
25920
+ * @param {string} compactedMessage - The compacted message to validate
25921
+ * @returns {Object} Validation result with isValid flag and any errors/warnings
25922
+ */
25923
+ static validateCompactedMessageFormat(compactedMessage) {
25924
+ const result = {
25925
+ isValid: true,
25926
+ errors: [],
25927
+ warnings: []
25928
+ };
25929
+
25930
+ if (!compactedMessage) {
25931
+ result.isValid = false;
25932
+ result.errors.push('Compacted message is empty or null');
25933
+ return result;
25934
+ }
25935
+
25936
+ // Check for required header
25937
+ if (!compactedMessage.includes('## Compacted Message')) {
25938
+ result.isValid = false;
25939
+ result.errors.push('Missing required "## Compacted Message" header');
25940
+ }
25941
+
25942
+ // Check for required metadata fields
25943
+ const metadata = this.extractCompactedMessageMetadata(compactedMessage);
25944
+
25945
+ if (!metadata) {
25946
+ result.isValid = false;
25947
+ result.errors.push('Could not extract metadata from compacted message');
25948
+ return result;
25949
+ }
25950
+
25951
+ if (!metadata.originalChatUuid) {
25952
+ result.isValid = false;
25953
+ result.errors.push('Missing or invalid Original Chat UUID');
25954
+ }
25955
+
25956
+ if (!metadata.messageRange) {
25957
+ result.isValid = false;
25958
+ result.errors.push('Missing or invalid Message Range');
25959
+ }
25960
+
25961
+ if (!metadata.compactedAt) {
25962
+ result.warnings.push('Missing or invalid Compacted At timestamp');
25963
+ }
25964
+
25965
+ // Validate UUID format
25966
+ if (metadata.originalChatUuid && !this.isValidUUID(metadata.originalChatUuid)) {
25967
+ result.isValid = false;
25968
+ result.errors.push('Original Chat UUID is not in valid UUID format');
25969
+ }
25970
+
25971
+ // Validate message range format
25972
+ if (metadata.messageRange && !this.isValidMessageRange(metadata.messageRange)) {
25973
+ result.isValid = false;
25974
+ result.errors.push('Message Range is not in valid format (expected "X-Y")');
25975
+ }
25976
+
25977
+ // Validate timestamp format
25978
+ if (metadata.compactedAt && !this.isValidISOTimestamp(metadata.compactedAt)) {
25979
+ result.warnings.push('Compacted At timestamp is not in valid ISO 8601 format');
25980
+ }
25981
+
25982
+ return result;
25983
+ }
25984
+
25985
+ /**
25986
+ * Checks if a message is a compacted message
25987
+ * @param {string} message - The message content to check
25988
+ * @returns {boolean} True if the message is a compacted message
25989
+ */
25990
+ static isCompactedMessage(message) {
25991
+ if (!message) return false;
25992
+
25993
+ // Check for the compacted message header
25994
+ return message.includes('## Compacted Message');
25995
+ }
25996
+
25997
+ /**
25998
+ * Validates if a string is a valid UUID v4
25999
+ * @param {string} uuid - The UUID string to validate
26000
+ * @returns {boolean} True if valid UUID v4
26001
+ */
26002
+ static isValidUUID(uuid) {
26003
+ 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;
26004
+ return uuidRegex.test(uuid);
26005
+ }
26006
+
26007
+ /**
26008
+ * Validates if a string is a valid message range (e.g., "3-6")
26009
+ * @param {string} range - The range string to validate
26010
+ * @returns {boolean} True if valid range format
26011
+ */
26012
+ static isValidMessageRange(range) {
26013
+ const rangeRegex = /^\d+-\d+$/;
26014
+ if (!rangeRegex.test(range)) return false;
26015
+
26016
+ const [from, to] = range.split('-').map(Number);
26017
+ return !isNaN(from) && !isNaN(to) && from <= to;
26018
+ }
26019
+
26020
+ /**
26021
+ * Validates if a string is a valid ISO 8601 timestamp
26022
+ * @param {string} timestamp - The timestamp string to validate
26023
+ * @returns {boolean} True if valid ISO 8601 timestamp
26024
+ */
26025
+ static isValidISOTimestamp(timestamp) {
26026
+ if (!timestamp) return false;
26027
+
26028
+ const date = new Date(timestamp);
26029
+ return !isNaN(date.getTime()) && timestamp === date.toISOString();
26030
+ }
26031
+ };
26032
+
26033
+ var CompactedMessageUtils_1 = { CompactedMessageUtils: CompactedMessageUtils$1 };
26034
+
26035
+ /**
26036
+ * Component: CompactChatUtils Index
26037
+ * Block-UUID: cd0a6091-edb6-4e92-9439-63dd8f6a6796
26038
+ * Parent-UUID: 5ffa0338-b4fb-4968-908c-f23a26d4923b
26039
+ * Version: 2.0.0
26040
+ * Description: Entry point for CompactChatUtils that exports both ReferenceMessageUtils and CompactedMessageUtils methods directly for easier access.
26041
+ * Language: JavaScript
26042
+ * Created-at: 2025-12-07T00:10:05.123Z
26043
+ * Authors: GLM-4.6 (v1.0.0), GLM-4.6 (v2.0.0)
26044
+ */
26045
+
26046
+ const { ReferenceMessageUtils } = ReferenceMessageUtils_1;
26047
+ const { CompactedMessageUtils } = CompactedMessageUtils_1;
26048
+
26049
+ /**
26050
+ * CompactChatUtils provides utilities for working with compacted messages in GitSense Chat.
26051
+ * It includes functionality for parsing reference messages and formatting/parsing compacted messages.
26052
+ */
26053
+ var CompactChatUtils$1 = {
26054
+ // Export the classes for those who want to access them directly
26055
+ ReferenceMessageUtils,
26056
+ CompactedMessageUtils,
26057
+
26058
+ // Export all methods from ReferenceMessageUtils directly
26059
+ extractReferenceMessageData: ReferenceMessageUtils.extractReferenceMessageData,
26060
+ parseMessageSection: ReferenceMessageUtils.parseMessageSection,
26061
+ extractReferenceMessageMetadata: ReferenceMessageUtils.extractReferenceMessageMetadata,
26062
+ isReferenceMessage: ReferenceMessageUtils.isReferenceMessage,
26063
+
26064
+ // Export all methods from CompactedMessageUtils directly
26065
+ formatCompactedMessage: CompactedMessageUtils.formatCompactedMessage,
26066
+ extractCompactedMessageMetadata: CompactedMessageUtils.extractCompactedMessageMetadata,
26067
+ extractCompactedMessageContent: CompactedMessageUtils.extractCompactedMessageContent,
26068
+ validateCompactedMessageFormat: CompactedMessageUtils.validateCompactedMessageFormat,
26069
+ isCompactedMessage: CompactedMessageUtils.isCompactedMessage,
26070
+ isValidUUID: CompactedMessageUtils.isValidUUID,
26071
+ isValidMessageRange: CompactedMessageUtils.isValidMessageRange,
26072
+ isValidISOTimestamp: CompactedMessageUtils.isValidISOTimestamp
26073
+ };
26074
+
24256
26075
  /*
24257
26076
  * Component: GitSenseChatUtils
24258
- * Block-UUID: 1793b3a8-4881-4306-bfdf-673eef503679
24259
- * Parent-UUID: eaeabebb-04b6-4881-9b29-88798c7f21b6
24260
- * Version: 2.4.0
24261
- * 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.
26077
+ * Block-UUID: c1b6c4d3-7959-4eb6-8022-6cd7aa59240d
26078
+ * Parent-UUID: 1793b3a8-4881-4306-bfdf-673eef503679
26079
+ * Version: 2.7.0
26080
+ * 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.
24262
26081
  * Language: JavaScript
24263
26082
  * Created-at: 2025-10-17T17:13:04.663Z
24264
- * 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)
26083
+ * 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)
24265
26084
  */
24266
26085
 
24267
- const ChatUtils = ChatUtils$1;
24268
- const CodeBlockUtils = CodeBlockUtils$5;
26086
+ const ChatUtils = ChatUtils$2;
26087
+ const CodeBlockUtils = CodeBlockUtils$7;
24269
26088
  const ContextUtils = ContextUtils$2;
24270
26089
  const MessageUtils = MessageUtils$3;
24271
26090
  const AnalysisBlockUtils = AnalysisBlockUtils$3;
@@ -24282,7 +26101,10 @@ const EnvUtils = EnvUtils$1;
24282
26101
  const DomUtils = DomUtils$1;
24283
26102
  const ObjectUtils = ObjectUtils$1;
24284
26103
  const SVGUtils = SVGUtils_1;
26104
+ const StringUtils = stringUtils;
24285
26105
  const LanguageNameUtils = LanguageNameUtils$1;
26106
+ const MetaRawResultUtils = MetaRawResultUtils$1;
26107
+ const CompactChatUtils = CompactChatUtils$1;
24286
26108
 
24287
26109
  const {
24288
26110
  normalizeLanguageName,
@@ -24368,6 +26190,7 @@ const {
24368
26190
  removeCodeBlockMarkers,
24369
26191
  updateCodeBlockByIndex,
24370
26192
  deleteCodeBlockByIndex,
26193
+ getLineage,
24371
26194
  } = CodeBlockUtils;
24372
26195
 
24373
26196
  const {
@@ -24413,6 +26236,7 @@ const {
24413
26236
  parseContextSection,
24414
26237
  extractContextSections,
24415
26238
  extractContextItemsOverviewTableRows,
26239
+ getContextFiles,
24416
26240
  formatContextContent,
24417
26241
  } = ContextUtils;
24418
26242
 
@@ -24420,6 +26244,28 @@ const {
24420
26244
  trimObjectStrings
24421
26245
  } = ObjectUtils;
24422
26246
 
26247
+ const {
26248
+ splitWithLimit,
26249
+ capitalize,
26250
+ titleCase,
26251
+ camelCase,
26252
+ kebabCase,
26253
+ snakeCase,
26254
+ constantCase,
26255
+ pascalCase,
26256
+ trimWhitespace,
26257
+ truncate,
26258
+ } = StringUtils;
26259
+
26260
+ const {
26261
+ extractMetaRawResultMappings,
26262
+ isMetaRawResultMessage,
26263
+ parseMetaRawResultContent,
26264
+ parseTableRow,
26265
+ parseSize,
26266
+ parseTokens
26267
+ } = MetaRawResultUtils;
26268
+
24423
26269
 
24424
26270
  /**
24425
26271
  * GitSenseChatUtils class provides a unified interface to code block and patch utilities.
@@ -24660,7 +26506,12 @@ var GitSenseChatUtils_1 = {
24660
26506
  EnvUtils,
24661
26507
  DomUtils,
24662
26508
  ObjectUtils,
26509
+ StringUtils,
24663
26510
  SVGUtils,
26511
+ MetaRawResultUtils,
26512
+ CompactChatUtils,
26513
+ ReferenceMessageUtils: CompactChatUtils.ReferenceMessageUtils,
26514
+ CompactedMessageUtils: CompactChatUtils.CompactedMessageUtils,
24664
26515
 
24665
26516
  // --- Individual Function Exports (sourced correctly) ---
24666
26517
 
@@ -24753,6 +26604,18 @@ var GitSenseChatUtils_1 = {
24753
26604
  // Object Utils
24754
26605
  trimObjectStrings,
24755
26606
 
26607
+ // String Utils
26608
+ splitWithLimit,
26609
+ capitalize,
26610
+ titleCase,
26611
+ camelCase,
26612
+ kebabCase,
26613
+ snakeCase,
26614
+ constantCase,
26615
+ pascalCase,
26616
+ trimWhitespace,
26617
+ truncate,
26618
+
24756
26619
  // Markdown Utils
24757
26620
  createMarkdownRenderer,
24758
26621
  removeSignature,
@@ -24772,10 +26635,19 @@ var GitSenseChatUtils_1 = {
24772
26635
  getApiKey,
24773
26636
 
24774
26637
  // Context Utils
26638
+ getContextFiles,
24775
26639
  parseContextSection,
24776
26640
  extractContextSections,
24777
26641
  extractContextItemsOverviewTableRows,
24778
26642
  formatContextContent,
26643
+
26644
+ // MetaRawResult Utils
26645
+ extractMetaRawResultMappings,
26646
+ isMetaRawResultMessage,
26647
+ parseMetaRawResultContent,
26648
+ parseTableRow,
26649
+ parseSize,
26650
+ parseTokens,
24779
26651
  };
24780
26652
 
24781
26653
  var GitSenseChatUtils$1 = /*@__PURE__*/getDefaultExportFromCjs(GitSenseChatUtils_1);