codecritique 1.2.3 → 2.0.0

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.
@@ -70,7 +70,8 @@ export class CustomDocumentProcessor {
70
70
  const headerMatch = content.match(/^#\s+(.+)$/m);
71
71
  if (headerMatch) {
72
72
  documentTitle = headerMatch[1].trim();
73
- } else {
73
+ }
74
+ else {
74
75
  // If no header found, try to extract filename from title like "instruction:./FILENAME.md"
75
76
  const filePathMatch = title.match(/:\.\/([^/]+)\.([a-zA-Z]+)$/);
76
77
  if (filePathMatch) {
@@ -88,7 +89,9 @@ export class CustomDocumentProcessor {
88
89
 
89
90
  for (let i = 0; i < sections.length; i++) {
90
91
  const section = sections[i].trim();
91
- if (!section) continue;
92
+ if (!section) {
93
+ continue;
94
+ }
92
95
 
93
96
  // Check if adding this section would exceed max chunk size
94
97
  if (currentChunk.length + section.length > maxChunkSize && currentChunk.length > minChunkSize) {
@@ -108,7 +111,8 @@ export class CustomDocumentProcessor {
108
111
 
109
112
  chunkIndex++;
110
113
  currentChunk = section;
111
- } else {
114
+ }
115
+ else {
112
116
  // Add section to current chunk
113
117
  currentChunk += (currentChunk ? '\n\n' : '') + section;
114
118
  }
@@ -142,7 +146,8 @@ export class CustomDocumentProcessor {
142
146
 
143
147
  verboseLog({}, chalk.gray(` Chunked document "${documentTitle}" into ${chunks.length} chunks`));
144
148
  return chunks;
145
- } catch (error) {
149
+ }
150
+ catch (error) {
146
151
  console.error(chalk.red(`Error chunking document: ${error.message}`));
147
152
  throw new EmbeddingError(`Document chunking failed: ${error.message}`);
148
153
  }
@@ -195,7 +200,8 @@ export class CustomDocumentProcessor {
195
200
  project_path: path.resolve(projectPath),
196
201
  created_at: new Date().toISOString(),
197
202
  };
198
- } else {
203
+ }
204
+ else {
199
205
  console.error(chalk.red(`Error generating embedding for chunk ${chunk.id}: batch processing failed`));
200
206
  return null;
201
207
  }
@@ -207,7 +213,8 @@ export class CustomDocumentProcessor {
207
213
 
208
214
  verboseLog({}, chalk.gray(` Generated embeddings for ${validChunks.length}/${chunks.length} chunks`));
209
215
  this.performanceMetrics.embeddingsCalculated += validChunks.length;
210
- } catch (error) {
216
+ }
217
+ catch (error) {
211
218
  console.error(chalk.red(`Error in batch embedding generation for document ${doc.title}: ${error.message}`));
212
219
  // Fallback to individual processing for this document
213
220
  verboseLog({}, chalk.yellow(` Falling back to individual processing for ${doc.title}`));
@@ -225,7 +232,8 @@ export class CustomDocumentProcessor {
225
232
  project_path: path.resolve(projectPath),
226
233
  created_at: new Date().toISOString(),
227
234
  };
228
- } catch (error) {
235
+ }
236
+ catch (error) {
229
237
  console.error(chalk.red(`Error generating embedding for chunk ${chunk.id}: ${error.message}`));
230
238
  return null;
231
239
  }
@@ -254,7 +262,8 @@ export class CustomDocumentProcessor {
254
262
 
255
263
  verboseLog({}, chalk.green(`Successfully processed ${allChunks.length} custom document chunks (${Date.now() - startTime}ms)`));
256
264
  return allChunks;
257
- } catch (error) {
265
+ }
266
+ catch (error) {
258
267
  console.error(chalk.red(`Error processing custom documents: ${error.message}`));
259
268
  throw new EmbeddingError(`Custom document processing failed: ${error.message}`);
260
269
  }
@@ -327,7 +336,8 @@ export class CustomDocumentProcessor {
327
336
  }
328
337
 
329
338
  return filteredResults;
330
- } catch (error) {
339
+ }
340
+ catch (error) {
331
341
  console.error(chalk.red(`Error searching custom document chunks: ${error.message}`));
332
342
  throw new EmbeddingError(`Custom document search failed: ${error.message}`);
333
343
  }
@@ -360,7 +370,8 @@ export class CustomDocumentProcessor {
360
370
 
361
371
  debug(`[getExistingChunks] No existing chunks found for project: ${resolvedProjectPath}`);
362
372
  return [];
363
- } catch (error) {
373
+ }
374
+ catch (error) {
364
375
  debug(`[getExistingChunks] Error checking existing chunks: ${error.message}`);
365
376
  return [];
366
377
  }
@@ -419,7 +430,8 @@ export class CustomDocumentProcessor {
419
430
  contextMatchBonus += MODERATE_BOOST_TECH_MATCH;
420
431
  }
421
432
  }
422
- } else if (queryArea !== 'GeneralJS_TS') {
433
+ }
434
+ else if (queryArea !== 'GeneralJS_TS') {
423
435
  contextMatchBonus += HEAVY_PENALTY_AREA_MISMATCH;
424
436
  }
425
437
  }
@@ -505,7 +517,8 @@ export class CustomDocumentProcessor {
505
517
  this.h1EmbeddingCache.set(docTitlesToCalculate[i], titleEmbeddings[i]);
506
518
  }
507
519
  }
508
- } catch (error) {
520
+ }
521
+ catch (error) {
509
522
  debug(`[OPTIMIZATION] Error in batch title embedding calculation: ${error.message}`);
510
523
  // Continue without title embeddings
511
524
  }
@@ -522,7 +535,8 @@ export class CustomDocumentProcessor {
522
535
  this.customDocumentChunks.delete(resolvedProjectPath);
523
536
  this.cacheManager.customDocumentChunks.delete(resolvedProjectPath);
524
537
  verboseLog({}, chalk.green(`Cleared custom document chunks for project: ${resolvedProjectPath}`));
525
- } catch (error) {
538
+ }
539
+ catch (error) {
526
540
  console.error(chalk.red(`Error clearing project chunks: ${error.message}`));
527
541
  }
528
542
  }
@@ -590,7 +604,8 @@ export class CustomDocumentProcessor {
590
604
  };
591
605
 
592
606
  verboseLog({}, chalk.green('CustomDocumentProcessor cleanup complete'));
593
- } finally {
607
+ }
608
+ finally {
594
609
  this.cleaningUp = false;
595
610
  }
596
611
  }
@@ -68,7 +68,8 @@ export async function loadFeedbackData(feedbackPath, options = {}) {
68
68
  totalItems += itemCount;
69
69
  verboseLog(verbose, chalk.cyan(`📋 Loaded feedback from ${file}: ${itemCount} items`));
70
70
  }
71
- } catch (parseError) {
71
+ }
72
+ catch (parseError) {
72
73
  console.warn(chalk.yellow(`⚠️ Error parsing feedback file ${file}: ${parseError.message}`));
73
74
  }
74
75
  }
@@ -79,7 +80,8 @@ export async function loadFeedbackData(feedbackPath, options = {}) {
79
80
  }
80
81
 
81
82
  return {};
82
- } catch (error) {
83
+ }
84
+ catch (error) {
83
85
  console.error(chalk.red(`❌ Error loading feedback data: ${error.message}`));
84
86
  return {};
85
87
  }
@@ -112,7 +114,8 @@ export async function initializeSemanticSimilarity() {
112
114
  semanticSimilarityInitialized = true;
113
115
  semanticSimilarityAvailable = true;
114
116
  verboseLog({}, chalk.green('[FeedbackLoader] Semantic similarity initialized using embeddings system'));
115
- } catch (error) {
117
+ }
118
+ catch (error) {
116
119
  console.warn(chalk.yellow(`[FeedbackLoader] Semantic similarity initialization failed: ${error.message}`));
117
120
  semanticSimilarityAvailable = false;
118
121
  }
@@ -154,7 +157,8 @@ async function calculateSemanticSimilarity(text1, text2) {
154
157
  const similarity = calculateCosineSimilarity(embedding1, embedding2);
155
158
  // Cosine similarity ranges from -1 to 1, normalize to 0-1
156
159
  return (similarity + 1) / 2;
157
- } catch (error) {
160
+ }
161
+ catch (error) {
158
162
  console.warn(chalk.yellow(`[FeedbackLoader] Semantic similarity calculation failed: ${error.message}`));
159
163
  return null;
160
164
  }
@@ -207,7 +211,9 @@ export async function shouldSkipSimilarIssue(issueDescription, feedbackData, opt
207
211
 
208
212
  // Check similarity with dismissed issues
209
213
  for (const dismissed of dismissedIssues) {
210
- if (!dismissed.originalIssue) continue;
214
+ if (!dismissed.originalIssue) {
215
+ continue;
216
+ }
211
217
 
212
218
  let similarity;
213
219
  let similarityMethod;
@@ -222,7 +228,8 @@ export async function shouldSkipSimilarIssue(issueDescription, feedbackData, opt
222
228
  similarity = calculateWordSimilarity(issueDescription, dismissed.originalIssue);
223
229
  similarityMethod = 'word-based';
224
230
  }
225
- } else {
231
+ }
232
+ else {
226
233
  // Use word-based similarity
227
234
  similarity = calculateWordSimilarity(issueDescription, dismissed.originalIssue);
228
235
  similarityMethod = 'word-based';
@@ -297,7 +304,9 @@ export async function calculateIssueSimilarity(text1, text2, options = {}) {
297
304
  * @returns {number} Similarity score (0-1)
298
305
  */
299
306
  export function calculateWordSimilarity(text1, text2) {
300
- if (!text1 || !text2) return 0;
307
+ if (!text1 || !text2) {
308
+ return 0;
309
+ }
301
310
 
302
311
  // Normalize and tokenize
303
312
  const normalize = (text) =>
@@ -310,7 +319,9 @@ export function calculateWordSimilarity(text1, text2) {
310
319
  const words1 = new Set(normalize(text1));
311
320
  const words2 = new Set(normalize(text2));
312
321
 
313
- if (words1.size === 0 || words2.size === 0) return 0;
322
+ if (words1.size === 0 || words2.size === 0) {
323
+ return 0;
324
+ }
314
325
 
315
326
  // Calculate Jaccard similarity (intersection over union)
316
327
  const intersection = [...words1].filter((word) => words2.has(word)).length;
package/src/index.js CHANGED
@@ -103,11 +103,13 @@ program
103
103
  await embeddingsSystem.clearAllEmbeddings();
104
104
  console.log(chalk.green('All embeddings have been cleared.'));
105
105
  await embeddingsSystem.databaseManager.cleanup();
106
- } catch (err) {
106
+ }
107
+ catch (err) {
107
108
  console.error(chalk.red('Error clearing all embeddings:'), err.message);
108
109
  try {
109
110
  await embeddingsSystem.databaseManager.cleanup();
110
- } catch (cleanupErr) {
111
+ }
112
+ catch (cleanupErr) {
111
113
  console.error(chalk.red('Error during cleanup:'), cleanupErr.message);
112
114
  }
113
115
  process.exit(1);
@@ -203,7 +205,8 @@ const hasCommand = process.argv
203
205
  if (!hasCommand && process.argv.length > 2) {
204
206
  // If no command is specified but there are arguments, default to 'analyze'
205
207
  program.parse(['node', 'index.js', 'analyze', ...process.argv.slice(2)]);
206
- } else {
208
+ }
209
+ else {
207
210
  program.parse();
208
211
  }
209
212
 
@@ -223,7 +226,8 @@ process.on('SIGINT', async () => {
223
226
  clearTimeout(forceExitTimeout); // Cleanup finished, clear the timeout
224
227
  console.log(chalk.cyan('SIGINT handler: Exiting normally (code 0).'));
225
228
  process.exit(0); // Exit normally
226
- } catch (err) {
229
+ }
230
+ catch (err) {
227
231
  console.error(chalk.red('Error during embeddingsSystem.cleanup():'), err.message);
228
232
  clearTimeout(forceExitTimeout);
229
233
  console.log(chalk.cyan('SIGINT handler: Exiting after error (code 1).'));
@@ -246,7 +250,8 @@ process.on('SIGTERM', async () => {
246
250
  clearTimeout(forceExitTimeout); // Cleanup finished, clear the timeout
247
251
  console.log(chalk.cyan('SIGTERM handler: Exiting normally (code 0).'));
248
252
  process.exit(0); // Exit normally
249
- } catch (err) {
253
+ }
254
+ catch (err) {
250
255
  console.error(chalk.red('Error during embeddingsSystem.cleanup():'), err.message);
251
256
  clearTimeout(forceExitTimeout);
252
257
  console.log(chalk.cyan('SIGTERM handler: Exiting after error (code 1).'));
@@ -366,13 +371,15 @@ async function runCodeReview(options) {
366
371
  diffWith: options.diffWith,
367
372
  };
368
373
  reviewTask = reviewPullRequest(changedFiles, enhancedReviewOptions);
369
- } else if (options.file) {
374
+ }
375
+ else if (options.file) {
370
376
  operationDescription = `single file: ${options.file}`;
371
377
  if (!fs.existsSync(options.file)) {
372
378
  throw new Error(`File not found: ${options.file}`);
373
379
  }
374
380
  reviewTask = reviewFile(options.file, reviewOptions);
375
- } else if (options.files && options.files.length > 0) {
381
+ }
382
+ else if (options.files && options.files.length > 0) {
376
383
  const filesToAnalyze = await expandFilePatterns(options.files);
377
384
  if (filesToAnalyze.length === 0) {
378
385
  console.log(chalk.yellow('No files found matching the specified patterns. Exiting.'));
@@ -380,7 +387,8 @@ async function runCodeReview(options) {
380
387
  }
381
388
  operationDescription = `${filesToAnalyze.length} specific files/patterns`;
382
389
  reviewTask = reviewFiles(filesToAnalyze, reviewOptions);
383
- } else {
390
+ }
391
+ else {
384
392
  // No valid options provided - show error and exit
385
393
  console.error(chalk.red('Error: You must specify one of the following:'));
386
394
  console.error(chalk.yellow(' --file <file> Analyze a single file'));
@@ -418,7 +426,8 @@ async function runCodeReview(options) {
418
426
  // Pass the detailed results array to the output function
419
427
  outputFn(reviewResult.results, options);
420
428
  console.log(chalk.bold.green(`\nAnalysis complete for ${operationDescription}! (${duration}s)`));
421
- } else {
429
+ }
430
+ else {
422
431
  // No results to display (e.g., all files were excluded/skipped)
423
432
  const message = reviewResult.message || 'All files were excluded from review (e.g., config files, lock files).';
424
433
  console.log(chalk.yellow(message));
@@ -432,7 +441,8 @@ async function runCodeReview(options) {
432
441
 
433
442
  console.log(chalk.bold.yellow(`\nReview complete for ${operationDescription} - no reviewable files found (${duration}s)`));
434
443
  }
435
- } else {
444
+ }
445
+ else {
436
446
  console.error(chalk.red('\nCode review process failed.'));
437
447
  if (reviewResult && reviewResult.error) {
438
448
  console.error(chalk.red(`Error: ${reviewResult.error}`));
@@ -445,11 +455,13 @@ async function runCodeReview(options) {
445
455
  await embeddingsSystem.cleanup();
446
456
  await cleanupClassifier();
447
457
  console.log(chalk.green('All resources cleaned up successfully'));
448
- } catch (cleanupErr) {
458
+ }
459
+ catch (cleanupErr) {
449
460
  console.error(chalk.yellow('Error during cleanup:'), cleanupErr.message);
450
461
  process.exit(1);
451
462
  }
452
- } catch (err) {
463
+ }
464
+ catch (err) {
453
465
  console.error(chalk.red(`\nError during code review (${operationDescription}):`), err.message);
454
466
  console.error(err.stack);
455
467
  // Clean up resources even on error
@@ -457,7 +469,8 @@ async function runCodeReview(options) {
457
469
  await embeddingsSystem.cleanup();
458
470
  await cleanupClassifier();
459
471
  console.log(chalk.green('All resources cleaned up successfully'));
460
- } catch (cleanupErr) {
472
+ }
473
+ catch (cleanupErr) {
461
474
  console.error(chalk.red('Error during cleanup:'), cleanupErr.message);
462
475
  }
463
476
  process.exit(1);
@@ -500,7 +513,8 @@ async function generateEmbeddings(options) {
500
513
  .map((line) => line.trim())
501
514
  .filter((line) => line && !line.startsWith('#'));
502
515
  excludePatterns = [...excludePatterns, ...filePatterns];
503
- } else {
516
+ }
517
+ else {
504
518
  console.warn(chalk.yellow(`Exclude file not found: ${excludeFilePath}`));
505
519
  }
506
520
  }
@@ -512,19 +526,22 @@ async function generateEmbeddings(options) {
512
526
  // Log gitignore status
513
527
  if (options.gitignore === false) {
514
528
  console.log(chalk.yellow('Automatic .gitignore exclusion is disabled.'));
515
- } else {
529
+ }
530
+ else {
516
531
  console.log(chalk.cyan('Respecting .gitignore patterns (if present).'));
517
532
  }
518
533
  console.log(chalk.green('Exclusion pattern processing complete.'));
519
534
 
520
535
  // Get files to process
521
536
  let filesToProcess = [];
537
+ const runMode = options.files && options.files.length > 0 ? 'partial' : 'full';
522
538
 
523
539
  if (options.files && options.files.length > 0) {
524
540
  console.log(chalk.cyan('Processing specified files/patterns...'));
525
541
  filesToProcess = await expandFilePatterns(options.files, baseDir);
526
542
  console.log(chalk.green(`Expanded specified files/patterns to ${filesToProcess.length} files.`));
527
- } else {
543
+ }
544
+ else {
528
545
  console.log(chalk.cyan(`Scanning directory for supported files: ${baseDir}`));
529
546
  // Show spinner during file discovery
530
547
  const scanSpinner = new Spinner('Scanning files... %s');
@@ -585,15 +602,19 @@ async function generateEmbeddings(options) {
585
602
  baseDir: baseDir,
586
603
  batchSize: 100, // Set a reasonable batch size
587
604
  maxLines: parseInt(options.maxLines || '1000', 10),
605
+ runMode,
588
606
  onProgress: (status) => {
589
607
  // Update counters based on status
590
608
  if (status === 'processed') {
591
609
  processedCount++;
592
- } else if (status === 'skipped') {
610
+ }
611
+ else if (status === 'skipped') {
593
612
  skippedCount++;
594
- } else if (status === 'failed') {
613
+ }
614
+ else if (status === 'failed') {
595
615
  failedCount++;
596
- } else if (status === 'excluded') {
616
+ }
617
+ else if (status === 'excluded') {
597
618
  excludedCount++;
598
619
  }
599
620
 
@@ -601,7 +622,8 @@ async function generateEmbeddings(options) {
601
622
  updateSpinner();
602
623
  },
603
624
  });
604
- } finally {
625
+ }
626
+ finally {
605
627
  // Clean up the progress display even if embedding generation fails.
606
628
  clearInterval(progressInterval);
607
629
  spinner.stop(true);
@@ -625,9 +647,6 @@ async function generateEmbeddings(options) {
625
647
  forceAnalysis: options.forceAnalysis,
626
648
  });
627
649
 
628
- // Store project summary in embeddings system for later use
629
- await embeddingsSystem.storeProjectSummary(projectDir, projectSummary);
630
-
631
650
  console.log(chalk.green('✅ Project analysis complete and stored'));
632
651
  verboseLog(options, chalk.gray(` Project: ${projectSummary.projectName}`));
633
652
  verboseLog(
@@ -637,7 +656,8 @@ async function generateEmbeddings(options) {
637
656
  )
638
657
  );
639
658
  verboseLog(options, chalk.gray(` Key patterns: ${projectSummary.keyPatterns.length}`));
640
- } catch (error) {
659
+ }
660
+ catch (error) {
641
661
  console.error(chalk.red('⚠️ Project analysis failed but continuing:'), error.message);
642
662
  }
643
663
 
@@ -645,7 +665,8 @@ async function generateEmbeddings(options) {
645
665
  console.log(chalk.cyan('Cleaning up resources...'));
646
666
  await embeddingsSystem.cleanup();
647
667
  console.log(chalk.green('Cleanup successful.'));
648
- } catch (err) {
668
+ }
669
+ catch (err) {
649
670
  console.error(chalk.red('Error generating embeddings:'), err.message);
650
671
  console.error(err.stack);
651
672
  // Clean up resources even on error
@@ -653,7 +674,8 @@ async function generateEmbeddings(options) {
653
674
  console.log(chalk.cyan('Cleaning up resources after error...'));
654
675
  await embeddingsSystem.cleanup();
655
676
  console.log(chalk.green('Cleanup successful.'));
656
- } catch (cleanupErr) {
677
+ }
678
+ catch (cleanupErr) {
657
679
  console.error(chalk.red('Error during cleanup:'), cleanupErr.message);
658
680
  }
659
681
  process.exit(1);
@@ -679,13 +701,15 @@ async function clearEmbeddings(options) {
679
701
  // Clean up resources (only database connection since we skipped full initialization)
680
702
  console.log(chalk.cyan('Cleaning up resources...'));
681
703
  await embeddingsSystem.databaseManager.cleanup();
682
- } catch (err) {
704
+ }
705
+ catch (err) {
683
706
  console.error(chalk.red('Error clearing embeddings:'), err.message);
684
707
  console.error(err.stack);
685
708
  // Clean up resources even on error (only database connection)
686
709
  try {
687
710
  await embeddingsSystem.databaseManager.cleanup();
688
- } catch (cleanupErr) {
711
+ }
712
+ catch (cleanupErr) {
689
713
  console.error(chalk.red('Error during cleanup:'), cleanupErr.message);
690
714
  }
691
715
  process.exit(1);
@@ -704,7 +728,8 @@ async function showEmbeddingStats(options) {
704
728
 
705
729
  if (options.directory) {
706
730
  console.log(chalk.cyan(`Fetching embedding statistics for project: ${projectDir}`));
707
- } else {
731
+ }
732
+ else {
708
733
  console.log(chalk.cyan('Fetching embedding statistics for all projects...'));
709
734
  }
710
735
 
@@ -715,7 +740,8 @@ async function showEmbeddingStats(options) {
715
740
 
716
741
  if (!stats || Object.keys(stats).length === 0 || stats.totalCount === 0) {
717
742
  console.log(chalk.yellow('No embeddings found or database is empty.'));
718
- } else {
743
+ }
744
+ else {
719
745
  console.log(` ${chalk.cyan('Total Embeddings:')} ${chalk.green(stats.totalCount)}`);
720
746
  if (stats.dimensions) {
721
747
  console.log(` ${chalk.cyan('Vector Dimensions:')} ${chalk.green(stats.dimensions)}`);
@@ -734,7 +760,8 @@ async function showEmbeddingStats(options) {
734
760
  // Clean up resources
735
761
  // console.log(chalk.cyan('Cleaning up resources...'));
736
762
  // await embeddingsSystem.cleanup();
737
- } catch (err) {
763
+ }
764
+ catch (err) {
738
765
  console.error(chalk.red('Error fetching embedding statistics:'), err.message);
739
766
  console.error(err.stack);
740
767
  // Clean up resources even on error
@@ -870,10 +897,12 @@ async function findSupportedFiles(directory, options = {}) {
870
897
  // Add log after the filtering loop (now just assignment)
871
898
  verboseLog(options, chalk.green(`Finished filtering glob results. ${finalFiles.length} files remain.`));
872
899
  return finalFiles;
873
- } catch (err) {
900
+ }
901
+ catch (err) {
874
902
  if (err.name === 'AbortError') {
875
903
  console.error(chalk.red('Glob operation timed out. The directory might be too large or complex.'));
876
- } else {
904
+ }
905
+ else {
877
906
  console.error(chalk.red(`Error during glob file search: ${err.message}`));
878
907
  }
879
908
  console.error(err.stack); // Log stack for debugging
@@ -898,7 +927,8 @@ async function expandFilePatterns(patterns, baseDir = process.cwd()) {
898
927
  // Check if it's a direct file path first
899
928
  if (fs.existsSync(absolutePattern) && fs.statSync(absolutePattern).isFile()) {
900
929
  files.add(absolutePattern);
901
- } else {
930
+ }
931
+ else {
902
932
  // Treat as a glob pattern
903
933
  // Use the original pattern with baseDir as cwd for correct globbing
904
934
  const matchedFiles = await glob.glob(pattern, { cwd: baseDir, absolute: true, nodir: true });
@@ -911,7 +941,8 @@ async function expandFilePatterns(patterns, baseDir = process.cwd()) {
911
941
  }
912
942
  }
913
943
  return Array.from(files);
914
- } catch (err) {
944
+ }
945
+ catch (err) {
915
946
  console.error(chalk.red('Error expanding file patterns:'), err.message);
916
947
  return [];
917
948
  }
@@ -940,7 +971,8 @@ function getChangedFiles(branch, workingDir = process.cwd()) {
940
971
  // Ensure the base branch exists locally as well (crucial for diff operations)
941
972
  try {
942
973
  ensureBranchExists(baseBranch, workingDir);
943
- } catch (error) {
974
+ }
975
+ catch (error) {
944
976
  console.warn(chalk.yellow(`Warning: Could not ensure base branch '${baseBranch}' exists locally: ${error.message}`));
945
977
  // Continue with the original baseBranch name, it might work with remote refs
946
978
  }
@@ -963,7 +995,8 @@ function getChangedFiles(branch, workingDir = process.cwd()) {
963
995
  }
964
996
 
965
997
  return changedFiles;
966
- } catch (err) {
998
+ }
999
+ catch (err) {
967
1000
  console.error(chalk.red('Error getting git diff:'), err.message);
968
1001
  return [];
969
1002
  }
@@ -989,7 +1022,9 @@ function outputJson(reviewResults, options) {
989
1022
  filesWithIssues: reviewResults.filter((r) => r.success && !r.skipped && r.results?.issues?.length > 0).length,
990
1023
  totalIssues: reviewResults.reduce((sum, r) => sum + (r.results?.issues?.length || 0), 0),
991
1024
  issuesWithCodeSuggestions: reviewResults.reduce((sum, r) => {
992
- if (!r.success || r.skipped || !r.results?.issues) return sum;
1025
+ if (!r.success || r.skipped || !r.results?.issues) {
1026
+ return sum;
1027
+ }
993
1028
  return sum + r.results.issues.filter((issue) => issue.codeSuggestion).length;
994
1029
  }, 0),
995
1030
  skippedFiles: reviewResults.filter((r) => r.skipped).length,
@@ -1020,7 +1055,8 @@ function outputJson(reviewResults, options) {
1020
1055
  if (options && options.outputFile) {
1021
1056
  fs.writeFileSync(options.outputFile, jsonOutput, 'utf8');
1022
1057
  console.log(chalk.green(`JSON output saved to: ${options.outputFile}`));
1023
- } else {
1058
+ }
1059
+ else {
1024
1060
  // Write JSON output to stdout (process.stdout is not buffered)
1025
1061
  process.stdout.write(jsonOutput);
1026
1062
  }
@@ -1049,8 +1085,12 @@ function outputMarkdown(reviewResults, options) {
1049
1085
  `- **Total Issues Found:** ${totalIssues}`,
1050
1086
  ];
1051
1087
 
1052
- if (skippedFiles > 0) lines.push(`- **Files Skipped:** ${skippedFiles}`);
1053
- if (errorFiles > 0) lines.push(`- **Errors:** ${errorFiles}`);
1088
+ if (skippedFiles > 0) {
1089
+ lines.push(`- **Files Skipped:** ${skippedFiles}`);
1090
+ }
1091
+ if (errorFiles > 0) {
1092
+ lines.push(`- **Errors:** ${errorFiles}`);
1093
+ }
1054
1094
 
1055
1095
  lines.push('', '## Detailed Review per File', '');
1056
1096
 
@@ -1129,8 +1169,12 @@ function outputText(reviewResults, cliOptions) {
1129
1169
  console.log(`Files Analyzed: ${chalk.bold(totalFiles)}`);
1130
1170
  console.log(`Files with Issues: ${chalk.bold(filesWithIssues)}`);
1131
1171
  console.log(`Total Issues Found: ${chalk.bold(totalIssues)}`);
1132
- if (skippedFiles > 0) console.log(`Files Skipped: ${chalk.yellow(skippedFiles)}`);
1133
- if (errorFiles > 0) console.log(`Errors: ${chalk.red(errorFiles)}`);
1172
+ if (skippedFiles > 0) {
1173
+ console.log(`Files Skipped: ${chalk.yellow(skippedFiles)}`);
1174
+ }
1175
+ if (errorFiles > 0) {
1176
+ console.log(`Errors: ${chalk.red(errorFiles)}`);
1177
+ }
1134
1178
  console.log(chalk.bold.blue('================================================'));
1135
1179
 
1136
1180
  reviewResults.forEach((fileResult) => {
@@ -1290,7 +1334,8 @@ async function analyzePRHistory(options) {
1290
1334
  // Display results using utility function
1291
1335
  displayAnalysisResults(results, duration);
1292
1336
  console.log(chalk.bold.green(`\nPR history analysis complete for ${repository}!`));
1293
- } catch (error) {
1337
+ }
1338
+ catch (error) {
1294
1339
  const endTime = Date.now();
1295
1340
  const duration = ((endTime - startTime) / 1000).toFixed(2);
1296
1341
  console.error(chalk.red(`\nError during PR history analysis (${duration}s):`), error.message);
@@ -1324,10 +1369,12 @@ async function getPRHistoryStatus(options) {
1324
1369
  if (hasComments) {
1325
1370
  const stats = await getPRCommentsStats(repository, projectPath);
1326
1371
  displayDatabaseStats(stats, hasComments);
1327
- } else {
1372
+ }
1373
+ else {
1328
1374
  displayDatabaseStats(null, hasComments);
1329
1375
  }
1330
- } catch (error) {
1376
+ }
1377
+ catch (error) {
1331
1378
  console.error(chalk.red('Error getting PR history status:'), error.message);
1332
1379
  process.exit(1);
1333
1380
  }
@@ -1388,10 +1435,12 @@ async function clearPRHistory(options) {
1388
1435
 
1389
1436
  if (cleared) {
1390
1437
  console.log(chalk.bold.green(`\nPR analysis data cleared successfully for ${repository}`));
1391
- } else {
1438
+ }
1439
+ else {
1392
1440
  console.log(chalk.yellow('No data was found to clear.'));
1393
1441
  }
1394
- } catch (error) {
1442
+ }
1443
+ catch (error) {
1395
1444
  console.error(chalk.red('Error clearing PR history data:'), error.message);
1396
1445
  verboseLog(options, error.stack);
1397
1446
  process.exit(1);
package/src/llm.js CHANGED
@@ -27,7 +27,9 @@ let anthropic = null;
27
27
  * @returns {Anthropic} The Anthropic client
28
28
  */
29
29
  function getAnthropicClient() {
30
- if (anthropic) return anthropic;
30
+ if (anthropic) {
31
+ return anthropic;
32
+ }
31
33
  const apiKey = process.env.ANTHROPIC_API_KEY;
32
34
  if (!apiKey) {
33
35
  throw new Error('ANTHROPIC_API_KEY is required for analysis. Set it in env or .env before running analyze.');
@@ -127,14 +129,16 @@ async function sendPromptToClaude(prompt, options = {}) {
127
129
  usage: response.usage,
128
130
  json: toolUse.input,
129
131
  };
130
- } else {
132
+ }
133
+ else {
131
134
  return {
132
135
  content: response.content[0]?.text || '',
133
136
  model: response.model,
134
137
  usage: response.usage,
135
138
  };
136
139
  }
137
- } catch (error) {
140
+ }
141
+ catch (error) {
138
142
  console.error(chalk.red(`Error sending prompt to Claude: ${error.message}`));
139
143
  throw error;
140
144
  }