@gitsense/gsc-utils 0.2.6 → 0.2.7

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.
@@ -18,8 +18,8 @@ function getDefaultExportFromCjs (x) {
18
18
  * Authors: Claude 3.7 Sonnet (v1.0.0), Gemini 2.5 Pro (v1.1.0), Gemini 2.5 Flash Thinking (v1.2.0)
19
19
  */
20
20
 
21
- const fs$6 = require$$0;
22
- const path$4 = require$$1;
21
+ const fs$7 = require$$0;
22
+ const path$5 = require$$1;
23
23
  const ANALYZE_MESSAGE_REGEXP = /^# Analyze - `([^`]+)`\n/;
24
24
 
25
25
  /**
@@ -53,12 +53,12 @@ function unescapeCodeBlocks(content) {
53
53
  * @returns {Array} An array of message objects with role and content properties
54
54
  */
55
55
  function getChatTemplateMessages$1(dirname, messageType) {
56
- const messagesDir = path$4.join(dirname, messageType);
56
+ const messagesDir = path$5.join(dirname, messageType);
57
57
  const messages = [];
58
58
 
59
59
  try {
60
60
  // Read all files in the directory
61
- const files = fs$6.readdirSync(messagesDir);
61
+ const files = fs$7.readdirSync(messagesDir);
62
62
 
63
63
  // Sort files numerically (1.md, 2.md, etc.)
64
64
  const sortedFiles = files.sort((a, b) => {
@@ -69,9 +69,9 @@ function getChatTemplateMessages$1(dirname, messageType) {
69
69
 
70
70
  // Process each file
71
71
  for (const file of sortedFiles) {
72
- if (path$4.extname(file) === '.md') {
73
- const filePath = path$4.join(messagesDir, file);
74
- const fileContent = fs$6.readFileSync(filePath, 'utf8');
72
+ if (path$5.extname(file) === '.md') {
73
+ const filePath = path$5.join(messagesDir, file);
74
+ const fileContent = fs$7.readFileSync(filePath, 'utf8');
75
75
 
76
76
  // Split by triple newline to separate metadata from content
77
77
  const parts = fileContent.split('\n\n\n');
@@ -10925,8 +10925,8 @@ var dataValidator = {
10925
10925
  * Authors: Gemini 2.5 Flash (v1.0.0)
10926
10926
  */
10927
10927
 
10928
- const fs$5 = require$$0.promises;
10929
- const path$3 = require$$1;
10928
+ const fs$6 = require$$0.promises;
10929
+ const path$4 = require$$1;
10930
10930
 
10931
10931
  /**
10932
10932
  * Reads and parses the config.json file in a directory.
@@ -10935,9 +10935,9 @@ const path$3 = require$$1;
10935
10935
  * or null if the file doesn't exist or is invalid.
10936
10936
  */
10937
10937
  async function readConfig$1(dirPath) {
10938
- const configPath = path$3.join(dirPath, 'config.json');
10938
+ const configPath = path$4.join(dirPath, 'config.json');
10939
10939
  try {
10940
- const fileContent = await fs$5.readFile(configPath, 'utf8');
10940
+ const fileContent = await fs$6.readFile(configPath, 'utf8');
10941
10941
  return JSON.parse(fileContent);
10942
10942
  } catch (error) {
10943
10943
  if (error.code !== 'ENOENT') {
@@ -10974,41 +10974,41 @@ async function getAnalyzers$2(analyzeMessagesBasePath) {
10974
10974
  const analyzers = [];
10975
10975
 
10976
10976
  try {
10977
- const analyzerEntries = await fs$5.readdir(analyzeMessagesBasePath, { withFileTypes: true });
10977
+ const analyzerEntries = await fs$6.readdir(analyzeMessagesBasePath, { withFileTypes: true });
10978
10978
 
10979
10979
  for (const analyzerEntry of analyzerEntries) {
10980
10980
  if (analyzerEntry.isDirectory() && isValidDirName(analyzerEntry.name)) {
10981
10981
  const analyzerName = analyzerEntry.name;
10982
- const analyzerPath = path$3.join(analyzeMessagesBasePath, analyzerName);
10982
+ const analyzerPath = path$4.join(analyzeMessagesBasePath, analyzerName);
10983
10983
  const analyzerConfig = await readConfig$1(analyzerPath);
10984
10984
  const analyzerLabel = analyzerConfig?.label || analyzerName;
10985
10985
 
10986
- const contentEntries = await fs$5.readdir(analyzerPath, { withFileTypes: true });
10986
+ const contentEntries = await fs$6.readdir(analyzerPath, { withFileTypes: true });
10987
10987
 
10988
10988
  for (const contentEntry of contentEntries) {
10989
10989
  if (contentEntry.isDirectory() && isValidDirName(contentEntry.name)) {
10990
10990
  const contentType = contentEntry.name;
10991
- const contentPath = path$3.join(analyzerPath, contentType);
10991
+ const contentPath = path$4.join(analyzerPath, contentType);
10992
10992
  const contentConfig = await readConfig$1(contentPath);
10993
10993
  const contentLabel = contentConfig?.label || contentType;
10994
10994
 
10995
- const instructionsEntries = await fs$5.readdir(contentPath, { withFileTypes: true });
10995
+ const instructionsEntries = await fs$6.readdir(contentPath, { withFileTypes: true });
10996
10996
 
10997
10997
  for (const instructionsEntry of instructionsEntries) {
10998
10998
  if (instructionsEntry.isDirectory() && isValidDirName(instructionsEntry.name)) {
10999
10999
  const instructionsType = instructionsEntry.name;
11000
- const instructionsPath = path$3.join(contentPath, instructionsType);
11000
+ const instructionsPath = path$4.join(contentPath, instructionsType);
11001
11001
  const instructionsConfig = await readConfig$1(instructionsPath);
11002
11002
  const instructionsLabel = instructionsConfig?.label || instructionsType;
11003
11003
 
11004
11004
  // Check for the existence of 1.md to confirm a valid analyzer configuration
11005
- const instructionsFilePath = path$3.join(instructionsPath, '1.md');
11005
+ const instructionsFilePath = path$4.join(instructionsPath, '1.md');
11006
11006
  try {
11007
- await fs$5.access(instructionsFilePath); // Check if file exists and is accessible
11007
+ await fs$6.access(instructionsFilePath); // Check if file exists and is accessible
11008
11008
 
11009
11009
  // If analyzerName starts with 'tutorial-', check its last modified time.
11010
11010
  if (analyzerName.startsWith('tutorial-')) {
11011
- const stats = await fs$5.stat(instructionsFilePath);
11011
+ const stats = await fs$6.stat(instructionsFilePath);
11012
11012
  const lastModified = stats.mtime.getTime(); // Get timestamp in milliseconds
11013
11013
  const sixtyMinutesAgo = Date.now() - (60 * 60 * 1000); // Current time - 60 minutes in ms
11014
11014
 
@@ -11062,12 +11062,128 @@ var discovery = {
11062
11062
  * Authors: Gemini 2.5 Flash Thinking (v1.0.0), Gemini 2.5 Flash Thinking (v1.1.0)
11063
11063
  */
11064
11064
 
11065
- require$$0.promises;
11065
+ const fs$5 = require$$0.promises;
11066
+ const path$3 = require$$1;
11066
11067
 
11068
+ /**
11069
+ * Saves or updates an analyzer configuration.
11070
+ *
11071
+ * This function takes the analyzer ID and its full instructions content,
11072
+ * parses the ID to determine the directory structure, creates directories
11073
+ * if necessary, saves the instructions to '1.md'. Optionally, it can
11074
+ * ensure config.json files exist with labels derived from directory names.
11075
+ *
11076
+ * @param {string} analyzeMessagesBasePath - The absolute or relative path to the 'messages/analyze' directory.
11077
+ * @param {string} analyzerId - The unique ID of the analyzer (format: 'analyzer_name::content_type::instructions_type').
11078
+ * @param {string} instructionsContent - The full content of the analyzer instructions message to be saved in '1.md'.
11079
+ * @param {object} [options={}] - Optional configuration options.
11080
+ * @param {boolean} [options.ensureConfigs=false] - If true, ensures config.json files exist in the analyzer, content, and instructions directories. Defaults to false.
11081
+ * @returns {Promise<{success: boolean, message?: string}>} A promise that resolves with a result object.
11082
+ */
11083
+ async function saveConfiguration$1(analyzeMessagesBasePath, analyzerId, instructionsContent, options = {}) {
11084
+ const { ensureConfigs = false } = options;
11067
11085
 
11068
- var saver = {
11086
+ // 1. Validate inputs
11087
+ if (typeof analyzeMessagesBasePath !== 'string' || analyzeMessagesBasePath.trim() === '') {
11088
+ return { success: false, message: 'analyzeMessagesBasePath is required.' };
11089
+ }
11090
+ if (typeof analyzerId !== 'string' || analyzerId.trim() === '') {
11091
+ return { success: false, message: 'analyzerId is required.' };
11092
+ }
11093
+ if (typeof instructionsContent !== 'string' || instructionsContent.trim() === '') {
11094
+ return { success: false, message: 'instructionsContent is required.' };
11095
+ }
11096
+
11097
+ // 2. Parse analyzerId
11098
+ const parts = analyzerId.split('::');
11099
+ if (parts.length !== 3) {
11100
+ return { success: false, message: `Invalid analyzerId format. Expected 'analyzer_name::content_type::instructions_type', but got '${analyzerId}'.` };
11101
+ }
11102
+ const [analyzerName, contentType, instructionsType] = parts;
11103
+
11104
+ // Helper to validate directory names based on README.md rules
11105
+ const isValidDirName = (name) => {
11106
+ // Cannot start with underscore, cannot contain dots, must be alphanumeric, dash, or underscore
11107
+ return /^[a-zA-Z0-9_-]+$/.test(name) && !name.startsWith('_') && !name.includes('.');
11069
11108
  };
11070
11109
 
11110
+ if (!isValidDirName(analyzerName)) {
11111
+ return { success: false, message: `Invalid analyzer name '${analyzerName}'. Names must be alphanumeric, dash, or underscore, cannot start with underscore, and cannot contain dots.` };
11112
+ }
11113
+ if (!isValidDirName(contentType)) {
11114
+ return { success: false, message: `Invalid content type name '${contentType}'. Names must be alphanumeric, dash, or underscore, cannot start with underscore, and cannot contain dots.` };
11115
+ }
11116
+ if (!isValidDirName(instructionsType)) {
11117
+ return { success: false, message: `Invalid instructions type name '${instructionsType}'. Names must be alphanumeric, dash, or underscore, cannot start with underscore, and cannot contain dots.` };
11118
+ }
11119
+
11120
+ // 3. Construct directory paths
11121
+ const analyzerDir = path$3.join(analyzeMessagesBasePath, analyzerName);
11122
+ const contentDir = path$3.join(analyzerDir, contentType);
11123
+ const instructionsDir = path$3.join(contentDir, instructionsType);
11124
+ const instructionsFilePath = path$3.join(instructionsDir, '1.md');
11125
+
11126
+ try {
11127
+ // 4. Create directories recursively
11128
+ await fs$5.mkdir(instructionsDir, { recursive: true });
11129
+
11130
+ // 5. Save instructions content to 1.md
11131
+ const finalContent = `; role: assistant\n\n\n${instructionsContent}`;
11132
+ await fs$5.writeFile(instructionsFilePath, finalContent, 'utf8');
11133
+
11134
+ // 6. Optionally create/Update config.json files
11135
+ if (ensureConfigs) {
11136
+ await ensureConfigJson(analyzerDir, analyzerName);
11137
+ await ensureConfigJson(contentDir, contentType);
11138
+ await ensureConfigJson(instructionsDir, instructionsType);
11139
+ }
11140
+
11141
+ return { success: true, message: `Analyzer configuration '${analyzerId}' saved successfully.` };
11142
+
11143
+ } catch (error) {
11144
+ console.error(`Error saving analyzer configuration '${analyzerId}':`, error);
11145
+ return { success: false, message: `Failed to save analyzer configuration: ${error.message}` };
11146
+ }
11147
+ }
11148
+
11149
+ /**
11150
+ * Ensures a config.json file exists in the given directory with a label.
11151
+ * If the file exists, it reads it and adds the label if missing.
11152
+ * If the file doesn't exist, it creates it with the label.
11153
+ *
11154
+ * @param {string} dirPath - The path to the directory.
11155
+ * @param {string} label - The label to ensure is in the config.json.
11156
+ */
11157
+ async function ensureConfigJson(dirPath, label) {
11158
+ const configPath = path$3.join(dirPath, 'config.json');
11159
+ let config = {};
11160
+
11161
+ try {
11162
+ const fileContent = await fs$5.readFile(configPath, 'utf8');
11163
+ config = JSON.parse(fileContent);
11164
+ } catch (error) {
11165
+ // If file doesn't exist or parsing fails, start with an empty config
11166
+ if (error.code !== 'ENOENT') {
11167
+ console.warn(`Failed to read or parse existing config.json in ${dirPath}: ${error.message}`);
11168
+ }
11169
+ config = {}; // Ensure config is an object even on error
11170
+ }
11171
+
11172
+ // Add or update the label if it's missing or empty
11173
+ if (!config.label || typeof config.label !== 'string' || config.label.trim() === '') {
11174
+ // Capitalize the first letter for the label
11175
+ config.label = label.charAt(0).toUpperCase() + label.slice(1);
11176
+ }
11177
+
11178
+ // Write the updated config back to the file
11179
+ await fs$5.writeFile(configPath, JSON.stringify(config, null, 4), 'utf8');
11180
+ }
11181
+
11182
+
11183
+ var saver = {
11184
+ saveConfiguration: saveConfiguration$1,
11185
+ };
11186
+
11071
11187
  /*
11072
11188
  * Component: AnalyzerUtils Schema Loader
11073
11189
  * Block-UUID: 0c1d2e3f-4a5b-6c7d-8e9f-0a1b2c3d4e5f
@@ -16,8 +16,8 @@ function getDefaultExportFromCjs (x) {
16
16
  * Authors: Claude 3.7 Sonnet (v1.0.0), Gemini 2.5 Pro (v1.1.0), Gemini 2.5 Flash Thinking (v1.2.0)
17
17
  */
18
18
 
19
- const fs$6 = require$$0;
20
- const path$4 = require$$1;
19
+ const fs$7 = require$$0;
20
+ const path$5 = require$$1;
21
21
  const ANALYZE_MESSAGE_REGEXP = /^# Analyze - `([^`]+)`\n/;
22
22
 
23
23
  /**
@@ -51,12 +51,12 @@ function unescapeCodeBlocks(content) {
51
51
  * @returns {Array} An array of message objects with role and content properties
52
52
  */
53
53
  function getChatTemplateMessages$1(dirname, messageType) {
54
- const messagesDir = path$4.join(dirname, messageType);
54
+ const messagesDir = path$5.join(dirname, messageType);
55
55
  const messages = [];
56
56
 
57
57
  try {
58
58
  // Read all files in the directory
59
- const files = fs$6.readdirSync(messagesDir);
59
+ const files = fs$7.readdirSync(messagesDir);
60
60
 
61
61
  // Sort files numerically (1.md, 2.md, etc.)
62
62
  const sortedFiles = files.sort((a, b) => {
@@ -67,9 +67,9 @@ function getChatTemplateMessages$1(dirname, messageType) {
67
67
 
68
68
  // Process each file
69
69
  for (const file of sortedFiles) {
70
- if (path$4.extname(file) === '.md') {
71
- const filePath = path$4.join(messagesDir, file);
72
- const fileContent = fs$6.readFileSync(filePath, 'utf8');
70
+ if (path$5.extname(file) === '.md') {
71
+ const filePath = path$5.join(messagesDir, file);
72
+ const fileContent = fs$7.readFileSync(filePath, 'utf8');
73
73
 
74
74
  // Split by triple newline to separate metadata from content
75
75
  const parts = fileContent.split('\n\n\n');
@@ -10923,8 +10923,8 @@ var dataValidator = {
10923
10923
  * Authors: Gemini 2.5 Flash (v1.0.0)
10924
10924
  */
10925
10925
 
10926
- const fs$5 = require$$0.promises;
10927
- const path$3 = require$$1;
10926
+ const fs$6 = require$$0.promises;
10927
+ const path$4 = require$$1;
10928
10928
 
10929
10929
  /**
10930
10930
  * Reads and parses the config.json file in a directory.
@@ -10933,9 +10933,9 @@ const path$3 = require$$1;
10933
10933
  * or null if the file doesn't exist or is invalid.
10934
10934
  */
10935
10935
  async function readConfig$1(dirPath) {
10936
- const configPath = path$3.join(dirPath, 'config.json');
10936
+ const configPath = path$4.join(dirPath, 'config.json');
10937
10937
  try {
10938
- const fileContent = await fs$5.readFile(configPath, 'utf8');
10938
+ const fileContent = await fs$6.readFile(configPath, 'utf8');
10939
10939
  return JSON.parse(fileContent);
10940
10940
  } catch (error) {
10941
10941
  if (error.code !== 'ENOENT') {
@@ -10972,41 +10972,41 @@ async function getAnalyzers$2(analyzeMessagesBasePath) {
10972
10972
  const analyzers = [];
10973
10973
 
10974
10974
  try {
10975
- const analyzerEntries = await fs$5.readdir(analyzeMessagesBasePath, { withFileTypes: true });
10975
+ const analyzerEntries = await fs$6.readdir(analyzeMessagesBasePath, { withFileTypes: true });
10976
10976
 
10977
10977
  for (const analyzerEntry of analyzerEntries) {
10978
10978
  if (analyzerEntry.isDirectory() && isValidDirName(analyzerEntry.name)) {
10979
10979
  const analyzerName = analyzerEntry.name;
10980
- const analyzerPath = path$3.join(analyzeMessagesBasePath, analyzerName);
10980
+ const analyzerPath = path$4.join(analyzeMessagesBasePath, analyzerName);
10981
10981
  const analyzerConfig = await readConfig$1(analyzerPath);
10982
10982
  const analyzerLabel = analyzerConfig?.label || analyzerName;
10983
10983
 
10984
- const contentEntries = await fs$5.readdir(analyzerPath, { withFileTypes: true });
10984
+ const contentEntries = await fs$6.readdir(analyzerPath, { withFileTypes: true });
10985
10985
 
10986
10986
  for (const contentEntry of contentEntries) {
10987
10987
  if (contentEntry.isDirectory() && isValidDirName(contentEntry.name)) {
10988
10988
  const contentType = contentEntry.name;
10989
- const contentPath = path$3.join(analyzerPath, contentType);
10989
+ const contentPath = path$4.join(analyzerPath, contentType);
10990
10990
  const contentConfig = await readConfig$1(contentPath);
10991
10991
  const contentLabel = contentConfig?.label || contentType;
10992
10992
 
10993
- const instructionsEntries = await fs$5.readdir(contentPath, { withFileTypes: true });
10993
+ const instructionsEntries = await fs$6.readdir(contentPath, { withFileTypes: true });
10994
10994
 
10995
10995
  for (const instructionsEntry of instructionsEntries) {
10996
10996
  if (instructionsEntry.isDirectory() && isValidDirName(instructionsEntry.name)) {
10997
10997
  const instructionsType = instructionsEntry.name;
10998
- const instructionsPath = path$3.join(contentPath, instructionsType);
10998
+ const instructionsPath = path$4.join(contentPath, instructionsType);
10999
10999
  const instructionsConfig = await readConfig$1(instructionsPath);
11000
11000
  const instructionsLabel = instructionsConfig?.label || instructionsType;
11001
11001
 
11002
11002
  // Check for the existence of 1.md to confirm a valid analyzer configuration
11003
- const instructionsFilePath = path$3.join(instructionsPath, '1.md');
11003
+ const instructionsFilePath = path$4.join(instructionsPath, '1.md');
11004
11004
  try {
11005
- await fs$5.access(instructionsFilePath); // Check if file exists and is accessible
11005
+ await fs$6.access(instructionsFilePath); // Check if file exists and is accessible
11006
11006
 
11007
11007
  // If analyzerName starts with 'tutorial-', check its last modified time.
11008
11008
  if (analyzerName.startsWith('tutorial-')) {
11009
- const stats = await fs$5.stat(instructionsFilePath);
11009
+ const stats = await fs$6.stat(instructionsFilePath);
11010
11010
  const lastModified = stats.mtime.getTime(); // Get timestamp in milliseconds
11011
11011
  const sixtyMinutesAgo = Date.now() - (60 * 60 * 1000); // Current time - 60 minutes in ms
11012
11012
 
@@ -11060,12 +11060,128 @@ var discovery = {
11060
11060
  * Authors: Gemini 2.5 Flash Thinking (v1.0.0), Gemini 2.5 Flash Thinking (v1.1.0)
11061
11061
  */
11062
11062
 
11063
- require$$0.promises;
11063
+ const fs$5 = require$$0.promises;
11064
+ const path$3 = require$$1;
11064
11065
 
11066
+ /**
11067
+ * Saves or updates an analyzer configuration.
11068
+ *
11069
+ * This function takes the analyzer ID and its full instructions content,
11070
+ * parses the ID to determine the directory structure, creates directories
11071
+ * if necessary, saves the instructions to '1.md'. Optionally, it can
11072
+ * ensure config.json files exist with labels derived from directory names.
11073
+ *
11074
+ * @param {string} analyzeMessagesBasePath - The absolute or relative path to the 'messages/analyze' directory.
11075
+ * @param {string} analyzerId - The unique ID of the analyzer (format: 'analyzer_name::content_type::instructions_type').
11076
+ * @param {string} instructionsContent - The full content of the analyzer instructions message to be saved in '1.md'.
11077
+ * @param {object} [options={}] - Optional configuration options.
11078
+ * @param {boolean} [options.ensureConfigs=false] - If true, ensures config.json files exist in the analyzer, content, and instructions directories. Defaults to false.
11079
+ * @returns {Promise<{success: boolean, message?: string}>} A promise that resolves with a result object.
11080
+ */
11081
+ async function saveConfiguration$1(analyzeMessagesBasePath, analyzerId, instructionsContent, options = {}) {
11082
+ const { ensureConfigs = false } = options;
11065
11083
 
11066
- var saver = {
11084
+ // 1. Validate inputs
11085
+ if (typeof analyzeMessagesBasePath !== 'string' || analyzeMessagesBasePath.trim() === '') {
11086
+ return { success: false, message: 'analyzeMessagesBasePath is required.' };
11087
+ }
11088
+ if (typeof analyzerId !== 'string' || analyzerId.trim() === '') {
11089
+ return { success: false, message: 'analyzerId is required.' };
11090
+ }
11091
+ if (typeof instructionsContent !== 'string' || instructionsContent.trim() === '') {
11092
+ return { success: false, message: 'instructionsContent is required.' };
11093
+ }
11094
+
11095
+ // 2. Parse analyzerId
11096
+ const parts = analyzerId.split('::');
11097
+ if (parts.length !== 3) {
11098
+ return { success: false, message: `Invalid analyzerId format. Expected 'analyzer_name::content_type::instructions_type', but got '${analyzerId}'.` };
11099
+ }
11100
+ const [analyzerName, contentType, instructionsType] = parts;
11101
+
11102
+ // Helper to validate directory names based on README.md rules
11103
+ const isValidDirName = (name) => {
11104
+ // Cannot start with underscore, cannot contain dots, must be alphanumeric, dash, or underscore
11105
+ return /^[a-zA-Z0-9_-]+$/.test(name) && !name.startsWith('_') && !name.includes('.');
11067
11106
  };
11068
11107
 
11108
+ if (!isValidDirName(analyzerName)) {
11109
+ return { success: false, message: `Invalid analyzer name '${analyzerName}'. Names must be alphanumeric, dash, or underscore, cannot start with underscore, and cannot contain dots.` };
11110
+ }
11111
+ if (!isValidDirName(contentType)) {
11112
+ return { success: false, message: `Invalid content type name '${contentType}'. Names must be alphanumeric, dash, or underscore, cannot start with underscore, and cannot contain dots.` };
11113
+ }
11114
+ if (!isValidDirName(instructionsType)) {
11115
+ return { success: false, message: `Invalid instructions type name '${instructionsType}'. Names must be alphanumeric, dash, or underscore, cannot start with underscore, and cannot contain dots.` };
11116
+ }
11117
+
11118
+ // 3. Construct directory paths
11119
+ const analyzerDir = path$3.join(analyzeMessagesBasePath, analyzerName);
11120
+ const contentDir = path$3.join(analyzerDir, contentType);
11121
+ const instructionsDir = path$3.join(contentDir, instructionsType);
11122
+ const instructionsFilePath = path$3.join(instructionsDir, '1.md');
11123
+
11124
+ try {
11125
+ // 4. Create directories recursively
11126
+ await fs$5.mkdir(instructionsDir, { recursive: true });
11127
+
11128
+ // 5. Save instructions content to 1.md
11129
+ const finalContent = `; role: assistant\n\n\n${instructionsContent}`;
11130
+ await fs$5.writeFile(instructionsFilePath, finalContent, 'utf8');
11131
+
11132
+ // 6. Optionally create/Update config.json files
11133
+ if (ensureConfigs) {
11134
+ await ensureConfigJson(analyzerDir, analyzerName);
11135
+ await ensureConfigJson(contentDir, contentType);
11136
+ await ensureConfigJson(instructionsDir, instructionsType);
11137
+ }
11138
+
11139
+ return { success: true, message: `Analyzer configuration '${analyzerId}' saved successfully.` };
11140
+
11141
+ } catch (error) {
11142
+ console.error(`Error saving analyzer configuration '${analyzerId}':`, error);
11143
+ return { success: false, message: `Failed to save analyzer configuration: ${error.message}` };
11144
+ }
11145
+ }
11146
+
11147
+ /**
11148
+ * Ensures a config.json file exists in the given directory with a label.
11149
+ * If the file exists, it reads it and adds the label if missing.
11150
+ * If the file doesn't exist, it creates it with the label.
11151
+ *
11152
+ * @param {string} dirPath - The path to the directory.
11153
+ * @param {string} label - The label to ensure is in the config.json.
11154
+ */
11155
+ async function ensureConfigJson(dirPath, label) {
11156
+ const configPath = path$3.join(dirPath, 'config.json');
11157
+ let config = {};
11158
+
11159
+ try {
11160
+ const fileContent = await fs$5.readFile(configPath, 'utf8');
11161
+ config = JSON.parse(fileContent);
11162
+ } catch (error) {
11163
+ // If file doesn't exist or parsing fails, start with an empty config
11164
+ if (error.code !== 'ENOENT') {
11165
+ console.warn(`Failed to read or parse existing config.json in ${dirPath}: ${error.message}`);
11166
+ }
11167
+ config = {}; // Ensure config is an object even on error
11168
+ }
11169
+
11170
+ // Add or update the label if it's missing or empty
11171
+ if (!config.label || typeof config.label !== 'string' || config.label.trim() === '') {
11172
+ // Capitalize the first letter for the label
11173
+ config.label = label.charAt(0).toUpperCase() + label.slice(1);
11174
+ }
11175
+
11176
+ // Write the updated config back to the file
11177
+ await fs$5.writeFile(configPath, JSON.stringify(config, null, 4), 'utf8');
11178
+ }
11179
+
11180
+
11181
+ var saver = {
11182
+ saveConfiguration: saveConfiguration$1,
11183
+ };
11184
+
11069
11185
  /*
11070
11186
  * Component: AnalyzerUtils Schema Loader
11071
11187
  * Block-UUID: 0c1d2e3f-4a5b-6c7d-8e9f-0a1b2c3d4e5f
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gitsense/gsc-utils",
3
- "version": "0.2.6",
3
+ "version": "0.2.7",
4
4
  "description": "Utilities for GitSense Chat (GSC)",
5
5
  "main": "dist/gsc-utils.cjs.js",
6
6
  "module": "dist/gsc-utils.esm.js",
@@ -28,7 +28,7 @@ const path = require('path');
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 saveAnalyzerConfiguration(analyzeMessagesBasePath, analyzerId, instructionsContent, options = {}) {
31
+ async function saveConfiguration(analyzeMessagesBasePath, analyzerId, instructionsContent, options = {}) {
32
32
  const { ensureConfigs = false } = options;
33
33
 
34
34
  // 1. Validate inputs
@@ -129,5 +129,5 @@ async function ensureConfigJson(dirPath, label) {
129
129
 
130
130
 
131
131
  module.exports = {
132
- saveAnalyzerConfiguration,
132
+ saveConfiguration,
133
133
  };