codecritique 1.2.1 → 1.2.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codecritique",
3
- "version": "1.2.1",
3
+ "version": "1.2.3",
4
4
  "description": "AI-powered code review tool for any programming language",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
@@ -23,7 +23,7 @@ import { calculateCosineSimilarity, calculatePathSimilarity } from './embeddings
23
23
  import { inferContextFromDocumentContent } from './utils/context-inference.js';
24
24
  import { isGenericDocument, getGenericDocumentContext } from './utils/document-detection.js';
25
25
  import { isDocumentationFile } from './utils/file-validation.js';
26
- import { debug } from './utils/logging.js';
26
+ import { debug, verboseLog } from './utils/logging.js';
27
27
 
28
28
  const FILE_EMBEDDINGS_TABLE = TABLE_NAMES.FILE_EMBEDDINGS;
29
29
  const DOCUMENT_CHUNK_TABLE = TABLE_NAMES.DOCUMENT_CHUNK;
@@ -79,7 +79,8 @@ export class ContentRetriever {
79
79
  return [];
80
80
  }
81
81
 
82
- console.log(
82
+ verboseLog(
83
+ options,
83
84
  chalk.cyan(`Native hybrid documentation search - limit: ${limit}, threshold: ${similarityThreshold}, reranking: ${useReranking}`)
84
85
  );
85
86
 
@@ -91,7 +92,7 @@ export class ContentRetriever {
91
92
  return [];
92
93
  }
93
94
 
94
- console.log(chalk.cyan('Performing native hybrid search for documentation...'));
95
+ verboseLog(options, chalk.cyan('Performing native hybrid search for documentation...'));
95
96
  let query = table.search(queryText).nearestToText(queryText);
96
97
 
97
98
  const resolvedProjectPath = path.resolve(projectPath);
@@ -106,7 +107,7 @@ export class ContentRetriever {
106
107
  }
107
108
 
108
109
  const results = await query.limit(Math.max(limit * 3, 20)).toArray();
109
- console.log(chalk.green(`Native hybrid search returned ${results.length} documentation results`));
110
+ verboseLog(options, chalk.green(`Native hybrid search returned ${results.length} documentation results`));
110
111
 
111
112
  // OPTIMIZATION: Enhanced batch file existence checks with parallel processing
112
113
  const docsToCheck = [];
@@ -168,7 +169,7 @@ export class ContentRetriever {
168
169
  // Filter results based on project match using the map
169
170
  const projectFilteredResults = results.filter((result, index) => docProjectMatchMap.get(index) === true);
170
171
 
171
- console.log(chalk.blue(`Filtered to ${projectFilteredResults.length} documentation results from current project`));
172
+ verboseLog(options, chalk.blue(`Filtered to ${projectFilteredResults.length} documentation results from current project`));
172
173
  let finalResults = projectFilteredResults.map((result) => {
173
174
  let similarity;
174
175
  if (result._distance !== undefined) {
@@ -197,7 +198,7 @@ export class ContentRetriever {
197
198
 
198
199
  let queryEmbedding = null;
199
200
  if (useReranking && queryContextForReranking && finalResults.length >= 3) {
200
- console.log(chalk.cyan('Applying sophisticated contextual reranking to documentation...'));
201
+ verboseLog(options, chalk.cyan('Applying sophisticated contextual reranking to documentation...'));
201
202
  const WEIGHT_INITIAL_SIM = 0.3;
202
203
  const WEIGHT_H1_CHUNK_RERANK = 0.15;
203
204
  const HEAVY_BOOST_SAME_AREA = 0.4;
@@ -416,7 +417,7 @@ export class ContentRetriever {
416
417
  finalResults = finalResults.slice(0, limit);
417
418
  }
418
419
 
419
- console.log(chalk.green(`Returning ${finalResults.length} documentation results`));
420
+ verboseLog(options, chalk.green(`Returning ${finalResults.length} documentation results`));
420
421
 
421
422
  return finalResults;
422
423
  } catch (error) {
@@ -443,7 +444,10 @@ export class ContentRetriever {
443
444
  precomputedQueryEmbedding = null,
444
445
  } = options;
445
446
 
446
- console.log(chalk.cyan(`Native hybrid code search - limit: ${limit}, threshold: ${similarityThreshold}, isTestFile: ${isTestFile}`));
447
+ verboseLog(
448
+ options,
449
+ chalk.cyan(`Native hybrid code search - limit: ${limit}, threshold: ${similarityThreshold}, isTestFile: ${isTestFile}`)
450
+ );
447
451
 
448
452
  try {
449
453
  if (!queryText?.trim()) {
@@ -460,7 +464,7 @@ export class ContentRetriever {
460
464
  }
461
465
 
462
466
  // Native hybrid search with automatic vector + FTS + RRF
463
- console.log(chalk.cyan('Performing native hybrid search for code...'));
467
+ verboseLog(options, chalk.cyan('Performing native hybrid search for code...'));
464
468
  let query = table.search(queryText).nearestToText(queryText);
465
469
 
466
470
  // Add filtering conditions
@@ -472,13 +476,13 @@ export class ContentRetriever {
472
476
  if (isTestFile) {
473
477
  // Only include test files
474
478
  conditions.push(`(path LIKE '%.test.%' OR path LIKE '%.spec.%' OR path LIKE '%_test.py' OR path LIKE 'test_%.py')`);
475
- console.log(chalk.blue(`Filtering to include only test files.`));
479
+ verboseLog(options, chalk.blue(`Filtering to include only test files.`));
476
480
  } else {
477
481
  // Exclude test files
478
482
  conditions.push(
479
483
  `(path NOT LIKE '%.test.%' AND path NOT LIKE '%.spec.%' AND path NOT LIKE '%_test.py' AND path NOT LIKE 'test_%.py')`
480
484
  );
481
- console.log(chalk.blue(`Filtering to exclude test files.`));
485
+ verboseLog(options, chalk.blue(`Filtering to exclude test files.`));
482
486
  }
483
487
  }
484
488
 
@@ -526,7 +530,7 @@ export class ContentRetriever {
526
530
 
527
531
  const results = await query.limit(Math.max(limit * 3, 20)).toArray();
528
532
 
529
- console.log(chalk.green(`Native hybrid search returned ${results.length} results`));
533
+ verboseLog(options, chalk.green(`Native hybrid search returned ${results.length} results`));
530
534
 
531
535
  // OPTIMIZATION: Batch file existence checks for better performance
532
536
  const resultsToCheck = [];
@@ -595,7 +599,7 @@ export class ContentRetriever {
595
599
  // Filter results based on project match using the map
596
600
  const projectFilteredResults = results.filter((result, index) => projectMatchMap.get(index) === true);
597
601
 
598
- console.log(chalk.blue(`Filtered to ${projectFilteredResults.length} results from current project`));
602
+ verboseLog(options, chalk.blue(`Filtered to ${projectFilteredResults.length} results from current project`));
599
603
 
600
604
  // Map results to expected format
601
605
  let finalResults = projectFilteredResults.map((result) => {
@@ -683,7 +687,7 @@ export class ContentRetriever {
683
687
  finalResults = finalResults.slice(0, limit);
684
688
  }
685
689
 
686
- console.log(chalk.green(`Returning ${finalResults.length} optimized hybrid search results`));
690
+ verboseLog(options, chalk.green(`Returning ${finalResults.length} optimized hybrid search results`));
687
691
  return finalResults;
688
692
  } catch (error) {
689
693
  console.error(chalk.red(`Error in optimized findSimilarCode: ${error.message}`), error);
@@ -712,7 +716,7 @@ export class ContentRetriever {
712
716
  this.h1EmbeddingCache.clear();
713
717
  this.documentContextCache.clear();
714
718
  this.documentContextPromiseCache.clear();
715
- console.log(chalk.green('ContentRetriever caches cleared'));
719
+ verboseLog({}, chalk.green('ContentRetriever caches cleared'));
716
720
  }
717
721
 
718
722
  /**
@@ -739,7 +743,7 @@ export class ContentRetriever {
739
743
  parallelRerankingTime: 0,
740
744
  };
741
745
 
742
- console.log(chalk.green('ContentRetriever cleanup complete'));
746
+ verboseLog({}, chalk.green('ContentRetriever cleanup complete'));
743
747
  } finally {
744
748
  this.cleaningUp = false;
745
749
  }
@@ -18,7 +18,7 @@ import { CacheManager } from './embeddings/cache-manager.js';
18
18
  import { EmbeddingError, ValidationError } from './embeddings/errors.js';
19
19
  import { ModelManager } from './embeddings/model-manager.js';
20
20
  import { calculateCosineSimilarity, calculatePathSimilarity } from './embeddings/similarity-calculator.js';
21
- import { debug } from './utils/logging.js';
21
+ import { debug, verboseLog } from './utils/logging.js';
22
22
  import { slugify } from './utils/string-utils.js';
23
23
 
24
24
  /**
@@ -140,7 +140,7 @@ export class CustomDocumentProcessor {
140
140
  this.performanceMetrics.averageChunkSize = chunks.reduce((sum, chunk) => sum + chunk.content.length, 0) / chunks.length;
141
141
  this.performanceMetrics.processingTime += Date.now() - startTime;
142
142
 
143
- console.log(chalk.gray(` Chunked document "${documentTitle}" into ${chunks.length} chunks`));
143
+ verboseLog({}, chalk.gray(` Chunked document "${documentTitle}" into ${chunks.length} chunks`));
144
144
  return chunks;
145
145
  } catch (error) {
146
146
  console.error(chalk.red(`Error chunking document: ${error.message}`));
@@ -159,18 +159,18 @@ export class CustomDocumentProcessor {
159
159
 
160
160
  try {
161
161
  if (!customDocs || customDocs.length === 0) {
162
- console.log(chalk.gray('No custom documents to process'));
162
+ verboseLog({}, chalk.gray('No custom documents to process'));
163
163
  return [];
164
164
  }
165
165
 
166
- console.log(chalk.cyan(`Processing ${customDocs.length} custom documents into chunks...`));
166
+ verboseLog({}, chalk.cyan(`Processing ${customDocs.length} custom documents into chunks...`));
167
167
 
168
168
  const allChunks = [];
169
169
  let totalBatchAttempts = 0;
170
170
  let successfulBatches = 0;
171
171
 
172
172
  for (const doc of customDocs) {
173
- console.log(chalk.gray(` Processing document: ${doc.title}`));
173
+ verboseLog({}, chalk.gray(` Processing document: ${doc.title}`));
174
174
 
175
175
  // Chunk the document
176
176
  const chunks = this.chunkDocument(doc);
@@ -205,12 +205,12 @@ export class CustomDocumentProcessor {
205
205
  const validChunks = chunksWithEmbeddings.filter((chunk) => chunk !== null);
206
206
  allChunks.push(...validChunks);
207
207
 
208
- console.log(chalk.gray(` Generated embeddings for ${validChunks.length}/${chunks.length} chunks`));
208
+ verboseLog({}, chalk.gray(` Generated embeddings for ${validChunks.length}/${chunks.length} chunks`));
209
209
  this.performanceMetrics.embeddingsCalculated += validChunks.length;
210
210
  } catch (error) {
211
211
  console.error(chalk.red(`Error in batch embedding generation for document ${doc.title}: ${error.message}`));
212
212
  // Fallback to individual processing for this document
213
- console.log(chalk.yellow(` Falling back to individual processing for ${doc.title}`));
213
+ verboseLog({}, chalk.yellow(` Falling back to individual processing for ${doc.title}`));
214
214
 
215
215
  const chunksWithEmbeddings = await Promise.all(
216
216
  chunks.map(async (chunk) => {
@@ -235,7 +235,7 @@ export class CustomDocumentProcessor {
235
235
  const validChunks = chunksWithEmbeddings.filter((chunk) => chunk !== null);
236
236
  allChunks.push(...validChunks);
237
237
 
238
- console.log(chalk.gray(` Generated embeddings for ${validChunks.length}/${chunks.length} chunks (fallback)`));
238
+ verboseLog({}, chalk.gray(` Generated embeddings for ${validChunks.length}/${chunks.length} chunks (fallback)`));
239
239
  }
240
240
  }
241
241
 
@@ -252,7 +252,7 @@ export class CustomDocumentProcessor {
252
252
  this.performanceMetrics.documentsProcessed += customDocs.length;
253
253
  this.performanceMetrics.processingTime += Date.now() - startTime;
254
254
 
255
- console.log(chalk.green(`Successfully processed ${allChunks.length} custom document chunks (${Date.now() - startTime}ms)`));
255
+ verboseLog({}, chalk.green(`Successfully processed ${allChunks.length} custom document chunks (${Date.now() - startTime}ms)`));
256
256
  return allChunks;
257
257
  } catch (error) {
258
258
  console.error(chalk.red(`Error processing custom documents: ${error.message}`));
@@ -285,11 +285,11 @@ export class CustomDocumentProcessor {
285
285
  }
286
286
 
287
287
  if (!chunks || chunks.length === 0) {
288
- console.log(chalk.gray('No custom document chunks available for search'));
288
+ verboseLog({}, chalk.gray('No custom document chunks available for search'));
289
289
  return [];
290
290
  }
291
291
 
292
- console.log(chalk.cyan(`Searching ${chunks.length} custom document chunks...`));
292
+ verboseLog({}, chalk.cyan(`Searching ${chunks.length} custom document chunks...`));
293
293
 
294
294
  // OPTIMIZATION: Use pre-computed query embedding if available
295
295
  let queryEmbedding = precomputedQueryEmbedding;
@@ -319,7 +319,7 @@ export class CustomDocumentProcessor {
319
319
  filteredResults = filteredResults.slice(0, limit);
320
320
  }
321
321
 
322
- console.log(chalk.green(`Found ${filteredResults.length} relevant custom document chunks (${Date.now() - startTime}ms)`));
322
+ verboseLog({}, chalk.green(`Found ${filteredResults.length} relevant custom document chunks (${Date.now() - startTime}ms)`));
323
323
 
324
324
  // Log top results for debugging
325
325
  if (filteredResults.length > 0) {
@@ -350,7 +350,7 @@ export class CustomDocumentProcessor {
350
350
  }
351
351
 
352
352
  // Try cache manager
353
- const cachedChunks = await this.cacheManager.getCustomDocuments(resolvedProjectPath);
353
+ const cachedChunks = this.cacheManager.getCustomDocumentChunks(resolvedProjectPath);
354
354
  if (cachedChunks && cachedChunks.length > 0) {
355
355
  // Restore to memory
356
356
  this.customDocumentChunks.set(resolvedProjectPath, cachedChunks);
@@ -371,7 +371,7 @@ export class CustomDocumentProcessor {
371
371
  * @private
372
372
  */
373
373
  async _applyParallelReranking(filteredResults, queryText, queryContextForReranking, queryFilePath, queryEmbedding) {
374
- console.log(chalk.cyan('Applying optimized parallel contextual reranking to custom document chunks...'));
374
+ verboseLog({}, chalk.cyan('Applying optimized parallel contextual reranking to custom document chunks...'));
375
375
 
376
376
  const WEIGHT_INITIAL_SIM = 0.4;
377
377
  const WEIGHT_DOCUMENT_TITLE_MATCH = 0.2;
@@ -466,7 +466,7 @@ export class CustomDocumentProcessor {
466
466
  // Wait for all reranking calculations to complete in parallel
467
467
  await Promise.all(rerankingPromises);
468
468
 
469
- console.log(chalk.cyan(`Parallel reranking completed for ${filteredResults.length} chunks`));
469
+ verboseLog({}, chalk.cyan(`Parallel reranking completed for ${filteredResults.length} chunks`));
470
470
 
471
471
  // Log debug info for first few results
472
472
  for (let i = 0; i < Math.min(3, filteredResults.length); i++) {
@@ -520,8 +520,8 @@ export class CustomDocumentProcessor {
520
520
  try {
521
521
  const resolvedProjectPath = path.resolve(projectPath);
522
522
  this.customDocumentChunks.delete(resolvedProjectPath);
523
- await this.cacheManager.clearCustomDocuments(resolvedProjectPath);
524
- console.log(chalk.green(`Cleared custom document chunks for project: ${resolvedProjectPath}`));
523
+ this.cacheManager.customDocumentChunks.delete(resolvedProjectPath);
524
+ verboseLog({}, chalk.green(`Cleared custom document chunks for project: ${resolvedProjectPath}`));
525
525
  } catch (error) {
526
526
  console.error(chalk.red(`Error clearing project chunks: ${error.message}`));
527
527
  }
@@ -561,7 +561,7 @@ export class CustomDocumentProcessor {
561
561
  clearCaches() {
562
562
  this.h1EmbeddingCache.clear();
563
563
  this.customDocumentChunks.clear();
564
- console.log(chalk.green('CustomDocumentProcessor caches cleared'));
564
+ verboseLog({}, chalk.green('CustomDocumentProcessor caches cleared'));
565
565
  }
566
566
 
567
567
  /**
@@ -589,7 +589,7 @@ export class CustomDocumentProcessor {
589
589
  processingTime: 0,
590
590
  };
591
591
 
592
- console.log(chalk.green('CustomDocumentProcessor cleanup complete'));
592
+ verboseLog({}, chalk.green('CustomDocumentProcessor cleanup complete'));
593
593
  } finally {
594
594
  this.cleaningUp = false;
595
595
  }
@@ -11,8 +11,7 @@ vi.mock('./embeddings/model-manager.js', () => ({
11
11
  vi.mock('./embeddings/cache-manager.js', () => ({
12
12
  CacheManager: class {
13
13
  storeCustomDocuments = vi.fn().mockResolvedValue(undefined);
14
- getCustomDocuments = vi.fn().mockResolvedValue([]);
15
- clearCustomDocuments = vi.fn().mockResolvedValue(undefined);
14
+ getCustomDocumentChunks = vi.fn().mockReturnValue([]);
16
15
  },
17
16
  }));
18
17
 
@@ -32,8 +31,7 @@ describe('CustomDocumentProcessor', () => {
32
31
 
33
32
  mockCacheManager = {
34
33
  storeCustomDocuments: vi.fn().mockResolvedValue(undefined),
35
- getCustomDocuments: vi.fn().mockResolvedValue([]),
36
- clearCustomDocuments: vi.fn().mockResolvedValue(undefined),
34
+ getCustomDocumentChunks: vi.fn().mockReturnValue([]),
37
35
  };
38
36
 
39
37
  processor = new CustomDocumentProcessor({
@@ -319,7 +317,7 @@ describe('CustomDocumentProcessor', () => {
319
317
 
320
318
  it('should return chunks from cache if not in memory', async () => {
321
319
  const cachedChunks = [{ id: 'cached', content: 'from cache' }];
322
- mockCacheManager.getCustomDocuments.mockResolvedValue(cachedChunks);
320
+ mockCacheManager.getCustomDocumentChunks.mockReturnValue(cachedChunks);
323
321
 
324
322
  const result = await processor.getExistingChunks('/project');
325
323
 
@@ -328,7 +326,7 @@ describe('CustomDocumentProcessor', () => {
328
326
 
329
327
  it('should restore cached chunks to memory', async () => {
330
328
  const cachedChunks = [{ id: 'cached', content: 'from cache' }];
331
- mockCacheManager.getCustomDocuments.mockResolvedValue(cachedChunks);
329
+ mockCacheManager.getCustomDocumentChunks.mockReturnValue(cachedChunks);
332
330
 
333
331
  await processor.getExistingChunks('/project');
334
332
 
@@ -336,7 +334,7 @@ describe('CustomDocumentProcessor', () => {
336
334
  });
337
335
 
338
336
  it('should return empty array when no chunks exist', async () => {
339
- mockCacheManager.getCustomDocuments.mockResolvedValue([]);
337
+ mockCacheManager.getCustomDocumentChunks.mockReturnValue([]);
340
338
 
341
339
  const result = await processor.getExistingChunks('/project');
342
340
 
@@ -353,10 +351,14 @@ describe('CustomDocumentProcessor', () => {
353
351
  expect(processor.customDocumentChunks.has('/project')).toBe(false);
354
352
  });
355
353
 
356
- it('should clear chunks from cache', async () => {
354
+ it('should clear chunks for only the selected project', async () => {
355
+ processor.customDocumentChunks.set('/project', [{ id: 'chunk-a' }]);
356
+ processor.customDocumentChunks.set('/other', [{ id: 'chunk-b' }]);
357
+
357
358
  await processor.clearProjectChunks('/project');
358
359
 
359
- expect(mockCacheManager.clearCustomDocuments).toHaveBeenCalled();
360
+ expect(processor.customDocumentChunks.has('/project')).toBe(false);
361
+ expect(processor.customDocumentChunks.has('/other')).toBe(true);
360
362
  });
361
363
  });
362
364
 
@@ -15,39 +15,41 @@ import path from 'path';
15
15
  import chalk from 'chalk';
16
16
  import { getDefaultEmbeddingsSystem } from './embeddings/factory.js';
17
17
  import { calculateCosineSimilarity } from './embeddings/similarity-calculator.js';
18
+ import { verboseLog } from './utils/logging.js';
18
19
 
19
20
  /**
20
21
  * Load feedback data from artifacts directory
21
22
  *
22
23
  * @param {string} feedbackPath - Path to feedback artifacts directory
23
24
  * @param {Object} options - Loading options
25
+ * @param {boolean} [options.verbose=false] - Enable verbose progress logging
24
26
  * @returns {Promise<Object>} Loaded feedback data
25
27
  */
26
28
  export async function loadFeedbackData(feedbackPath, options = {}) {
27
29
  const { verbose = false } = options;
28
30
 
29
31
  if (!feedbackPath) {
30
- if (verbose) console.log(chalk.gray('No feedback path provided'));
32
+ verboseLog(verbose, chalk.gray('No feedback path provided'));
31
33
  return {};
32
34
  }
33
35
 
34
36
  try {
35
37
  if (!fs.existsSync(feedbackPath)) {
36
- if (verbose) console.log(chalk.gray(`Feedback directory not found: ${feedbackPath}`));
38
+ verboseLog(verbose, chalk.gray(`Feedback directory not found: ${feedbackPath}`));
37
39
  return {};
38
40
  }
39
41
 
40
- if (verbose) console.log(chalk.cyan(`📁 Loading feedback from: ${feedbackPath}`));
42
+ verboseLog(verbose, chalk.cyan(`📁 Loading feedback from: ${feedbackPath}`));
41
43
 
42
44
  // Look for feedback files in the directory
43
45
  const feedbackFiles = fs.readdirSync(feedbackPath).filter((file) => file.startsWith('feedback-') && file.endsWith('.json'));
44
46
 
45
47
  if (feedbackFiles.length === 0) {
46
- if (verbose) console.log(chalk.gray('No feedback files found'));
48
+ verboseLog(verbose, chalk.gray('No feedback files found'));
47
49
  return {};
48
50
  }
49
51
 
50
- if (verbose) console.log(chalk.cyan(`📥 Found ${feedbackFiles.length} feedback file(s)`));
52
+ verboseLog(verbose, chalk.cyan(`📥 Found ${feedbackFiles.length} feedback file(s)`));
51
53
 
52
54
  // Load and merge all feedback files
53
55
  const allFeedback = {};
@@ -64,25 +66,21 @@ export async function loadFeedbackData(feedbackPath, options = {}) {
64
66
  Object.assign(allFeedback, feedbackData.feedback);
65
67
  const itemCount = Object.keys(feedbackData.feedback).length;
66
68
  totalItems += itemCount;
67
- if (verbose) {
68
- console.log(chalk.cyan(`📋 Loaded feedback from ${file}: ${itemCount} items`));
69
- }
69
+ verboseLog(verbose, chalk.cyan(`📋 Loaded feedback from ${file}: ${itemCount} items`));
70
70
  }
71
71
  } catch (parseError) {
72
- console.log(chalk.yellow(`⚠️ Error parsing feedback file ${file}: ${parseError.message}`));
72
+ console.warn(chalk.yellow(`⚠️ Error parsing feedback file ${file}: ${parseError.message}`));
73
73
  }
74
74
  }
75
75
 
76
76
  if (totalItems > 0) {
77
- if (verbose) {
78
- console.log(chalk.green(`✅ Successfully loaded ${totalItems} feedback items total`));
79
- }
77
+ verboseLog(verbose, chalk.green(`✅ Successfully loaded ${totalItems} feedback items total`));
80
78
  return allFeedback;
81
79
  }
82
80
 
83
81
  return {};
84
82
  } catch (error) {
85
- console.log(chalk.red(`❌ Error loading feedback data: ${error.message}`));
83
+ console.error(chalk.red(`❌ Error loading feedback data: ${error.message}`));
86
84
  return {};
87
85
  }
88
86
  }
@@ -113,9 +111,9 @@ export async function initializeSemanticSimilarity() {
113
111
  await embeddingsSystem.initialize();
114
112
  semanticSimilarityInitialized = true;
115
113
  semanticSimilarityAvailable = true;
116
- console.log(chalk.green('[FeedbackLoader] Semantic similarity initialized using embeddings system'));
114
+ verboseLog({}, chalk.green('[FeedbackLoader] Semantic similarity initialized using embeddings system'));
117
115
  } catch (error) {
118
- console.log(chalk.yellow(`[FeedbackLoader] Semantic similarity initialization failed: ${error.message}`));
116
+ console.warn(chalk.yellow(`[FeedbackLoader] Semantic similarity initialization failed: ${error.message}`));
119
117
  semanticSimilarityAvailable = false;
120
118
  }
121
119
  }
@@ -157,7 +155,7 @@ async function calculateSemanticSimilarity(text1, text2) {
157
155
  // Cosine similarity ranges from -1 to 1, normalize to 0-1
158
156
  return (similarity + 1) / 2;
159
157
  } catch (error) {
160
- console.log(chalk.yellow(`[FeedbackLoader] Semantic similarity calculation failed: ${error.message}`));
158
+ console.warn(chalk.yellow(`[FeedbackLoader] Semantic similarity calculation failed: ${error.message}`));
161
159
  return null;
162
160
  }
163
161
  }
@@ -173,9 +171,9 @@ async function calculateSemanticSimilarity(text1, text2) {
173
171
  * @param {string} issueDescription - Description of the current issue
174
172
  * @param {Object} feedbackData - Loaded feedback data
175
173
  * @param {Object} options - Filtering options
176
- * @param {number} options.similarityThreshold - Threshold for considering issues similar (default: 0.7)
177
- * @param {boolean} options.verbose - Enable verbose logging
178
- * @param {boolean} options.useSemanticSimilarity - Use semantic similarity when available (default: true)
174
+ * @param {number} [options.similarityThreshold=0.7] - Threshold for considering issues similar
175
+ * @param {boolean} [options.verbose=false] - Enable verbose progress logging
176
+ * @param {boolean} [options.useSemanticSimilarity=true] - Use semantic similarity when available
179
177
  * @returns {Promise<boolean>} True if issue should be skipped
180
178
  */
181
179
  export async function shouldSkipSimilarIssue(issueDescription, feedbackData, options = {}) {
@@ -205,9 +203,7 @@ export async function shouldSkipSimilarIssue(issueDescription, feedbackData, opt
205
203
  // Determine if we should use semantic similarity
206
204
  const canUseSemanticSimilarity = useSemanticSimilarity && isSemanticSimilarityAvailable();
207
205
 
208
- if (verbose && canUseSemanticSimilarity) {
209
- console.log(chalk.cyan('🔍 Using semantic similarity for issue comparison'));
210
- }
206
+ verboseLog(verbose && canUseSemanticSimilarity, chalk.cyan('🔍 Using semantic similarity for issue comparison'));
211
207
 
212
208
  // Check similarity with dismissed issues
213
209
  for (const dismissed of dismissedIssues) {
@@ -233,11 +229,12 @@ export async function shouldSkipSimilarIssue(issueDescription, feedbackData, opt
233
229
  }
234
230
 
235
231
  if (similarity > similarityThreshold) {
236
- if (verbose) {
237
- console.log(chalk.yellow(`⏭️ Skipping similar dismissed issue (${(similarity * 100).toFixed(1)}% ${similarityMethod} similarity)`));
238
- console.log(chalk.gray(` Current: ${issueDescription.substring(0, 80)}...`));
239
- console.log(chalk.gray(` Previous: ${dismissed.originalIssue.substring(0, 80)}...`));
240
- }
232
+ verboseLog(
233
+ verbose,
234
+ chalk.yellow(`⏭️ Skipping similar dismissed issue (${(similarity * 100).toFixed(1)}% ${similarityMethod} similarity)`)
235
+ );
236
+ verboseLog(verbose, chalk.gray(` Current: ${issueDescription.substring(0, 80)}...`));
237
+ verboseLog(verbose, chalk.gray(` Previous: ${dismissed.originalIssue.substring(0, 80)}...`));
241
238
  return true;
242
239
  }
243
240
  }
@@ -252,7 +249,7 @@ export async function shouldSkipSimilarIssue(issueDescription, feedbackData, opt
252
249
  * @param {string} text1 - First text
253
250
  * @param {string} text2 - Second text
254
251
  * @param {Object} options - Options
255
- * @param {boolean} options.useSemanticSimilarity - Use semantic similarity when available (default: true)
252
+ * @param {boolean} [options.useSemanticSimilarity=true] - Use semantic similarity when available
256
253
  * @returns {Promise<{similarity: number, method: string}>} Similarity result with method used
257
254
  */
258
255
  export async function calculateIssueSimilarity(text1, text2, options = {}) {
@@ -327,6 +324,8 @@ export function calculateWordSimilarity(text1, text2) {
327
324
  *
328
325
  * @param {Object} feedbackData - Loaded feedback data
329
326
  * @param {Object} options - Extraction options
327
+ * @param {number} [options.maxPatterns=10] - Maximum number of dismissed patterns to include
328
+ * @param {boolean} [options.verbose=false] - Enable verbose progress logging
330
329
  * @returns {Array} Array of dismissed issue patterns
331
330
  */
332
331
  export function extractDismissedPatterns(feedbackData, options = {}) {
@@ -355,9 +354,10 @@ export function extractDismissedPatterns(feedbackData, options = {}) {
355
354
  }))
356
355
  .slice(0, maxPatterns);
357
356
 
358
- if (verbose && dismissedIssues.length > 0) {
359
- console.log(chalk.cyan(`📋 Extracted ${dismissedIssues.length} dismissed issue patterns for LLM context`));
360
- }
357
+ verboseLog(
358
+ verbose && dismissedIssues.length > 0,
359
+ chalk.cyan(`📋 Extracted ${dismissedIssues.length} dismissed issue patterns for LLM context`)
360
+ );
361
361
 
362
362
  return dismissedIssues;
363
363
  }