@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.
- package/README.md +380 -62
- package/dist/gsc-utils.cjs.js +2203 -331
- package/dist/gsc-utils.esm.js +2203 -331
- package/package.json +1 -1
- package/src/AnalyzerUtils/cloner.js +149 -0
- package/src/AnalyzerUtils/constants.js +1 -1
- package/src/AnalyzerUtils/defaultPromptLoader.js +10 -10
- package/src/AnalyzerUtils/discovery.js +48 -39
- package/src/AnalyzerUtils/index.js +13 -7
- package/src/AnalyzerUtils/instructionLoader.js +6 -6
- package/src/AnalyzerUtils/jsonParser.js +35 -0
- package/src/AnalyzerUtils/management.js +6 -6
- package/src/AnalyzerUtils/saver.js +5 -5
- package/src/AnalyzerUtils/schemaLoader.js +194 -26
- package/src/AnalyzerUtils/updater.js +187 -0
- package/src/CodeBlockUtils/blockProcessor.js +14 -32
- package/src/CodeBlockUtils/index.js +7 -6
- package/src/CodeBlockUtils/lineageTracer.js +95 -0
- package/src/CompactChatUtils/CompactedMessageUtils.js +224 -0
- package/src/CompactChatUtils/README.md +321 -0
- package/src/CompactChatUtils/ReferenceMessageUtils.js +143 -0
- package/src/CompactChatUtils/index.js +40 -0
- package/src/ContextUtils.js +40 -4
- package/src/DomUtils.js +86 -12
- package/src/GSToolBlockUtils.js +66 -1
- package/src/GitSenseChatUtils.js +58 -5
- package/src/MarkdownUtils.js +4 -1
- package/src/MessageUtils.js +1 -1
- package/src/MetaRawResultUtils.js +244 -0
- package/src/PatchUtils/constants.js +9 -3
- package/src/PatchUtils/patchParser.js +60 -36
- package/src/PatchUtils/patchVerifier/detectAndFixOverlappingHunks.js +1 -1
- package/src/SVGUtils.js +57 -0
- package/src/SharedUtils/stringUtils.js +303 -0
- package/src/CodeBlockUtils/blockProcessor.js.rej +0 -8
package/package.json
CHANGED
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Component: AnalyzerUtils Cloner
|
|
3
|
+
* Block-UUID: 56b8c9f4-97ce-406c-a598-22a838ebefda
|
|
4
|
+
* Parent-UUID: N/A
|
|
5
|
+
* Version: 1.0.0
|
|
6
|
+
* Description: Provides utility functions for cloning analyzer configurations.
|
|
7
|
+
* Language: JavaScript
|
|
8
|
+
* Created-at: 2025-12-26T16:59:39.782Z
|
|
9
|
+
* Authors: GLM-4.6 (v1.0.0)
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
const fs = require('fs').promises;
|
|
14
|
+
const path = require('path');
|
|
15
|
+
const CodeBlockUtils = require('../CodeBlockUtils');
|
|
16
|
+
const { preprocessJsonForValidation } = require('./jsonParser');
|
|
17
|
+
const { isValidDirName } = require('./discovery');
|
|
18
|
+
const { saveConfiguration } = require('./saver');
|
|
19
|
+
const { getAnalyzers } = require('./discovery');
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Clones an existing analyzer with a new name.
|
|
23
|
+
*
|
|
24
|
+
* @param {string} analyzersBasePath - The absolute path to the base directory containing the analyzers.
|
|
25
|
+
* @param {string} originalAnalyzerId - The ID of the analyzer to clone (format: 'analyzer_name::content_type::instructions_type').
|
|
26
|
+
* @param {string} newAnalyzerName - The new name for the cloned analyzer.
|
|
27
|
+
* @param {object} [options={}] - Optional configuration options.
|
|
28
|
+
* @param {string} [options.label] - The new label for the cloned analyzer (optional, defaults to newAnalyzerName).
|
|
29
|
+
* @param {string} [options.description] - The new description for the cloned analyzer (optional, keeps original if not provided).
|
|
30
|
+
* @returns {Promise<{success: boolean, message: string, newAnalyzerId?: string}>} A promise that resolves with a result object.
|
|
31
|
+
*/
|
|
32
|
+
async function cloneAnalyzer(analyzersBasePath, originalAnalyzerId, newAnalyzerName, options = {}) {
|
|
33
|
+
// 1. Validate inputs
|
|
34
|
+
if (typeof analyzersBasePath !== 'string' || analyzersBasePath.trim() === '') {
|
|
35
|
+
return { success: false, message: 'analyzersBasePath is required.' };
|
|
36
|
+
}
|
|
37
|
+
if (typeof originalAnalyzerId !== 'string' || originalAnalyzerId.trim() === '') {
|
|
38
|
+
return { success: false, message: 'originalAnalyzerId is required.' };
|
|
39
|
+
}
|
|
40
|
+
if (typeof newAnalyzerName !== 'string' || newAnalyzerName.trim() === '') {
|
|
41
|
+
return { success: false, message: 'newAnalyzerName is required.' };
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// 2. Parse original analyzerId
|
|
45
|
+
const parts = originalAnalyzerId.split('::');
|
|
46
|
+
if (parts.length !== 3) {
|
|
47
|
+
return { success: false, message: `Invalid originalAnalyzerId format. Expected 'analyzer_name::content_type::instructions_type', but got '${originalAnalyzerId}'.` };
|
|
48
|
+
}
|
|
49
|
+
const [originalAnalyzerName, contentType, instructionsType] = parts;
|
|
50
|
+
|
|
51
|
+
// 3. Validate new analyzer name
|
|
52
|
+
if (!isValidDirName(newAnalyzerName)) {
|
|
53
|
+
return { success: false, message: `Invalid analyzer name '${newAnalyzerName}'. Names must be alphanumeric, dash, or underscore, cannot start with underscore, and cannot contain dots.` };
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// 4. Check if new analyzer name already exists
|
|
57
|
+
try {
|
|
58
|
+
const existingAnalyzers = await getAnalyzers(analyzersBasePath);
|
|
59
|
+
const nameExists = existingAnalyzers.some(analyzer => analyzer.name === newAnalyzerName);
|
|
60
|
+
if (nameExists) {
|
|
61
|
+
return { success: false, message: `An analyzer with name '${newAnalyzerName}' already exists. Please choose a different name.` };
|
|
62
|
+
}
|
|
63
|
+
} catch (error) {
|
|
64
|
+
return { success: false, message: `Failed to check for existing analyzers: ${error.message}` };
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// 5. Get original analyzer's instructions content
|
|
68
|
+
const { getAnalyzerInstructionsContent } = require('./instructionLoader');
|
|
69
|
+
let instructionsContent;
|
|
70
|
+
try {
|
|
71
|
+
instructionsContent = await getAnalyzerInstructionsContent(analyzersBasePath, originalAnalyzerId);
|
|
72
|
+
if (!instructionsContent) {
|
|
73
|
+
return { success: false, message: `Could not find original analyzer '${originalAnalyzerId}'.` };
|
|
74
|
+
}
|
|
75
|
+
} catch (error) {
|
|
76
|
+
return { success: false, message: `Failed to read original analyzer instructions: ${error.message}` };
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// 6. Extract and update JSON block
|
|
80
|
+
const { blocks } = CodeBlockUtils.extractCodeBlocks(instructionsContent, { silent: true });
|
|
81
|
+
const jsonBlocks = blocks.filter((block, index) => {
|
|
82
|
+
if (block.language === 'json') {
|
|
83
|
+
block.index = index;
|
|
84
|
+
return block;
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
const jsonBlock = jsonBlocks[jsonBlocks.length - 1];
|
|
88
|
+
|
|
89
|
+
if (!jsonBlock) {
|
|
90
|
+
return { success: false, message: 'No JSON block found in original analyzer instructions.' };
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
let jsonData;
|
|
94
|
+
try {
|
|
95
|
+
const preprocessedContent = preprocessJsonForValidation(jsonBlock.content);
|
|
96
|
+
jsonData = JSON.parse(preprocessedContent);
|
|
97
|
+
} catch (error) {
|
|
98
|
+
return { success: false, message: `Failed to parse JSON block: ${error.message}` };
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// 7. Update JSON data with new values
|
|
102
|
+
if (options.label !== undefined) {
|
|
103
|
+
jsonData.label = options.label;
|
|
104
|
+
}
|
|
105
|
+
if (options.description !== undefined) {
|
|
106
|
+
jsonData.description = options.description;
|
|
107
|
+
}
|
|
108
|
+
// Reset version for cloned analyzer
|
|
109
|
+
jsonData.version = '1.0.0';
|
|
110
|
+
|
|
111
|
+
// 8. Rebuild the instructions content with updated JSON
|
|
112
|
+
let updatedInstructionsContent = CodeBlockUtils.updateCodeBlockByIndex(
|
|
113
|
+
instructionsContent,
|
|
114
|
+
jsonBlock.index,
|
|
115
|
+
JSON.stringify(jsonData, null, 2),
|
|
116
|
+
'json'
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
// 9. Create the new analyzer ID
|
|
120
|
+
const newAnalyzerId = `${newAnalyzerName}::${contentType}::${instructionsType}`;
|
|
121
|
+
|
|
122
|
+
// 10. Update the analyzer id in the instructions
|
|
123
|
+
updatedInstructionsContent = updatedInstructionsContent.replaceAll(originalAnalyzerId, newAnalyzerId);
|
|
124
|
+
|
|
125
|
+
// 11. Save the new analyzer
|
|
126
|
+
try {
|
|
127
|
+
const saveResult = await saveConfiguration(
|
|
128
|
+
analyzersBasePath,
|
|
129
|
+
newAnalyzerId,
|
|
130
|
+
updatedInstructionsContent
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
if (saveResult.success) {
|
|
134
|
+
return {
|
|
135
|
+
success: true,
|
|
136
|
+
message: `Analyzer '${originalAnalyzerName}' cloned successfully as '${newAnalyzerName}'.`,
|
|
137
|
+
newAnalyzerId
|
|
138
|
+
};
|
|
139
|
+
} else {
|
|
140
|
+
return { success: false, error: saveResult.message };
|
|
141
|
+
}
|
|
142
|
+
} catch (error) {
|
|
143
|
+
return { success: false, message: `Failed to save cloned analyzer: ${error.message}` };
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
module.exports = {
|
|
148
|
+
cloneAnalyzer
|
|
149
|
+
};
|
|
@@ -16,16 +16,16 @@ const path = require('path');
|
|
|
16
16
|
/**
|
|
17
17
|
* Retrieves the raw Markdown content of the shared system message ('_shared/system/1.md').
|
|
18
18
|
*
|
|
19
|
-
* @param {string}
|
|
19
|
+
* @param {string} analyzersBasePath - The absolute path to the base directory containing the analyzers (e.g., 'analyzers/analyzer-1').
|
|
20
20
|
* @returns {Promise<string|null>} A promise that resolves with the full Markdown content, or null if not found/invalid.
|
|
21
21
|
*/
|
|
22
|
-
async function getSystemMessageContent(
|
|
23
|
-
if (typeof
|
|
24
|
-
console.error('Error:
|
|
22
|
+
async function getSystemMessageContent(analyzersBasePath) {
|
|
23
|
+
if (typeof analyzersBasePath !== 'string' || analyzersBasePath.trim() === '') {
|
|
24
|
+
console.error('Error: analyzersBasePath is required for getSystemMessageContent.');
|
|
25
25
|
return null;
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
const systemMessageFilePath = path.join(
|
|
28
|
+
const systemMessageFilePath = path.join(analyzersBasePath, '_shared', 'system', '1.md');
|
|
29
29
|
|
|
30
30
|
try {
|
|
31
31
|
const fileContent = await fs.readFile(systemMessageFilePath, 'utf8');
|
|
@@ -46,16 +46,16 @@ async function getSystemMessageContent(analyzeMessagesBasePath) {
|
|
|
46
46
|
/**
|
|
47
47
|
* Retrieves the raw Markdown content of the shared start message ('_shared/start/1.md').
|
|
48
48
|
*
|
|
49
|
-
* @param {string}
|
|
49
|
+
* @param {string} analyzersBasePath - The absolute path to the base directory containing the analyzers (e.g., 'analyzers/analyzer-1').
|
|
50
50
|
* @returns {Promise<string|null>} A promise that resolves with the full Markdown content, or null if not found/invalid.
|
|
51
51
|
*/
|
|
52
|
-
async function getStartMessageContent(
|
|
53
|
-
if (typeof
|
|
54
|
-
console.error('Error:
|
|
52
|
+
async function getStartMessageContent(analyzersBasePath) {
|
|
53
|
+
if (typeof analyzersBasePath !== 'string' || analyzersBasePath.trim() === '') {
|
|
54
|
+
console.error('Error: analyzersBasePath is required for getStartMessageContent.');
|
|
55
55
|
return null;
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
-
const startMessageFilePath = path.join(
|
|
58
|
+
const startMessageFilePath = path.join(analyzersBasePath, '_shared', 'start', '1.md');
|
|
59
59
|
|
|
60
60
|
try {
|
|
61
61
|
const fileContent = await fs.readFile(startMessageFilePath, 'utf8');
|
|
@@ -1,18 +1,19 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
2
|
* Component: AnalyzerUtils Discovery
|
|
3
|
-
* Block-UUID:
|
|
4
|
-
* Parent-UUID:
|
|
5
|
-
* Version: 1.1
|
|
6
|
-
* Description: Provides utility functions for discovering available analyzers.
|
|
3
|
+
* Block-UUID: 26a7df9b-ef29-4dcd-aab2-9cdc3a11133c
|
|
4
|
+
* Parent-UUID: aa999515-84fb-43f6-91a7-cf79248d7286
|
|
5
|
+
* Version: 1.3.1
|
|
6
|
+
* Description: Provides utility functions for discovering available analyzers. Updated to include version and tags in analyzer objects.
|
|
7
7
|
* Language: JavaScript
|
|
8
|
-
* Created-at: 2025-
|
|
9
|
-
* Authors: Gemini 2.5 Flash (v1.0.0), Gemini 2.5 Flash (v1.1.0)
|
|
8
|
+
* Created-at: 2025-11-27T14:45:30.978Z
|
|
9
|
+
* 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)
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
const fs = require('fs').promises;
|
|
14
14
|
const path = require('path');
|
|
15
15
|
const { getAnalyzerInstructionsContent } = require('./instructionLoader');
|
|
16
|
+
const { preprocessJsonForValidation } = require('./jsonParser');
|
|
16
17
|
const CodeBlockUtils = require('../CodeBlockUtils');
|
|
17
18
|
|
|
18
19
|
/**
|
|
@@ -36,7 +37,7 @@ async function readConfig(dirPath) {
|
|
|
36
37
|
}
|
|
37
38
|
|
|
38
39
|
/**
|
|
39
|
-
* Checks if a directory name is valid
|
|
40
|
+
* Checks if a directory name is valid
|
|
40
41
|
* Allowed: a-z, A-Z, 0-9, dash (-), underscore (_). Cannot start with underscore or contain dots.
|
|
41
42
|
* @param {string} name - The directory name to check.
|
|
42
43
|
* @returns {boolean} True if the name is valid, false otherwise.
|
|
@@ -54,22 +55,21 @@ function isValidDirName(name) {
|
|
|
54
55
|
* Discovers and lists all available analyzers by traversing the directory structure.
|
|
55
56
|
* An analyzer is considered valid if a '1.md' file exists in the instructions directory.
|
|
56
57
|
*
|
|
57
|
-
* @param {string}
|
|
58
|
+
* @param {string} analyzersBasePath - The absolute path to the base directory containing the analyzers (e.g., 'analyzers/analyzer-1').
|
|
58
59
|
* @param {object} [options={}] - Optional configuration.
|
|
59
|
-
* @
|
|
60
|
-
* @returns {Promise<Array<{id: string, label: string, name: string, protected: boolean, description?: string}>>} A promise that resolves to an array of analyzer objects.
|
|
60
|
+
* @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.
|
|
61
61
|
*/
|
|
62
|
-
async function getAnalyzers(
|
|
63
|
-
const { includeDescription = false } = options;
|
|
62
|
+
async function getAnalyzers(analyzersBasePath, options = {}) {
|
|
64
63
|
const analyzers = [];
|
|
64
|
+
const demoAnalyzerPattern = /^demo-.+-\w{6}$/;
|
|
65
65
|
|
|
66
66
|
try {
|
|
67
|
-
const analyzerEntries = await fs.readdir(
|
|
67
|
+
const analyzerEntries = await fs.readdir(analyzersBasePath, { withFileTypes: true });
|
|
68
68
|
|
|
69
69
|
for (const analyzerEntry of analyzerEntries) {
|
|
70
70
|
if (analyzerEntry.isDirectory() && isValidDirName(analyzerEntry.name)) {
|
|
71
71
|
const analyzerName = analyzerEntry.name;
|
|
72
|
-
const analyzerPath = path.join(
|
|
72
|
+
const analyzerPath = path.join(analyzersBasePath, analyzerName);
|
|
73
73
|
const analyzerConfig = await readConfig(analyzerPath);
|
|
74
74
|
const analyzerLabel = analyzerConfig?.label || analyzerName;
|
|
75
75
|
|
|
@@ -91,13 +91,16 @@ async function getAnalyzers(analyzeMessagesBasePath, options = {}) {
|
|
|
91
91
|
const instructionsConfig = await readConfig(instructionsPath);
|
|
92
92
|
const instructionsLabel = instructionsConfig?.label || instructionsType;
|
|
93
93
|
|
|
94
|
+
// Construct the analyzer ID and label
|
|
95
|
+
const analyzerId = `${analyzerName}::${contentType}::${instructionsType}`;
|
|
96
|
+
|
|
94
97
|
// Check for the existence of 1.md to confirm a valid analyzer configuration
|
|
95
98
|
const instructionsFilePath = path.join(instructionsPath, '1.md');
|
|
96
99
|
try {
|
|
97
100
|
await fs.access(instructionsFilePath); // Check if file exists and is accessible
|
|
98
101
|
|
|
99
102
|
// If analyzerName starts with 'tutorial-', check its last modified time.
|
|
100
|
-
if (analyzerName.startsWith('tutorial-')) {
|
|
103
|
+
if (analyzerName.startsWith('tutorial-') || demoAnalyzerPattern.test(analyzerName)) {
|
|
101
104
|
const stats = await fs.stat(instructionsFilePath);
|
|
102
105
|
const lastModified = stats.mtime.getTime(); // Get timestamp in milliseconds
|
|
103
106
|
const sixtyMinutesAgo = Date.now() - (60 * 60 * 1000); // Current time - 60 minutes in ms
|
|
@@ -107,37 +110,43 @@ async function getAnalyzers(analyzeMessagesBasePath, options = {}) {
|
|
|
107
110
|
continue;
|
|
108
111
|
}
|
|
109
112
|
}
|
|
110
|
-
//
|
|
111
|
-
const
|
|
112
|
-
const analyzerFullLabel = `${analyzerLabel} (${contentLabel} - ${instructionsLabel})`;
|
|
113
|
+
// TODO: Decide if we should show the contentLabel and instructionsLabel. For now we will ignore them.
|
|
114
|
+
const analyzerFullLabel = `${analyzerLabel}`; // (${contentLabel} - ${instructionsLabel})`;
|
|
113
115
|
|
|
116
|
+
// Extract description, version, and tags from the JSON block in 1.md
|
|
114
117
|
let description = null;
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
118
|
+
let version = null;
|
|
119
|
+
let tags = [];
|
|
120
|
+
let label = null;
|
|
121
|
+
let requires_reference_files = null;
|
|
122
|
+
|
|
123
|
+
try {
|
|
124
|
+
const content = await getAnalyzerInstructionsContent(analyzersBasePath, analyzerId);
|
|
125
|
+
const { blocks, warnings } = CodeBlockUtils.extractCodeBlocks(content, { silent: true });
|
|
126
|
+
const jsonBlock = blocks.find(block => block.language === 'json');
|
|
127
|
+
|
|
128
|
+
if (jsonBlock && jsonBlock.content) {
|
|
129
|
+
const preprocessedContent = preprocessJsonForValidation(jsonBlock.content);
|
|
130
|
+
const json = JSON.parse(preprocessedContent);
|
|
131
|
+
description = json.description || null;
|
|
132
|
+
label = json.label || null;
|
|
133
|
+
requires_reference_files = json.requires_reference_files || false;
|
|
134
|
+
version = json.version || null;
|
|
135
|
+
tags = Array.isArray(json.tags) ? json.tags : [];
|
|
132
136
|
}
|
|
137
|
+
} catch (descError) {
|
|
138
|
+
console.warn(`Warning: Could not load metadata for ${analyzerId}: ${descError.message}`);
|
|
133
139
|
}
|
|
134
140
|
|
|
135
141
|
analyzers.push({
|
|
136
142
|
id: analyzerId,
|
|
137
|
-
label: analyzerFullLabel,
|
|
138
143
|
name: analyzerName,
|
|
144
|
+
description,
|
|
145
|
+
label: label || analyzerFullLabel,
|
|
146
|
+
requires_reference_files,
|
|
139
147
|
protected: analyzerConfig?.protected || false,
|
|
140
|
-
|
|
148
|
+
version,
|
|
149
|
+
tags
|
|
141
150
|
});
|
|
142
151
|
} catch (error) {
|
|
143
152
|
// If 1.md doesn't exist, this is not a complete analyzer configuration, skip.
|
|
@@ -152,7 +161,7 @@ async function getAnalyzers(analyzeMessagesBasePath, options = {}) {
|
|
|
152
161
|
}
|
|
153
162
|
}
|
|
154
163
|
} catch (error) {
|
|
155
|
-
console.error(`Error traversing
|
|
164
|
+
console.error(`Error traversing analyzers directory ${analyzersBasePath}: ${error.message}`);
|
|
156
165
|
// Depending on requirements, you might want to throw the error or return an empty array
|
|
157
166
|
throw error; // Re-throw to indicate failure
|
|
158
167
|
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/*
|
|
2
2
|
* Component: AnalyzerUtils Index
|
|
3
|
-
* Block-UUID:
|
|
4
|
-
* Parent-UUID:
|
|
5
|
-
* Version: 1.
|
|
6
|
-
* Description: Aggregates and exports all utility functions from the AnalyzerUtils module.
|
|
3
|
+
* Block-UUID: 780e17b0-0c1e-4d77-bf6e-302951b341bf
|
|
4
|
+
* Parent-UUID: b403b6a1-230b-4247-8cd6-2a3d068f4bbf
|
|
5
|
+
* Version: 1.5.0
|
|
6
|
+
* Description: Aggregates and exports all utility functions from the AnalyzerUtils module. Added cloneAnalyzer method.
|
|
7
7
|
* Language: JavaScript
|
|
8
8
|
* Created-at: 2025-08-28T15:56:40.319Z
|
|
9
|
-
* Authors: Gemini 2.5 Flash (v1.0.0), Gemini 2.5 Flash (v1.1.0), Gemini 2.5 Flash (v1.2.0)
|
|
9
|
+
* 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)
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
|
|
@@ -18,7 +18,10 @@ const { saveConfiguration } = require('./saver');
|
|
|
18
18
|
const { getAnalyzerSchema } = require('./schemaLoader');
|
|
19
19
|
const { deleteAnalyzer } = require('./management');
|
|
20
20
|
const { getAnalyzerInstructionsContent } = require('./instructionLoader');
|
|
21
|
-
const { getSystemMessageContent, getStartMessageContent } = require('./defaultPromptLoader');
|
|
21
|
+
const { getSystemMessageContent, getStartMessageContent } = require('./defaultPromptLoader');
|
|
22
|
+
const { preprocessJsonForValidation } = require('./jsonParser');
|
|
23
|
+
const { updateAnalyzer } = require('./updater');
|
|
24
|
+
const { cloneAnalyzer } = require('./cloner');
|
|
22
25
|
|
|
23
26
|
module.exports = {
|
|
24
27
|
buildChatIdToPathMap,
|
|
@@ -30,5 +33,8 @@ module.exports = {
|
|
|
30
33
|
getAnalyzerInstructionsContent,
|
|
31
34
|
saveConfiguration,
|
|
32
35
|
getSystemMessageContent,
|
|
33
|
-
getStartMessageContent
|
|
36
|
+
getStartMessageContent,
|
|
37
|
+
preprocessJsonForValidation,
|
|
38
|
+
updateAnalyzer,
|
|
39
|
+
cloneAnalyzer
|
|
34
40
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/*
|
|
2
2
|
* Component: AnalyzerUtils Instruction Loader
|
|
3
|
-
* Block-UUID:
|
|
3
|
+
* Block-UUID: 3ecd422e-1dd8-482d-ae3f-90a5eae3ae44
|
|
4
4
|
* Parent-UUID: N/A
|
|
5
5
|
* Version: 1.0.0
|
|
6
6
|
* Description: Provides utility functions for loading raw analyzer instruction content.
|
|
@@ -16,13 +16,13 @@ const path = require('path');
|
|
|
16
16
|
/**
|
|
17
17
|
* Retrieves the raw Markdown content of the analyzer's '1.md' instruction file.
|
|
18
18
|
*
|
|
19
|
-
* @param {string}
|
|
19
|
+
* @param {string} analyzersBasePath - The absolute path to the base directory containing the analyzer message files (e.g., 'messages/analyze').
|
|
20
20
|
* @param {string} analyzerId - The unique ID of the analyzer (format: 'analyzer_name::content_type::instructions_type').
|
|
21
21
|
* @returns {Promise<string|null>} A promise that resolves with the full Markdown content of the '1.md' file, or null if not found/invalid.
|
|
22
22
|
*/
|
|
23
|
-
async function getAnalyzerInstructionsContent(
|
|
24
|
-
if (typeof
|
|
25
|
-
console.error('Error:
|
|
23
|
+
async function getAnalyzerInstructionsContent(analyzersBasePath, analyzerId) {
|
|
24
|
+
if (typeof analyzersBasePath !== 'string' || analyzersBasePath.trim() === '') {
|
|
25
|
+
console.error('Error: analyzersBasePath is required.');
|
|
26
26
|
return null;
|
|
27
27
|
}
|
|
28
28
|
if (typeof analyzerId !== 'string' || analyzerId.trim() === '') {
|
|
@@ -37,7 +37,7 @@ async function getAnalyzerInstructionsContent(analyzeMessagesBasePath, analyzerI
|
|
|
37
37
|
}
|
|
38
38
|
const [analyzerName, contentType, instructionsType] = parts;
|
|
39
39
|
|
|
40
|
-
const instructionsFilePath = path.join(
|
|
40
|
+
const instructionsFilePath = path.join(analyzersBasePath, analyzerName, contentType, instructionsType, '1.md');
|
|
41
41
|
|
|
42
42
|
try {
|
|
43
43
|
const fileContent = await fs.readFile(instructionsFilePath, 'utf8');
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Component: AnalyzerUtils JSON Parser
|
|
3
|
+
* Block-UUID: 4dd21efd-3ad3-43e1-adf0-d52d7c560970
|
|
4
|
+
* Version: 1.0.0
|
|
5
|
+
* Description: Provides utility functions for pre-processing JSON content from analyzer instructions.
|
|
6
|
+
* Language: JavaScript
|
|
7
|
+
* Created-at: 2025-11-23T05:38:15.725Z
|
|
8
|
+
* Authors: GLM-4.6 (v1.0.0)
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Pre-processes JSON content to quote unquoted template strings before parsing.
|
|
14
|
+
* This handles cases where {{SYSTEM: ...}} and {{ANALYZER: ...}} placeholders
|
|
15
|
+
* are not properly quoted in the JSON.
|
|
16
|
+
*
|
|
17
|
+
* @param {string} jsonString - The raw JSON string content.
|
|
18
|
+
* @returns {string} The processed JSON string with template strings quoted.
|
|
19
|
+
*/
|
|
20
|
+
function preprocessJsonForValidation(jsonString) {
|
|
21
|
+
// Find all unquoted template strings and quote them with proper escaping
|
|
22
|
+
// This regex looks for template strings that aren't already quoted
|
|
23
|
+
return jsonString.replace(
|
|
24
|
+
/(?<!")(\{\{(SYSTEM|ANALYZER):[^}]+\}\})(?!")/g,
|
|
25
|
+
(match, template) => {
|
|
26
|
+
// Escape any double quotes within the template string
|
|
27
|
+
const escapedTemplate = template.replace(/"/g, '\\"');
|
|
28
|
+
return `"${escapedTemplate}"`;
|
|
29
|
+
}
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
module.exports = {
|
|
34
|
+
preprocessJsonForValidation
|
|
35
|
+
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/*
|
|
2
2
|
* Component: AnalyzerUtils Management
|
|
3
|
-
* Block-UUID:
|
|
3
|
+
* Block-UUID: 6241f381-512b-48a7-b70e-6b45683831fe
|
|
4
4
|
* Parent-UUID: N/A
|
|
5
5
|
* Version: 1.0.0
|
|
6
6
|
* Description: Provides utility functions for managing (deleting) analyzer configurations.
|
|
@@ -34,13 +34,13 @@ async function isDirectoryEmpty(dirPath) {
|
|
|
34
34
|
/**
|
|
35
35
|
* Deletes a specific analyzer configuration and intelligently cleans up empty directories.
|
|
36
36
|
*
|
|
37
|
-
* @param {string}
|
|
37
|
+
* @param {string} analyzersBasePath - The absolute path to the base directory containing the analyzers (e.g., 'analyzers/analyzer-1').
|
|
38
38
|
* @param {string} analyzerId - The unique ID of the analyzer to delete (format: 'analyzer_name::content_type::instructions_type').
|
|
39
39
|
* @returns {Promise<{success: boolean, message: string}>} A promise that resolves with a result object indicating success or failure.
|
|
40
40
|
*/
|
|
41
|
-
async function deleteAnalyzer(
|
|
42
|
-
if (typeof
|
|
43
|
-
return { success: false, message: '
|
|
41
|
+
async function deleteAnalyzer(analyzersBasePath, analyzerId) {
|
|
42
|
+
if (typeof analyzersBasePath !== 'string' || analyzersBasePath.trim() === '') {
|
|
43
|
+
return { success: false, message: 'analyzersBasePath is required.' };
|
|
44
44
|
}
|
|
45
45
|
if (typeof analyzerId !== 'string' || analyzerId.trim() === '') {
|
|
46
46
|
return { success: false, message: 'analyzerId is required.' };
|
|
@@ -52,7 +52,7 @@ async function deleteAnalyzer(analyzeMessagesBasePath, analyzerId) {
|
|
|
52
52
|
}
|
|
53
53
|
const [analyzerName, contentType, instructionsType] = parts;
|
|
54
54
|
|
|
55
|
-
const analyzerDir = path.join(
|
|
55
|
+
const analyzerDir = path.join(analyzersBasePath, analyzerName);
|
|
56
56
|
const contentDir = path.join(analyzerDir, contentType);
|
|
57
57
|
const instructionsDir = path.join(contentDir, instructionsType);
|
|
58
58
|
const instructionsFilePath = path.join(instructionsDir, '1.md');
|
|
@@ -21,19 +21,19 @@ const path = require('path');
|
|
|
21
21
|
* if necessary, saves the instructions to '1.md'. Optionally, it can
|
|
22
22
|
* ensure config.json files exist with labels derived from directory names.
|
|
23
23
|
*
|
|
24
|
-
* @param {string}
|
|
24
|
+
* @param {string} analyzersBasePath - The absolute path to the base directory containing the analyzers (e.g., 'analyzers/analyzer-1').
|
|
25
25
|
* @param {string} analyzerId - The unique ID of the analyzer (format: 'analyzer_name::content_type::instructions_type').
|
|
26
26
|
* @param {string} instructionsContent - The full content of the analyzer instructions message to be saved in '1.md'.
|
|
27
27
|
* @param {object} [options={}] - Optional configuration options.
|
|
28
28
|
* @param {boolean} [options.ensureConfigs=false] - If true, ensures config.json files exist in the analyzer, content, and instructions directories. Defaults to false.
|
|
29
29
|
* @returns {Promise<{success: boolean, message?: string}>} A promise that resolves with a result object.
|
|
30
30
|
*/
|
|
31
|
-
async function saveConfiguration(
|
|
31
|
+
async function saveConfiguration(analyzersBasePath, analyzerId, instructionsContent, options = {}) {
|
|
32
32
|
const { ensureConfigs = false } = options;
|
|
33
33
|
|
|
34
34
|
// 1. Validate inputs
|
|
35
|
-
if (typeof
|
|
36
|
-
return { success: false, message: '
|
|
35
|
+
if (typeof analyzersBasePath !== 'string' || analyzersBasePath.trim() === '') {
|
|
36
|
+
return { success: false, message: 'analyzersBasePath is required.' };
|
|
37
37
|
}
|
|
38
38
|
if (typeof analyzerId !== 'string' || analyzerId.trim() === '') {
|
|
39
39
|
return { success: false, message: 'analyzerId is required.' };
|
|
@@ -66,7 +66,7 @@ async function saveConfiguration(analyzeMessagesBasePath, analyzerId, instructio
|
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
// 3. Construct directory paths
|
|
69
|
-
const analyzerDir = path.join(
|
|
69
|
+
const analyzerDir = path.join(analyzersBasePath, analyzerName);
|
|
70
70
|
const contentDir = path.join(analyzerDir, contentType);
|
|
71
71
|
const instructionsDir = path.join(contentDir, instructionsType);
|
|
72
72
|
const instructionsFilePath = path.join(instructionsDir, '1.md');
|