@gitsense/gsc-utils 0.2.15 → 0.2.17

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.
@@ -8684,13 +8684,13 @@ var PatchUtils$2 = {
8684
8684
 
8685
8685
  /**
8686
8686
  * Component: GitSense Tool Block Utilities
8687
- * Block-UUID: 72db6a5f-3b09-49e8-8f39-d4e3d37b510a
8688
- * Parent-UUID: N/A
8689
- * Version: 1.0.0
8690
- * Description: Provides utility functions for identifying and parsing GitSense Chat Tool Blocks.
8687
+ * Block-UUID: 8e1f7b4e-7e30-48b4-a7fc-643bf647661f
8688
+ * Parent-UUID: 72db6a5f-3b09-49e8-8f39-d4e3d37b510a
8689
+ * Version: 1.1.0
8690
+ * Description: Provides utility functions for identifying and parsing GitSense Chat Tool Blocks, and now for replacing them within markdown.
8691
8691
  * Language: JavaScript
8692
- * Created-at: 2025-04-27T01:29:53.576Z
8693
- * Authors: Gemini 2.5 Pro (v1.0.0)
8692
+ * Created-at: 2025-09-12T17:23:34.779Z
8693
+ * Authors: Gemini 2.5 Pro (v1.0.0), Gemini 2.5 Flash (v1.1.0)
8694
8694
  */
8695
8695
 
8696
8696
  /**
@@ -8780,14 +8780,75 @@ function parseToolBlock$1(content) {
8780
8780
  }
8781
8781
  }
8782
8782
 
8783
+ /**
8784
+ * Formats tool data into a GitSense Chat Tool Block string.
8785
+ *
8786
+ * @param {object} toolData - The tool data object to format.
8787
+ * @returns {string} The formatted GitSense Chat Tool Block string.
8788
+ */
8783
8789
  function formatToolBlock(toolData) {
8784
8790
  return `# GitSense Chat Tool\n\n${JSON.stringify(toolData, null, 2)}`;
8785
8791
  }
8786
8792
 
8793
+ /**
8794
+ * Replaces a specific GitSense Chat Tool Block within a markdown string with updated tool data.
8795
+ * This function leverages CodeBlockUtils for robust parsing and updating.
8796
+ *
8797
+ * @param {string} markdownContent - The original markdown string containing code blocks.
8798
+ * @param {string} toolName - The 'tool' property value of the target GitSense Chat Tool Block to replace.
8799
+ * @param {Object} newToolData - The new tool data object to insert into the block.
8800
+ * @returns {string} The markdown content with the specified tool block updated.
8801
+ * @throws {Error} If the target tool block is not found or if CodeBlockUtils encounters an error.
8802
+ */
8803
+ function replaceToolBlock(markdownContent, toolName, newToolData, processCodeBlocks, updateCodeBlockByIndex) {
8804
+ if (typeof markdownContent !== 'string' || !toolName || !newToolData) {
8805
+ throw new Error("Missing required parameters for replaceToolBlock.");
8806
+ }
8807
+
8808
+ // We can't require them as this will create a circular dependency
8809
+ if (!processCodeBlocks || !updateCodeBlockByIndex) {
8810
+ throw new Error("Missing required dependencies processCodeBlocks and/or updateCodeBlockByIndex.");
8811
+ }
8812
+
8813
+ // 1. Process the markdown content to find all code blocks
8814
+ const { blocks, warnings } = processCodeBlocks(markdownContent, { silent: true });
8815
+
8816
+ let targetBlockIndex = -1;
8817
+
8818
+ // 2. Iterate through the processed blocks to find the target GitSense Chat Tool Block
8819
+ for (let i = 0; i < blocks.length; i++) {
8820
+ const block = blocks[i];
8821
+ if (block.type === 'gs-tool' && block.toolData && block.toolData.tool === toolName) {
8822
+ targetBlockIndex = i;
8823
+ break; // Found the first matching tool block
8824
+ }
8825
+ }
8826
+
8827
+ if (targetBlockIndex === -1) {
8828
+ console.warn(`replaceToolBlock: No GitSense Chat Tool Block with name "${toolName}" found to replace.`);
8829
+ return markdownContent; // Return original content if no replacement occurred
8830
+ }
8831
+
8832
+ // 3. Format the new tool data into the content that goes *inside* the fences
8833
+ const newContentBetweenFences = formatToolBlock(newToolData);
8834
+
8835
+ // 4. Use CodeBlockUtils.updateCodeBlockByIndex to perform the replacement
8836
+ // The language for GitSense Tool Blocks is always 'txt'
8837
+ const updatedMarkdown = updateCodeBlockByIndex(
8838
+ markdownContent,
8839
+ targetBlockIndex,
8840
+ newContentBetweenFences,
8841
+ 'txt'
8842
+ );
8843
+
8844
+ return updatedMarkdown;
8845
+ }
8846
+
8787
8847
  var GSToolBlockUtils$3 = {
8788
8848
  isToolBlock: isToolBlock$1,
8789
8849
  parseToolBlock: parseToolBlock$1,
8790
- formatToolBlock
8850
+ formatToolBlock,
8851
+ replaceToolBlock,
8791
8852
  };
8792
8853
 
8793
8854
  /*
@@ -11072,11 +11133,11 @@ var dataValidator = {
11072
11133
  };
11073
11134
 
11074
11135
  /*
11075
- * Component: AnalyzerUtils Discovery
11076
- * Block-UUID: 0b1c2d3e-4f5a-6b7c-8d9e-0f1a2b3c4d5f
11136
+ * Component: AnalyzerUtils Instruction Loader
11137
+ * Block-UUID: 0a1b2c3d-4e5f-6a7b-8c9d-0e1f2a3b4c5e
11077
11138
  * Parent-UUID: N/A
11078
11139
  * Version: 1.0.0
11079
- * Description: Provides utility functions for discovering available analyzers.
11140
+ * Description: Provides utility functions for loading raw analyzer instruction content.
11080
11141
  * Language: JavaScript
11081
11142
  * Created-at: 2025-08-28T23:48:00.000Z
11082
11143
  * Authors: Gemini 2.5 Flash (v1.0.0)
@@ -11085,6 +11146,67 @@ var dataValidator = {
11085
11146
  const fs$7 = require$$0.promises;
11086
11147
  const path$5 = require$$1;
11087
11148
 
11149
+ /**
11150
+ * Retrieves the raw Markdown content of the analyzer's '1.md' instruction file.
11151
+ *
11152
+ * @param {string} analyzeMessagesBasePath - The absolute path to the base directory containing the analyzer message files (e.g., 'messages/analyze').
11153
+ * @param {string} analyzerId - The unique ID of the analyzer (format: 'analyzer_name::content_type::instructions_type').
11154
+ * @returns {Promise<string|null>} A promise that resolves with the full Markdown content of the '1.md' file, or null if not found/invalid.
11155
+ */
11156
+ async function getAnalyzerInstructionsContent$3(analyzeMessagesBasePath, analyzerId) {
11157
+ if (typeof analyzeMessagesBasePath !== 'string' || analyzeMessagesBasePath.trim() === '') {
11158
+ console.error('Error: analyzeMessagesBasePath is required.');
11159
+ return null;
11160
+ }
11161
+ if (typeof analyzerId !== 'string' || analyzerId.trim() === '') {
11162
+ console.error('Error: analyzerId is required.');
11163
+ return null;
11164
+ }
11165
+
11166
+ const parts = analyzerId.split('::');
11167
+ if (parts.length !== 3) {
11168
+ console.error(`Error: Invalid analyzerId format. Expected 'analyzer_name::content_type::instructions_type', but got '${analyzerId}'.`);
11169
+ return null;
11170
+ }
11171
+ const [analyzerName, contentType, instructionsType] = parts;
11172
+
11173
+ const instructionsFilePath = path$5.join(analyzeMessagesBasePath, analyzerName, contentType, instructionsType, '1.md');
11174
+
11175
+ try {
11176
+ const fileContent = await fs$7.readFile(instructionsFilePath, 'utf8');
11177
+ const parts = fileContent.split('\n\n\n');
11178
+ parts.shift();
11179
+ return parts.join('\n\n\n');
11180
+ } catch (error) {
11181
+ if (error.code === 'ENOENT') {
11182
+ console.warn(`Analyzer instructions file not found: ${instructionsFilePath}`);
11183
+ return null;
11184
+ } else {
11185
+ console.error(`Error reading analyzer instructions file ${instructionsFilePath}: ${error.message}`);
11186
+ throw error;
11187
+ }
11188
+ }
11189
+ }
11190
+
11191
+ var instructionLoader = {
11192
+ getAnalyzerInstructionsContent: getAnalyzerInstructionsContent$3
11193
+ };
11194
+
11195
+ /*
11196
+ * Component: AnalyzerUtils Discovery
11197
+ * Block-UUID: a87a86c4-69fc-4cbb-80a8-857f56122395
11198
+ * Parent-UUID: 0b1c2d3e-4f5a-6b7c-8d9e-0f1a2b3c4d5f
11199
+ * Version: 1.1.0
11200
+ * Description: Provides utility functions for discovering available analyzers.
11201
+ * Language: JavaScript
11202
+ * Created-at: 2025-08-28T23:48:00.000Z
11203
+ * Authors: Gemini 2.5 Flash (v1.0.0), Gemini 2.5 Flash (v1.1.0)
11204
+ */
11205
+
11206
+ const fs$6 = require$$0.promises;
11207
+ const path$4 = require$$1;
11208
+ const { getAnalyzerInstructionsContent: getAnalyzerInstructionsContent$2 } = instructionLoader;
11209
+
11088
11210
  /**
11089
11211
  * Reads and parses the config.json file in a directory.
11090
11212
  * @param {string} dirPath - The path to the directory.
@@ -11092,9 +11214,9 @@ const path$5 = require$$1;
11092
11214
  * or null if the file doesn't exist or is invalid.
11093
11215
  */
11094
11216
  async function readConfig$1(dirPath) {
11095
- const configPath = path$5.join(dirPath, 'config.json');
11217
+ const configPath = path$4.join(dirPath, 'config.json');
11096
11218
  try {
11097
- const fileContent = await fs$7.readFile(configPath, 'utf8');
11219
+ const fileContent = await fs$6.readFile(configPath, 'utf8');
11098
11220
  return JSON.parse(fileContent);
11099
11221
  } catch (error) {
11100
11222
  if (error.code !== 'ENOENT') {
@@ -11125,47 +11247,50 @@ function isValidDirName(name) {
11125
11247
  * An analyzer is considered valid if a '1.md' file exists in the instructions directory.
11126
11248
  *
11127
11249
  * @param {string} analyzeMessagesBasePath - The absolute or relative path to the base directory containing the analyzer message files (e.g., 'messages/analyze').
11128
- * @returns {Promise<Array<{id: string, label: string}>>} A promise that resolves to an array of analyzer objects.
11250
+ * @param {object} [options={}] - Optional configuration.
11251
+ * @param {boolean} [options.includeDescription=false] - Whether to include the description of the analyzer.
11252
+ * @returns {Promise<Array<{id: string, label: string, name: string, protected: boolean, description?: string}>>} A promise that resolves to an array of analyzer objects.
11129
11253
  */
11130
- async function getAnalyzers$2(analyzeMessagesBasePath) {
11254
+ async function getAnalyzers$2(analyzeMessagesBasePath, options = {}) {
11255
+ const { includeDescription = false } = options;
11131
11256
  const analyzers = [];
11132
11257
 
11133
11258
  try {
11134
- const analyzerEntries = await fs$7.readdir(analyzeMessagesBasePath, { withFileTypes: true });
11259
+ const analyzerEntries = await fs$6.readdir(analyzeMessagesBasePath, { withFileTypes: true });
11135
11260
 
11136
11261
  for (const analyzerEntry of analyzerEntries) {
11137
11262
  if (analyzerEntry.isDirectory() && isValidDirName(analyzerEntry.name)) {
11138
11263
  const analyzerName = analyzerEntry.name;
11139
- const analyzerPath = path$5.join(analyzeMessagesBasePath, analyzerName);
11264
+ const analyzerPath = path$4.join(analyzeMessagesBasePath, analyzerName);
11140
11265
  const analyzerConfig = await readConfig$1(analyzerPath);
11141
11266
  const analyzerLabel = analyzerConfig?.label || analyzerName;
11142
11267
 
11143
- const contentEntries = await fs$7.readdir(analyzerPath, { withFileTypes: true });
11268
+ const contentEntries = await fs$6.readdir(analyzerPath, { withFileTypes: true });
11144
11269
 
11145
11270
  for (const contentEntry of contentEntries) {
11146
11271
  if (contentEntry.isDirectory() && isValidDirName(contentEntry.name)) {
11147
11272
  const contentType = contentEntry.name;
11148
- const contentPath = path$5.join(analyzerPath, contentType);
11273
+ const contentPath = path$4.join(analyzerPath, contentType);
11149
11274
  const contentConfig = await readConfig$1(contentPath);
11150
11275
  const contentLabel = contentConfig?.label || contentType;
11151
11276
 
11152
- const instructionsEntries = await fs$7.readdir(contentPath, { withFileTypes: true });
11277
+ const instructionsEntries = await fs$6.readdir(contentPath, { withFileTypes: true });
11153
11278
 
11154
11279
  for (const instructionsEntry of instructionsEntries) {
11155
11280
  if (instructionsEntry.isDirectory() && isValidDirName(instructionsEntry.name)) {
11156
11281
  const instructionsType = instructionsEntry.name;
11157
- const instructionsPath = path$5.join(contentPath, instructionsType);
11282
+ const instructionsPath = path$4.join(contentPath, instructionsType);
11158
11283
  const instructionsConfig = await readConfig$1(instructionsPath);
11159
11284
  const instructionsLabel = instructionsConfig?.label || instructionsType;
11160
11285
 
11161
11286
  // Check for the existence of 1.md to confirm a valid analyzer configuration
11162
- const instructionsFilePath = path$5.join(instructionsPath, '1.md');
11287
+ const instructionsFilePath = path$4.join(instructionsPath, '1.md');
11163
11288
  try {
11164
- await fs$7.access(instructionsFilePath); // Check if file exists and is accessible
11289
+ await fs$6.access(instructionsFilePath); // Check if file exists and is accessible
11165
11290
 
11166
11291
  // If analyzerName starts with 'tutorial-', check its last modified time.
11167
11292
  if (analyzerName.startsWith('tutorial-')) {
11168
- const stats = await fs$7.stat(instructionsFilePath);
11293
+ const stats = await fs$6.stat(instructionsFilePath);
11169
11294
  const lastModified = stats.mtime.getTime(); // Get timestamp in milliseconds
11170
11295
  const sixtyMinutesAgo = Date.now() - (60 * 60 * 1000); // Current time - 60 minutes in ms
11171
11296
 
@@ -11178,11 +11303,23 @@ async function getAnalyzers$2(analyzeMessagesBasePath) {
11178
11303
  const analyzerId = `${analyzerName}::${contentType}::${instructionsType}`;
11179
11304
  const analyzerFullLabel = `${analyzerLabel} (${contentLabel} - ${instructionsLabel})`;
11180
11305
 
11306
+ let descriptionContent = null;
11307
+ if (includeDescription) {
11308
+ try {
11309
+ descriptionContent = await getAnalyzerInstructionsContent$2(analyzeMessagesBasePath, analyzerId);
11310
+ } catch (descError) {
11311
+ console.warn(`Warning: Could not load description for ${analyzerId}: ${descError.message}`);
11312
+ descriptionContent = null;
11313
+ }
11314
+ }
11315
+
11181
11316
  analyzers.push({
11182
11317
  id: analyzerId,
11183
11318
  label: analyzerFullLabel,
11184
- protected: analyzerConfig?.protected || false
11185
- });
11319
+ name: analyzerName,
11320
+ protected: analyzerConfig?.protected || false,
11321
+ ...(descriptionContent && { description: descriptionContent })
11322
+ });
11186
11323
  } catch (error) {
11187
11324
  // If 1.md doesn't exist, this is not a complete analyzer configuration, skip.
11188
11325
  if (error.code !== 'ENOENT') {
@@ -11219,8 +11356,8 @@ var discovery = {
11219
11356
  * Authors: Gemini 2.5 Flash Thinking (v1.0.0), Gemini 2.5 Flash Thinking (v1.1.0)
11220
11357
  */
11221
11358
 
11222
- const fs$6 = require$$0.promises;
11223
- const path$4 = require$$1;
11359
+ const fs$5 = require$$0.promises;
11360
+ const path$3 = require$$1;
11224
11361
 
11225
11362
  /**
11226
11363
  * Saves or updates an analyzer configuration.
@@ -11275,18 +11412,18 @@ async function saveConfiguration$1(analyzeMessagesBasePath, analyzerId, instruct
11275
11412
  }
11276
11413
 
11277
11414
  // 3. Construct directory paths
11278
- const analyzerDir = path$4.join(analyzeMessagesBasePath, analyzerName);
11279
- const contentDir = path$4.join(analyzerDir, contentType);
11280
- const instructionsDir = path$4.join(contentDir, instructionsType);
11281
- const instructionsFilePath = path$4.join(instructionsDir, '1.md');
11415
+ const analyzerDir = path$3.join(analyzeMessagesBasePath, analyzerName);
11416
+ const contentDir = path$3.join(analyzerDir, contentType);
11417
+ const instructionsDir = path$3.join(contentDir, instructionsType);
11418
+ const instructionsFilePath = path$3.join(instructionsDir, '1.md');
11282
11419
 
11283
11420
  try {
11284
11421
  // 4. Create directories recursively
11285
- await fs$6.mkdir(instructionsDir, { recursive: true });
11422
+ await fs$5.mkdir(instructionsDir, { recursive: true });
11286
11423
 
11287
11424
  // 5. Save instructions content to 1.md
11288
11425
  const finalContent = `; role: assistant\n\n\n${instructionsContent}`;
11289
- await fs$6.writeFile(instructionsFilePath, finalContent, 'utf8');
11426
+ await fs$5.writeFile(instructionsFilePath, finalContent, 'utf8');
11290
11427
 
11291
11428
  // 6. Optionally create/Update config.json files
11292
11429
  if (ensureConfigs) {
@@ -11312,11 +11449,11 @@ async function saveConfiguration$1(analyzeMessagesBasePath, analyzerId, instruct
11312
11449
  * @param {string} label - The label to ensure is in the config.json.
11313
11450
  */
11314
11451
  async function ensureConfigJson(dirPath, label) {
11315
- const configPath = path$4.join(dirPath, 'config.json');
11452
+ const configPath = path$3.join(dirPath, 'config.json');
11316
11453
  let config = {};
11317
11454
 
11318
11455
  try {
11319
- const fileContent = await fs$6.readFile(configPath, 'utf8');
11456
+ const fileContent = await fs$5.readFile(configPath, 'utf8');
11320
11457
  config = JSON.parse(fileContent);
11321
11458
  } catch (error) {
11322
11459
  // If file doesn't exist or parsing fails, start with an empty config
@@ -11333,7 +11470,7 @@ async function ensureConfigJson(dirPath, label) {
11333
11470
  }
11334
11471
 
11335
11472
  // Write the updated config back to the file
11336
- await fs$6.writeFile(configPath, JSON.stringify(config, null, 4), 'utf8');
11473
+ await fs$5.writeFile(configPath, JSON.stringify(config, null, 4), 'utf8');
11337
11474
  }
11338
11475
 
11339
11476
 
@@ -11352,8 +11489,8 @@ var saver = {
11352
11489
  * Authors: Gemini 2.5 Flash (v1.0.0)
11353
11490
  */
11354
11491
 
11355
- const fs$5 = require$$0.promises;
11356
- const path$3 = require$$1;
11492
+ const fs$4 = require$$0.promises;
11493
+ const path$2 = require$$1;
11357
11494
  const CodeBlockUtils$1 = CodeBlockUtils$4;
11358
11495
 
11359
11496
  /**
@@ -11439,10 +11576,10 @@ async function getAnalyzerSchema$2(analyzeMessagesBasePath, analyzerId) {
11439
11576
  }
11440
11577
  const [analyzerName, contentType, instructionsType] = parts;
11441
11578
 
11442
- const instructionsFilePath = path$3.join(analyzeMessagesBasePath, analyzerName, contentType, instructionsType, '1.md');
11579
+ const instructionsFilePath = path$2.join(analyzeMessagesBasePath, analyzerName, contentType, instructionsType, '1.md');
11443
11580
 
11444
11581
  try {
11445
- const fileContent = await fs$5.readFile(instructionsFilePath, 'utf8');
11582
+ const fileContent = await fs$4.readFile(instructionsFilePath, 'utf8');
11446
11583
  const { blocks } = CodeBlockUtils$1.extractCodeBlocks(fileContent, { silent: true });
11447
11584
  const jsonBlocks = blocks.filter(block => block.type === 'code' && block.language === 'json');
11448
11585
 
@@ -11513,8 +11650,8 @@ var schemaLoader = {
11513
11650
  * Authors: Gemini 2.5 Flash (v1.0.0)
11514
11651
  */
11515
11652
 
11516
- const fs$4 = require$$0.promises;
11517
- const path$2 = require$$1;
11653
+ const fs$3 = require$$0.promises;
11654
+ const path$1 = require$$1;
11518
11655
  const { readConfig } = discovery; // Import helper from discovery
11519
11656
 
11520
11657
  /**
@@ -11524,7 +11661,7 @@ const { readConfig } = discovery; // Import helper from discovery
11524
11661
  */
11525
11662
  async function isDirectoryEmpty(dirPath) {
11526
11663
  try {
11527
- const files = await fs$4.readdir(dirPath);
11664
+ const files = await fs$3.readdir(dirPath);
11528
11665
  return files.length === 0 || (files.length === 1 && files[0] === 'config.json');
11529
11666
  } catch (error) {
11530
11667
  if (error.code === 'ENOENT') {
@@ -11555,10 +11692,10 @@ async function deleteAnalyzer$2(analyzeMessagesBasePath, analyzerId) {
11555
11692
  }
11556
11693
  const [analyzerName, contentType, instructionsType] = parts;
11557
11694
 
11558
- const analyzerDir = path$2.join(analyzeMessagesBasePath, analyzerName);
11559
- const contentDir = path$2.join(analyzerDir, contentType);
11560
- const instructionsDir = path$2.join(contentDir, instructionsType);
11561
- const instructionsFilePath = path$2.join(instructionsDir, '1.md');
11695
+ const analyzerDir = path$1.join(analyzeMessagesBasePath, analyzerName);
11696
+ const contentDir = path$1.join(analyzerDir, contentType);
11697
+ const instructionsDir = path$1.join(contentDir, instructionsType);
11698
+ const instructionsFilePath = path$1.join(instructionsDir, '1.md');
11562
11699
 
11563
11700
  try {
11564
11701
  // 1. Check for protection at all levels
@@ -11579,7 +11716,7 @@ async function deleteAnalyzer$2(analyzeMessagesBasePath, analyzerId) {
11579
11716
 
11580
11717
  // 2. Delete the 1.md file
11581
11718
  try {
11582
- await fs$4.unlink(instructionsFilePath);
11719
+ await fs$3.unlink(instructionsFilePath);
11583
11720
  } catch (error) {
11584
11721
  if (error.code === 'ENOENT') {
11585
11722
  return { success: false, message: `Analyzer instructions file not found: ${instructionsFilePath}. It may have already been deleted.` };
@@ -11593,7 +11730,7 @@ async function deleteAnalyzer$2(analyzeMessagesBasePath, analyzerId) {
11593
11730
  // Check and delete instructions directory
11594
11731
  if (await isDirectoryEmpty(instructionsDir)) {
11595
11732
  try {
11596
- await fs$4.rmdir(instructionsDir);
11733
+ await fs$3.rmdir(instructionsDir);
11597
11734
  deletedDirs.push(instructionsDir);
11598
11735
  } catch (error) {
11599
11736
  console.warn(`Warning: Could not remove empty instructions directory ${instructionsDir}: ${error.message}`);
@@ -11603,7 +11740,7 @@ async function deleteAnalyzer$2(analyzeMessagesBasePath, analyzerId) {
11603
11740
  // Check and delete content directory
11604
11741
  if (await isDirectoryEmpty(contentDir)) {
11605
11742
  try {
11606
- await fs$4.rmdir(contentDir);
11743
+ await fs$3.rmdir(contentDir);
11607
11744
  deletedDirs.push(contentDir);
11608
11745
  } catch (error) {
11609
11746
  console.warn(`Warning: Could not remove empty content directory ${contentDir}: ${error.message}`);
@@ -11613,7 +11750,7 @@ async function deleteAnalyzer$2(analyzeMessagesBasePath, analyzerId) {
11613
11750
  // Check and delete analyzer directory
11614
11751
  if (await isDirectoryEmpty(analyzerDir)) {
11615
11752
  try {
11616
- await fs$4.rmdir(analyzerDir);
11753
+ await fs$3.rmdir(analyzerDir);
11617
11754
  deletedDirs.push(analyzerDir);
11618
11755
  } catch (error) {
11619
11756
  console.warn(`Warning: Could not remove empty analyzer directory ${analyzerDir}: ${error.message}`);
@@ -11631,69 +11768,9 @@ async function deleteAnalyzer$2(analyzeMessagesBasePath, analyzerId) {
11631
11768
  var management = {
11632
11769
  deleteAnalyzer: deleteAnalyzer$2};
11633
11770
 
11634
- /*
11635
- * Component: AnalyzerUtils Instruction Loader
11636
- * Block-UUID: 0a1b2c3d-4e5f-6a7b-8c9d-0e1f2a3b4c5e
11637
- * Parent-UUID: N/A
11638
- * Version: 1.0.0
11639
- * Description: Provides utility functions for loading raw analyzer instruction content.
11640
- * Language: JavaScript
11641
- * Created-at: 2025-08-28T23:48:00.000Z
11642
- * Authors: Gemini 2.5 Flash (v1.0.0)
11643
- */
11644
-
11645
- const fs$3 = require$$0.promises;
11646
- const path$1 = require$$1;
11647
-
11648
- /**
11649
- * Retrieves the raw Markdown content of the analyzer's '1.md' instruction file.
11650
- *
11651
- * @param {string} analyzeMessagesBasePath - The absolute path to the base directory containing the analyzer message files (e.g., 'messages/analyze').
11652
- * @param {string} analyzerId - The unique ID of the analyzer (format: 'analyzer_name::content_type::instructions_type').
11653
- * @returns {Promise<string|null>} A promise that resolves with the full Markdown content of the '1.md' file, or null if not found/invalid.
11654
- */
11655
- async function getAnalyzerInstructionsContent$2(analyzeMessagesBasePath, analyzerId) {
11656
- if (typeof analyzeMessagesBasePath !== 'string' || analyzeMessagesBasePath.trim() === '') {
11657
- console.error('Error: analyzeMessagesBasePath is required.');
11658
- return null;
11659
- }
11660
- if (typeof analyzerId !== 'string' || analyzerId.trim() === '') {
11661
- console.error('Error: analyzerId is required.');
11662
- return null;
11663
- }
11664
-
11665
- const parts = analyzerId.split('::');
11666
- if (parts.length !== 3) {
11667
- console.error(`Error: Invalid analyzerId format. Expected 'analyzer_name::content_type::instructions_type', but got '${analyzerId}'.`);
11668
- return null;
11669
- }
11670
- const [analyzerName, contentType, instructionsType] = parts;
11671
-
11672
- const instructionsFilePath = path$1.join(analyzeMessagesBasePath, analyzerName, contentType, instructionsType, '1.md');
11673
-
11674
- try {
11675
- const fileContent = await fs$3.readFile(instructionsFilePath, 'utf8');
11676
- const parts = fileContent.split('\n\n\n');
11677
- parts.shift();
11678
- return parts.join('\n\n\n');
11679
- } catch (error) {
11680
- if (error.code === 'ENOENT') {
11681
- console.warn(`Analyzer instructions file not found: ${instructionsFilePath}`);
11682
- return null;
11683
- } else {
11684
- console.error(`Error reading analyzer instructions file ${instructionsFilePath}: ${error.message}`);
11685
- throw error;
11686
- }
11687
- }
11688
- }
11689
-
11690
- var instructionLoader = {
11691
- getAnalyzerInstructionsContent: getAnalyzerInstructionsContent$2
11692
- };
11693
-
11694
11771
  /*
11695
11772
  * Component: AnalyzerUtils Default Prompt Loader
11696
- * Block-UUID: 0b1c2d3e-4f5a-6b7c-8d9e-0f1a2b3c4d5f
11773
+ * Block-UUID: be863744-6461-4b94-97ce-b1d177e9e881
11697
11774
  * Parent-UUID: N/A
11698
11775
  * Version: 1.0.0
11699
11776
  * Description: Provides utility functions for loading shared, default prompt components (system and start messages).
@@ -8682,13 +8682,13 @@ var PatchUtils$2 = {
8682
8682
 
8683
8683
  /**
8684
8684
  * Component: GitSense Tool Block Utilities
8685
- * Block-UUID: 72db6a5f-3b09-49e8-8f39-d4e3d37b510a
8686
- * Parent-UUID: N/A
8687
- * Version: 1.0.0
8688
- * Description: Provides utility functions for identifying and parsing GitSense Chat Tool Blocks.
8685
+ * Block-UUID: 8e1f7b4e-7e30-48b4-a7fc-643bf647661f
8686
+ * Parent-UUID: 72db6a5f-3b09-49e8-8f39-d4e3d37b510a
8687
+ * Version: 1.1.0
8688
+ * Description: Provides utility functions for identifying and parsing GitSense Chat Tool Blocks, and now for replacing them within markdown.
8689
8689
  * Language: JavaScript
8690
- * Created-at: 2025-04-27T01:29:53.576Z
8691
- * Authors: Gemini 2.5 Pro (v1.0.0)
8690
+ * Created-at: 2025-09-12T17:23:34.779Z
8691
+ * Authors: Gemini 2.5 Pro (v1.0.0), Gemini 2.5 Flash (v1.1.0)
8692
8692
  */
8693
8693
 
8694
8694
  /**
@@ -8778,14 +8778,75 @@ function parseToolBlock$1(content) {
8778
8778
  }
8779
8779
  }
8780
8780
 
8781
+ /**
8782
+ * Formats tool data into a GitSense Chat Tool Block string.
8783
+ *
8784
+ * @param {object} toolData - The tool data object to format.
8785
+ * @returns {string} The formatted GitSense Chat Tool Block string.
8786
+ */
8781
8787
  function formatToolBlock(toolData) {
8782
8788
  return `# GitSense Chat Tool\n\n${JSON.stringify(toolData, null, 2)}`;
8783
8789
  }
8784
8790
 
8791
+ /**
8792
+ * Replaces a specific GitSense Chat Tool Block within a markdown string with updated tool data.
8793
+ * This function leverages CodeBlockUtils for robust parsing and updating.
8794
+ *
8795
+ * @param {string} markdownContent - The original markdown string containing code blocks.
8796
+ * @param {string} toolName - The 'tool' property value of the target GitSense Chat Tool Block to replace.
8797
+ * @param {Object} newToolData - The new tool data object to insert into the block.
8798
+ * @returns {string} The markdown content with the specified tool block updated.
8799
+ * @throws {Error} If the target tool block is not found or if CodeBlockUtils encounters an error.
8800
+ */
8801
+ function replaceToolBlock(markdownContent, toolName, newToolData, processCodeBlocks, updateCodeBlockByIndex) {
8802
+ if (typeof markdownContent !== 'string' || !toolName || !newToolData) {
8803
+ throw new Error("Missing required parameters for replaceToolBlock.");
8804
+ }
8805
+
8806
+ // We can't require them as this will create a circular dependency
8807
+ if (!processCodeBlocks || !updateCodeBlockByIndex) {
8808
+ throw new Error("Missing required dependencies processCodeBlocks and/or updateCodeBlockByIndex.");
8809
+ }
8810
+
8811
+ // 1. Process the markdown content to find all code blocks
8812
+ const { blocks, warnings } = processCodeBlocks(markdownContent, { silent: true });
8813
+
8814
+ let targetBlockIndex = -1;
8815
+
8816
+ // 2. Iterate through the processed blocks to find the target GitSense Chat Tool Block
8817
+ for (let i = 0; i < blocks.length; i++) {
8818
+ const block = blocks[i];
8819
+ if (block.type === 'gs-tool' && block.toolData && block.toolData.tool === toolName) {
8820
+ targetBlockIndex = i;
8821
+ break; // Found the first matching tool block
8822
+ }
8823
+ }
8824
+
8825
+ if (targetBlockIndex === -1) {
8826
+ console.warn(`replaceToolBlock: No GitSense Chat Tool Block with name "${toolName}" found to replace.`);
8827
+ return markdownContent; // Return original content if no replacement occurred
8828
+ }
8829
+
8830
+ // 3. Format the new tool data into the content that goes *inside* the fences
8831
+ const newContentBetweenFences = formatToolBlock(newToolData);
8832
+
8833
+ // 4. Use CodeBlockUtils.updateCodeBlockByIndex to perform the replacement
8834
+ // The language for GitSense Tool Blocks is always 'txt'
8835
+ const updatedMarkdown = updateCodeBlockByIndex(
8836
+ markdownContent,
8837
+ targetBlockIndex,
8838
+ newContentBetweenFences,
8839
+ 'txt'
8840
+ );
8841
+
8842
+ return updatedMarkdown;
8843
+ }
8844
+
8785
8845
  var GSToolBlockUtils$3 = {
8786
8846
  isToolBlock: isToolBlock$1,
8787
8847
  parseToolBlock: parseToolBlock$1,
8788
- formatToolBlock
8848
+ formatToolBlock,
8849
+ replaceToolBlock,
8789
8850
  };
8790
8851
 
8791
8852
  /*
@@ -11070,11 +11131,11 @@ var dataValidator = {
11070
11131
  };
11071
11132
 
11072
11133
  /*
11073
- * Component: AnalyzerUtils Discovery
11074
- * Block-UUID: 0b1c2d3e-4f5a-6b7c-8d9e-0f1a2b3c4d5f
11134
+ * Component: AnalyzerUtils Instruction Loader
11135
+ * Block-UUID: 0a1b2c3d-4e5f-6a7b-8c9d-0e1f2a3b4c5e
11075
11136
  * Parent-UUID: N/A
11076
11137
  * Version: 1.0.0
11077
- * Description: Provides utility functions for discovering available analyzers.
11138
+ * Description: Provides utility functions for loading raw analyzer instruction content.
11078
11139
  * Language: JavaScript
11079
11140
  * Created-at: 2025-08-28T23:48:00.000Z
11080
11141
  * Authors: Gemini 2.5 Flash (v1.0.0)
@@ -11083,6 +11144,67 @@ var dataValidator = {
11083
11144
  const fs$7 = require$$0.promises;
11084
11145
  const path$5 = require$$1;
11085
11146
 
11147
+ /**
11148
+ * Retrieves the raw Markdown content of the analyzer's '1.md' instruction file.
11149
+ *
11150
+ * @param {string} analyzeMessagesBasePath - The absolute path to the base directory containing the analyzer message files (e.g., 'messages/analyze').
11151
+ * @param {string} analyzerId - The unique ID of the analyzer (format: 'analyzer_name::content_type::instructions_type').
11152
+ * @returns {Promise<string|null>} A promise that resolves with the full Markdown content of the '1.md' file, or null if not found/invalid.
11153
+ */
11154
+ async function getAnalyzerInstructionsContent$3(analyzeMessagesBasePath, analyzerId) {
11155
+ if (typeof analyzeMessagesBasePath !== 'string' || analyzeMessagesBasePath.trim() === '') {
11156
+ console.error('Error: analyzeMessagesBasePath is required.');
11157
+ return null;
11158
+ }
11159
+ if (typeof analyzerId !== 'string' || analyzerId.trim() === '') {
11160
+ console.error('Error: analyzerId is required.');
11161
+ return null;
11162
+ }
11163
+
11164
+ const parts = analyzerId.split('::');
11165
+ if (parts.length !== 3) {
11166
+ console.error(`Error: Invalid analyzerId format. Expected 'analyzer_name::content_type::instructions_type', but got '${analyzerId}'.`);
11167
+ return null;
11168
+ }
11169
+ const [analyzerName, contentType, instructionsType] = parts;
11170
+
11171
+ const instructionsFilePath = path$5.join(analyzeMessagesBasePath, analyzerName, contentType, instructionsType, '1.md');
11172
+
11173
+ try {
11174
+ const fileContent = await fs$7.readFile(instructionsFilePath, 'utf8');
11175
+ const parts = fileContent.split('\n\n\n');
11176
+ parts.shift();
11177
+ return parts.join('\n\n\n');
11178
+ } catch (error) {
11179
+ if (error.code === 'ENOENT') {
11180
+ console.warn(`Analyzer instructions file not found: ${instructionsFilePath}`);
11181
+ return null;
11182
+ } else {
11183
+ console.error(`Error reading analyzer instructions file ${instructionsFilePath}: ${error.message}`);
11184
+ throw error;
11185
+ }
11186
+ }
11187
+ }
11188
+
11189
+ var instructionLoader = {
11190
+ getAnalyzerInstructionsContent: getAnalyzerInstructionsContent$3
11191
+ };
11192
+
11193
+ /*
11194
+ * Component: AnalyzerUtils Discovery
11195
+ * Block-UUID: a87a86c4-69fc-4cbb-80a8-857f56122395
11196
+ * Parent-UUID: 0b1c2d3e-4f5a-6b7c-8d9e-0f1a2b3c4d5f
11197
+ * Version: 1.1.0
11198
+ * Description: Provides utility functions for discovering available analyzers.
11199
+ * Language: JavaScript
11200
+ * Created-at: 2025-08-28T23:48:00.000Z
11201
+ * Authors: Gemini 2.5 Flash (v1.0.0), Gemini 2.5 Flash (v1.1.0)
11202
+ */
11203
+
11204
+ const fs$6 = require$$0.promises;
11205
+ const path$4 = require$$1;
11206
+ const { getAnalyzerInstructionsContent: getAnalyzerInstructionsContent$2 } = instructionLoader;
11207
+
11086
11208
  /**
11087
11209
  * Reads and parses the config.json file in a directory.
11088
11210
  * @param {string} dirPath - The path to the directory.
@@ -11090,9 +11212,9 @@ const path$5 = require$$1;
11090
11212
  * or null if the file doesn't exist or is invalid.
11091
11213
  */
11092
11214
  async function readConfig$1(dirPath) {
11093
- const configPath = path$5.join(dirPath, 'config.json');
11215
+ const configPath = path$4.join(dirPath, 'config.json');
11094
11216
  try {
11095
- const fileContent = await fs$7.readFile(configPath, 'utf8');
11217
+ const fileContent = await fs$6.readFile(configPath, 'utf8');
11096
11218
  return JSON.parse(fileContent);
11097
11219
  } catch (error) {
11098
11220
  if (error.code !== 'ENOENT') {
@@ -11123,47 +11245,50 @@ function isValidDirName(name) {
11123
11245
  * An analyzer is considered valid if a '1.md' file exists in the instructions directory.
11124
11246
  *
11125
11247
  * @param {string} analyzeMessagesBasePath - The absolute or relative path to the base directory containing the analyzer message files (e.g., 'messages/analyze').
11126
- * @returns {Promise<Array<{id: string, label: string}>>} A promise that resolves to an array of analyzer objects.
11248
+ * @param {object} [options={}] - Optional configuration.
11249
+ * @param {boolean} [options.includeDescription=false] - Whether to include the description of the analyzer.
11250
+ * @returns {Promise<Array<{id: string, label: string, name: string, protected: boolean, description?: string}>>} A promise that resolves to an array of analyzer objects.
11127
11251
  */
11128
- async function getAnalyzers$2(analyzeMessagesBasePath) {
11252
+ async function getAnalyzers$2(analyzeMessagesBasePath, options = {}) {
11253
+ const { includeDescription = false } = options;
11129
11254
  const analyzers = [];
11130
11255
 
11131
11256
  try {
11132
- const analyzerEntries = await fs$7.readdir(analyzeMessagesBasePath, { withFileTypes: true });
11257
+ const analyzerEntries = await fs$6.readdir(analyzeMessagesBasePath, { withFileTypes: true });
11133
11258
 
11134
11259
  for (const analyzerEntry of analyzerEntries) {
11135
11260
  if (analyzerEntry.isDirectory() && isValidDirName(analyzerEntry.name)) {
11136
11261
  const analyzerName = analyzerEntry.name;
11137
- const analyzerPath = path$5.join(analyzeMessagesBasePath, analyzerName);
11262
+ const analyzerPath = path$4.join(analyzeMessagesBasePath, analyzerName);
11138
11263
  const analyzerConfig = await readConfig$1(analyzerPath);
11139
11264
  const analyzerLabel = analyzerConfig?.label || analyzerName;
11140
11265
 
11141
- const contentEntries = await fs$7.readdir(analyzerPath, { withFileTypes: true });
11266
+ const contentEntries = await fs$6.readdir(analyzerPath, { withFileTypes: true });
11142
11267
 
11143
11268
  for (const contentEntry of contentEntries) {
11144
11269
  if (contentEntry.isDirectory() && isValidDirName(contentEntry.name)) {
11145
11270
  const contentType = contentEntry.name;
11146
- const contentPath = path$5.join(analyzerPath, contentType);
11271
+ const contentPath = path$4.join(analyzerPath, contentType);
11147
11272
  const contentConfig = await readConfig$1(contentPath);
11148
11273
  const contentLabel = contentConfig?.label || contentType;
11149
11274
 
11150
- const instructionsEntries = await fs$7.readdir(contentPath, { withFileTypes: true });
11275
+ const instructionsEntries = await fs$6.readdir(contentPath, { withFileTypes: true });
11151
11276
 
11152
11277
  for (const instructionsEntry of instructionsEntries) {
11153
11278
  if (instructionsEntry.isDirectory() && isValidDirName(instructionsEntry.name)) {
11154
11279
  const instructionsType = instructionsEntry.name;
11155
- const instructionsPath = path$5.join(contentPath, instructionsType);
11280
+ const instructionsPath = path$4.join(contentPath, instructionsType);
11156
11281
  const instructionsConfig = await readConfig$1(instructionsPath);
11157
11282
  const instructionsLabel = instructionsConfig?.label || instructionsType;
11158
11283
 
11159
11284
  // Check for the existence of 1.md to confirm a valid analyzer configuration
11160
- const instructionsFilePath = path$5.join(instructionsPath, '1.md');
11285
+ const instructionsFilePath = path$4.join(instructionsPath, '1.md');
11161
11286
  try {
11162
- await fs$7.access(instructionsFilePath); // Check if file exists and is accessible
11287
+ await fs$6.access(instructionsFilePath); // Check if file exists and is accessible
11163
11288
 
11164
11289
  // If analyzerName starts with 'tutorial-', check its last modified time.
11165
11290
  if (analyzerName.startsWith('tutorial-')) {
11166
- const stats = await fs$7.stat(instructionsFilePath);
11291
+ const stats = await fs$6.stat(instructionsFilePath);
11167
11292
  const lastModified = stats.mtime.getTime(); // Get timestamp in milliseconds
11168
11293
  const sixtyMinutesAgo = Date.now() - (60 * 60 * 1000); // Current time - 60 minutes in ms
11169
11294
 
@@ -11176,11 +11301,23 @@ async function getAnalyzers$2(analyzeMessagesBasePath) {
11176
11301
  const analyzerId = `${analyzerName}::${contentType}::${instructionsType}`;
11177
11302
  const analyzerFullLabel = `${analyzerLabel} (${contentLabel} - ${instructionsLabel})`;
11178
11303
 
11304
+ let descriptionContent = null;
11305
+ if (includeDescription) {
11306
+ try {
11307
+ descriptionContent = await getAnalyzerInstructionsContent$2(analyzeMessagesBasePath, analyzerId);
11308
+ } catch (descError) {
11309
+ console.warn(`Warning: Could not load description for ${analyzerId}: ${descError.message}`);
11310
+ descriptionContent = null;
11311
+ }
11312
+ }
11313
+
11179
11314
  analyzers.push({
11180
11315
  id: analyzerId,
11181
11316
  label: analyzerFullLabel,
11182
- protected: analyzerConfig?.protected || false
11183
- });
11317
+ name: analyzerName,
11318
+ protected: analyzerConfig?.protected || false,
11319
+ ...(descriptionContent && { description: descriptionContent })
11320
+ });
11184
11321
  } catch (error) {
11185
11322
  // If 1.md doesn't exist, this is not a complete analyzer configuration, skip.
11186
11323
  if (error.code !== 'ENOENT') {
@@ -11217,8 +11354,8 @@ var discovery = {
11217
11354
  * Authors: Gemini 2.5 Flash Thinking (v1.0.0), Gemini 2.5 Flash Thinking (v1.1.0)
11218
11355
  */
11219
11356
 
11220
- const fs$6 = require$$0.promises;
11221
- const path$4 = require$$1;
11357
+ const fs$5 = require$$0.promises;
11358
+ const path$3 = require$$1;
11222
11359
 
11223
11360
  /**
11224
11361
  * Saves or updates an analyzer configuration.
@@ -11273,18 +11410,18 @@ async function saveConfiguration$1(analyzeMessagesBasePath, analyzerId, instruct
11273
11410
  }
11274
11411
 
11275
11412
  // 3. Construct directory paths
11276
- const analyzerDir = path$4.join(analyzeMessagesBasePath, analyzerName);
11277
- const contentDir = path$4.join(analyzerDir, contentType);
11278
- const instructionsDir = path$4.join(contentDir, instructionsType);
11279
- const instructionsFilePath = path$4.join(instructionsDir, '1.md');
11413
+ const analyzerDir = path$3.join(analyzeMessagesBasePath, analyzerName);
11414
+ const contentDir = path$3.join(analyzerDir, contentType);
11415
+ const instructionsDir = path$3.join(contentDir, instructionsType);
11416
+ const instructionsFilePath = path$3.join(instructionsDir, '1.md');
11280
11417
 
11281
11418
  try {
11282
11419
  // 4. Create directories recursively
11283
- await fs$6.mkdir(instructionsDir, { recursive: true });
11420
+ await fs$5.mkdir(instructionsDir, { recursive: true });
11284
11421
 
11285
11422
  // 5. Save instructions content to 1.md
11286
11423
  const finalContent = `; role: assistant\n\n\n${instructionsContent}`;
11287
- await fs$6.writeFile(instructionsFilePath, finalContent, 'utf8');
11424
+ await fs$5.writeFile(instructionsFilePath, finalContent, 'utf8');
11288
11425
 
11289
11426
  // 6. Optionally create/Update config.json files
11290
11427
  if (ensureConfigs) {
@@ -11310,11 +11447,11 @@ async function saveConfiguration$1(analyzeMessagesBasePath, analyzerId, instruct
11310
11447
  * @param {string} label - The label to ensure is in the config.json.
11311
11448
  */
11312
11449
  async function ensureConfigJson(dirPath, label) {
11313
- const configPath = path$4.join(dirPath, 'config.json');
11450
+ const configPath = path$3.join(dirPath, 'config.json');
11314
11451
  let config = {};
11315
11452
 
11316
11453
  try {
11317
- const fileContent = await fs$6.readFile(configPath, 'utf8');
11454
+ const fileContent = await fs$5.readFile(configPath, 'utf8');
11318
11455
  config = JSON.parse(fileContent);
11319
11456
  } catch (error) {
11320
11457
  // If file doesn't exist or parsing fails, start with an empty config
@@ -11331,7 +11468,7 @@ async function ensureConfigJson(dirPath, label) {
11331
11468
  }
11332
11469
 
11333
11470
  // Write the updated config back to the file
11334
- await fs$6.writeFile(configPath, JSON.stringify(config, null, 4), 'utf8');
11471
+ await fs$5.writeFile(configPath, JSON.stringify(config, null, 4), 'utf8');
11335
11472
  }
11336
11473
 
11337
11474
 
@@ -11350,8 +11487,8 @@ var saver = {
11350
11487
  * Authors: Gemini 2.5 Flash (v1.0.0)
11351
11488
  */
11352
11489
 
11353
- const fs$5 = require$$0.promises;
11354
- const path$3 = require$$1;
11490
+ const fs$4 = require$$0.promises;
11491
+ const path$2 = require$$1;
11355
11492
  const CodeBlockUtils$1 = CodeBlockUtils$4;
11356
11493
 
11357
11494
  /**
@@ -11437,10 +11574,10 @@ async function getAnalyzerSchema$2(analyzeMessagesBasePath, analyzerId) {
11437
11574
  }
11438
11575
  const [analyzerName, contentType, instructionsType] = parts;
11439
11576
 
11440
- const instructionsFilePath = path$3.join(analyzeMessagesBasePath, analyzerName, contentType, instructionsType, '1.md');
11577
+ const instructionsFilePath = path$2.join(analyzeMessagesBasePath, analyzerName, contentType, instructionsType, '1.md');
11441
11578
 
11442
11579
  try {
11443
- const fileContent = await fs$5.readFile(instructionsFilePath, 'utf8');
11580
+ const fileContent = await fs$4.readFile(instructionsFilePath, 'utf8');
11444
11581
  const { blocks } = CodeBlockUtils$1.extractCodeBlocks(fileContent, { silent: true });
11445
11582
  const jsonBlocks = blocks.filter(block => block.type === 'code' && block.language === 'json');
11446
11583
 
@@ -11511,8 +11648,8 @@ var schemaLoader = {
11511
11648
  * Authors: Gemini 2.5 Flash (v1.0.0)
11512
11649
  */
11513
11650
 
11514
- const fs$4 = require$$0.promises;
11515
- const path$2 = require$$1;
11651
+ const fs$3 = require$$0.promises;
11652
+ const path$1 = require$$1;
11516
11653
  const { readConfig } = discovery; // Import helper from discovery
11517
11654
 
11518
11655
  /**
@@ -11522,7 +11659,7 @@ const { readConfig } = discovery; // Import helper from discovery
11522
11659
  */
11523
11660
  async function isDirectoryEmpty(dirPath) {
11524
11661
  try {
11525
- const files = await fs$4.readdir(dirPath);
11662
+ const files = await fs$3.readdir(dirPath);
11526
11663
  return files.length === 0 || (files.length === 1 && files[0] === 'config.json');
11527
11664
  } catch (error) {
11528
11665
  if (error.code === 'ENOENT') {
@@ -11553,10 +11690,10 @@ async function deleteAnalyzer$2(analyzeMessagesBasePath, analyzerId) {
11553
11690
  }
11554
11691
  const [analyzerName, contentType, instructionsType] = parts;
11555
11692
 
11556
- const analyzerDir = path$2.join(analyzeMessagesBasePath, analyzerName);
11557
- const contentDir = path$2.join(analyzerDir, contentType);
11558
- const instructionsDir = path$2.join(contentDir, instructionsType);
11559
- const instructionsFilePath = path$2.join(instructionsDir, '1.md');
11693
+ const analyzerDir = path$1.join(analyzeMessagesBasePath, analyzerName);
11694
+ const contentDir = path$1.join(analyzerDir, contentType);
11695
+ const instructionsDir = path$1.join(contentDir, instructionsType);
11696
+ const instructionsFilePath = path$1.join(instructionsDir, '1.md');
11560
11697
 
11561
11698
  try {
11562
11699
  // 1. Check for protection at all levels
@@ -11577,7 +11714,7 @@ async function deleteAnalyzer$2(analyzeMessagesBasePath, analyzerId) {
11577
11714
 
11578
11715
  // 2. Delete the 1.md file
11579
11716
  try {
11580
- await fs$4.unlink(instructionsFilePath);
11717
+ await fs$3.unlink(instructionsFilePath);
11581
11718
  } catch (error) {
11582
11719
  if (error.code === 'ENOENT') {
11583
11720
  return { success: false, message: `Analyzer instructions file not found: ${instructionsFilePath}. It may have already been deleted.` };
@@ -11591,7 +11728,7 @@ async function deleteAnalyzer$2(analyzeMessagesBasePath, analyzerId) {
11591
11728
  // Check and delete instructions directory
11592
11729
  if (await isDirectoryEmpty(instructionsDir)) {
11593
11730
  try {
11594
- await fs$4.rmdir(instructionsDir);
11731
+ await fs$3.rmdir(instructionsDir);
11595
11732
  deletedDirs.push(instructionsDir);
11596
11733
  } catch (error) {
11597
11734
  console.warn(`Warning: Could not remove empty instructions directory ${instructionsDir}: ${error.message}`);
@@ -11601,7 +11738,7 @@ async function deleteAnalyzer$2(analyzeMessagesBasePath, analyzerId) {
11601
11738
  // Check and delete content directory
11602
11739
  if (await isDirectoryEmpty(contentDir)) {
11603
11740
  try {
11604
- await fs$4.rmdir(contentDir);
11741
+ await fs$3.rmdir(contentDir);
11605
11742
  deletedDirs.push(contentDir);
11606
11743
  } catch (error) {
11607
11744
  console.warn(`Warning: Could not remove empty content directory ${contentDir}: ${error.message}`);
@@ -11611,7 +11748,7 @@ async function deleteAnalyzer$2(analyzeMessagesBasePath, analyzerId) {
11611
11748
  // Check and delete analyzer directory
11612
11749
  if (await isDirectoryEmpty(analyzerDir)) {
11613
11750
  try {
11614
- await fs$4.rmdir(analyzerDir);
11751
+ await fs$3.rmdir(analyzerDir);
11615
11752
  deletedDirs.push(analyzerDir);
11616
11753
  } catch (error) {
11617
11754
  console.warn(`Warning: Could not remove empty analyzer directory ${analyzerDir}: ${error.message}`);
@@ -11629,69 +11766,9 @@ async function deleteAnalyzer$2(analyzeMessagesBasePath, analyzerId) {
11629
11766
  var management = {
11630
11767
  deleteAnalyzer: deleteAnalyzer$2};
11631
11768
 
11632
- /*
11633
- * Component: AnalyzerUtils Instruction Loader
11634
- * Block-UUID: 0a1b2c3d-4e5f-6a7b-8c9d-0e1f2a3b4c5e
11635
- * Parent-UUID: N/A
11636
- * Version: 1.0.0
11637
- * Description: Provides utility functions for loading raw analyzer instruction content.
11638
- * Language: JavaScript
11639
- * Created-at: 2025-08-28T23:48:00.000Z
11640
- * Authors: Gemini 2.5 Flash (v1.0.0)
11641
- */
11642
-
11643
- const fs$3 = require$$0.promises;
11644
- const path$1 = require$$1;
11645
-
11646
- /**
11647
- * Retrieves the raw Markdown content of the analyzer's '1.md' instruction file.
11648
- *
11649
- * @param {string} analyzeMessagesBasePath - The absolute path to the base directory containing the analyzer message files (e.g., 'messages/analyze').
11650
- * @param {string} analyzerId - The unique ID of the analyzer (format: 'analyzer_name::content_type::instructions_type').
11651
- * @returns {Promise<string|null>} A promise that resolves with the full Markdown content of the '1.md' file, or null if not found/invalid.
11652
- */
11653
- async function getAnalyzerInstructionsContent$2(analyzeMessagesBasePath, analyzerId) {
11654
- if (typeof analyzeMessagesBasePath !== 'string' || analyzeMessagesBasePath.trim() === '') {
11655
- console.error('Error: analyzeMessagesBasePath is required.');
11656
- return null;
11657
- }
11658
- if (typeof analyzerId !== 'string' || analyzerId.trim() === '') {
11659
- console.error('Error: analyzerId is required.');
11660
- return null;
11661
- }
11662
-
11663
- const parts = analyzerId.split('::');
11664
- if (parts.length !== 3) {
11665
- console.error(`Error: Invalid analyzerId format. Expected 'analyzer_name::content_type::instructions_type', but got '${analyzerId}'.`);
11666
- return null;
11667
- }
11668
- const [analyzerName, contentType, instructionsType] = parts;
11669
-
11670
- const instructionsFilePath = path$1.join(analyzeMessagesBasePath, analyzerName, contentType, instructionsType, '1.md');
11671
-
11672
- try {
11673
- const fileContent = await fs$3.readFile(instructionsFilePath, 'utf8');
11674
- const parts = fileContent.split('\n\n\n');
11675
- parts.shift();
11676
- return parts.join('\n\n\n');
11677
- } catch (error) {
11678
- if (error.code === 'ENOENT') {
11679
- console.warn(`Analyzer instructions file not found: ${instructionsFilePath}`);
11680
- return null;
11681
- } else {
11682
- console.error(`Error reading analyzer instructions file ${instructionsFilePath}: ${error.message}`);
11683
- throw error;
11684
- }
11685
- }
11686
- }
11687
-
11688
- var instructionLoader = {
11689
- getAnalyzerInstructionsContent: getAnalyzerInstructionsContent$2
11690
- };
11691
-
11692
11769
  /*
11693
11770
  * Component: AnalyzerUtils Default Prompt Loader
11694
- * Block-UUID: 0b1c2d3e-4f5a-6b7c-8d9e-0f1a2b3c4d5f
11771
+ * Block-UUID: be863744-6461-4b94-97ce-b1d177e9e881
11695
11772
  * Parent-UUID: N/A
11696
11773
  * Version: 1.0.0
11697
11774
  * Description: Provides utility functions for loading shared, default prompt components (system and start messages).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gitsense/gsc-utils",
3
- "version": "0.2.15",
3
+ "version": "0.2.17",
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,6 +1,6 @@
1
1
  /*
2
2
  * Component: AnalyzerUtils Default Prompt Loader
3
- * Block-UUID: 0b1c2d3e-4f5a-6b7c-8d9e-0f1a2b3c4d5f
3
+ * Block-UUID: be863744-6461-4b94-97ce-b1d177e9e881
4
4
  * Parent-UUID: N/A
5
5
  * Version: 1.0.0
6
6
  * Description: Provides utility functions for loading shared, default prompt components (system and start messages).
@@ -1,17 +1,18 @@
1
1
  /*
2
2
  * Component: AnalyzerUtils Discovery
3
- * Block-UUID: 0b1c2d3e-4f5a-6b7c-8d9e-0f1a2b3c4d5f
4
- * Parent-UUID: N/A
5
- * Version: 1.0.0
3
+ * Block-UUID: a87a86c4-69fc-4cbb-80a8-857f56122395
4
+ * Parent-UUID: 0b1c2d3e-4f5a-6b7c-8d9e-0f1a2b3c4d5f
5
+ * Version: 1.1.0
6
6
  * Description: Provides utility functions for discovering available analyzers.
7
7
  * Language: JavaScript
8
8
  * Created-at: 2025-08-28T23:48:00.000Z
9
- * Authors: Gemini 2.5 Flash (v1.0.0)
9
+ * Authors: Gemini 2.5 Flash (v1.0.0), Gemini 2.5 Flash (v1.1.0)
10
10
  */
11
11
 
12
12
 
13
13
  const fs = require('fs').promises;
14
14
  const path = require('path');
15
+ const { getAnalyzerInstructionsContent } = require('./instructionLoader');
15
16
 
16
17
  /**
17
18
  * Reads and parses the config.json file in a directory.
@@ -53,9 +54,12 @@ function isValidDirName(name) {
53
54
  * An analyzer is considered valid if a '1.md' file exists in the instructions directory.
54
55
  *
55
56
  * @param {string} analyzeMessagesBasePath - The absolute or relative path to the base directory containing the analyzer message files (e.g., 'messages/analyze').
56
- * @returns {Promise<Array<{id: string, label: string}>>} A promise that resolves to an array of analyzer objects.
57
+ * @param {object} [options={}] - Optional configuration.
58
+ * @param {boolean} [options.includeDescription=false] - Whether to include the description of the analyzer.
59
+ * @returns {Promise<Array<{id: string, label: string, name: string, protected: boolean, description?: string}>>} A promise that resolves to an array of analyzer objects.
57
60
  */
58
- async function getAnalyzers(analyzeMessagesBasePath) {
61
+ async function getAnalyzers(analyzeMessagesBasePath, options = {}) {
62
+ const { includeDescription = false } = options;
59
63
  const analyzers = [];
60
64
 
61
65
  try {
@@ -106,11 +110,23 @@ async function getAnalyzers(analyzeMessagesBasePath) {
106
110
  const analyzerId = `${analyzerName}::${contentType}::${instructionsType}`;
107
111
  const analyzerFullLabel = `${analyzerLabel} (${contentLabel} - ${instructionsLabel})`;
108
112
 
113
+ let descriptionContent = null;
114
+ if (includeDescription) {
115
+ try {
116
+ descriptionContent = await getAnalyzerInstructionsContent(analyzeMessagesBasePath, analyzerId);
117
+ } catch (descError) {
118
+ console.warn(`Warning: Could not load description for ${analyzerId}: ${descError.message}`);
119
+ descriptionContent = null;
120
+ }
121
+ }
122
+
109
123
  analyzers.push({
110
124
  id: analyzerId,
111
125
  label: analyzerFullLabel,
112
- protected: analyzerConfig?.protected || false
113
- });
126
+ name: analyzerName,
127
+ protected: analyzerConfig?.protected || false,
128
+ ...(descriptionContent && { description: descriptionContent })
129
+ });
114
130
  } catch (error) {
115
131
  // If 1.md doesn't exist, this is not a complete analyzer configuration, skip.
116
132
  if (error.code !== 'ENOENT') {
@@ -1,12 +1,12 @@
1
1
  /**
2
2
  * Component: GitSense Tool Block Utilities
3
- * Block-UUID: 72db6a5f-3b09-49e8-8f39-d4e3d37b510a
4
- * Parent-UUID: N/A
5
- * Version: 1.0.0
6
- * Description: Provides utility functions for identifying and parsing GitSense Chat Tool Blocks.
3
+ * Block-UUID: 8e1f7b4e-7e30-48b4-a7fc-643bf647661f
4
+ * Parent-UUID: 72db6a5f-3b09-49e8-8f39-d4e3d37b510a
5
+ * Version: 1.1.0
6
+ * Description: Provides utility functions for identifying and parsing GitSense Chat Tool Blocks, and now for replacing them within markdown.
7
7
  * Language: JavaScript
8
- * Created-at: 2025-04-27T01:29:53.576Z
9
- * Authors: Gemini 2.5 Pro (v1.0.0)
8
+ * Created-at: 2025-09-12T17:23:34.779Z
9
+ * Authors: Gemini 2.5 Pro (v1.0.0), Gemini 2.5 Flash (v1.1.0)
10
10
  */
11
11
 
12
12
 
@@ -97,12 +97,73 @@ function parseToolBlock(content) {
97
97
  }
98
98
  }
99
99
 
100
+ /**
101
+ * Formats tool data into a GitSense Chat Tool Block string.
102
+ *
103
+ * @param {object} toolData - The tool data object to format.
104
+ * @returns {string} The formatted GitSense Chat Tool Block string.
105
+ */
100
106
  function formatToolBlock(toolData) {
101
107
  return `# GitSense Chat Tool\n\n${JSON.stringify(toolData, null, 2)}`;
102
108
  }
103
109
 
110
+ /**
111
+ * Replaces a specific GitSense Chat Tool Block within a markdown string with updated tool data.
112
+ * This function leverages CodeBlockUtils for robust parsing and updating.
113
+ *
114
+ * @param {string} markdownContent - The original markdown string containing code blocks.
115
+ * @param {string} toolName - The 'tool' property value of the target GitSense Chat Tool Block to replace.
116
+ * @param {Object} newToolData - The new tool data object to insert into the block.
117
+ * @returns {string} The markdown content with the specified tool block updated.
118
+ * @throws {Error} If the target tool block is not found or if CodeBlockUtils encounters an error.
119
+ */
120
+ function replaceToolBlock(markdownContent, toolName, newToolData, processCodeBlocks, updateCodeBlockByIndex) {
121
+ if (typeof markdownContent !== 'string' || !toolName || !newToolData) {
122
+ throw new Error("Missing required parameters for replaceToolBlock.");
123
+ }
124
+
125
+ // We can't require them as this will create a circular dependency
126
+ if (!processCodeBlocks || !updateCodeBlockByIndex) {
127
+ throw new Error("Missing required dependencies processCodeBlocks and/or updateCodeBlockByIndex.");
128
+ }
129
+
130
+ // 1. Process the markdown content to find all code blocks
131
+ const { blocks, warnings } = processCodeBlocks(markdownContent, { silent: true });
132
+
133
+ let targetBlockIndex = -1;
134
+
135
+ // 2. Iterate through the processed blocks to find the target GitSense Chat Tool Block
136
+ for (let i = 0; i < blocks.length; i++) {
137
+ const block = blocks[i];
138
+ if (block.type === 'gs-tool' && block.toolData && block.toolData.tool === toolName) {
139
+ targetBlockIndex = i;
140
+ break; // Found the first matching tool block
141
+ }
142
+ }
143
+
144
+ if (targetBlockIndex === -1) {
145
+ console.warn(`replaceToolBlock: No GitSense Chat Tool Block with name "${toolName}" found to replace.`);
146
+ return markdownContent; // Return original content if no replacement occurred
147
+ }
148
+
149
+ // 3. Format the new tool data into the content that goes *inside* the fences
150
+ const newContentBetweenFences = formatToolBlock(newToolData);
151
+
152
+ // 4. Use CodeBlockUtils.updateCodeBlockByIndex to perform the replacement
153
+ // The language for GitSense Tool Blocks is always 'txt'
154
+ const updatedMarkdown = updateCodeBlockByIndex(
155
+ markdownContent,
156
+ targetBlockIndex,
157
+ newContentBetweenFences,
158
+ 'txt'
159
+ );
160
+
161
+ return updatedMarkdown;
162
+ }
163
+
104
164
  module.exports = {
105
165
  isToolBlock,
106
166
  parseToolBlock,
107
- formatToolBlock
167
+ formatToolBlock,
168
+ replaceToolBlock,
108
169
  }