codecritique 1.2.4 → 2.0.1

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.
@@ -55,7 +55,8 @@ async function ensureSemanticSimilarityInitialized() {
55
55
  // Initialize semantic similarity using the shared embeddings system
56
56
  await initializeSemanticSimilarity();
57
57
  semanticSimilarityInitialized = true;
58
- } catch (error) {
58
+ }
59
+ catch (error) {
59
60
  console.warn(chalk.yellow(`⚠️ Could not initialize semantic similarity: ${error.message}`));
60
61
  // Continue without semantic similarity - word-based fallback will be used
61
62
  }
@@ -228,7 +229,8 @@ async function getProjectSummary(projectPath, options = {}) {
228
229
  }
229
230
 
230
231
  return summary;
231
- } catch (error) {
232
+ }
233
+ catch (error) {
232
234
  console.error(chalk.red(`Error retrieving project summary: ${error.message}`));
233
235
  return null;
234
236
  }
@@ -240,7 +242,9 @@ async function getProjectSummary(projectPath, options = {}) {
240
242
  * @returns {string} Formatted context string
241
243
  */
242
244
  function formatProjectSummaryForLLM(summary) {
243
- if (!summary) return '';
245
+ if (!summary) {
246
+ return '';
247
+ }
244
248
 
245
249
  let context = `\n## PROJECT ARCHITECTURE CONTEXT\n\n`;
246
250
 
@@ -456,7 +460,8 @@ async function runAnalysis(filePath, options = {}) {
456
460
  // For PR reviews, always read the full file content for context awareness
457
461
  fullFileContent = fs.existsSync(filePath) ? fs.readFileSync(filePath, 'utf8') : null;
458
462
  verboseLog(options, chalk.blue(`Analyzing diff only for ${path.basename(filePath)}`));
459
- } else {
463
+ }
464
+ else {
460
465
  content = fs.readFileSync(filePath, 'utf8');
461
466
  fullFileContent = content;
462
467
  verboseLog(options, chalk.blue(`Analyzing full file ${path.basename(filePath)}`));
@@ -503,7 +508,8 @@ async function runAnalysis(filePath, options = {}) {
503
508
  verboseLog(options, chalk.magenta(` [${i + 1}] Path: ${g.path} ${g.headingText ? `(Heading: "${g.headingText}")` : ''}`));
504
509
  verboseLog(options, chalk.gray(` Content: ${g.content.substring(0, 100).replace(/\\n/g, ' ')}...`));
505
510
  });
506
- } else {
511
+ }
512
+ else {
507
513
  verboseLog(options, chalk.magenta(' (None)'));
508
514
  }
509
515
 
@@ -513,7 +519,8 @@ async function runAnalysis(filePath, options = {}) {
513
519
  verboseLog(options, chalk.magenta(` [${i + 1}] Path: ${ex.path} (Similarity: ${ex.similarity?.toFixed(3) || 'N/A'})`));
514
520
  verboseLog(options, chalk.gray(` Content: ${ex.content.substring(0, 100).replace(/\\n/g, ' ')}...`));
515
521
  });
516
- } else {
522
+ }
523
+ else {
517
524
  verboseLog(options, chalk.magenta(' (None)'));
518
525
  }
519
526
 
@@ -524,7 +531,8 @@ async function runAnalysis(filePath, options = {}) {
524
531
  verboseLog(options, chalk.magenta(` Similarity: ${chunk.similarity?.toFixed(3) || 'N/A'}`));
525
532
  verboseLog(options, chalk.gray(` Content: ${chunk.content.substring(0, 100).replace(/\\n/g, ' ')}...`));
526
533
  });
527
- } else {
534
+ }
535
+ else {
528
536
  verboseLog(options, chalk.magenta(' (None)'));
529
537
  }
530
538
  verboseLog(options, chalk.magenta('---------------------------------'));
@@ -593,7 +601,8 @@ async function runAnalysis(filePath, options = {}) {
593
601
  ...(filteredResults.metadata || {}),
594
602
  },
595
603
  };
596
- } catch (error) {
604
+ }
605
+ catch (error) {
597
606
  console.error(chalk.red(`Error analyzing file: ${error.message}`));
598
607
  return {
599
608
  success: false,
@@ -745,7 +754,8 @@ async function callLLMForAnalysis(context, options = {}) {
745
754
 
746
755
  if (options.isHolisticPRReview) {
747
756
  prompt = generateHolisticPRAnalysisPrompt(context);
748
- } else {
757
+ }
758
+ else {
749
759
  prompt = options.isTestFile ? generateTestFileAnalysisPrompt(context) : generateAnalysisPrompt(context);
750
760
  }
751
761
 
@@ -781,7 +791,8 @@ async function callLLMForAnalysis(context, options = {}) {
781
791
 
782
792
  verboseLog(options, chalk.green('Successfully parsed LLM response with expected structure'));
783
793
  return analysisResponse;
784
- } catch (error) {
794
+ }
795
+ catch (error) {
785
796
  console.error(chalk.red(`Error calling LLM for analysis: ${error.message}`));
786
797
  console.error(error.stack);
787
798
  throw error;
@@ -945,7 +956,8 @@ async function sendPromptToLLM(promptConfig, llmOptions) {
945
956
  });
946
957
 
947
958
  return response;
948
- } catch (error) {
959
+ }
960
+ catch (error) {
949
961
  console.error(chalk.red(`Error in LLM call: ${error.message}`));
950
962
  throw error;
951
963
  }
@@ -1013,10 +1025,12 @@ Similar code patterns and issues identified by human reviewers in past PRs
1013
1025
  prHistorySection += `Only report issues that EXACTLY match historical patterns with SPECIFIC code fixes.\n\n`;
1014
1026
 
1015
1027
  verboseLog(context.options, chalk.blue(`PR History section preview: ${prHistorySection.substring(0, 200)}...`));
1016
- } else {
1028
+ }
1029
+ else {
1017
1030
  verboseLog(context.options, chalk.yellow(`❌ No PR comments section found in context`));
1018
1031
  }
1019
- } else {
1032
+ }
1033
+ else {
1020
1034
  verboseLog(context.options, chalk.yellow(`❌ No context sections available for PR comments`));
1021
1035
  }
1022
1036
 
@@ -1476,7 +1490,8 @@ async function getPRCommentContext(filePath, options = {}) {
1476
1490
  fileContent = fs.readFileSync(filePath, 'utf8');
1477
1491
  const maxEmbeddingLength = 8000; // Keep consistent with original truncation
1478
1492
  contentForSearch = fileContent.length > maxEmbeddingLength ? fileContent.substring(0, maxEmbeddingLength) : fileContent;
1479
- } catch (readError) {
1493
+ }
1494
+ catch (readError) {
1480
1495
  debug(`[getPRCommentContext] Could not read file ${filePath}: ${readError.message}`);
1481
1496
  return {
1482
1497
  success: false,
@@ -1486,11 +1501,13 @@ async function getPRCommentContext(filePath, options = {}) {
1486
1501
  summary: 'Failed to read file for context analysis',
1487
1502
  };
1488
1503
  }
1489
- } else {
1504
+ }
1505
+ else {
1490
1506
  // Fallback to original behavior if no pre-computed embedding provided
1491
1507
  try {
1492
1508
  fileContent = fs.readFileSync(filePath, 'utf8');
1493
- } catch (readError) {
1509
+ }
1510
+ catch (readError) {
1494
1511
  debug(`[getPRCommentContext] Could not read file ${filePath}: ${readError.message}`);
1495
1512
  return {
1496
1513
  success: false,
@@ -1537,7 +1554,8 @@ async function getPRCommentContext(filePath, options = {}) {
1537
1554
  );
1538
1555
  });
1539
1556
  }
1540
- } catch (dbError) {
1557
+ }
1558
+ catch (dbError) {
1541
1559
  console.warn(chalk.yellow(`⚠️ Hybrid search failed: ${dbError.message}`));
1542
1560
  debug(`[getPRCommentContext] Hybrid search failed: ${dbError.message}`);
1543
1561
  // No fallback needed - if hybrid search fails, we just return empty results
@@ -1568,7 +1586,8 @@ async function getPRCommentContext(filePath, options = {}) {
1568
1586
  relevantComments.length > 0 && relevantComments[0].similarity_score !== 0.5 ? 'semantic_embedding' : 'file_path_fallback',
1569
1587
  },
1570
1588
  };
1571
- } catch (error) {
1589
+ }
1590
+ catch (error) {
1572
1591
  debug(`[getPRCommentContext] Error getting PR comment context: ${error.message}`);
1573
1592
  return {
1574
1593
  success: false,
@@ -1737,7 +1756,8 @@ async function performHolisticPRAnalysis(options) {
1737
1756
  );
1738
1757
  verboseLog(options, chalk.gray(` Content: ${g.content.substring(0, 100).replace(/\n/g, ' ')}...`));
1739
1758
  });
1740
- } else {
1759
+ }
1760
+ else {
1741
1761
  verboseLog(options, chalk.magenta(' (None)'));
1742
1762
  }
1743
1763
 
@@ -1747,7 +1767,8 @@ async function performHolisticPRAnalysis(options) {
1747
1767
  verboseLog(options, chalk.magenta(` [${i + 1}] Path: ${ex.path} (Similarity: ${ex.similarity?.toFixed(3) || 'N/A'})`));
1748
1768
  verboseLog(options, chalk.gray(` Content: ${ex.content.substring(0, 100).replace(/\\n/g, ' ')}...`));
1749
1769
  });
1750
- } else {
1770
+ }
1771
+ else {
1751
1772
  verboseLog(options, chalk.magenta(' (None)'));
1752
1773
  }
1753
1774
 
@@ -1763,7 +1784,8 @@ async function performHolisticPRAnalysis(options) {
1763
1784
  verboseLog(options, chalk.gray(` File: ${comment.filePath}`));
1764
1785
  verboseLog(options, chalk.gray(` Comment: ${comment.body.substring(0, 100).replace(/\n/g, ' ')}...`));
1765
1786
  });
1766
- } else {
1787
+ }
1788
+ else {
1767
1789
  verboseLog(options, chalk.magenta(' (None)'));
1768
1790
  }
1769
1791
 
@@ -1774,7 +1796,8 @@ async function performHolisticPRAnalysis(options) {
1774
1796
  verboseLog(options, chalk.gray(` Similarity: ${chunk.similarity?.toFixed(3) || 'N/A'}`));
1775
1797
  verboseLog(options, chalk.gray(` Content: ${chunk.content.substring(0, 100).replace(/\n/g, ' ')}...`));
1776
1798
  });
1777
- } else {
1799
+ }
1800
+ else {
1778
1801
  verboseLog(options, chalk.magenta(' (None)'));
1779
1802
  }
1780
1803
  verboseLog(options, chalk.magenta('--- Sending Holistic PR Analysis Prompt to LLM ---'));
@@ -1821,7 +1844,8 @@ async function performHolisticPRAnalysis(options) {
1821
1844
  },
1822
1845
  },
1823
1846
  };
1824
- } catch (error) {
1847
+ }
1848
+ catch (error) {
1825
1849
  console.error(chalk.red(`Error in holistic PR analysis: ${error.message}`));
1826
1850
  return {
1827
1851
  success: false,
@@ -1852,7 +1876,8 @@ async function getContextForFile(filePath, content, options = {}) {
1852
1876
  // Note: This may be called concurrently. `initializeTables` should be idempotent.
1853
1877
  try {
1854
1878
  await embeddingsSystem.initialize();
1855
- } catch (initError) {
1879
+ }
1880
+ catch (initError) {
1856
1881
  console.warn(chalk.yellow(`Database initialization warning: ${initError.message}`));
1857
1882
  }
1858
1883
 
@@ -1932,7 +1957,8 @@ async function getContextForFile(filePath, content, options = {}) {
1932
1957
  verboseLog(options, chalk.cyan('📄 Custom documents not yet processed for this project, processing now...'));
1933
1958
  // Process custom documents into chunks (only if not already processed)
1934
1959
  processedChunks = await embeddingsSystem.processCustomDocumentsInMemory(options.customDocs, projectPath);
1935
- } else {
1960
+ }
1961
+ else {
1936
1962
  verboseLog(options, chalk.green(`📄 Reusing ${processedChunks.length} already processed custom document chunks`));
1937
1963
  }
1938
1964
 
@@ -1961,7 +1987,8 @@ async function getContextForFile(filePath, content, options = {}) {
1961
1987
 
1962
1988
  return relevantChunks;
1963
1989
  }
1964
- } catch (error) {
1990
+ }
1991
+ catch (error) {
1965
1992
  console.error(chalk.red(`Error processing custom documents: ${error.message}`));
1966
1993
  }
1967
1994
 
@@ -1973,7 +2000,8 @@ async function getContextForFile(filePath, content, options = {}) {
1973
2000
  try {
1974
2001
  // Use the statically imported function
1975
2002
  return await embeddingsSystem.getExistingCustomDocumentChunks(projectPath);
1976
- } catch {
2003
+ }
2004
+ catch {
1977
2005
  verboseLog(options, chalk.gray('No existing custom document chunks found, will process from scratch'));
1978
2006
  return [];
1979
2007
  }
@@ -1982,7 +2010,8 @@ async function getContextForFile(filePath, content, options = {}) {
1982
2010
  const withContextFallback = async (promise, fallbackValue, label) => {
1983
2011
  try {
1984
2012
  return await promise;
1985
- } catch (error) {
2013
+ }
2014
+ catch (error) {
1986
2015
  console.warn(chalk.yellow(`${label} failed: ${error.message}`));
1987
2016
  return fallbackValue;
1988
2017
  }
@@ -2055,11 +2084,14 @@ async function getContextForFile(filePath, content, options = {}) {
2055
2084
  if (isGenericDocument(docPath, docH1)) {
2056
2085
  candidateDocFullContext = getGenericDocumentContext(docPath, docH1);
2057
2086
  debug(`[FAST-PATH] Using pre-computed context for generic document in RAG: ${docPath}`);
2058
- } else {
2087
+ }
2088
+ else {
2059
2089
  candidateDocFullContext = await inferContextFromDocumentContent(docPath, docH1, docChunks, language);
2060
2090
  }
2061
2091
  const relevantChunksForDoc = docChunks.filter((c) => c.similarity >= RELEVANT_CHUNK_THRESHOLD);
2062
- if (relevantChunksForDoc.length === 0) continue;
2092
+ if (relevantChunksForDoc.length === 0) {
2093
+ continue;
2094
+ }
2063
2095
 
2064
2096
  const maxChunkScoreInDoc = Math.max(...relevantChunksForDoc.map((c) => c.similarity));
2065
2097
  const avgChunkScoreInDoc = relevantChunksForDoc.reduce((sum, c) => sum + c.similarity, 0) / relevantChunksForDoc.length;
@@ -2080,7 +2112,8 @@ async function getContextForFile(filePath, content, options = {}) {
2080
2112
  break;
2081
2113
  }
2082
2114
  }
2083
- } else if (reviewedSnippetContext.area !== 'GeneralJS_TS') {
2115
+ }
2116
+ else if (reviewedSnippetContext.area !== 'GeneralJS_TS') {
2084
2117
  docLevelContextMatchScore -= 0.2;
2085
2118
  }
2086
2119
  }
@@ -2205,13 +2238,15 @@ async function gatherUnifiedContextForPR(prFiles, options = {}) {
2205
2238
  if (!processedChunks || processedChunks.length === 0) {
2206
2239
  verboseLog(options, chalk.cyan('📄 Custom documents not yet processed for this project, processing now...'));
2207
2240
  processedChunks = await embeddingsSystem.processCustomDocumentsInMemory(options.customDocs, projectPath);
2208
- } else {
2241
+ }
2242
+ else {
2209
2243
  verboseLog(options, chalk.green(`📄 Reusing ${processedChunks.length} already processed custom document chunks`));
2210
2244
  }
2211
2245
 
2212
2246
  globalCustomDocChunks = processedChunks;
2213
2247
  verboseLog(options, chalk.green(`📄 Custom documents processed: ${globalCustomDocChunks.length} chunks available for PR analysis`));
2214
- } catch (error) {
2248
+ }
2249
+ catch (error) {
2215
2250
  console.error(chalk.red(`Error processing custom documents for PR: ${error.message}`));
2216
2251
  }
2217
2252
  }
@@ -2231,7 +2266,8 @@ async function gatherUnifiedContextForPR(prFiles, options = {}) {
2231
2266
  ...context,
2232
2267
  filePath,
2233
2268
  };
2234
- } catch (error) {
2269
+ }
2270
+ catch (error) {
2235
2271
  console.error(chalk.red(`Error gathering context for file ${file.filePath}: ${error.message}`));
2236
2272
  return null; // Return null on error for this file
2237
2273
  }
@@ -942,7 +942,9 @@ describe('rag-analyzer', () => {
942
942
  describe('gatherUnifiedContextForPR error handling', () => {
943
943
  it('should handle file context gathering errors', async () => {
944
944
  fs.readFileSync.mockImplementation((path) => {
945
- if (path.includes('error-file')) throw new Error('Read error');
945
+ if (path.includes('error-file')) {
946
+ throw new Error('Read error');
947
+ }
946
948
  return 'const x = 1;';
947
949
  });
948
950
  const prFiles = [
package/src/rag-review.js CHANGED
@@ -73,7 +73,8 @@ async function reviewFile(filePath, options = {}) {
73
73
 
74
74
  // If analysis failed, return the error
75
75
  return analyzeResult;
76
- } catch (error) {
76
+ }
77
+ catch (error) {
77
78
  console.error(chalk.red(`Error reviewing file ${filePath}:`), error.message);
78
79
  return {
79
80
  success: false,
@@ -143,7 +144,8 @@ async function reviewFiles(filePaths, options = {}) {
143
144
  results: validResults, // Return array of individual file results
144
145
  message: finalMessage,
145
146
  };
146
- } catch (error) {
147
+ }
148
+ catch (error) {
147
149
  console.error(chalk.red(`Error reviewing multiple files: ${error.message}`));
148
150
  console.error(error.stack);
149
151
  return {
@@ -186,7 +188,8 @@ async function reviewPullRequest(changedFilePaths, options = {}) {
186
188
 
187
189
  // Use enhanced PR review with cross-file context
188
190
  return await reviewPullRequestWithCrossFileContext(filesToReview, options);
189
- } catch (error) {
191
+ }
192
+ catch (error) {
190
193
  console.error(chalk.red(`Error reviewing pull request files: ${error.message}`));
191
194
  console.error(error.stack);
192
195
  return {
@@ -263,7 +266,8 @@ async function reviewPullRequestWithCrossFileContext(filesToReview, options = {}
263
266
  baseBranch,
264
267
  targetBranch,
265
268
  });
266
- } catch (error) {
269
+ }
270
+ catch (error) {
267
271
  console.warn(chalk.yellow(`Error processing file ${filePath}: ${error.message}`));
268
272
  }
269
273
  }
@@ -429,7 +433,8 @@ async function reviewPullRequestWithCrossFileContext(filesToReview, options = {}
429
433
  },
430
434
  },
431
435
  };
432
- } catch (error) {
436
+ }
437
+ catch (error) {
433
438
  console.error(chalk.red(`Error in holistic PR review: ${error.message}`));
434
439
 
435
440
  // Fallback to individual file review if holistic review fails
@@ -469,7 +474,8 @@ async function reviewPullRequestWithCrossFileContext(filesToReview, options = {}
469
474
 
470
475
  const result = await runAnalysis(file.filePath, enhancedOptions);
471
476
  return result;
472
- } catch (error) {
477
+ }
478
+ catch (error) {
473
479
  console.error(chalk.red(`Error reviewing ${file.filePath}: ${error.message}`));
474
480
  return {
475
481
  filePath: file.filePath,
@@ -496,7 +502,8 @@ async function reviewPullRequestWithCrossFileContext(filesToReview, options = {}
496
502
  },
497
503
  };
498
504
  }
499
- } catch (error) {
505
+ }
506
+ catch (error) {
500
507
  console.error(chalk.red(`Error in enhanced PR review: ${error.message}`));
501
508
  return {
502
509
  success: false,
@@ -105,7 +105,9 @@ class OpenZeroShotClassifier {
105
105
  */
106
106
  async initialize() {
107
107
  // If already initialized, return immediately
108
- if (this.isInitialized) return;
108
+ if (this.isInitialized) {
109
+ return;
110
+ }
109
111
 
110
112
  // If currently initializing, wait for the existing initialization
111
113
  if (this.initializationPromise) {
@@ -117,7 +119,8 @@ class OpenZeroShotClassifier {
117
119
 
118
120
  try {
119
121
  await this.initializationPromise;
120
- } finally {
122
+ }
123
+ finally {
121
124
  // Clean up the promise after initialization (success or failure)
122
125
  this.initializationPromise = null;
123
126
  }
@@ -133,7 +136,8 @@ class OpenZeroShotClassifier {
133
136
 
134
137
  this.isInitialized = true;
135
138
  verboseLog({}, '✓ Open-ended zero-shot classifier initialized successfully');
136
- } catch (error) {
139
+ }
140
+ catch (error) {
137
141
  console.error('Error initializing classifier:', error);
138
142
  this.isInitialized = false;
139
143
  throw error;
@@ -174,7 +178,8 @@ class OpenZeroShotClassifier {
174
178
  for (const value of Object.values(obj)) {
175
179
  if (Array.isArray(value)) {
176
180
  value.forEach((tech) => techs.add(tech.toLowerCase()));
177
- } else if (typeof value === 'object') {
181
+ }
182
+ else if (typeof value === 'object') {
178
183
  addTechsFromObject(value);
179
184
  }
180
185
  }
@@ -234,7 +239,9 @@ class OpenZeroShotClassifier {
234
239
  const word = words[i].replace(/[.,;:!?'"()[\]{}]/g, '');
235
240
 
236
241
  // Skip if it's a common word
237
- if (this.commonWords.has(word.toLowerCase())) continue;
242
+ if (this.commonWords.has(word.toLowerCase())) {
243
+ continue;
244
+ }
238
245
 
239
246
  // Check if word is capitalized and not at sentence start
240
247
  if (i > 0 && /^[A-Z][a-zA-Z]+/.test(word) && word.length > 2 && word.length < 20) {
@@ -302,7 +309,8 @@ class OpenZeroShotClassifier {
302
309
 
303
310
  this.cache.set(cacheKey, classifications);
304
311
  return classifications;
305
- } catch (error) {
312
+ }
313
+ catch (error) {
306
314
  console.error('Error in technology classification:', error);
307
315
  return [];
308
316
  }
@@ -368,7 +376,8 @@ class OpenZeroShotClassifier {
368
376
 
369
377
  this.cache.set(cacheKey, classifications);
370
378
  return classifications;
371
- } catch (error) {
379
+ }
380
+ catch (error) {
372
381
  console.error('Error in domain classification:', error);
373
382
  return [];
374
383
  }