@gitsense/gsc-utils 0.2.20 → 0.2.23

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.
@@ -373,7 +373,7 @@ function getMessageContentType$1(messageContent) {
373
373
  return 'file-content-context';
374
374
  } else if (trimmedContent.startsWith('## OVERVIEW')) {
375
375
  return 'overview-context';
376
- } else if (trimmedContent.startsWith('## Context Items Overview\n\nThis section provides')) {
376
+ } else if (trimmedContent.startsWith('## Context Overview\n\nThis context overview')) {
377
377
  return 'context-items-overview';
378
378
  } else if (trimmedContent.match(ANALYZE_MESSAGE_REGEXP)) {
379
379
  return 'analyze-message';
@@ -6808,7 +6808,9 @@ function createPatchFromCodeBlocks(CodeBlockUtils, sourceCodeBlockText, targetCo
6808
6808
  const targetContent = targetContentTemp.join('\n\n');
6809
6809
 
6810
6810
  // Get the number of lines in the header + two blank lines
6811
- const sourceHeaderLineCount = sourceHeaderText.split('\n').length;
6811
+ // Note, before the code block header format was solidified, we use to include 'Updated-at' but no more so
6812
+ // we need to filter it out from the source header
6813
+ const sourceHeaderLineCount = sourceHeaderText.split('\n').filter(line => !line.includes('Updated-at')).length;
6812
6814
  const targetHeaderLineCount = targetHeaderText.split('\n').length;
6813
6815
 
6814
6816
  // To keep things simple we are going to require the source and target header line count to be the same.
@@ -6816,7 +6818,7 @@ function createPatchFromCodeBlocks(CodeBlockUtils, sourceCodeBlockText, targetCo
6816
6818
  if (sourceHeaderLineCount !== targetHeaderLineCount) {
6817
6819
  // Some LLMs will not generate a Parent-UUID for the first version so we won't make this
6818
6820
  // fatal if the sourceHeader does not have a Parent-UUID
6819
- if (sourceHeaderLineCount.includes('Parent-UUID')) {
6821
+ if (sourceHeaderText.includes('Parent-UUID')) {
6820
6822
  throw new Error('Source and target header line count must be the same');
6821
6823
  }
6822
6824
  }
@@ -10626,13 +10628,13 @@ var CodeBlockUtils$5 = {
10626
10628
 
10627
10629
  /*
10628
10630
  * Component: ContextUtils
10629
- * Block-UUID: c199efe3-003c-4226-af3c-d460392a6569
10630
- * Parent-UUID: N/A
10631
- * Version: 1.1.0
10631
+ * Block-UUID: a0b71292-b1cc-401a-8ce2-544a047b0fef
10632
+ * Parent-UUID: c018b1f9-2291-4bc9-9c4b-ab53a5db745e
10633
+ * Version: 1.3.0
10632
10634
  * Description: Provides utility functions for parsing context message sections to extract file details and code blocks, and for formatting content for LLM context.
10633
10635
  * Language: JavaScript
10634
10636
  * Created-at: 2025-05-09T01:36:20.107Z
10635
- * Authors: Gemini 2.5 Flash Thinking (v1.0.0), Gemini 2.5 Flash (v1.1.0)
10637
+ * 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)
10636
10638
  */
10637
10639
 
10638
10640
  const CodeBlockUtils$4 = CodeBlockUtils$5;
@@ -10813,12 +10815,26 @@ function extractContextSections$1(messageContent) {
10813
10815
  const [summary, items] = messageContent.split(/\n---Start of Context---\n\n/);
10814
10816
  const sections = items.split(/\n---End of Item---\n/);
10815
10817
 
10818
+ // Determine the origin of the context based on the message header
10819
+ let contextOrigin = 'unknown'; // Default value if not determinable
10820
+ if (messageContent.includes("## FILE CONTENT - WORKING DIRECTORY")) {
10821
+ contextOrigin = 'working-directory';
10822
+ } else if (messageContent.includes("## FILE CONTENT - IMPORTED")) {
10823
+ contextOrigin = 'imported';
10824
+ } else if (messageContent.includes("## OVERVIEW - IMPORTED")) {
10825
+ // Handle the overview case as well, though it uses 'file content' type
10826
+ contextOrigin = 'imported';
10827
+ }
10828
+
10816
10829
  // Process sections starting from the first potential path delimiter.
10817
10830
  for (let i = 0; i < sections.length; i++) {
10818
10831
  const contextSection = parseContextSection$1(sections[i]);
10819
10832
 
10820
10833
  if (contextSection) {
10821
10834
  contextSections.push(contextSection);
10835
+ // Attach the determined context origin to the section
10836
+ // This provides information on how the context was introduced
10837
+ contextSection.contextOrigin = contextOrigin;
10822
10838
  }
10823
10839
  }
10824
10840
 
@@ -10839,6 +10855,7 @@ function extractContextItemsOverviewTableRows$1(messageContent) {
10839
10855
  return null;
10840
10856
  }
10841
10857
 
10858
+ // Map the column number to the type
10842
10859
  const col2Field = {
10843
10860
  1: 'chatId',
10844
10861
  2: 'type', // file, tree, etc.
@@ -10899,10 +10916,14 @@ function formatContextContent$1(items, contentType, contentOption) {
10899
10916
  // Header based on content type
10900
10917
  if (contentType === 'overview') {
10901
10918
  result += `## OVERVIEW - ${contentOption.toUpperCase()}\n`;
10919
+ result += '\n';
10902
10920
  } else {
10903
10921
  result += `## FILE CONTENT - ${contentOption.toUpperCase()}\n`;
10922
+ result += '\n';
10904
10923
  }
10905
10924
 
10925
+ result += 'The following files are provided as context for your request. Use this information to understand the project\'s structure and content. This is a data payload for context; please do not mirror this format in your response.\n';
10926
+ result += '\n';
10906
10927
  // Summary of items
10907
10928
  result += _createContextSummary(items, contentType);
10908
10929
  result += "\n---Start of Context---\n\n";
@@ -12032,6 +12053,174 @@ var LLMUtils$1 = {
12032
12053
  estimateTokens: estimateTokens$1,
12033
12054
  };
12034
12055
 
12056
+ /**
12057
+ * Component: Date Utilities
12058
+ * Block-UUID: fef3b0f4-8055-4690-9336-e68a9cc33a61
12059
+ * Parent-UUID: 716e5ce9-84df-4f12-89e7-4a28e4a39a81
12060
+ * Version: 1.0.0
12061
+ * Description: Date formatting and manipulation utilities for GitSense Chat
12062
+ * Language: JavaScript
12063
+ * Created-at: 2025-10-11T23:26:20.815Z
12064
+ * Authors: Claude-3.5-Sonnet (v1.0.0), Qwen 3 Coder 480B - Cerebras (v1.0.0)
12065
+ */
12066
+
12067
+ /**
12068
+ * Ensures a date string has a timezone indicator
12069
+ * @param {string} dateString - Date string in format "YYYY-MM-DD(T| )HH:mm:ss.SSS"
12070
+ * @returns {string} Date string with 'Z' timezone indicator if not present
12071
+ */
12072
+ function normalizeDateTime(dateString) {
12073
+ return dateString.includes('Z') ? dateString : dateString + 'Z';
12074
+ }
12075
+
12076
+ /**
12077
+ * Calculates the time difference between now and a given date
12078
+ * @param {string} dateString - Date string to compare against current time
12079
+ * @returns {number} Difference in seconds
12080
+ */
12081
+ function getTimeDifference(dateString) {
12082
+ const now = new Date();
12083
+ const date = new Date(normalizeDateTime(dateString));
12084
+ return Math.floor((now - date) / 1000);
12085
+ }
12086
+
12087
+ /**
12088
+ * Formats a time difference in seconds to a human-readable string
12089
+ * @param {number} diff - Time difference in seconds
12090
+ * @returns {string} Formatted string (e.g., "5s ago", "2m ago", "3h ago", "2d ago")
12091
+ */
12092
+ function formatTimeDifference(diff) {
12093
+ if (diff < 60) {
12094
+ return `${diff}s ago`;
12095
+ } else if (diff < 3600) {
12096
+ const minutes = Math.floor(diff / 60);
12097
+ return `${minutes}m ago`;
12098
+ } else if (diff < 86400) {
12099
+ const hours = Math.floor(diff / 3600);
12100
+ return `${hours}h ago`;
12101
+ } else {
12102
+ const days = Math.floor(diff / 86400);
12103
+ return `${days}d ago`;
12104
+ }
12105
+ }
12106
+
12107
+ /**
12108
+ * Formats a date string into a relative time string
12109
+ * @param {string} dateString - Date string to format
12110
+ * @returns {string} Formatted relative time string
12111
+ */
12112
+ function formatAge$1(dateString) {
12113
+ if (!dateString) {
12114
+ return 'N/A';
12115
+ }
12116
+
12117
+ try {
12118
+ const diff = getTimeDifference(dateString);
12119
+ return formatTimeDifference(diff);
12120
+ } catch (error) {
12121
+ console.error('Error formatting date:', error);
12122
+ return 'Invalid date';
12123
+ }
12124
+ }
12125
+
12126
+ /**
12127
+ * Validates a date string format
12128
+ * @param {string} dateString - Date string to validate
12129
+ * @returns {boolean} True if date string is valid
12130
+ */
12131
+ function isValidDateString$1(dateString) {
12132
+ if (!dateString) return false;
12133
+
12134
+ const pattern = /^\d{4}-\d{2}-\d{2}(T| )\d{2}:\d{2}:\d{2}\.\d{3}Z?$/;
12135
+ if (!pattern.test(dateString)) return false;
12136
+
12137
+ const date = new Date(normalizeDateTime(dateString));
12138
+ return date instanceof Date && !isNaN(date);
12139
+ }
12140
+
12141
+ /**
12142
+ * Compares two date strings
12143
+ * @param {string} dateA - First date string
12144
+ * @param {string} dateB - Second date string
12145
+ * @returns {number} -1 if dateA is earlier, 0 if equal, 1 if dateA is later
12146
+ */
12147
+ function compareDates$1(dateA, dateB) {
12148
+ if (!isValidDateString$1(dateA) || !isValidDateString$1(dateB)) {
12149
+ throw new Error(`Invalid date string format. A: ${dateA} B: ${dateB})`);
12150
+ }
12151
+
12152
+ const timeA = new Date(normalizeDateTime(dateA)).getTime();
12153
+ const timeB = new Date(normalizeDateTime(dateB)).getTime();
12154
+
12155
+ if (timeA < timeB) return -1;
12156
+ if (timeA > timeB) return 1;
12157
+ return 0;
12158
+ }
12159
+
12160
+ var DateUtils$1 = {
12161
+ formatAge: formatAge$1,
12162
+ isValidDateString: isValidDateString$1,
12163
+ compareDates: compareDates$1,
12164
+ normalizeDateTime,
12165
+ getTimeDifference,
12166
+ formatTimeDifference
12167
+ };
12168
+
12169
+ /**
12170
+ * Component: Formatter Utilities
12171
+ * Block-UUID: 9b1e07bf-e92c-43c1-9ac0-5dacdb7ae618
12172
+ * Parent-UUID: 9c07d12f-5a05-4402-99d2-85d872d7b2f7
12173
+ * Version: 1.0.0
12174
+ * Description: Utility functions for formatting content in GitSense Chat
12175
+ * Language: JavaScript
12176
+ * Created-at: 2025-10-11T23:27:15.420Z
12177
+ * Authors: Claude 3.7 Sonnet (v1.0.0), Claude 3.7 Sonnet (v1.1.0), Gemini 2.5 Flash Thinking (v1.2.0), Gemini 2.5 Flash Thinking (v1.3.0), Qwen 3 Coder 480B - Cerebras (v1.4.0), Qwen 3 Coder 480B - Cerebras (v1.5.0), Qwen 3 Coder 480B - Cerebras (v1.0.0)
12178
+ */
12179
+
12180
+ /**
12181
+ * Formats bytes into human-readable string (KB, MB, GB)
12182
+ * @param {number} bytes - Number of bytes
12183
+ * @returns {string} Formatted size string
12184
+ */
12185
+ function formatBytes$1(bytes) {
12186
+ if (typeof bytes !== 'number' || isNaN(bytes)) return '0 bytes';
12187
+ if (bytes === 0) return '0 bytes';
12188
+
12189
+ const k = 1024;
12190
+ const sizes = ['bytes', 'KB', 'MB', 'GB'];
12191
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
12192
+
12193
+ // Special case for bytes (no decimal)
12194
+ if (i === 0) return `${bytes} ${sizes[i]}`;
12195
+
12196
+ const value = bytes / Math.pow(k, i);
12197
+ // Show no decimal if whole number
12198
+ const decimalPlaces = value % 1 === 0 ? 0 : 1;
12199
+ return `${value.toFixed(decimalPlaces)} ${sizes[i]}`;
12200
+ }
12201
+
12202
+ /**
12203
+ * Formats token count into human-readable string (k, m, b)
12204
+ * @param {number} count - Number of tokens
12205
+ * @returns {string} Formatted token string
12206
+ */
12207
+ function formatTokens$1(count) {
12208
+ if (typeof count !== 'number' || isNaN(count)) return '0';
12209
+ if (count === 0) return '0';
12210
+
12211
+ if (count < 1000) return `${count}`;
12212
+ if (count < 1000000) return `${(count/1000).toFixed(count >= 10000 ? 1 : 0)}k`;
12213
+ if (count < 1000000000) return `${(count/1000000).toFixed(1)}m`;
12214
+
12215
+ // For billions
12216
+ return `${(count/1000000000).toFixed(1)}b`;
12217
+ }
12218
+
12219
+ var FormatterUtils$1 = {
12220
+ formatBytes: formatBytes$1,
12221
+ formatTokens: formatTokens$1
12222
+ };
12223
+
12035
12224
  /*
12036
12225
  * Component: Config Utilities
12037
12226
  * Block-UUID: 65b39008-da68-462e-bcac-27db873c9ddc
@@ -12230,13 +12419,13 @@ var EnvUtils$1 = {
12230
12419
 
12231
12420
  /*
12232
12421
  * Component: GitSenseChatUtils
12233
- * Block-UUID: 5e8d1a9c-0b3f-4e1a-8c7d-9f0b2e1d3a4b
12234
- * Parent-UUID: 7a9b1c8e-f1a4-4b2d-9e8f-6f7a0b1c2d3f
12235
- * Version: 2.1.4
12422
+ * Block-UUID: d7cdbb94-f335-4f85-bdc4-0dd3ae95127e
12423
+ * Parent-UUID: 5e8d1a9c-0b3f-4e1a-8c7d-9f0b2e1d3a4b
12424
+ * Version: 2.1.5
12236
12425
  * 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.
12237
12426
  * Language: JavaScript
12238
12427
  * Created-at: 2025-04-15T16:04:26.780Z
12239
- * 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)
12428
+ * 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)
12240
12429
  */
12241
12430
 
12242
12431
  const ChatUtils = ChatUtils$1;
@@ -12249,6 +12438,8 @@ const PatchUtils = PatchUtils$2;
12249
12438
  const GSToolBlockUtils = GSToolBlockUtils$3;
12250
12439
  const LLMUtils = LLMUtils$1;
12251
12440
  const JsonUtils = JsonUtils$2;
12441
+ const DateUtils = DateUtils$1;
12442
+ const FormatterUtils = FormatterUtils$1;
12252
12443
  const ConfigUtils = ConfigUtils$1;
12253
12444
  const EnvUtils = EnvUtils$1;
12254
12445
 
@@ -12353,6 +12544,11 @@ const {
12353
12544
  getApiKey
12354
12545
  } = EnvUtils;
12355
12546
 
12547
+ const {
12548
+ formatBytes,
12549
+ formatTokens,
12550
+ } = FormatterUtils;
12551
+
12356
12552
  const {
12357
12553
  parseContextSection,
12358
12554
  extractContextSections,
@@ -12567,6 +12763,15 @@ class GitSenseChatUtils {
12567
12763
  // Export the main class, the aggregated CodeBlockUtils, PatchUtils,
12568
12764
  // and individual functions for backward compatibility or direct use.
12569
12765
  var GitSenseChatUtils_1 = {
12766
+ // New Utility Modules
12767
+ DateUtils,
12768
+ FormatterUtils,
12769
+
12770
+ // Date Utility Functions
12771
+ formatAge: DateUtils.formatAge,
12772
+ compareDates: DateUtils.compareDates,
12773
+ isValidDateString: DateUtils.isValidDateString,
12774
+
12570
12775
  // Main class
12571
12776
  GitSenseChatUtils,
12572
12777
 
@@ -12582,6 +12787,9 @@ var GitSenseChatUtils_1 = {
12582
12787
  GSToolBlockUtils,
12583
12788
  JsonUtils,
12584
12789
  ConfigUtils,
12790
+ DateUtils,
12791
+ FormatterUtils,
12792
+
12585
12793
  EnvUtils,
12586
12794
 
12587
12795
  // --- Individual Function Exports (sourced correctly) ---
@@ -12656,6 +12864,16 @@ var GitSenseChatUtils_1 = {
12656
12864
  // LLM Utils
12657
12865
  estimateTokens,
12658
12866
 
12867
+ // Date Utils
12868
+ formatAge,
12869
+ compareDates,
12870
+ isValidDateString,
12871
+
12872
+ // Formatter Utils
12873
+ formatBytes,
12874
+ formatTokens,
12875
+ formatOverviewTable,
12876
+
12659
12877
  // GS Tool Block
12660
12878
  isToolBlock,
12661
12879
  parseToolBlock,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gitsense/gsc-utils",
3
- "version": "0.2.20",
3
+ "version": "0.2.23",
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,12 +1,12 @@
1
1
  /*
2
2
  * Component: ContextUtils
3
- * Block-UUID: c199efe3-003c-4226-af3c-d460392a6569
4
- * Parent-UUID: N/A
5
- * Version: 1.1.0
3
+ * Block-UUID: a0b71292-b1cc-401a-8ce2-544a047b0fef
4
+ * Parent-UUID: c018b1f9-2291-4bc9-9c4b-ab53a5db745e
5
+ * Version: 1.3.0
6
6
  * Description: Provides utility functions for parsing context message sections to extract file details and code blocks, and for formatting content for LLM context.
7
7
  * Language: JavaScript
8
8
  * Created-at: 2025-05-09T01:36:20.107Z
9
- * Authors: Gemini 2.5 Flash Thinking (v1.0.0), Gemini 2.5 Flash (v1.1.0)
9
+ * Authors: Gemini 2.5 Flash Thinking (v1.0.0), Gemini 2.5 Flash (v1.1.0), Qwen 3 Coder 480B - Cerebras (v1.2.0), Qwen 3 Coder 480B - Cerebras (v1.3.0)
10
10
  */
11
11
 
12
12
 
@@ -188,12 +188,26 @@ function extractContextSections(messageContent) {
188
188
  const [summary, items] = messageContent.split(/\n---Start of Context---\n\n/);
189
189
  const sections = items.split(/\n---End of Item---\n/);
190
190
 
191
+ // Determine the origin of the context based on the message header
192
+ let contextOrigin = 'unknown'; // Default value if not determinable
193
+ if (messageContent.includes("## FILE CONTENT - WORKING DIRECTORY")) {
194
+ contextOrigin = 'working-directory';
195
+ } else if (messageContent.includes("## FILE CONTENT - IMPORTED")) {
196
+ contextOrigin = 'imported';
197
+ } else if (messageContent.includes("## OVERVIEW - IMPORTED")) {
198
+ // Handle the overview case as well, though it uses 'file content' type
199
+ contextOrigin = 'imported';
200
+ }
201
+
191
202
  // Process sections starting from the first potential path delimiter.
192
203
  for (let i = 0; i < sections.length; i++) {
193
204
  const contextSection = parseContextSection(sections[i]);
194
205
 
195
206
  if (contextSection) {
196
207
  contextSections.push(contextSection);
208
+ // Attach the determined context origin to the section
209
+ // This provides information on how the context was introduced
210
+ contextSection.contextOrigin = contextOrigin;
197
211
  }
198
212
  }
199
213
 
@@ -214,6 +228,7 @@ function extractContextItemsOverviewTableRows(messageContent) {
214
228
  return null;
215
229
  }
216
230
 
231
+ // Map the column number to the type
217
232
  const col2Field = {
218
233
  1: 'chatId',
219
234
  2: 'type', // file, tree, etc.
@@ -273,12 +288,15 @@ function formatContextContent(items, contentType, contentOption) {
273
288
 
274
289
  // Header based on content type
275
290
  if (contentType === 'overview') {
276
- const label = contentOption === "long" ? "comprehensive" : "short";
277
291
  result += `## OVERVIEW - ${contentOption.toUpperCase()}\n`;
292
+ result += '\n';
278
293
  } else {
279
294
  result += `## FILE CONTENT - ${contentOption.toUpperCase()}\n`;
295
+ result += '\n';
280
296
  }
281
297
 
298
+ result += 'The following files are provided as context for your request. Use this information to understand the project\'s structure and content. This is a data payload for context; please do not mirror this format in your response.\n';
299
+ result += '\n';
282
300
  // Summary of items
283
301
  result += _createContextSummary(items, contentType);
284
302
  result += "\n---Start of Context---\n\n";
@@ -0,0 +1,113 @@
1
+ /**
2
+ * Component: Date Utilities
3
+ * Block-UUID: fef3b0f4-8055-4690-9336-e68a9cc33a61
4
+ * Parent-UUID: 716e5ce9-84df-4f12-89e7-4a28e4a39a81
5
+ * Version: 1.0.0
6
+ * Description: Date formatting and manipulation utilities for GitSense Chat
7
+ * Language: JavaScript
8
+ * Created-at: 2025-10-11T23:26:20.815Z
9
+ * Authors: Claude-3.5-Sonnet (v1.0.0), Qwen 3 Coder 480B - Cerebras (v1.0.0)
10
+ */
11
+
12
+
13
+ /**
14
+ * Ensures a date string has a timezone indicator
15
+ * @param {string} dateString - Date string in format "YYYY-MM-DD(T| )HH:mm:ss.SSS"
16
+ * @returns {string} Date string with 'Z' timezone indicator if not present
17
+ */
18
+ function normalizeDateTime(dateString) {
19
+ return dateString.includes('Z') ? dateString : dateString + 'Z';
20
+ }
21
+
22
+ /**
23
+ * Calculates the time difference between now and a given date
24
+ * @param {string} dateString - Date string to compare against current time
25
+ * @returns {number} Difference in seconds
26
+ */
27
+ function getTimeDifference(dateString) {
28
+ const now = new Date();
29
+ const date = new Date(normalizeDateTime(dateString));
30
+ return Math.floor((now - date) / 1000);
31
+ }
32
+
33
+ /**
34
+ * Formats a time difference in seconds to a human-readable string
35
+ * @param {number} diff - Time difference in seconds
36
+ * @returns {string} Formatted string (e.g., "5s ago", "2m ago", "3h ago", "2d ago")
37
+ */
38
+ function formatTimeDifference(diff) {
39
+ if (diff < 60) {
40
+ return `${diff}s ago`;
41
+ } else if (diff < 3600) {
42
+ const minutes = Math.floor(diff / 60);
43
+ return `${minutes}m ago`;
44
+ } else if (diff < 86400) {
45
+ const hours = Math.floor(diff / 3600);
46
+ return `${hours}h ago`;
47
+ } else {
48
+ const days = Math.floor(diff / 86400);
49
+ return `${days}d ago`;
50
+ }
51
+ }
52
+
53
+ /**
54
+ * Formats a date string into a relative time string
55
+ * @param {string} dateString - Date string to format
56
+ * @returns {string} Formatted relative time string
57
+ */
58
+ function formatAge(dateString) {
59
+ if (!dateString) {
60
+ return 'N/A';
61
+ }
62
+
63
+ try {
64
+ const diff = getTimeDifference(dateString);
65
+ return formatTimeDifference(diff);
66
+ } catch (error) {
67
+ console.error('Error formatting date:', error);
68
+ return 'Invalid date';
69
+ }
70
+ }
71
+
72
+ /**
73
+ * Validates a date string format
74
+ * @param {string} dateString - Date string to validate
75
+ * @returns {boolean} True if date string is valid
76
+ */
77
+ function isValidDateString(dateString) {
78
+ if (!dateString) return false;
79
+
80
+ const pattern = /^\d{4}-\d{2}-\d{2}(T| )\d{2}:\d{2}:\d{2}\.\d{3}Z?$/;
81
+ if (!pattern.test(dateString)) return false;
82
+
83
+ const date = new Date(normalizeDateTime(dateString));
84
+ return date instanceof Date && !isNaN(date);
85
+ }
86
+
87
+ /**
88
+ * Compares two date strings
89
+ * @param {string} dateA - First date string
90
+ * @param {string} dateB - Second date string
91
+ * @returns {number} -1 if dateA is earlier, 0 if equal, 1 if dateA is later
92
+ */
93
+ function compareDates(dateA, dateB) {
94
+ if (!isValidDateString(dateA) || !isValidDateString(dateB)) {
95
+ throw new Error(`Invalid date string format. A: ${dateA} B: ${dateB})`);
96
+ }
97
+
98
+ const timeA = new Date(normalizeDateTime(dateA)).getTime();
99
+ const timeB = new Date(normalizeDateTime(dateB)).getTime();
100
+
101
+ if (timeA < timeB) return -1;
102
+ if (timeA > timeB) return 1;
103
+ return 0;
104
+ }
105
+
106
+ module.exports = {
107
+ formatAge,
108
+ isValidDateString,
109
+ compareDates,
110
+ normalizeDateTime,
111
+ getTimeDifference,
112
+ formatTimeDifference
113
+ };
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Component: Formatter Utilities
3
+ * Block-UUID: 9b1e07bf-e92c-43c1-9ac0-5dacdb7ae618
4
+ * Parent-UUID: 9c07d12f-5a05-4402-99d2-85d872d7b2f7
5
+ * Version: 1.0.0
6
+ * Description: Utility functions for formatting content in GitSense Chat
7
+ * Language: JavaScript
8
+ * Created-at: 2025-10-11T23:27:15.420Z
9
+ * Authors: Claude 3.7 Sonnet (v1.0.0), Claude 3.7 Sonnet (v1.1.0), Gemini 2.5 Flash Thinking (v1.2.0), Gemini 2.5 Flash Thinking (v1.3.0), Qwen 3 Coder 480B - Cerebras (v1.4.0), Qwen 3 Coder 480B - Cerebras (v1.5.0), Qwen 3 Coder 480B - Cerebras (v1.0.0)
10
+ */
11
+
12
+
13
+ /**
14
+ * Formats bytes into human-readable string (KB, MB, GB)
15
+ * @param {number} bytes - Number of bytes
16
+ * @returns {string} Formatted size string
17
+ */
18
+ function formatBytes(bytes) {
19
+ if (typeof bytes !== 'number' || isNaN(bytes)) return '0 bytes';
20
+ if (bytes === 0) return '0 bytes';
21
+
22
+ const k = 1024;
23
+ const sizes = ['bytes', 'KB', 'MB', 'GB'];
24
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
25
+
26
+ // Special case for bytes (no decimal)
27
+ if (i === 0) return `${bytes} ${sizes[i]}`;
28
+
29
+ const value = bytes / Math.pow(k, i);
30
+ // Show no decimal if whole number
31
+ const decimalPlaces = value % 1 === 0 ? 0 : 1;
32
+ return `${value.toFixed(decimalPlaces)} ${sizes[i]}`;
33
+ }
34
+
35
+ /**
36
+ * Formats token count into human-readable string (k, m, b)
37
+ * @param {number} count - Number of tokens
38
+ * @returns {string} Formatted token string
39
+ */
40
+ function formatTokens(count) {
41
+ if (typeof count !== 'number' || isNaN(count)) return '0';
42
+ if (count === 0) return '0';
43
+
44
+ if (count < 1000) return `${count}`;
45
+ if (count < 1000000) return `${(count/1000).toFixed(count >= 10000 ? 1 : 0)}k`;
46
+ if (count < 1000000000) return `${(count/1000000).toFixed(1)}m`;
47
+
48
+ // For billions
49
+ return `${(count/1000000000).toFixed(1)}b`;
50
+ }
51
+
52
+ module.exports = {
53
+ formatBytes,
54
+ formatTokens
55
+ };
@@ -1,12 +1,12 @@
1
1
  /*
2
2
  * Component: GitSenseChatUtils
3
- * Block-UUID: 5e8d1a9c-0b3f-4e1a-8c7d-9f0b2e1d3a4b
4
- * Parent-UUID: 7a9b1c8e-f1a4-4b2d-9e8f-6f7a0b1c2d3f
5
- * Version: 2.1.4
3
+ * Block-UUID: d7cdbb94-f335-4f85-bdc4-0dd3ae95127e
4
+ * Parent-UUID: 5e8d1a9c-0b3f-4e1a-8c7d-9f0b2e1d3a4b
5
+ * Version: 2.1.5
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), Gemini 2.5 Flash (v2.1.4)
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), Qwen 3 Coder 480B - Cerebras (v2.1.5)
10
10
  */
11
11
 
12
12
 
@@ -20,6 +20,8 @@ const PatchUtils = require('./PatchUtils');
20
20
  const GSToolBlockUtils = require('./GSToolBlockUtils');
21
21
  const LLMUtils = require('./LLMUtils');
22
22
  const JsonUtils = require('./JsonUtils');
23
+ const DateUtils = require('./DateUtils');
24
+ const FormatterUtils = require('./FormatterUtils');
23
25
  const ConfigUtils = require('./ConfigUtils');
24
26
  const EnvUtils = require('./EnvUtils');
25
27
 
@@ -124,6 +126,11 @@ const {
124
126
  getApiKey
125
127
  } = EnvUtils;
126
128
 
129
+ const {
130
+ formatBytes,
131
+ formatTokens,
132
+ } = FormatterUtils;
133
+
127
134
  const {
128
135
  parseContextSection,
129
136
  extractContextSections,
@@ -338,6 +345,15 @@ class GitSenseChatUtils {
338
345
  // Export the main class, the aggregated CodeBlockUtils, PatchUtils,
339
346
  // and individual functions for backward compatibility or direct use.
340
347
  module.exports = {
348
+ // New Utility Modules
349
+ DateUtils,
350
+ FormatterUtils,
351
+
352
+ // Date Utility Functions
353
+ formatAge: DateUtils.formatAge,
354
+ compareDates: DateUtils.compareDates,
355
+ isValidDateString: DateUtils.isValidDateString,
356
+
341
357
  // Main class
342
358
  GitSenseChatUtils,
343
359
 
@@ -353,6 +369,9 @@ module.exports = {
353
369
  GSToolBlockUtils,
354
370
  JsonUtils,
355
371
  ConfigUtils,
372
+ DateUtils,
373
+ FormatterUtils,
374
+
356
375
  EnvUtils,
357
376
 
358
377
  // --- Individual Function Exports (sourced correctly) ---
@@ -427,6 +446,16 @@ module.exports = {
427
446
  // LLM Utils
428
447
  estimateTokens,
429
448
 
449
+ // Date Utils
450
+ formatAge,
451
+ compareDates,
452
+ isValidDateString,
453
+
454
+ // Formatter Utils
455
+ formatBytes,
456
+ formatTokens,
457
+ formatOverviewTable,
458
+
430
459
  // GS Tool Block
431
460
  isToolBlock,
432
461
  parseToolBlock,
@@ -367,7 +367,7 @@ function getMessageContentType(messageContent) {
367
367
  return 'file-content-context';
368
368
  } else if (trimmedContent.startsWith('## OVERVIEW')) {
369
369
  return 'overview-context';
370
- } else if (trimmedContent.startsWith('## Context Items Overview\n\nThis section provides')) {
370
+ } else if (trimmedContent.startsWith('## Context Overview\n\nThis context overview')) {
371
371
  return 'context-items-overview';
372
372
  } else if (trimmedContent.match(ANALYZE_MESSAGE_REGEXP)) {
373
373
  return 'analyze-message';