@gitsense/gsc-utils 0.2.8 → 0.2.9

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.
@@ -10455,26 +10455,108 @@ var CodeBlockUtils$4 = {
10455
10455
  removeLineNumbers: removeLineNumbers$1,
10456
10456
  };
10457
10457
 
10458
- /**
10458
+ /*
10459
10459
  * Component: ContextUtils
10460
10460
  * Block-UUID: c199efe3-003c-4226-af3c-d460392a6569
10461
10461
  * Parent-UUID: N/A
10462
- * Version: 1.0.0
10463
- * Description: Provides utility functions for parsing context message sections to extract file details and code blocks.
10462
+ * Version: 1.1.0
10463
+ * Description: Provides utility functions for parsing context message sections to extract file details and code blocks, and for formatting content for LLM context.
10464
10464
  * Language: JavaScript
10465
10465
  * Created-at: 2025-05-09T01:36:20.107Z
10466
- * Authors: Gemini 2.5 Flash Thinking (v1.0.0)
10466
+ * Authors: Gemini 2.5 Flash Thinking (v1.0.0), Gemini 2.5 Flash (v1.1.0)
10467
10467
  */
10468
10468
 
10469
10469
  const CodeBlockUtils$3 = CodeBlockUtils$4;
10470
10470
  const MessageUtils$2 = MessageUtils$3;
10471
10471
 
10472
+ /**
10473
+ * Formats bytes into human-readable string (KB, MB, GB)
10474
+ * @param {number} bytes - Number of bytes
10475
+ * @returns {string} Formatted size string
10476
+ */
10477
+ function _formatBytes(bytes) {
10478
+ if (typeof bytes !== 'number' || isNaN(bytes)) return '0 bytes';
10479
+ if (bytes === 0) return '0 bytes';
10480
+
10481
+ const k = 1024;
10482
+ const sizes = ['bytes', 'KB', 'MB', 'GB'];
10483
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
10484
+
10485
+ // Special case for bytes (no decimal)
10486
+ if (i === 0) return `${bytes} ${sizes[i]}`;
10487
+
10488
+ const value = bytes / Math.pow(k, i);
10489
+ // Show no decimal if whole number
10490
+ const decimalPlaces = value % 1 === 0 ? 0 : 1;
10491
+ return `${value.toFixed(decimalPlaces)} ${sizes[i]}`;
10492
+ }
10493
+
10494
+ /**
10495
+ * Creates a summary of items for human readers
10496
+ * @param {Array} items - Array of loaded items
10497
+ * @param {string} contentType - Content type
10498
+ * @returns {string} Human-readable summary
10499
+ */
10500
+ function _createContextSummary(items, contentType) {
10501
+ if (items.length === 0) return '';
10502
+
10503
+ // Group by files and trees
10504
+ const files = items.filter(item => item.metadata?.type === 'git-blob');
10505
+ const trees = items.filter(item => item.metadata?.type === 'git-tree' || item.metadata?.type === 'git-ref');
10506
+
10507
+ // Calculate total size and tokens
10508
+ const totalSize = items.reduce((sum, item) => sum + (item.size || 0), 0);
10509
+ const totalTokens = items.reduce((sum, item) => sum + (item.tokenCount || 0), 0);
10510
+
10511
+ let summary = contentType === 'file content'
10512
+ ? `\n**Summary:** ${items.length} file${items.length === 1 ? '' : 's'} (${_formatBytes(totalSize)}, ${totalTokens.toLocaleString()} tokens)\n\n`
10513
+ : `\n**Summary:** ${files.length} file${files.length === 1 ? '' : 's'} - ${trees.length} tree${trees.length === 1 ? '' : 's'}\n\n`;
10514
+
10515
+ // Add brief description of first X files
10516
+ const maxFiles = 10;
10517
+ const displayItems = items.slice(0, maxFiles);
10518
+ if (displayItems.length > 0) {
10519
+ displayItems.forEach(item => {
10520
+ if (item.tokenCount) {
10521
+ summary += `- ${item.name} - ${_formatBytes(item.size)}, ${item.tokenCount.toLocaleString()} tokens\n`;
10522
+ } else {
10523
+ summary += `- ${item.name} - Not analyzed\n`;
10524
+ }
10525
+ });
10526
+
10527
+ // Add note if there are more files
10528
+ if (items.length > maxFiles) {
10529
+ summary += `- ... and ${items.length - maxFiles} more\n`;
10530
+ }
10531
+ }
10532
+
10533
+ return summary + "\n";
10534
+ }
10535
+
10536
+ /**
10537
+ * Escapes backticks in code blocks to prevent premature termination of LLM-generated code blocks.
10538
+ * @param {string} content - The content string to escape.
10539
+ * @returns {{escapedContent: string, escapedLineNums: Array<number>}} Object with escaped content and line numbers of escaped lines.
10540
+ */
10541
+ function _escapeCodeBlocks(content) {
10542
+ const escapedLineNums = [];
10543
+ const escapedLines = content.replace(/\n$/, '').split('\n').map((line, i) => {
10544
+ if (line.trimStart().startsWith('```')) {
10545
+ line = '\\' + line.trimStart();
10546
+ escapedLineNums.push(i + 1);
10547
+ }
10548
+ return line;
10549
+ });
10550
+ return { escapedContent: escapedLines.join('\n'), escapedLineNums };
10551
+ }
10552
+
10553
+
10472
10554
  /**
10473
10555
  * Parses context details from a context message section.
10474
10556
  * @param {string} sectionText - The text content of a single context section (starting from the file header).
10475
10557
  * @returns {Object|null} An object with file details (name, path, meta, content) or null if parsing fails.
10476
10558
  */
10477
- function parseContextSection(sectionText) {
10559
+ function parseContextSection$1(sectionText) {
10478
10560
  const contextSection = {
10479
10561
  name: 'Unknown File',
10480
10562
  content: null,
@@ -10550,7 +10632,7 @@ function parseContextSection(sectionText) {
10550
10632
  * @returns {Array<Object>} An array of parsed context section objects.
10551
10633
  * @throws {Error} If the message content is not a valid context message.
10552
10634
  */
10553
- function extractContextSections(messageContent) {
10635
+ function extractContextSections$1(messageContent) {
10554
10636
  // Use the utility function to validate the message type
10555
10637
  if (!MessageUtils$2.isContextMessage(messageContent)) {
10556
10638
  throw new Error("Invalid message type: Content is not a context message.");
@@ -10564,7 +10646,7 @@ function extractContextSections(messageContent) {
10564
10646
 
10565
10647
  // Process sections starting from the first potential path delimiter.
10566
10648
  for (let i = 0; i < sections.length; i++) {
10567
- const contextSection = parseContextSection(sections[i]);
10649
+ const contextSection = parseContextSection$1(sections[i]);
10568
10650
 
10569
10651
  if (contextSection) {
10570
10652
  contextSections.push(contextSection);
@@ -10579,7 +10661,7 @@ function extractContextSections(messageContent) {
10579
10661
  return contextSections;
10580
10662
  }
10581
10663
 
10582
- function extractContextItemsOverviewTableRows(messageContent) {
10664
+ function extractContextItemsOverviewTableRows$1(messageContent) {
10583
10665
  const lines = messageContent.trim().split('\n');
10584
10666
  const startIndex = lines.findIndex(line => line.startsWith('---Start of Overview Items---')) + 4;
10585
10667
 
@@ -10629,10 +10711,85 @@ function extractContextItemsOverviewTableRows(messageContent) {
10629
10711
  return rows;
10630
10712
  }
10631
10713
 
10714
+ /**
10715
+ * Formats content for context based on content type.
10716
+ * This function is adapted from the original `formatContentForContext` in `formatterUtils.js`.
10717
+ *
10718
+ * @param {Array<Object>} items - Array of loaded items, each with `chatId`, `name`, `content`, `meta`, `repo` (from chatsApi.getBlobDetailsByChatIds).
10719
+ * @param {string} contentType - Type of content ('file content' or 'overview'). For batch analysis, always 'file content'.
10720
+ * @param {string} contentOption - Option for the content type. For batch analysis, always 'imported'.
10721
+ * @returns {string} Formatted text for context.
10722
+ */
10723
+ function formatContextContent$1(items, contentType, contentOption) {
10724
+ if (items.length === 0) {
10725
+ return 'No content loaded';
10726
+ }
10727
+
10728
+ let result = '';
10729
+
10730
+ // Header based on content type
10731
+ if (contentType === 'overview') {
10732
+ result += `## OVERVIEW - ${contentOption.toUpperCase()}\n`;
10733
+ } else {
10734
+ result += `## FILE CONTENT - ${contentOption.toUpperCase()}\n`;
10735
+ }
10736
+
10737
+ // Summary of items
10738
+ result += _createContextSummary(items, contentType);
10739
+ result += "\n---Start of Context---\n\n";
10740
+
10741
+ items.forEach((item, index) => {
10742
+ // Ensure item has necessary properties, especially meta and repo
10743
+ const itemMeta = item.meta || {};
10744
+ const itemRepo = item.repo || {};
10745
+
10746
+ result += "#### `"+item.name+"`\n";
10747
+
10748
+ const { escapedContent, escapedLineNums } = _escapeCodeBlocks(item.content);
10749
+
10750
+ // Always include metadata for batch analysis context
10751
+ result +=
10752
+ `- Repo: ${itemRepo.fullName || 'N/A'}\n`+
10753
+ `- Path: ${itemMeta.path || 'N/A'}\n`;
10754
+
10755
+ // Size and Tokens are specific to 'file content'
10756
+ if (contentType === 'file content') {
10757
+ result += `- Size: ${_formatBytes(itemMeta.size)}\n`;
10758
+ result += `- Tokens: ${itemMeta.tokens?.content?.estimate || 'N/A'}\n`;
10759
+ } else {
10760
+ const type = itemMeta.type || 'unknown';
10761
+ result += `- Type: ${type === 'git-blob' ? 'file' : type === 'git-tree' || type === 'git-ref' ? 'directory' : type }\n`;
10762
+ result += `- Tokens: ${itemMeta.tokens?.analysis?.[contentOption.toLowerCase()]?.estimate || 'N/A'}\n`;
10763
+ }
10764
+
10765
+ result += `- Chat ID: ${item.chatId}\n`;
10766
+
10767
+ if (escapedLineNums.length) {
10768
+ result += `- Escaped Lines: ${escapedLineNums.join(',')}\n`;
10769
+ }
10770
+
10771
+ result += '\n';
10772
+
10773
+ // The original formatterUtils had a special summary handling.
10774
+ // For batch analysis, we want the full file content.
10775
+ // The `if (!escapedContent.includes('Component: New Analyzer Chat') && escapedContent.match(summary))`
10776
+ // logic is specific to the frontend's overview builder and should not be applied here.
10777
+ result += "```"+(itemMeta.highlight || '')+"\n"+escapedContent+"\n```";
10778
+
10779
+ if (index !== items.length - 1) {
10780
+ result += '\n---End of Item---\n';
10781
+ }
10782
+ });
10783
+
10784
+ return result;
10785
+ }
10786
+
10787
+
10632
10788
  var ContextUtils$2 = {
10633
- parseContextSection,
10634
- extractContextSections,
10635
- extractContextItemsOverviewTableRows
10789
+ parseContextSection: parseContextSection$1,
10790
+ extractContextSections: extractContextSections$1,
10791
+ extractContextItemsOverviewTableRows: extractContextItemsOverviewTableRows$1,
10792
+ formatContextContent: formatContextContent$1, // Export the new method
10636
10793
  };
10637
10794
 
10638
10795
  /*
@@ -11849,11 +12006,11 @@ var EnvUtils$1 = {
11849
12006
  * Component: GitSenseChatUtils
11850
12007
  * Block-UUID: 5e8d1a9c-0b3f-4e1a-8c7d-9f0b2e1d3a4b
11851
12008
  * Parent-UUID: 7a9b1c8e-f1a4-4b2d-9e8f-6f7a0b1c2d3f
11852
- * Version: 2.1.3
12009
+ * Version: 2.1.4
11853
12010
  * 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 and EnvUtils.
11854
12011
  * Language: JavaScript
11855
12012
  * Created-at: 2025-04-15T16:04:26.780Z
11856
- * 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)
12013
+ * 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)
11857
12014
  */
11858
12015
 
11859
12016
  const ChatUtils = ChatUtils$1;
@@ -11970,6 +12127,13 @@ const {
11970
12127
  getApiKey
11971
12128
  } = EnvUtils;
11972
12129
 
12130
+ const {
12131
+ parseContextSection,
12132
+ extractContextSections,
12133
+ extractContextItemsOverviewTableRows,
12134
+ formatContextContent,
12135
+ } = ContextUtils;
12136
+
11973
12137
 
11974
12138
  /**
11975
12139
  * GitSenseChatUtils class provides a unified interface to code block and patch utilities.
@@ -12280,8 +12444,11 @@ var GitSenseChatUtils_1 = {
12280
12444
  loadEnv,
12281
12445
  getApiKey,
12282
12446
 
12283
- // Note: Internal helpers like findAllCodeFences, matchFencesAndExtractBlocks are
12284
- // typically not exported directly from the facade but are available via CodeBlockUtils object.
12447
+ // Context Utils
12448
+ parseContextSection,
12449
+ extractContextSections,
12450
+ extractContextItemsOverviewTableRows,
12451
+ formatContextContent,
12285
12452
  };
12286
12453
 
12287
12454
  var GitSenseChatUtils$1 = /*@__PURE__*/getDefaultExportFromCjs(GitSenseChatUtils_1);
@@ -10453,26 +10453,108 @@ var CodeBlockUtils$4 = {
10453
10453
  removeLineNumbers: removeLineNumbers$1,
10454
10454
  };
10455
10455
 
10456
- /**
10456
+ /*
10457
10457
  * Component: ContextUtils
10458
10458
  * Block-UUID: c199efe3-003c-4226-af3c-d460392a6569
10459
10459
  * Parent-UUID: N/A
10460
- * Version: 1.0.0
10461
- * Description: Provides utility functions for parsing context message sections to extract file details and code blocks.
10460
+ * Version: 1.1.0
10461
+ * Description: Provides utility functions for parsing context message sections to extract file details and code blocks, and for formatting content for LLM context.
10462
10462
  * Language: JavaScript
10463
10463
  * Created-at: 2025-05-09T01:36:20.107Z
10464
- * Authors: Gemini 2.5 Flash Thinking (v1.0.0)
10464
+ * Authors: Gemini 2.5 Flash Thinking (v1.0.0), Gemini 2.5 Flash (v1.1.0)
10465
10465
  */
10466
10466
 
10467
10467
  const CodeBlockUtils$3 = CodeBlockUtils$4;
10468
10468
  const MessageUtils$2 = MessageUtils$3;
10469
10469
 
10470
+ /**
10471
+ * Formats bytes into human-readable string (KB, MB, GB)
10472
+ * @param {number} bytes - Number of bytes
10473
+ * @returns {string} Formatted size string
10474
+ */
10475
+ function _formatBytes(bytes) {
10476
+ if (typeof bytes !== 'number' || isNaN(bytes)) return '0 bytes';
10477
+ if (bytes === 0) return '0 bytes';
10478
+
10479
+ const k = 1024;
10480
+ const sizes = ['bytes', 'KB', 'MB', 'GB'];
10481
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
10482
+
10483
+ // Special case for bytes (no decimal)
10484
+ if (i === 0) return `${bytes} ${sizes[i]}`;
10485
+
10486
+ const value = bytes / Math.pow(k, i);
10487
+ // Show no decimal if whole number
10488
+ const decimalPlaces = value % 1 === 0 ? 0 : 1;
10489
+ return `${value.toFixed(decimalPlaces)} ${sizes[i]}`;
10490
+ }
10491
+
10492
+ /**
10493
+ * Creates a summary of items for human readers
10494
+ * @param {Array} items - Array of loaded items
10495
+ * @param {string} contentType - Content type
10496
+ * @returns {string} Human-readable summary
10497
+ */
10498
+ function _createContextSummary(items, contentType) {
10499
+ if (items.length === 0) return '';
10500
+
10501
+ // Group by files and trees
10502
+ const files = items.filter(item => item.metadata?.type === 'git-blob');
10503
+ const trees = items.filter(item => item.metadata?.type === 'git-tree' || item.metadata?.type === 'git-ref');
10504
+
10505
+ // Calculate total size and tokens
10506
+ const totalSize = items.reduce((sum, item) => sum + (item.size || 0), 0);
10507
+ const totalTokens = items.reduce((sum, item) => sum + (item.tokenCount || 0), 0);
10508
+
10509
+ let summary = contentType === 'file content'
10510
+ ? `\n**Summary:** ${items.length} file${items.length === 1 ? '' : 's'} (${_formatBytes(totalSize)}, ${totalTokens.toLocaleString()} tokens)\n\n`
10511
+ : `\n**Summary:** ${files.length} file${files.length === 1 ? '' : 's'} - ${trees.length} tree${trees.length === 1 ? '' : 's'}\n\n`;
10512
+
10513
+ // Add brief description of first X files
10514
+ const maxFiles = 10;
10515
+ const displayItems = items.slice(0, maxFiles);
10516
+ if (displayItems.length > 0) {
10517
+ displayItems.forEach(item => {
10518
+ if (item.tokenCount) {
10519
+ summary += `- ${item.name} - ${_formatBytes(item.size)}, ${item.tokenCount.toLocaleString()} tokens\n`;
10520
+ } else {
10521
+ summary += `- ${item.name} - Not analyzed\n`;
10522
+ }
10523
+ });
10524
+
10525
+ // Add note if there are more files
10526
+ if (items.length > maxFiles) {
10527
+ summary += `- ... and ${items.length - maxFiles} more\n`;
10528
+ }
10529
+ }
10530
+
10531
+ return summary + "\n";
10532
+ }
10533
+
10534
+ /**
10535
+ * Escapes backticks in code blocks to prevent premature termination of LLM-generated code blocks.
10536
+ * @param {string} content - The content string to escape.
10537
+ * @returns {{escapedContent: string, escapedLineNums: Array<number>}} Object with escaped content and line numbers of escaped lines.
10538
+ */
10539
+ function _escapeCodeBlocks(content) {
10540
+ const escapedLineNums = [];
10541
+ const escapedLines = content.replace(/\n$/, '').split('\n').map((line, i) => {
10542
+ if (line.trimStart().startsWith('```')) {
10543
+ line = '\\' + line.trimStart();
10544
+ escapedLineNums.push(i + 1);
10545
+ }
10546
+ return line;
10547
+ });
10548
+ return { escapedContent: escapedLines.join('\n'), escapedLineNums };
10549
+ }
10550
+
10551
+
10470
10552
  /**
10471
10553
  * Parses context details from a context message section.
10472
10554
  * @param {string} sectionText - The text content of a single context section (starting from the file header).
10473
10555
  * @returns {Object|null} An object with file details (name, path, meta, content) or null if parsing fails.
10474
10556
  */
10475
- function parseContextSection(sectionText) {
10557
+ function parseContextSection$1(sectionText) {
10476
10558
  const contextSection = {
10477
10559
  name: 'Unknown File',
10478
10560
  content: null,
@@ -10548,7 +10630,7 @@ function parseContextSection(sectionText) {
10548
10630
  * @returns {Array<Object>} An array of parsed context section objects.
10549
10631
  * @throws {Error} If the message content is not a valid context message.
10550
10632
  */
10551
- function extractContextSections(messageContent) {
10633
+ function extractContextSections$1(messageContent) {
10552
10634
  // Use the utility function to validate the message type
10553
10635
  if (!MessageUtils$2.isContextMessage(messageContent)) {
10554
10636
  throw new Error("Invalid message type: Content is not a context message.");
@@ -10562,7 +10644,7 @@ function extractContextSections(messageContent) {
10562
10644
 
10563
10645
  // Process sections starting from the first potential path delimiter.
10564
10646
  for (let i = 0; i < sections.length; i++) {
10565
- const contextSection = parseContextSection(sections[i]);
10647
+ const contextSection = parseContextSection$1(sections[i]);
10566
10648
 
10567
10649
  if (contextSection) {
10568
10650
  contextSections.push(contextSection);
@@ -10577,7 +10659,7 @@ function extractContextSections(messageContent) {
10577
10659
  return contextSections;
10578
10660
  }
10579
10661
 
10580
- function extractContextItemsOverviewTableRows(messageContent) {
10662
+ function extractContextItemsOverviewTableRows$1(messageContent) {
10581
10663
  const lines = messageContent.trim().split('\n');
10582
10664
  const startIndex = lines.findIndex(line => line.startsWith('---Start of Overview Items---')) + 4;
10583
10665
 
@@ -10627,10 +10709,85 @@ function extractContextItemsOverviewTableRows(messageContent) {
10627
10709
  return rows;
10628
10710
  }
10629
10711
 
10712
+ /**
10713
+ * Formats content for context based on content type.
10714
+ * This function is adapted from the original `formatContentForContext` in `formatterUtils.js`.
10715
+ *
10716
+ * @param {Array<Object>} items - Array of loaded items, each with `chatId`, `name`, `content`, `meta`, `repo` (from chatsApi.getBlobDetailsByChatIds).
10717
+ * @param {string} contentType - Type of content ('file content' or 'overview'). For batch analysis, always 'file content'.
10718
+ * @param {string} contentOption - Option for the content type. For batch analysis, always 'imported'.
10719
+ * @returns {string} Formatted text for context.
10720
+ */
10721
+ function formatContextContent$1(items, contentType, contentOption) {
10722
+ if (items.length === 0) {
10723
+ return 'No content loaded';
10724
+ }
10725
+
10726
+ let result = '';
10727
+
10728
+ // Header based on content type
10729
+ if (contentType === 'overview') {
10730
+ result += `## OVERVIEW - ${contentOption.toUpperCase()}\n`;
10731
+ } else {
10732
+ result += `## FILE CONTENT - ${contentOption.toUpperCase()}\n`;
10733
+ }
10734
+
10735
+ // Summary of items
10736
+ result += _createContextSummary(items, contentType);
10737
+ result += "\n---Start of Context---\n\n";
10738
+
10739
+ items.forEach((item, index) => {
10740
+ // Ensure item has necessary properties, especially meta and repo
10741
+ const itemMeta = item.meta || {};
10742
+ const itemRepo = item.repo || {};
10743
+
10744
+ result += "#### `"+item.name+"`\n";
10745
+
10746
+ const { escapedContent, escapedLineNums } = _escapeCodeBlocks(item.content);
10747
+
10748
+ // Always include metadata for batch analysis context
10749
+ result +=
10750
+ `- Repo: ${itemRepo.fullName || 'N/A'}\n`+
10751
+ `- Path: ${itemMeta.path || 'N/A'}\n`;
10752
+
10753
+ // Size and Tokens are specific to 'file content'
10754
+ if (contentType === 'file content') {
10755
+ result += `- Size: ${_formatBytes(itemMeta.size)}\n`;
10756
+ result += `- Tokens: ${itemMeta.tokens?.content?.estimate || 'N/A'}\n`;
10757
+ } else {
10758
+ const type = itemMeta.type || 'unknown';
10759
+ result += `- Type: ${type === 'git-blob' ? 'file' : type === 'git-tree' || type === 'git-ref' ? 'directory' : type }\n`;
10760
+ result += `- Tokens: ${itemMeta.tokens?.analysis?.[contentOption.toLowerCase()]?.estimate || 'N/A'}\n`;
10761
+ }
10762
+
10763
+ result += `- Chat ID: ${item.chatId}\n`;
10764
+
10765
+ if (escapedLineNums.length) {
10766
+ result += `- Escaped Lines: ${escapedLineNums.join(',')}\n`;
10767
+ }
10768
+
10769
+ result += '\n';
10770
+
10771
+ // The original formatterUtils had a special summary handling.
10772
+ // For batch analysis, we want the full file content.
10773
+ // The `if (!escapedContent.includes('Component: New Analyzer Chat') && escapedContent.match(summary))`
10774
+ // logic is specific to the frontend's overview builder and should not be applied here.
10775
+ result += "```"+(itemMeta.highlight || '')+"\n"+escapedContent+"\n```";
10776
+
10777
+ if (index !== items.length - 1) {
10778
+ result += '\n---End of Item---\n';
10779
+ }
10780
+ });
10781
+
10782
+ return result;
10783
+ }
10784
+
10785
+
10630
10786
  var ContextUtils$2 = {
10631
- parseContextSection,
10632
- extractContextSections,
10633
- extractContextItemsOverviewTableRows
10787
+ parseContextSection: parseContextSection$1,
10788
+ extractContextSections: extractContextSections$1,
10789
+ extractContextItemsOverviewTableRows: extractContextItemsOverviewTableRows$1,
10790
+ formatContextContent: formatContextContent$1, // Export the new method
10634
10791
  };
10635
10792
 
10636
10793
  /*
@@ -11847,11 +12004,11 @@ var EnvUtils$1 = {
11847
12004
  * Component: GitSenseChatUtils
11848
12005
  * Block-UUID: 5e8d1a9c-0b3f-4e1a-8c7d-9f0b2e1d3a4b
11849
12006
  * Parent-UUID: 7a9b1c8e-f1a4-4b2d-9e8f-6f7a0b1c2d3f
11850
- * Version: 2.1.3
12007
+ * Version: 2.1.4
11851
12008
  * 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 and EnvUtils.
11852
12009
  * Language: JavaScript
11853
12010
  * Created-at: 2025-04-15T16:04:26.780Z
11854
- * 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)
12011
+ * 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)
11855
12012
  */
11856
12013
 
11857
12014
  const ChatUtils = ChatUtils$1;
@@ -11968,6 +12125,13 @@ const {
11968
12125
  getApiKey
11969
12126
  } = EnvUtils;
11970
12127
 
12128
+ const {
12129
+ parseContextSection,
12130
+ extractContextSections,
12131
+ extractContextItemsOverviewTableRows,
12132
+ formatContextContent,
12133
+ } = ContextUtils;
12134
+
11971
12135
 
11972
12136
  /**
11973
12137
  * GitSenseChatUtils class provides a unified interface to code block and patch utilities.
@@ -12278,8 +12442,11 @@ var GitSenseChatUtils_1 = {
12278
12442
  loadEnv,
12279
12443
  getApiKey,
12280
12444
 
12281
- // Note: Internal helpers like findAllCodeFences, matchFencesAndExtractBlocks are
12282
- // typically not exported directly from the facade but are available via CodeBlockUtils object.
12445
+ // Context Utils
12446
+ parseContextSection,
12447
+ extractContextSections,
12448
+ extractContextItemsOverviewTableRows,
12449
+ formatContextContent,
12283
12450
  };
12284
12451
 
12285
12452
  var GitSenseChatUtils$1 = /*@__PURE__*/getDefaultExportFromCjs(GitSenseChatUtils_1);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gitsense/gsc-utils",
3
- "version": "0.2.8",
3
+ "version": "0.2.9",
4
4
  "description": "Utilities for GitSense Chat (GSC)",
5
5
  "main": "dist/gsc-utils.cjs.js",
6
6
  "module": "dist/gsc-utils.esm.js",
@@ -1,18 +1,100 @@
1
- /**
1
+ /*
2
2
  * Component: ContextUtils
3
3
  * Block-UUID: c199efe3-003c-4226-af3c-d460392a6569
4
4
  * Parent-UUID: N/A
5
- * Version: 1.0.0
6
- * Description: Provides utility functions for parsing context message sections to extract file details and code blocks.
5
+ * Version: 1.1.0
6
+ * Description: Provides utility functions for parsing context message sections to extract file details and code blocks, and for formatting content for LLM context.
7
7
  * Language: JavaScript
8
8
  * Created-at: 2025-05-09T01:36:20.107Z
9
- * Authors: Gemini 2.5 Flash Thinking (v1.0.0)
9
+ * Authors: Gemini 2.5 Flash Thinking (v1.0.0), Gemini 2.5 Flash (v1.1.0)
10
10
  */
11
11
 
12
12
 
13
13
  const CodeBlockUtils = require('./CodeBlockUtils');
14
14
  const MessageUtils = require('./MessageUtils');
15
15
 
16
+ /**
17
+ * Formats bytes into human-readable string (KB, MB, GB)
18
+ * @param {number} bytes - Number of bytes
19
+ * @returns {string} Formatted size string
20
+ */
21
+ function _formatBytes(bytes) {
22
+ if (typeof bytes !== 'number' || isNaN(bytes)) return '0 bytes';
23
+ if (bytes === 0) return '0 bytes';
24
+
25
+ const k = 1024;
26
+ const sizes = ['bytes', 'KB', 'MB', 'GB'];
27
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
28
+
29
+ // Special case for bytes (no decimal)
30
+ if (i === 0) return `${bytes} ${sizes[i]}`;
31
+
32
+ const value = bytes / Math.pow(k, i);
33
+ // Show no decimal if whole number
34
+ const decimalPlaces = value % 1 === 0 ? 0 : 1;
35
+ return `${value.toFixed(decimalPlaces)} ${sizes[i]}`;
36
+ }
37
+
38
+ /**
39
+ * Creates a summary of items for human readers
40
+ * @param {Array} items - Array of loaded items
41
+ * @param {string} contentType - Content type
42
+ * @returns {string} Human-readable summary
43
+ */
44
+ function _createContextSummary(items, contentType) {
45
+ if (items.length === 0) return '';
46
+
47
+ // Group by files and trees
48
+ const files = items.filter(item => item.metadata?.type === 'git-blob');
49
+ const trees = items.filter(item => item.metadata?.type === 'git-tree' || item.metadata?.type === 'git-ref');
50
+
51
+ // Calculate total size and tokens
52
+ const totalSize = items.reduce((sum, item) => sum + (item.size || 0), 0);
53
+ const totalTokens = items.reduce((sum, item) => sum + (item.tokenCount || 0), 0);
54
+
55
+ let summary = contentType === 'file content'
56
+ ? `\n**Summary:** ${items.length} file${items.length === 1 ? '' : 's'} (${_formatBytes(totalSize)}, ${totalTokens.toLocaleString()} tokens)\n\n`
57
+ : `\n**Summary:** ${files.length} file${files.length === 1 ? '' : 's'} - ${trees.length} tree${trees.length === 1 ? '' : 's'}\n\n`;
58
+
59
+ // Add brief description of first X files
60
+ const maxFiles = 10;
61
+ const displayItems = items.slice(0, maxFiles);
62
+ if (displayItems.length > 0) {
63
+ displayItems.forEach(item => {
64
+ if (item.tokenCount) {
65
+ summary += `- ${item.name} - ${_formatBytes(item.size)}, ${item.tokenCount.toLocaleString()} tokens\n`;
66
+ } else {
67
+ summary += `- ${item.name} - Not analyzed\n`;
68
+ }
69
+ });
70
+
71
+ // Add note if there are more files
72
+ if (items.length > maxFiles) {
73
+ summary += `- ... and ${items.length - maxFiles} more\n`;
74
+ }
75
+ }
76
+
77
+ return summary + "\n";
78
+ }
79
+
80
+ /**
81
+ * Escapes backticks in code blocks to prevent premature termination of LLM-generated code blocks.
82
+ * @param {string} content - The content string to escape.
83
+ * @returns {{escapedContent: string, escapedLineNums: Array<number>}} Object with escaped content and line numbers of escaped lines.
84
+ */
85
+ function _escapeCodeBlocks(content) {
86
+ const escapedLineNums = [];
87
+ const escapedLines = content.replace(/\n$/, '').split('\n').map((line, i) => {
88
+ if (line.trimStart().startsWith('```')) {
89
+ line = '\\' + line.trimStart();
90
+ escapedLineNums.push(i + 1);
91
+ }
92
+ return line;
93
+ });
94
+ return { escapedContent: escapedLines.join('\n'), escapedLineNums };
95
+ }
96
+
97
+
16
98
  /**
17
99
  * Parses context details from a context message section.
18
100
  * @param {string} sectionText - The text content of a single context section (starting from the file header).
@@ -173,8 +255,84 @@ function extractContextItemsOverviewTableRows(messageContent) {
173
255
  return rows;
174
256
  }
175
257
 
258
+ /**
259
+ * Formats content for context based on content type.
260
+ * This function is adapted from the original `formatContentForContext` in `formatterUtils.js`.
261
+ *
262
+ * @param {Array<Object>} items - Array of loaded items, each with `chatId`, `name`, `content`, `meta`, `repo` (from chatsApi.getBlobDetailsByChatIds).
263
+ * @param {string} contentType - Type of content ('file content' or 'overview'). For batch analysis, always 'file content'.
264
+ * @param {string} contentOption - Option for the content type. For batch analysis, always 'imported'.
265
+ * @returns {string} Formatted text for context.
266
+ */
267
+ function formatContextContent(items, contentType, contentOption) {
268
+ if (items.length === 0) {
269
+ return 'No content loaded';
270
+ }
271
+
272
+ let result = '';
273
+
274
+ // Header based on content type
275
+ if (contentType === 'overview') {
276
+ const label = contentOption === "long" ? "comprehensive" : "short";
277
+ result += `## OVERVIEW - ${contentOption.toUpperCase()}\n`;
278
+ } else {
279
+ result += `## FILE CONTENT - ${contentOption.toUpperCase()}\n`;
280
+ }
281
+
282
+ // Summary of items
283
+ result += _createContextSummary(items, contentType);
284
+ result += "\n---Start of Context---\n\n";
285
+
286
+ items.forEach((item, index) => {
287
+ // Ensure item has necessary properties, especially meta and repo
288
+ const itemMeta = item.meta || {};
289
+ const itemRepo = item.repo || {};
290
+
291
+ result += "#### `"+item.name+"`\n";
292
+
293
+ const { escapedContent, escapedLineNums } = _escapeCodeBlocks(item.content);
294
+
295
+ // Always include metadata for batch analysis context
296
+ result +=
297
+ `- Repo: ${itemRepo.fullName || 'N/A'}\n`+
298
+ `- Path: ${itemMeta.path || 'N/A'}\n`;
299
+
300
+ // Size and Tokens are specific to 'file content'
301
+ if (contentType === 'file content') {
302
+ result += `- Size: ${_formatBytes(itemMeta.size)}\n`;
303
+ result += `- Tokens: ${itemMeta.tokens?.content?.estimate || 'N/A'}\n`;
304
+ } else {
305
+ const type = itemMeta.type || 'unknown';
306
+ result += `- Type: ${type === 'git-blob' ? 'file' : type === 'git-tree' || type === 'git-ref' ? 'directory' : type }\n`;
307
+ result += `- Tokens: ${itemMeta.tokens?.analysis?.[contentOption.toLowerCase()]?.estimate || 'N/A'}\n`;
308
+ }
309
+
310
+ result += `- Chat ID: ${item.chatId}\n`;
311
+
312
+ if (escapedLineNums.length) {
313
+ result += `- Escaped Lines: ${escapedLineNums.join(',')}\n`;
314
+ }
315
+
316
+ result += '\n';
317
+
318
+ // The original formatterUtils had a special summary handling.
319
+ // For batch analysis, we want the full file content.
320
+ // The `if (!escapedContent.includes('Component: New Analyzer Chat') && escapedContent.match(summary))`
321
+ // logic is specific to the frontend's overview builder and should not be applied here.
322
+ result += "```"+(itemMeta.highlight || '')+"\n"+escapedContent+"\n```";
323
+
324
+ if (index !== items.length - 1) {
325
+ result += '\n---End of Item---\n';
326
+ }
327
+ });
328
+
329
+ return result;
330
+ }
331
+
332
+
176
333
  module.exports = {
177
334
  parseContextSection,
178
335
  extractContextSections,
179
- extractContextItemsOverviewTableRows
336
+ extractContextItemsOverviewTableRows,
337
+ formatContextContent, // Export the new method
180
338
  };
@@ -2,11 +2,11 @@
2
2
  * Component: GitSenseChatUtils
3
3
  * Block-UUID: 5e8d1a9c-0b3f-4e1a-8c7d-9f0b2e1d3a4b
4
4
  * Parent-UUID: 7a9b1c8e-f1a4-4b2d-9e8f-6f7a0b1c2d3f
5
- * Version: 2.1.3
5
+ * Version: 2.1.4
6
6
  * 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 and EnvUtils.
7
7
  * Language: JavaScript
8
8
  * Created-at: 2025-04-15T16:04:26.780Z
9
- * 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)
9
+ * 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)
10
10
  */
11
11
 
12
12
 
@@ -124,6 +124,13 @@ const {
124
124
  getApiKey
125
125
  } = EnvUtils;
126
126
 
127
+ const {
128
+ parseContextSection,
129
+ extractContextSections,
130
+ extractContextItemsOverviewTableRows,
131
+ formatContextContent,
132
+ } = ContextUtils;
133
+
127
134
 
128
135
  /**
129
136
  * GitSenseChatUtils class provides a unified interface to code block and patch utilities.
@@ -434,6 +441,9 @@ module.exports = {
434
441
  loadEnv,
435
442
  getApiKey,
436
443
 
437
- // Note: Internal helpers like findAllCodeFences, matchFencesAndExtractBlocks are
438
- // typically not exported directly from the facade but are available via CodeBlockUtils object.
444
+ // Context Utils
445
+ parseContextSection,
446
+ extractContextSections,
447
+ extractContextItemsOverviewTableRows,
448
+ formatContextContent,
439
449
  };