codecritique 1.2.2 → 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/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) {
@@ -630,15 +629,14 @@ async function generateEmbeddings(options) {
630
629
  await embeddingsSystem.storeProjectSummary(projectDir, projectSummary);
631
630
 
632
631
  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
- }
632
+ verboseLog(options, chalk.gray(` Project: ${projectSummary.projectName}`));
633
+ verboseLog(
634
+ options,
635
+ chalk.gray(
636
+ ` Technologies: ${projectSummary.technologies.slice(0, 5).join(', ')}${projectSummary.technologies.length > 5 ? '...' : ''}`
637
+ )
638
+ );
639
+ verboseLog(options, chalk.gray(` Key patterns: ${projectSummary.keyPatterns.length}`));
642
640
  } catch (error) {
643
641
  console.error(chalk.red('⚠️ Project analysis failed but continuing:'), error.message);
644
642
  }
@@ -756,10 +754,12 @@ async function showEmbeddingStats(options) {
756
754
  *
757
755
  * @param {string} directory - Directory to search
758
756
  * @param {object} options - Options from generateEmbeddings command
757
+ * @param {boolean} [options.verbose=false] - Enable verbose glob and filtering logs
758
+ * @param {string} [options.filePattern] - Optional override pattern instead of the default supported-file patterns
759
+ * @param {string[]} [options.excludePatterns] - Additional glob exclusion patterns
759
760
  * @returns {Promise<Array<string>>} Array of file paths
760
761
  */
761
762
  async function findSupportedFiles(directory, options = {}) {
762
- const verbose = options.verbose || false;
763
763
  const baseDir = path.resolve(directory);
764
764
 
765
765
  // Default patterns match common code files - adjust as needed
@@ -835,19 +835,15 @@ async function findSupportedFiles(directory, options = {}) {
835
835
  // Instead, we rely on the shouldProcessFile check in embeddings.js which uses git check-ignore
836
836
  globOptions.ignore = [...excludePatterns]; // Use only explicit excludes
837
837
 
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
- }
838
+ verboseLog(options, chalk.cyan('Using async glob to find files...'));
839
+ verboseLog(options, chalk.gray(` Patterns: ${patternsToUse.join(', ')}`));
840
+ verboseLog(options, chalk.gray(` Options:`), globOptions);
843
841
 
844
842
  try {
845
843
  // Use asynchronous glob
846
844
  const files = await glob.glob(patternsToUse, globOptions);
847
845
 
848
- if (verbose) {
849
- console.log(chalk.green(`Glob found ${files.length} potential files.`));
850
- }
846
+ verboseLog(options, chalk.green(`Glob found ${files.length} potential files.`));
851
847
 
852
848
  // Filter results to ensure they are actual files (glob with stat should mostly handle this)
853
849
  // And apply the final utilsShouldProcessFile check (e.g., for binary content if needed)
@@ -864,9 +860,7 @@ async function findSupportedFiles(directory, options = {}) {
864
860
  // finalFiles.push(file);
865
861
  // }
866
862
  // } catch (statError) {
867
- // if (verbose) {
868
- // console.warn(chalk.yellow(`Skipping file due to stat error ${path.relative(baseDir, file)}: ${statError.message}`));
869
- // }
863
+ // console.warn(chalk.yellow(`Skipping file due to stat error ${path.relative(baseDir, file)}: ${statError.message}`));
870
864
  // }
871
865
  // }
872
866
 
@@ -874,9 +868,7 @@ async function findSupportedFiles(directory, options = {}) {
874
868
  const finalFiles = files;
875
869
 
876
870
  // 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
- }
871
+ verboseLog(options, chalk.green(`Finished filtering glob results. ${finalFiles.length} files remain.`));
880
872
  return finalFiles;
881
873
  } catch (err) {
882
874
  if (err.name === 'AbortError') {
@@ -981,13 +973,13 @@ function getChangedFiles(branch, workingDir = process.cwd()) {
981
973
  // REMOVED: checkBranchExists function - Moved to utils.js
982
974
 
983
975
  // --- Output Formatting Functions --- //
984
- // These need to be adapted to the structure returned by cag-review.js functions
976
+ // These consume the normalized results returned by the RAG review pipeline
985
977
 
986
978
  /**
987
979
  * Output results in JSON format
988
980
  *
989
- * @param {Array<Object>} reviewResults - Array of individual file review results from cag-review
990
- * @param {Object} cliOptions - Command line options
981
+ * @param {Array<Object>} reviewResults - Array of individual file review results
982
+ * @param {Object} options - Command line options
991
983
  */
992
984
  function outputJson(reviewResults, options) {
993
985
  // Structure the output to be informative
@@ -1015,7 +1007,7 @@ function outputJson(reviewResults, options) {
1015
1007
  filePath: r.filePath,
1016
1008
  success: true,
1017
1009
  language: r.language,
1018
- review: r.results, // Contains summary, issues (with optional codeSuggestion), positives from LLM
1010
+ review: r.results, // Contains summary and actionable issues (with optional codeSuggestion)
1019
1011
  // Optionally include similar examples if needed
1020
1012
  // similarExamplesUsed: r.similarExamples
1021
1013
  };
@@ -1038,86 +1030,86 @@ function outputJson(reviewResults, options) {
1038
1030
  * Output results in Markdown format
1039
1031
  *
1040
1032
  * @param {Array<Object>} reviewResults - Array of individual file review results
1041
- * @param {Object} cliOptions - Command line options
1033
+ * @param {Object} options - Command line options
1042
1034
  */
1043
- function outputMarkdown(reviewResults) {
1044
- console.log('# AI Code Review Results (RAG Approach)\n');
1045
-
1035
+ function outputMarkdown(reviewResults, options) {
1046
1036
  const totalFiles = reviewResults.length;
1047
1037
  const filesWithIssues = reviewResults.filter((r) => r.success && !r.skipped && r.results?.issues?.length > 0).length;
1048
1038
  const totalIssues = reviewResults.reduce((sum, r) => sum + (r.results?.issues?.length || 0), 0);
1049
1039
  const skippedFiles = reviewResults.filter((r) => r.skipped).length;
1050
1040
  const errorFiles = reviewResults.filter((r) => !r.success).length;
1051
1041
 
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');
1042
+ const lines = [
1043
+ '# AI Code Review Results (RAG Approach)',
1044
+ '',
1045
+ '## Summary',
1046
+ '',
1047
+ `- **Files Analyzed:** ${totalFiles}`,
1048
+ `- **Files with Issues:** ${filesWithIssues}`,
1049
+ `- **Total Issues Found:** ${totalIssues}`,
1050
+ ];
1051
+
1052
+ if (skippedFiles > 0) lines.push(`- **Files Skipped:** ${skippedFiles}`);
1053
+ if (errorFiles > 0) lines.push(`- **Errors:** ${errorFiles}`);
1059
1054
 
1060
- console.log('## Detailed Review per File\n');
1055
+ lines.push('', '## Detailed Review per File', '');
1061
1056
 
1062
1057
  reviewResults.forEach((fileResult) => {
1063
- console.log(`### ${fileResult.filePath}\n`);
1058
+ lines.push(`### ${fileResult.filePath}`, '');
1064
1059
  if (!fileResult.success) {
1065
- console.log(`**Error:** ${fileResult.error}\n`);
1060
+ lines.push(`**Error:** ${fileResult.error}`, '');
1066
1061
  return;
1067
1062
  }
1068
1063
  if (fileResult.skipped) {
1069
- console.log(`*Skipped (based on exclusion patterns or file type).*\n`);
1064
+ lines.push('*Skipped (based on exclusion patterns or file type).*', '');
1070
1065
  return;
1071
1066
  }
1072
- if (!fileResult.results || (!fileResult.results.issues?.length && !fileResult.results.positives?.length)) {
1073
- console.log(`*No significant findings or issues reported.*\n`);
1067
+ if (!fileResult.results || !fileResult.results.issues?.length) {
1068
+ lines.push('*No actionable issues reported.*', '');
1074
1069
  if (fileResult.results?.summary) {
1075
- console.log(`**Summary:** ${fileResult.results.summary}\n`);
1070
+ lines.push(`**Summary:** ${fileResult.results.summary}`, '');
1076
1071
  }
1077
1072
  return;
1078
1073
  }
1079
1074
 
1080
1075
  const review = fileResult.results;
1081
1076
  if (review.summary) {
1082
- console.log(`**Summary:** ${review.summary}\n`);
1077
+ lines.push(`**Summary:** ${review.summary}`, '');
1083
1078
  }
1084
1079
 
1085
1080
  if (review.issues && review.issues.length > 0) {
1086
- console.log(`**Issues Found (${review.issues.length}):**\n`);
1081
+ lines.push(`**Issues Found (${review.issues.length}):**`, '');
1087
1082
  review.issues.forEach((issue) => {
1088
1083
  const severityEmoji = getSeverityEmoji(issue.severity);
1089
- console.log(
1084
+ lines.push(
1090
1085
  `- **[${issue.severity.toUpperCase()}] ${severityEmoji} (Lines: ${issue.lineNumbers?.join(', ') || 'N/A'})**: ${
1091
1086
  issue.description
1092
1087
  }`
1093
1088
  );
1094
1089
  if (issue.suggestion) {
1095
- console.log(`\n *Suggestion:* ${issue.suggestion}\n`);
1090
+ lines.push('', ` *Suggestion:* ${issue.suggestion}`, '');
1096
1091
  }
1097
1092
  // Include code suggestion if available
1098
1093
  if (issue.codeSuggestion) {
1099
1094
  const { startLine, endLine, newCode } = issue.codeSuggestion;
1100
1095
  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');
1096
+ lines.push('', ` **Suggested change (lines ${lineRange}):**`, '', ' ```suggestion');
1097
+ lines.push(...newCode.split('\n').map((line) => ` ${line}`));
1098
+ lines.push(' ```', '');
1110
1099
  }
1111
1100
  });
1112
1101
  }
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
1102
  });
1103
+
1104
+ const markdownOutput = `${lines.join('\n')}\n`;
1105
+
1106
+ if (options?.outputFile) {
1107
+ fs.writeFileSync(options.outputFile, markdownOutput, 'utf8');
1108
+ console.log(chalk.green(`Markdown output saved to: ${options.outputFile}`));
1109
+ return;
1110
+ }
1111
+
1112
+ process.stdout.write(markdownOutput);
1121
1113
  }
1122
1114
 
1123
1115
  /**
@@ -1149,17 +1141,13 @@ function outputText(reviewResults, cliOptions) {
1149
1141
  return;
1150
1142
  }
1151
1143
  if (fileResult.skipped) {
1152
- if (cliOptions.verbose) {
1153
- console.log(chalk.yellow(`\nSkipped: ${fileResult.filePath}`));
1154
- }
1144
+ verboseLog(cliOptions, chalk.yellow(`\nSkipped: ${fileResult.filePath}`));
1155
1145
  return;
1156
1146
  }
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
- }
1147
+ if (!fileResult.results || !fileResult.results.issues?.length) {
1148
+ verboseLog(cliOptions, chalk.green(`\nNo findings for: ${fileResult.filePath}`));
1149
+ if (fileResult.results?.summary) {
1150
+ verboseLog(cliOptions, chalk.green(` Summary: ${fileResult.results.summary}`));
1163
1151
  }
1164
1152
  return;
1165
1153
  }
@@ -1194,13 +1182,6 @@ function outputText(reviewResults, cliOptions) {
1194
1182
  });
1195
1183
  }
1196
1184
 
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
1185
  console.log(chalk.gray(`========================================${'='.repeat(fileResult.filePath.length)}`));
1205
1186
  });
1206
1187
  }
@@ -1294,6 +1275,7 @@ async function analyzePRHistory(options) {
1294
1275
  resume: options.resume,
1295
1276
  clearExisting: options.clear,
1296
1277
  projectPath,
1278
+ verbose: options.verbose,
1297
1279
  onProgress: (progress) => displayProgress(progress, options.verbose),
1298
1280
  };
1299
1281
 
@@ -1312,9 +1294,7 @@ async function analyzePRHistory(options) {
1312
1294
  const endTime = Date.now();
1313
1295
  const duration = ((endTime - startTime) / 1000).toFixed(2);
1314
1296
  console.error(chalk.red(`\nError during PR history analysis (${duration}s):`), error.message);
1315
- if (options.verbose) {
1316
- console.error(error.stack);
1317
- }
1297
+ verboseLog(options, error.stack);
1318
1298
  process.exit(1);
1319
1299
  }
1320
1300
  }
@@ -1413,9 +1393,7 @@ async function clearPRHistory(options) {
1413
1393
  }
1414
1394
  } catch (error) {
1415
1395
  console.error(chalk.red('Error clearing PR history data:'), error.message);
1416
- if (options.verbose) {
1417
- console.error(error.stack);
1418
- }
1396
+ verboseLog(options, error.stack);
1419
1397
  process.exit(1);
1420
1398
  }
1421
1399
  }
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) {
@@ -13,6 +13,7 @@ import { getDefaultEmbeddingsSystem } from './embeddings/factory.js';
13
13
  import * as llm from './llm.js';
14
14
  import { FILE_SELECTION_SYSTEM_PROMPT, PROJECT_SUMMARY_SYSTEM_PROMPT } from './prompt-cache.js';
15
15
  import { isDocumentationFile, isTestFile } from './utils/file-validation.js';
16
+ import { verboseLog } from './utils/logging.js';
16
17
 
17
18
  // Consolidated file classification configuration
18
19
  const FILE_PATTERNS = {
@@ -198,9 +199,7 @@ export class ProjectAnalyzer {
198
199
  const { verbose = false, forceAnalysis = false } = options;
199
200
 
200
201
  try {
201
- if (verbose) {
202
- console.log(chalk.cyan('🔍 Starting project architecture analysis...'));
203
- }
202
+ verboseLog(verbose, chalk.cyan('🔍 Starting project architecture analysis...'));
204
203
 
205
204
  // Initialize LLM client
206
205
  if (!this.llm) {
@@ -212,16 +211,13 @@ export class ProjectAnalyzer {
212
211
  if (existingSummary && !forceAnalysis) {
213
212
  const currentHash = await this.calculateKeyFilesHash(existingSummary.keyFiles);
214
213
  if (existingSummary.keyFilesHash === currentHash) {
215
- if (verbose) {
216
- console.log(chalk.green('✅ Project analysis up-to-date (no key file changes detected)'));
217
- }
214
+ verboseLog(verbose, chalk.green('✅ Project analysis up-to-date (no key file changes detected)'));
218
215
  return existingSummary;
219
216
  }
220
- if (verbose) {
221
- console.log(chalk.yellow('🔄 Key files changed, regenerating analysis...'));
222
- }
223
- } else if (verbose) {
224
- console.log(
217
+ verboseLog(verbose, chalk.yellow('🔄 Key files changed, regenerating analysis...'));
218
+ } else {
219
+ verboseLog(
220
+ verbose,
225
221
  chalk.cyan(
226
222
  forceAnalysis
227
223
  ? '🔄 Force analysis requested - regenerating from scratch...'
@@ -235,10 +231,8 @@ export class ProjectAnalyzer {
235
231
  ? await this.validateAndUpdateKeyFiles(existingSummary.keyFiles, projectPath)
236
232
  : await this.discoverKeyFilesWithLLM(projectPath);
237
233
 
238
- if (verbose) {
239
- console.log(chalk.gray(` Found ${keyFiles.length} key architectural files`));
240
- console.log(chalk.cyan('🧠 Generating LLM-based project analysis...'));
241
- }
234
+ verboseLog(verbose, chalk.gray(` Found ${keyFiles.length} key architectural files`));
235
+ verboseLog(verbose, chalk.cyan('🧠 Generating LLM-based project analysis...'));
242
236
 
243
237
  // Generate summary
244
238
  const projectSummary = await this.generateProjectSummary(keyFiles, projectPath);
@@ -254,12 +248,10 @@ export class ProjectAnalyzer {
254
248
  this.keyFiles = keyFiles;
255
249
  this.lastAnalysisHash = currentHash;
256
250
 
257
- if (verbose) {
258
- console.log(chalk.green('✅ Project analysis complete'));
259
- console.log(chalk.gray(` Technologies: ${(projectSummary.technologies || []).join(', ')}`));
260
- console.log(chalk.gray(` Key patterns: ${(projectSummary.keyPatterns || []).length} identified`));
261
- console.log(chalk.gray(` Key files tracked: ${keyFiles.length}`));
262
- }
251
+ verboseLog(verbose, chalk.green('✅ Project analysis complete'));
252
+ verboseLog(verbose, chalk.gray(` Technologies: ${(projectSummary.technologies || []).join(', ')}`));
253
+ verboseLog(verbose, chalk.gray(` Key patterns: ${(projectSummary.keyPatterns || []).length} identified`));
254
+ verboseLog(verbose, chalk.gray(` Key files tracked: ${keyFiles.length}`));
263
255
 
264
256
  return projectSummary;
265
257
  } catch (error) {
@@ -300,7 +292,7 @@ export class ProjectAnalyzer {
300
292
  try {
301
293
  const embeddingsSystem = getDefaultEmbeddingsSystem();
302
294
  await embeddingsSystem.storeProjectSummary(projectPath, projectSummary);
303
- console.log(chalk.green('✅ Project analysis stored in database'));
295
+ verboseLog({}, chalk.green('✅ Project analysis stored in database'));
304
296
  } catch (error) {
305
297
  console.error(chalk.yellow('Warning: Could not store analysis:'), error.message);
306
298
  }
@@ -328,7 +320,7 @@ export class ProjectAnalyzer {
328
320
 
329
321
  // If we lost more than 30% of key files, trigger fresh discovery
330
322
  if (validatedFiles.length < existingKeyFiles.length * 0.7) {
331
- console.log(chalk.yellow('⚠️ Many key files missing, performing fresh discovery...'));
323
+ verboseLog({}, chalk.yellow('⚠️ Many key files missing, performing fresh discovery...'));
332
324
  return await this.discoverKeyFilesWithLLM(projectPath);
333
325
  }
334
326
 
@@ -339,10 +331,10 @@ export class ProjectAnalyzer {
339
331
  * Discover key architectural files using LanceDB hybrid search
340
332
  */
341
333
  async discoverKeyFilesWithLLM(projectPath) {
342
- console.log(chalk.cyan('🔍 Mining codebase embeddings with LanceDB hybrid search...'));
334
+ verboseLog({}, chalk.cyan('🔍 Mining codebase embeddings with LanceDB hybrid search...'));
343
335
 
344
336
  const keyFilesByCategory = await this.mineKeyFilesFromEmbeddings(projectPath);
345
- console.log(chalk.cyan(`🧠 LLM analyzing ${keyFilesByCategory.length} candidates from embedding search...`));
337
+ verboseLog({}, chalk.cyan(`🧠 LLM analyzing ${keyFilesByCategory.length} candidates from embedding search...`));
346
338
 
347
339
  const keyFiles = await this.selectFinalKeyFiles(keyFilesByCategory, projectPath);
348
340
  return keyFiles;
@@ -362,7 +354,7 @@ export class ProjectAnalyzer {
362
354
  await table.optimize();
363
355
  } catch (optimizeError) {
364
356
  if (optimizeError.message && optimizeError.message.includes('legacy format')) {
365
- console.log(chalk.yellow(`Skipping optimization due to legacy index format - will be auto-upgraded during normal operations`));
357
+ console.warn(chalk.yellow(`Skipping optimization due to legacy index format - will be auto-upgraded during normal operations`));
366
358
  } else {
367
359
  console.warn(chalk.yellow(`Warning: Failed to optimize file embeddings table: ${optimizeError.message}`));
368
360
  }
@@ -371,7 +363,7 @@ export class ProjectAnalyzer {
371
363
  const keyFiles = new Map();
372
364
 
373
365
  try {
374
- console.log(chalk.gray(` 📊 Using LanceDB hybrid search for project: ${projectPath}`));
366
+ verboseLog({}, chalk.gray(` 📊 Using LanceDB hybrid search for project: ${projectPath}`));
375
367
 
376
368
  // Unified query function
377
369
  const queryFiles = async (config) => {
@@ -414,17 +406,17 @@ export class ProjectAnalyzer {
414
406
 
415
407
  return await query.limit(config.limit || 30).toArray();
416
408
  } catch (error) {
417
- console.log(chalk.yellow(` ⚠️ Query failed for ${config.category}: ${error.message}`));
409
+ verboseLog({}, chalk.yellow(` ⚠️ Query failed for ${config.category}: ${error.message}`));
418
410
  return [];
419
411
  }
420
412
  };
421
413
 
422
414
  // Execute all searches
423
415
  for (const config of DB_SEARCH_CONFIGS) {
424
- console.log(chalk.gray(` 🔍 Searching for ${config.category} files...`));
416
+ verboseLog({}, chalk.gray(` 🔍 Searching for ${config.category} files...`));
425
417
 
426
418
  const results = await queryFiles(config);
427
- console.log(chalk.gray(` 📦 Found ${results.length} ${config.category} file candidates`));
419
+ verboseLog({}, chalk.gray(` 📦 Found ${results.length} ${config.category} file candidates`));
428
420
 
429
421
  results.forEach((result) => {
430
422
  if (this.matchesFileType(result.path, result.name, config.matcher)) {
@@ -438,7 +430,7 @@ export class ProjectAnalyzer {
438
430
  }
439
431
 
440
432
  const results = Array.from(keyFiles.values());
441
- console.log(chalk.cyan(`🗃️ Found ${results.length} key files from embeddings database`));
433
+ verboseLog({}, chalk.cyan(`🗃️ Found ${results.length} key files from embeddings database`));
442
434
  return results;
443
435
  }
444
436
 
@@ -475,11 +467,11 @@ export class ProjectAnalyzer {
475
467
  */
476
468
  async selectFinalKeyFiles(candidates, projectPath) {
477
469
  if (candidates.length === 0) {
478
- console.log(chalk.yellow('⚠️ No candidates found from embeddings search'));
470
+ verboseLog({}, chalk.yellow('⚠️ No candidates found from embeddings search'));
479
471
  return [];
480
472
  }
481
473
 
482
- console.log(chalk.cyan(`🤖 LLM analyzing ${candidates.length} candidates...`));
474
+ verboseLog({}, chalk.cyan(`🤖 LLM analyzing ${candidates.length} candidates...`));
483
475
 
484
476
  const candidatesSummary = candidates
485
477
  .map((file, index) => {
@@ -520,7 +512,7 @@ Select files following the criteria in the system instructions.`;
520
512
  jsonSchema: fileSelectionSchema,
521
513
  });
522
514
 
523
- console.log(chalk.gray(' 📄 LLM Response preview:'), response.content.substring(0, 200));
515
+ verboseLog({}, chalk.gray(' 📄 LLM Response preview:'), response.content.substring(0, 200));
524
516
 
525
517
  const selectedPaths = response.json.selectedFiles;
526
518
 
@@ -546,14 +538,14 @@ Select files following the criteria in the system instructions.`;
546
538
  })
547
539
  .filter(Boolean);
548
540
 
549
- console.log(chalk.cyan(`🎯 LLM selected ${keyFiles.length} final key files`));
541
+ verboseLog({}, chalk.cyan(`🎯 LLM selected ${keyFiles.length} final key files`));
550
542
  return keyFiles;
551
543
  } else {
552
544
  throw new Error(`Failed to extract valid JSON array from LLM response`);
553
545
  }
554
546
  } catch (error) {
555
547
  console.error(chalk.red('Error in LLM selection:'), error.message);
556
- console.log(chalk.yellow(' 🔄 Falling back to automatic selection...'));
548
+ verboseLog({}, chalk.yellow(' 🔄 Falling back to automatic selection...'));
557
549
  return this.fallbackFileSelection(candidates, projectPath);
558
550
  }
559
551
  }
@@ -588,7 +580,7 @@ Select files following the criteria in the system instructions.`;
588
580
  }
589
581
  }
590
582
 
591
- console.log(chalk.yellow(`⚠️ Used fallback selection: ${fallbackFiles.length} files`));
583
+ verboseLog({}, chalk.yellow(`⚠️ Used fallback selection: ${fallbackFiles.length} files`));
592
584
  return fallbackFiles;
593
585
  }
594
586
 
@@ -793,7 +785,7 @@ Follow the analysis guidelines from the system instructions to identify custom i
793
785
  } catch (error) {
794
786
  console.error(chalk.red('Error generating project summary:'), error.message);
795
787
  const fallback = this.createFallbackSummary(projectPath, keyFiles);
796
- console.log(chalk.yellow('Using fallback summary with technologies:'), fallback.technologies);
788
+ verboseLog({}, chalk.yellow('Using fallback summary with technologies:'), fallback.technologies);
797
789
  return fallback;
798
790
  }
799
791
  }
@@ -857,7 +849,8 @@ Follow the analysis guidelines from the system instructions to identify custom i
857
849
  };
858
850
  }
859
851
 
860
- console.log(
852
+ verboseLog(
853
+ {},
861
854
  chalk.cyan(
862
855
  `✅ Project summary validated - Technologies: ${validatedSummary.technologies.length}, Frameworks: ${validatedSummary.mainFrameworks.length}`
863
856
  )
@@ -272,7 +272,6 @@ describe('ProjectAnalyzer', () => {
272
272
  await analyzer.storeAnalysis(mockProjectPath, summary);
273
273
 
274
274
  expect(mockEmbeddingsSystem.storeProjectSummary).toHaveBeenCalledWith(mockProjectPath, summary);
275
- expect(console.log).toHaveBeenCalledWith(expect.stringContaining('Project analysis stored'));
276
275
  });
277
276
 
278
277
  it('should handle storage errors gracefully', async () => {
@@ -319,7 +318,7 @@ describe('ProjectAnalyzer', () => {
319
318
  await analyzer.validateAndUpdateKeyFiles(existingFiles, mockProjectPath);
320
319
 
321
320
  // With 1 of 3 files found (33%), it should trigger fresh discovery
322
- expect(console.log).toHaveBeenCalledWith(expect.stringContaining('Many key files missing'));
321
+ expect(mockEmbeddingsSystem.initialize).toHaveBeenCalled();
323
322
  });
324
323
 
325
324
  it('should filter out missing files and keep existing ones', async () => {
@@ -366,7 +365,7 @@ describe('ProjectAnalyzer', () => {
366
365
  await analyzer.validateAndUpdateKeyFiles(existingFiles, mockProjectPath);
367
366
 
368
367
  // Should trigger discoverKeyFilesWithLLM
369
- expect(console.log).toHaveBeenCalledWith(expect.stringContaining('Many key files missing'));
368
+ expect(mockEmbeddingsSystem.initialize).toHaveBeenCalled();
370
369
  });
371
370
  });
372
371
 
@@ -410,7 +409,7 @@ describe('ProjectAnalyzer', () => {
410
409
  const result = await analyzer.mineKeyFilesFromEmbeddings(mockProjectPath);
411
410
 
412
411
  expect(result).toEqual([]);
413
- expect(console.log).toHaveBeenCalledWith(expect.stringContaining('Skipping optimization'));
412
+ expect(console.warn).toHaveBeenCalledWith(expect.stringContaining('legacy index format'));
414
413
  });
415
414
 
416
415
  it('should return empty array on query error', async () => {
@@ -496,7 +495,6 @@ describe('ProjectAnalyzer', () => {
496
495
  await analyzer.selectFinalKeyFiles(candidates, mockProjectPath);
497
496
 
498
497
  expect(console.error).toHaveBeenCalled();
499
- expect(console.log).toHaveBeenCalledWith(expect.stringContaining('Falling back to automatic selection'));
500
498
  });
501
499
 
502
500
  it('should fallback if LLM returns invalid response', async () => {