codecritique 1.2.2 → 1.2.4

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.
@@ -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
  }
package/src/index.js CHANGED
@@ -30,6 +30,7 @@ import { ProjectAnalyzer } from './project-analyzer.js';
30
30
  import { reviewFile, reviewFiles, reviewPullRequest } from './rag-review.js';
31
31
  import { execGitSafe } from './utils/command.js';
32
32
  import { ensureBranchExists, findBaseBranch } from './utils/git.js';
33
+ import { verboseLog } from './utils/logging.js';
33
34
 
34
35
  // Create a default embeddings system instance
35
36
  const embeddingsSystem = getDefaultEmbeddingsSystem();
@@ -406,9 +407,7 @@ async function runCodeReview(options) {
406
407
  const endTime = Date.now();
407
408
  const duration = ((endTime - startTime) / 1000).toFixed(2);
408
409
 
409
- if (options.verbose) {
410
- console.log(chalk.blue(`Review process took ${duration} seconds.`));
411
- }
410
+ verboseLog(options, chalk.blue(`Review process took ${duration} seconds.`));
412
411
 
413
412
  // Process and output results
414
413
  if (reviewResult && reviewResult.success) {
@@ -520,6 +519,7 @@ async function generateEmbeddings(options) {
520
519
 
521
520
  // Get files to process
522
521
  let filesToProcess = [];
522
+ const runMode = options.files && options.files.length > 0 ? 'partial' : 'full';
523
523
 
524
524
  if (options.files && options.files.length > 0) {
525
525
  console.log(chalk.cyan('Processing specified files/patterns...'));
@@ -586,6 +586,7 @@ async function generateEmbeddings(options) {
586
586
  baseDir: baseDir,
587
587
  batchSize: 100, // Set a reasonable batch size
588
588
  maxLines: parseInt(options.maxLines || '1000', 10),
589
+ runMode,
589
590
  onProgress: (status) => {
590
591
  // Update counters based on status
591
592
  if (status === 'processed') {
@@ -626,19 +627,15 @@ async function generateEmbeddings(options) {
626
627
  forceAnalysis: options.forceAnalysis,
627
628
  });
628
629
 
629
- // Store project summary in embeddings system for later use
630
- await embeddingsSystem.storeProjectSummary(projectDir, projectSummary);
631
-
632
630
  console.log(chalk.green('✅ Project analysis complete and stored'));
633
- if (options.verbose) {
634
- console.log(chalk.gray(` Project: ${projectSummary.projectName}`));
635
- console.log(
636
- chalk.gray(
637
- ` Technologies: ${projectSummary.technologies.slice(0, 5).join(', ')}${projectSummary.technologies.length > 5 ? '...' : ''}`
638
- )
639
- );
640
- console.log(chalk.gray(` Key patterns: ${projectSummary.keyPatterns.length}`));
641
- }
631
+ verboseLog(options, chalk.gray(` Project: ${projectSummary.projectName}`));
632
+ verboseLog(
633
+ options,
634
+ chalk.gray(
635
+ ` Technologies: ${projectSummary.technologies.slice(0, 5).join(', ')}${projectSummary.technologies.length > 5 ? '...' : ''}`
636
+ )
637
+ );
638
+ verboseLog(options, chalk.gray(` Key patterns: ${projectSummary.keyPatterns.length}`));
642
639
  } catch (error) {
643
640
  console.error(chalk.red('⚠️ Project analysis failed but continuing:'), error.message);
644
641
  }
@@ -756,10 +753,12 @@ async function showEmbeddingStats(options) {
756
753
  *
757
754
  * @param {string} directory - Directory to search
758
755
  * @param {object} options - Options from generateEmbeddings command
756
+ * @param {boolean} [options.verbose=false] - Enable verbose glob and filtering logs
757
+ * @param {string} [options.filePattern] - Optional override pattern instead of the default supported-file patterns
758
+ * @param {string[]} [options.excludePatterns] - Additional glob exclusion patterns
759
759
  * @returns {Promise<Array<string>>} Array of file paths
760
760
  */
761
761
  async function findSupportedFiles(directory, options = {}) {
762
- const verbose = options.verbose || false;
763
762
  const baseDir = path.resolve(directory);
764
763
 
765
764
  // Default patterns match common code files - adjust as needed
@@ -835,19 +834,15 @@ async function findSupportedFiles(directory, options = {}) {
835
834
  // Instead, we rely on the shouldProcessFile check in embeddings.js which uses git check-ignore
836
835
  globOptions.ignore = [...excludePatterns]; // Use only explicit excludes
837
836
 
838
- if (verbose) {
839
- console.log(chalk.cyan('Using async glob to find files...'));
840
- console.log(chalk.gray(` Patterns: ${patternsToUse.join(', ')}`));
841
- console.log(chalk.gray(` Options:`), globOptions);
842
- }
837
+ verboseLog(options, chalk.cyan('Using async glob to find files...'));
838
+ verboseLog(options, chalk.gray(` Patterns: ${patternsToUse.join(', ')}`));
839
+ verboseLog(options, chalk.gray(` Options:`), globOptions);
843
840
 
844
841
  try {
845
842
  // Use asynchronous glob
846
843
  const files = await glob.glob(patternsToUse, globOptions);
847
844
 
848
- if (verbose) {
849
- console.log(chalk.green(`Glob found ${files.length} potential files.`));
850
- }
845
+ verboseLog(options, chalk.green(`Glob found ${files.length} potential files.`));
851
846
 
852
847
  // Filter results to ensure they are actual files (glob with stat should mostly handle this)
853
848
  // And apply the final utilsShouldProcessFile check (e.g., for binary content if needed)
@@ -864,9 +859,7 @@ async function findSupportedFiles(directory, options = {}) {
864
859
  // finalFiles.push(file);
865
860
  // }
866
861
  // } catch (statError) {
867
- // if (verbose) {
868
- // console.warn(chalk.yellow(`Skipping file due to stat error ${path.relative(baseDir, file)}: ${statError.message}`));
869
- // }
862
+ // console.warn(chalk.yellow(`Skipping file due to stat error ${path.relative(baseDir, file)}: ${statError.message}`));
870
863
  // }
871
864
  // }
872
865
 
@@ -874,9 +867,7 @@ async function findSupportedFiles(directory, options = {}) {
874
867
  const finalFiles = files;
875
868
 
876
869
  // Add log after the filtering loop (now just assignment)
877
- if (verbose) {
878
- console.log(chalk.green(`Finished filtering glob results. ${finalFiles.length} files remain.`));
879
- }
870
+ verboseLog(options, chalk.green(`Finished filtering glob results. ${finalFiles.length} files remain.`));
880
871
  return finalFiles;
881
872
  } catch (err) {
882
873
  if (err.name === 'AbortError') {
@@ -981,13 +972,13 @@ function getChangedFiles(branch, workingDir = process.cwd()) {
981
972
  // REMOVED: checkBranchExists function - Moved to utils.js
982
973
 
983
974
  // --- Output Formatting Functions --- //
984
- // These need to be adapted to the structure returned by cag-review.js functions
975
+ // These consume the normalized results returned by the RAG review pipeline
985
976
 
986
977
  /**
987
978
  * Output results in JSON format
988
979
  *
989
- * @param {Array<Object>} reviewResults - Array of individual file review results from cag-review
990
- * @param {Object} cliOptions - Command line options
980
+ * @param {Array<Object>} reviewResults - Array of individual file review results
981
+ * @param {Object} options - Command line options
991
982
  */
992
983
  function outputJson(reviewResults, options) {
993
984
  // Structure the output to be informative
@@ -1015,7 +1006,7 @@ function outputJson(reviewResults, options) {
1015
1006
  filePath: r.filePath,
1016
1007
  success: true,
1017
1008
  language: r.language,
1018
- review: r.results, // Contains summary, issues (with optional codeSuggestion), positives from LLM
1009
+ review: r.results, // Contains summary and actionable issues (with optional codeSuggestion)
1019
1010
  // Optionally include similar examples if needed
1020
1011
  // similarExamplesUsed: r.similarExamples
1021
1012
  };
@@ -1038,86 +1029,86 @@ function outputJson(reviewResults, options) {
1038
1029
  * Output results in Markdown format
1039
1030
  *
1040
1031
  * @param {Array<Object>} reviewResults - Array of individual file review results
1041
- * @param {Object} cliOptions - Command line options
1032
+ * @param {Object} options - Command line options
1042
1033
  */
1043
- function outputMarkdown(reviewResults) {
1044
- console.log('# AI Code Review Results (RAG Approach)\n');
1045
-
1034
+ function outputMarkdown(reviewResults, options) {
1046
1035
  const totalFiles = reviewResults.length;
1047
1036
  const filesWithIssues = reviewResults.filter((r) => r.success && !r.skipped && r.results?.issues?.length > 0).length;
1048
1037
  const totalIssues = reviewResults.reduce((sum, r) => sum + (r.results?.issues?.length || 0), 0);
1049
1038
  const skippedFiles = reviewResults.filter((r) => r.skipped).length;
1050
1039
  const errorFiles = reviewResults.filter((r) => !r.success).length;
1051
1040
 
1052
- console.log('## Summary\n');
1053
- console.log(`- **Files Analyzed:** ${totalFiles}`);
1054
- console.log(`- **Files with Issues:** ${filesWithIssues}`);
1055
- console.log(`- **Total Issues Found:** ${totalIssues}`);
1056
- if (skippedFiles > 0) console.log(`- **Files Skipped:** ${skippedFiles}`);
1057
- if (errorFiles > 0) console.log(`- **Errors:** ${errorFiles}`);
1058
- console.log('\n');
1041
+ const lines = [
1042
+ '# AI Code Review Results (RAG Approach)',
1043
+ '',
1044
+ '## Summary',
1045
+ '',
1046
+ `- **Files Analyzed:** ${totalFiles}`,
1047
+ `- **Files with Issues:** ${filesWithIssues}`,
1048
+ `- **Total Issues Found:** ${totalIssues}`,
1049
+ ];
1050
+
1051
+ if (skippedFiles > 0) lines.push(`- **Files Skipped:** ${skippedFiles}`);
1052
+ if (errorFiles > 0) lines.push(`- **Errors:** ${errorFiles}`);
1059
1053
 
1060
- console.log('## Detailed Review per File\n');
1054
+ lines.push('', '## Detailed Review per File', '');
1061
1055
 
1062
1056
  reviewResults.forEach((fileResult) => {
1063
- console.log(`### ${fileResult.filePath}\n`);
1057
+ lines.push(`### ${fileResult.filePath}`, '');
1064
1058
  if (!fileResult.success) {
1065
- console.log(`**Error:** ${fileResult.error}\n`);
1059
+ lines.push(`**Error:** ${fileResult.error}`, '');
1066
1060
  return;
1067
1061
  }
1068
1062
  if (fileResult.skipped) {
1069
- console.log(`*Skipped (based on exclusion patterns or file type).*\n`);
1063
+ lines.push('*Skipped (based on exclusion patterns or file type).*', '');
1070
1064
  return;
1071
1065
  }
1072
- if (!fileResult.results || (!fileResult.results.issues?.length && !fileResult.results.positives?.length)) {
1073
- console.log(`*No significant findings or issues reported.*\n`);
1066
+ if (!fileResult.results || !fileResult.results.issues?.length) {
1067
+ lines.push('*No actionable issues reported.*', '');
1074
1068
  if (fileResult.results?.summary) {
1075
- console.log(`**Summary:** ${fileResult.results.summary}\n`);
1069
+ lines.push(`**Summary:** ${fileResult.results.summary}`, '');
1076
1070
  }
1077
1071
  return;
1078
1072
  }
1079
1073
 
1080
1074
  const review = fileResult.results;
1081
1075
  if (review.summary) {
1082
- console.log(`**Summary:** ${review.summary}\n`);
1076
+ lines.push(`**Summary:** ${review.summary}`, '');
1083
1077
  }
1084
1078
 
1085
1079
  if (review.issues && review.issues.length > 0) {
1086
- console.log(`**Issues Found (${review.issues.length}):**\n`);
1080
+ lines.push(`**Issues Found (${review.issues.length}):**`, '');
1087
1081
  review.issues.forEach((issue) => {
1088
1082
  const severityEmoji = getSeverityEmoji(issue.severity);
1089
- console.log(
1083
+ lines.push(
1090
1084
  `- **[${issue.severity.toUpperCase()}] ${severityEmoji} (Lines: ${issue.lineNumbers?.join(', ') || 'N/A'})**: ${
1091
1085
  issue.description
1092
1086
  }`
1093
1087
  );
1094
1088
  if (issue.suggestion) {
1095
- console.log(`\n *Suggestion:* ${issue.suggestion}\n`);
1089
+ lines.push('', ` *Suggestion:* ${issue.suggestion}`, '');
1096
1090
  }
1097
1091
  // Include code suggestion if available
1098
1092
  if (issue.codeSuggestion) {
1099
1093
  const { startLine, endLine, newCode } = issue.codeSuggestion;
1100
1094
  const lineRange = endLine ? `${startLine}-${endLine}` : `${startLine}`;
1101
- console.log(`\n **Suggested change (lines ${lineRange}):**\n`);
1102
- console.log(' ```suggestion');
1103
- console.log(
1104
- newCode
1105
- .split('\n')
1106
- .map((line) => ` ${line}`)
1107
- .join('\n')
1108
- );
1109
- console.log(' ```\n');
1095
+ lines.push('', ` **Suggested change (lines ${lineRange}):**`, '', ' ```suggestion');
1096
+ lines.push(...newCode.split('\n').map((line) => ` ${line}`));
1097
+ lines.push(' ```', '');
1110
1098
  }
1111
1099
  });
1112
1100
  }
1113
-
1114
- if (review.positives && review.positives.length > 0) {
1115
- console.log(`**Positives Found (${review.positives.length}):**\n`);
1116
- review.positives.forEach((positive) => {
1117
- console.log(` - ${positive}\n`);
1118
- });
1119
- }
1120
1101
  });
1102
+
1103
+ const markdownOutput = `${lines.join('\n')}\n`;
1104
+
1105
+ if (options?.outputFile) {
1106
+ fs.writeFileSync(options.outputFile, markdownOutput, 'utf8');
1107
+ console.log(chalk.green(`Markdown output saved to: ${options.outputFile}`));
1108
+ return;
1109
+ }
1110
+
1111
+ process.stdout.write(markdownOutput);
1121
1112
  }
1122
1113
 
1123
1114
  /**
@@ -1149,17 +1140,13 @@ function outputText(reviewResults, cliOptions) {
1149
1140
  return;
1150
1141
  }
1151
1142
  if (fileResult.skipped) {
1152
- if (cliOptions.verbose) {
1153
- console.log(chalk.yellow(`\nSkipped: ${fileResult.filePath}`));
1154
- }
1143
+ verboseLog(cliOptions, chalk.yellow(`\nSkipped: ${fileResult.filePath}`));
1155
1144
  return;
1156
1145
  }
1157
- if (!fileResult.results || (!fileResult.results.issues?.length && !fileResult.results.positives?.length)) {
1158
- if (cliOptions.verbose) {
1159
- console.log(chalk.green(`\nNo findings for: ${fileResult.filePath}`));
1160
- if (fileResult.results?.summary) {
1161
- console.log(chalk.green(` Summary: ${fileResult.results.summary}`));
1162
- }
1146
+ if (!fileResult.results || !fileResult.results.issues?.length) {
1147
+ verboseLog(cliOptions, chalk.green(`\nNo findings for: ${fileResult.filePath}`));
1148
+ if (fileResult.results?.summary) {
1149
+ verboseLog(cliOptions, chalk.green(` Summary: ${fileResult.results.summary}`));
1163
1150
  }
1164
1151
  return;
1165
1152
  }
@@ -1194,13 +1181,6 @@ function outputText(reviewResults, cliOptions) {
1194
1181
  });
1195
1182
  }
1196
1183
 
1197
- if (review.positives && review.positives.length > 0) {
1198
- console.log(chalk.bold.green('\nPositives:'));
1199
- review.positives.forEach((positive) => {
1200
- console.log(` - ${positive}`);
1201
- });
1202
- console.log('');
1203
- }
1204
1184
  console.log(chalk.gray(`========================================${'='.repeat(fileResult.filePath.length)}`));
1205
1185
  });
1206
1186
  }
@@ -1294,6 +1274,7 @@ async function analyzePRHistory(options) {
1294
1274
  resume: options.resume,
1295
1275
  clearExisting: options.clear,
1296
1276
  projectPath,
1277
+ verbose: options.verbose,
1297
1278
  onProgress: (progress) => displayProgress(progress, options.verbose),
1298
1279
  };
1299
1280
 
@@ -1312,9 +1293,7 @@ async function analyzePRHistory(options) {
1312
1293
  const endTime = Date.now();
1313
1294
  const duration = ((endTime - startTime) / 1000).toFixed(2);
1314
1295
  console.error(chalk.red(`\nError during PR history analysis (${duration}s):`), error.message);
1315
- if (options.verbose) {
1316
- console.error(error.stack);
1317
- }
1296
+ verboseLog(options, error.stack);
1318
1297
  process.exit(1);
1319
1298
  }
1320
1299
  }
@@ -1413,9 +1392,7 @@ async function clearPRHistory(options) {
1413
1392
  }
1414
1393
  } catch (error) {
1415
1394
  console.error(chalk.red('Error clearing PR history data:'), error.message);
1416
- if (options.verbose) {
1417
- console.error(error.stack);
1418
- }
1395
+ verboseLog(options, error.stack);
1419
1396
  process.exit(1);
1420
1397
  }
1421
1398
  }
package/src/llm.js CHANGED
@@ -15,6 +15,7 @@
15
15
  import { Anthropic } from '@anthropic-ai/sdk';
16
16
  import chalk from 'chalk';
17
17
  import dotenv from 'dotenv';
18
+ import { verboseLog } from './utils/logging.js';
18
19
 
19
20
  // Load env variables if present; do not enforce key at import time
20
21
  dotenv.config();
@@ -56,7 +57,7 @@ async function sendPromptToClaude(prompt, options = {}) {
56
57
  const { model = DEFAULT_MODEL, maxTokens = MAX_TOKENS, temperature = 0.7, system = '', jsonSchema = null, cacheTtl = '5m' } = options;
57
58
 
58
59
  try {
59
- console.log(chalk.cyan('Sending prompt to Claude...'));
60
+ verboseLog(options, chalk.cyan('Sending prompt to Claude...'));
60
61
 
61
62
  const client = getAnthropicClient();
62
63
 
@@ -104,8 +105,8 @@ async function sendPromptToClaude(prompt, options = {}) {
104
105
  const response = await client.messages.create(requestParams);
105
106
 
106
107
  // Log response structure for debugging
107
- console.log(chalk.gray(` Response stop_reason: ${response.stop_reason}`));
108
- console.log(chalk.gray(` Response content blocks: ${response.content?.length || 0}`));
108
+ verboseLog(options, chalk.gray(` Response stop_reason: ${response.stop_reason}`));
109
+ verboseLog(options, chalk.gray(` Response content blocks: ${response.content?.length || 0}`));
109
110
 
110
111
  // Process response based on whether we used tool calling
111
112
  if (jsonSchema) {