@gitsense/gsc-utils 0.2.0 → 0.2.2

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.
@@ -0,0 +1,81 @@
1
+ /*
2
+ * Component: AnalyzerUtils Data Validator
3
+ * Block-UUID: 1317cc51-fca0-49b7-9e4b-b3e1a06ab93f
4
+ * Parent-UUID: N/A
5
+ * Version: 1.0.0
6
+ * Description: Validates extracted analysis data against chat context and expected schema.
7
+ * Language: JavaScript
8
+ * Created-at: 2025-08-28T15:56:40.319Z
9
+ * Authors: Gemini 2.5 Flash (v1.0.0)
10
+ */
11
+
12
+
13
+ const AnalysisBlockUtils = require('../AnalysisBlockUtils');
14
+
15
+ /**
16
+ * Validates the extracted analysis blocks and their metadata.
17
+ * Checks for valid chat IDs/paths and uses AnalysisBlockUtils for further metadata validation.
18
+ * This function is adapted from the original `analysisValidation.js` in `gsc-conversation-view`.
19
+ *
20
+ * @param {Array<Object>} analysisBlocks - Array of extracted analysis code blocks (Markdown overviews).
21
+ * @param {Array<Object|null>} analysisMetadataBlocks - Array of extracted analysis metadata blocks (JSON).
22
+ * @param {Map<number, string>} chatIdToPathMap - Map of chat IDs to file paths from context messages.
23
+ * @returns {{validAnalysisData: Array<Object>, invalidAnalysisBlocks: Array<string|Object>}}
24
+ * An object containing arrays of valid analysis data and invalid analysis blocks.
25
+ */
26
+ function validateLLMAnalysisData(analysisBlocks, analysisMetadataBlocks, chatIdToPathMap) {
27
+ const validAnalysisData = []; // Store data for the "Save All" button
28
+ const invalidAnalysisBlocks = []; // Store invalid analysis blocks
29
+
30
+ analysisBlocks.forEach((block, index) => {
31
+ // Ensure metadata is parsed from the block's content for initial checks
32
+ const overviewMetadata = AnalysisBlockUtils.parseOverviewMetadata(block.content);
33
+ const metadataBlock = analysisMetadataBlocks[index];
34
+ const isDone = metadataBlock ? true : false; // Check if metadata block was successfully processed
35
+
36
+ // Skip if metadata block is missing (still streaming or error)
37
+ if (!overviewMetadata || !isDone) {
38
+ // If metadata block is missing but streaming stopped, it's an error handled in responseProcessor
39
+ // If overviewMetadata is missing, it's an error handled in responseProcessor
40
+ return;
41
+ }
42
+
43
+ const chatId = overviewMetadata['Chat ID'];
44
+ const path = overviewMetadata['Path'];
45
+
46
+ // Is this a valid chat id and path id? Some LLMs will actually hallucinate this
47
+ if (!chatId || !path || !chatIdToPathMap.has(chatId)) {
48
+ invalidAnalysisBlocks.push(`<li>#${chatId || 'N/A'} => ${path || 'N/A'} (Invalid Chat ID or Path)</li>`);
49
+ return;
50
+ }
51
+
52
+ // Perform detailed metadata validation using AnalysisBlockUtils
53
+ const validation = AnalysisBlockUtils.validateAnalysisMetadata(overviewMetadata);
54
+
55
+ if (validation.isValid) {
56
+ // Prepare data for API call (map keys if necessary)
57
+ const analysisData = {
58
+ chatId: overviewMetadata['Chat ID'],
59
+ // The original analyzeHandler used 'Message ID', 'Repository', 'Summarized At', 'Analyze At',
60
+ // 'Summary', 'Key Functionality', 'Keywords' from overviewMetadata.
61
+ // For batch processing, we primarily need chatId, content, and the full JSON metadata.
62
+ // The other fields can be derived from the full JSON metadata if needed.
63
+ path: overviewMetadata['Path'],
64
+ content: block.content, // The full Markdown overview
65
+ type: AnalysisBlockUtils.getAnalysisBlockType(block.content), // e.g., 'tiny-overview::file-content::default'
66
+ // We know metadataBlock.content is a valid JSON from responseProcessor
67
+ metadata: JSON.parse(metadataBlock.content) // The full JSON metadata object
68
+ };
69
+ validAnalysisData.push(analysisData); // Add to list for saving
70
+ } else {
71
+ // Add invalid analysis block with validation details
72
+ invalidAnalysisBlocks.push({ block, validation });
73
+ }
74
+ });
75
+
76
+ return { validAnalysisData, invalidAnalysisBlocks };
77
+ }
78
+
79
+ module.exports = {
80
+ validateLLMAnalysisData
81
+ };
@@ -0,0 +1,21 @@
1
+ /*
2
+ * Component: AnalyzerUtils Index
3
+ * Block-UUID: b403b6a1-230b-4247-8cd6-2a3d068f4bbf
4
+ * Parent-UUID: N/A
5
+ * Version: 1.0.0
6
+ * Description: Aggregates and exports all utility functions from the AnalyzerUtils module.
7
+ * Language: JavaScript
8
+ * Created-at: 2025-08-28T15:56:40.319Z
9
+ * Authors: Gemini 2.5 Flash (v1.0.0)
10
+ */
11
+
12
+
13
+ const { buildChatIdToPathMap } = require('./contextMapper');
14
+ const { processLLMAnalysisResponse } = require('./responseProcessor');
15
+ const { validateLLMAnalysisData } = require('./dataValidator');
16
+
17
+ module.exports = {
18
+ buildChatIdToPathMap,
19
+ processLLMAnalysisResponse,
20
+ validateLLMAnalysisData
21
+ };
@@ -0,0 +1,131 @@
1
+ /*
2
+ * Component: AnalyzerUtils Response Processor
3
+ * Block-UUID: 2a3b4c5d-6e7f-4a8b-9c0d-1e2f3a4b5c6d
4
+ * Parent-UUID: N/A
5
+ * Version: 1.0.0
6
+ * Description: Extracts and performs initial validation of analysis and metadata code blocks from raw LLM message content.
7
+ * Language: JavaScript
8
+ * Created-at: 2025-08-28T15:56:40.319Z
9
+ * Authors: Gemini 2.5 Flash (v1.0.0)
10
+ */
11
+
12
+
13
+ const CodeBlockUtils = require('../CodeBlockUtils');
14
+ const AnalysisBlockUtils = require('../AnalysisBlockUtils');
15
+ const GSToolBlockUtils = require('../GSToolBlockUtils');
16
+ const JsonUtils = require('../JsonUtils');
17
+ const { ANALYZE_HEADER_PREFIX } = require('./constants');
18
+
19
+ /**
20
+ * Extracts code blocks from the message content, identifies analysis and metadata blocks,
21
+ * and performs initial validation (block type, metadata presence, JSON format).
22
+ * This function is adapted from the original `blockProcessing.js` in `gsc-conversation-view`.
23
+ *
24
+ * @param {string} messageContent - The full content of the message.
25
+ * @param {boolean} stoppedStreaming - Flag indicating if the message streaming has stopped.
26
+ * @returns {{analysisBlocks: Array<Object>, analysisMetadataBlocks: Array<Object|null>, error: string|null}}
27
+ * An object containing the extracted blocks and any processing error.
28
+ */
29
+ function processLLMAnalysisResponse(messageContent, stoppedStreaming) {
30
+ const silent = { silent: true };
31
+ const { blocks, warnings } = CodeBlockUtils.extractCodeBlocks(messageContent, silent);
32
+
33
+ const analysisBlocks = [];
34
+ const analysisMetadataBlocks = [];
35
+ let error = null;
36
+
37
+ for (let i = 0; i < blocks.length; i += 2) {
38
+ const analysisBlock = blocks[i];
39
+
40
+ // Make sure this is an analysis block by checking its content
41
+ if (!analysisBlock.content.trimStart().startsWith(ANALYZE_HEADER_PREFIX)) {
42
+ // Also check if it's a GitSense Tool Block, which should be ignored by this analyzer
43
+ if (GSToolBlockUtils.isToolBlock(analysisBlock.content)) {
44
+ // If it's a tool block, it's not an analysis block, so skip it and don't treat as error
45
+ // Adjust index to skip this block and its potential "metadata" if it was paired
46
+ if (blocks[i+1] && blocks[i+1].language === 'json') {
47
+ i += 1; // Skip the next block too if it's a JSON block (likely tool config)
48
+ }
49
+ continue;
50
+ }
51
+
52
+ if (stoppedStreaming) {
53
+ error = `Analysis block #${i} does not start with the "${ANALYZE_HEADER_PREFIX}" header.`;
54
+ }
55
+ // If we are still streaming, we may not have enough information to tell what type
56
+ // of block this is, so stop looking
57
+ break;
58
+ }
59
+
60
+ // Add the analysis block (Markdown overview)
61
+ analysisBlocks.push(analysisBlock);
62
+
63
+ // Make sure there is an analysis metadata block (JSON)
64
+ const analysisMetadataBlock = blocks[i + 1];
65
+
66
+ if (!analysisMetadataBlock) {
67
+ if (stoppedStreaming) {
68
+ error = `No analysis metadata found for analysis block (index ${i}).`;
69
+ } else {
70
+ // Add a null metadata block to let use know we are still streaming
71
+ analysisMetadataBlocks.push(null);
72
+ }
73
+ break;
74
+ }
75
+
76
+ // We found a code block but we don't know if it is valid or not
77
+ // 1. First make sure the code block is a JSON type
78
+ // 2. Try to parse the JSON
79
+ if (analysisMetadataBlock.language !== 'json') {
80
+ if (stoppedStreaming) {
81
+ error = `Analysis metadata block for "${analysisBlock.overviewMetadata?.Path || 'Unknown Path'}" has invalid language "${analysisMetadataBlock.language}". Expected "json".`;
82
+ } else {
83
+ // Add a null metadata block to let use know we are still streaming
84
+ analysisMetadataBlocks.push(null);
85
+ }
86
+ break;
87
+ }
88
+
89
+ try {
90
+ // Check for comments within JSON, which would make it invalid for direct parsing
91
+ const comments = JsonUtils.detectJsonComments(analysisMetadataBlock.content);
92
+ if (comments.length > 0) {
93
+ error = `The analysis metadata block for "${analysisBlock.overviewMetadata?.Path || 'Unknown Path'}" contains comments, which makes it invalid JSON.`;
94
+ if (stoppedStreaming || blocks[i + 2]) { // If streaming stopped or not the last block
95
+ break;
96
+ } else {
97
+ analysisMetadataBlocks.push(null); // Add null to keep arrays aligned
98
+ return { analysisBlocks, analysisMetadataBlocks, error: null };
99
+ }
100
+ }
101
+
102
+ JSON.parse(analysisMetadataBlock.content.trim());
103
+ analysisMetadataBlocks.push(analysisMetadataBlock);
104
+ } catch (err) {
105
+ const path = analysisBlock.overviewMetadata?.Path || 'Unknown Path';
106
+ error = `The analysis metadata block for "${path}" contains an invalid JSON: ${err.message}`;
107
+
108
+ // If streaming has stopped or if this is not the last block, set error and break.
109
+ // Remember to increment by two since every second block is the meta block.
110
+ if (stoppedStreaming || blocks[i + 2]) {
111
+ break;
112
+ } else {
113
+ // Do nothing since the error is most certaintly due to the fact that we are still
114
+ // streaming the JSON
115
+ analysisMetadataBlocks.push(null); // Add null to keep arrays aligned
116
+ return { analysisBlocks, analysisMetadataBlocks, error: null };
117
+ }
118
+ }
119
+ }
120
+
121
+ // If streaming stopped and no overview blocks were found, set an error
122
+ if (stoppedStreaming && analysisBlocks.length === 0) {
123
+ error = 'Analysis stopped without producing any data.';
124
+ }
125
+
126
+ return { analysisBlocks, analysisMetadataBlocks, error };
127
+ }
128
+
129
+ module.exports = {
130
+ processLLMAnalysisResponse
131
+ };
package/src/ChatUtils.js CHANGED
@@ -62,13 +62,21 @@ function isNewAnalyzerChat(chat, model) {
62
62
  /**
63
63
  * Determines if the current chat session appears to be an "Analyze" session.
64
64
  * @param {object} chat - The chat object.
65
- * @param {string} model - Optional model.
66
65
  * @returns {boolean} True if the chat matches the Overview Builder pattern, false otherwise.
67
66
  */
68
- function isAnalyzeChat(chat, model) {
67
+ function isAnalyzeChat(chat) {
69
68
  return chat.type === 'analyze';
70
69
  }
71
70
 
71
+ /**
72
+ * Determines if the current chat session appears to be an "Analyze Builder" session.
73
+ * @param {object} chat - The chat object.
74
+ * @returns {boolean} True if the chat matches the Overview Builder pattern, false otherwise.
75
+ */
76
+ function isAnalyzeBuilderChat(chat) {
77
+ return chat.type === 'analyze-builder';
78
+ }
79
+
72
80
  /**
73
81
  * Determines if the current chat session appears to be an "Ask" session.
74
82
  * @param {object} chat - The chat object.
@@ -120,6 +128,7 @@ module.exports = {
120
128
  isAskChat,
121
129
  isNewAnalyzerChat,
122
130
  isAnalyzeChat,
131
+ isAnalyzeBuilderChat,
123
132
  isPlanChat,
124
133
  isCodeChat,
125
134
  getChatMessages
@@ -15,6 +15,7 @@ const CodeBlockUtils = require('./CodeBlockUtils');
15
15
  const ContextUtils = require('./ContextUtils');
16
16
  const MessageUtils = require('./MessageUtils');
17
17
  const AnalysisBlockUtils = require('./AnalysisBlockUtils');
18
+ const AnalyzerUtils = require('./AnalyzerUtils');
18
19
  const PatchUtils = require('./PatchUtils');
19
20
  const GSToolBlockUtils = require('./GSToolBlockUtils');
20
21
  const LLMUtils = require('./LLMUtils');
@@ -360,6 +361,11 @@ module.exports = {
360
361
  parseOverviewMetadata,
361
362
  validateOverviewMetadata,
362
363
 
364
+ // Analyzer Utilities
365
+ buildChatIdToPathMap: AnalyzerUtils.buildChatIdToPathMap,
366
+ processLLMAnalysisResponse: AnalyzerUtils.processLLMAnalysisResponse,
367
+ validateLLMAnalysisData: AnalyzerUtils.validateLLMAnalysisData,
368
+
363
369
  // ChatUtils
364
370
  getChatMessages,
365
371