codecritique 1.2.1 → 1.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/content-retrieval.js +20 -16
- package/src/custom-documents.js +19 -19
- package/src/custom-documents.test.js +11 -9
- package/src/feedback-loader.js +31 -31
- package/src/index.js +99 -119
- package/src/llm.js +4 -3
- package/src/project-analyzer.js +32 -39
- package/src/project-analyzer.test.js +3 -5
- package/src/prompt-cache.js +1 -0
- package/src/rag-analyzer.js +199 -178
- package/src/rag-analyzer.test.js +57 -0
- package/src/rag-review.js +105 -74
- package/src/rag-review.test.js +115 -3
- package/src/zero-shot-classifier-open.js +3 -2
package/src/rag-analyzer.js
CHANGED
|
@@ -26,7 +26,8 @@ import { inferContextFromCodeContent, inferContextFromDocumentContent } from './
|
|
|
26
26
|
import { isGenericDocument, getGenericDocumentContext } from './utils/document-detection.js';
|
|
27
27
|
import { isTestFile, shouldProcessFile } from './utils/file-validation.js';
|
|
28
28
|
import { detectFileType, detectLanguageFromExtension } from './utils/language-detection.js';
|
|
29
|
-
import { debug } from './utils/logging.js';
|
|
29
|
+
import { debug, verboseLog } from './utils/logging.js';
|
|
30
|
+
import { addLineNumbers } from './utils/string-utils.js';
|
|
30
31
|
|
|
31
32
|
// Constants for content processing
|
|
32
33
|
const MAX_QUERY_CONTEXT_LENGTH = 1500;
|
|
@@ -55,7 +56,7 @@ async function ensureSemanticSimilarityInitialized() {
|
|
|
55
56
|
await initializeSemanticSimilarity();
|
|
56
57
|
semanticSimilarityInitialized = true;
|
|
57
58
|
} catch (error) {
|
|
58
|
-
console.
|
|
59
|
+
console.warn(chalk.yellow(`⚠️ Could not initialize semantic similarity: ${error.message}`));
|
|
59
60
|
// Continue without semantic similarity - word-based fallback will be used
|
|
60
61
|
}
|
|
61
62
|
}
|
|
@@ -215,7 +216,7 @@ ${ex.content}
|
|
|
215
216
|
* @param {string} projectPath - Project path
|
|
216
217
|
* @returns {Promise<Object|null>} Project summary or null
|
|
217
218
|
*/
|
|
218
|
-
async function getProjectSummary(projectPath) {
|
|
219
|
+
async function getProjectSummary(projectPath, options = {}) {
|
|
219
220
|
const resolvedPath = path.resolve(projectPath);
|
|
220
221
|
|
|
221
222
|
try {
|
|
@@ -223,7 +224,7 @@ async function getProjectSummary(projectPath) {
|
|
|
223
224
|
const summary = await embeddingsSystem.getProjectSummary(resolvedPath);
|
|
224
225
|
|
|
225
226
|
if (summary) {
|
|
226
|
-
|
|
227
|
+
verboseLog(options, chalk.cyan(`📋 Retrieved project summary for: ${path.basename(resolvedPath)}`));
|
|
227
228
|
}
|
|
228
229
|
|
|
229
230
|
return summary;
|
|
@@ -429,16 +430,16 @@ async function runAnalysis(filePath, options = {}) {
|
|
|
429
430
|
try {
|
|
430
431
|
// Check if this is a holistic PR review
|
|
431
432
|
if (options.isHolisticPRReview && filePath === 'PR_HOLISTIC_REVIEW') {
|
|
432
|
-
|
|
433
|
+
verboseLog(options, chalk.blue(`Performing holistic PR review for ${options.prFiles?.length || 0} files`));
|
|
433
434
|
return await performHolisticPRAnalysis(options);
|
|
434
435
|
}
|
|
435
436
|
|
|
436
|
-
|
|
437
|
+
verboseLog(options, chalk.blue(`Analyzing file: ${filePath}`));
|
|
437
438
|
|
|
438
439
|
// Load feedback data if feedback tracking is enabled
|
|
439
440
|
let feedbackData = {};
|
|
440
441
|
if (options.trackFeedback && options.feedbackPath) {
|
|
441
|
-
|
|
442
|
+
verboseLog(options, chalk.cyan('--- Loading Feedback Data ---'));
|
|
442
443
|
feedbackData = await loadFeedbackData(options.feedbackPath, { verbose: options.verbose });
|
|
443
444
|
}
|
|
444
445
|
|
|
@@ -454,16 +455,16 @@ async function runAnalysis(filePath, options = {}) {
|
|
|
454
455
|
content = options.diffContent;
|
|
455
456
|
// For PR reviews, always read the full file content for context awareness
|
|
456
457
|
fullFileContent = fs.existsSync(filePath) ? fs.readFileSync(filePath, 'utf8') : null;
|
|
457
|
-
|
|
458
|
+
verboseLog(options, chalk.blue(`Analyzing diff only for ${path.basename(filePath)}`));
|
|
458
459
|
} else {
|
|
459
460
|
content = fs.readFileSync(filePath, 'utf8');
|
|
460
461
|
fullFileContent = content;
|
|
461
|
-
|
|
462
|
+
verboseLog(options, chalk.blue(`Analyzing full file ${path.basename(filePath)}`));
|
|
462
463
|
}
|
|
463
464
|
|
|
464
465
|
// Check if file should be processed
|
|
465
466
|
if (!shouldProcessFile(filePath, content)) {
|
|
466
|
-
|
|
467
|
+
verboseLog(options, chalk.yellow(`Skipping file based on exclusion patterns: ${filePath}`));
|
|
467
468
|
return {
|
|
468
469
|
success: true,
|
|
469
470
|
skipped: true,
|
|
@@ -472,7 +473,7 @@ async function runAnalysis(filePath, options = {}) {
|
|
|
472
473
|
}
|
|
473
474
|
|
|
474
475
|
// --- Stage 1: CONTEXT RETRIEVAL ---
|
|
475
|
-
|
|
476
|
+
verboseLog(options, chalk.blue('--- Stage 1: Context Retrieval ---'));
|
|
476
477
|
const {
|
|
477
478
|
language,
|
|
478
479
|
isTestFile,
|
|
@@ -484,49 +485,49 @@ async function runAnalysis(filePath, options = {}) {
|
|
|
484
485
|
} = await getContextForFile(filePath, content, options);
|
|
485
486
|
|
|
486
487
|
// --- Stage 1.5: PROJECT ARCHITECTURE CONTEXT ---
|
|
487
|
-
|
|
488
|
+
verboseLog(options, chalk.blue('--- Stage 1.5: Retrieving Project Architecture Context ---'));
|
|
488
489
|
const projectPath = options.projectPath || process.cwd();
|
|
489
|
-
const projectSummary = await getProjectSummary(projectPath);
|
|
490
|
+
const projectSummary = await getProjectSummary(projectPath, options);
|
|
490
491
|
|
|
491
492
|
// --- Stage 2: PREPARE CONTEXT FOR LLM ---
|
|
492
|
-
|
|
493
|
+
verboseLog(options, chalk.blue('--- Stage 2: Preparing Context for LLM ---'));
|
|
493
494
|
|
|
494
495
|
// Format the lists that will be passed
|
|
495
496
|
const formattedCodeExamples = formatContextItems(finalCodeExamples, 'code');
|
|
496
497
|
const formattedGuidelines = formatContextItems(finalGuidelineSnippets, 'guideline');
|
|
497
498
|
|
|
498
499
|
// --- Log the context being sent to the LLM --- >
|
|
499
|
-
|
|
500
|
+
verboseLog(options, chalk.magenta('--- Guidelines Sent to LLM ---'));
|
|
500
501
|
if (formattedGuidelines.length > 0) {
|
|
501
502
|
formattedGuidelines.forEach((g, i) => {
|
|
502
|
-
|
|
503
|
-
|
|
503
|
+
verboseLog(options, chalk.magenta(` [${i + 1}] Path: ${g.path} ${g.headingText ? `(Heading: "${g.headingText}")` : ''}`));
|
|
504
|
+
verboseLog(options, chalk.gray(` Content: ${g.content.substring(0, 100).replace(/\\n/g, ' ')}...`));
|
|
504
505
|
});
|
|
505
506
|
} else {
|
|
506
|
-
|
|
507
|
+
verboseLog(options, chalk.magenta(' (None)'));
|
|
507
508
|
}
|
|
508
509
|
|
|
509
|
-
|
|
510
|
+
verboseLog(options, chalk.magenta('--- Code Examples Sent to LLM ---'));
|
|
510
511
|
if (finalCodeExamples.length > 0) {
|
|
511
512
|
finalCodeExamples.forEach((ex, i) => {
|
|
512
|
-
|
|
513
|
-
|
|
513
|
+
verboseLog(options, chalk.magenta(` [${i + 1}] Path: ${ex.path} (Similarity: ${ex.similarity?.toFixed(3) || 'N/A'})`));
|
|
514
|
+
verboseLog(options, chalk.gray(` Content: ${ex.content.substring(0, 100).replace(/\\n/g, ' ')}...`));
|
|
514
515
|
});
|
|
515
516
|
} else {
|
|
516
|
-
|
|
517
|
+
verboseLog(options, chalk.magenta(' (None)'));
|
|
517
518
|
}
|
|
518
519
|
|
|
519
|
-
|
|
520
|
+
verboseLog(options, chalk.magenta('--- Custom Document Chunks Sent to LLM ---'));
|
|
520
521
|
if (relevantCustomDocChunks && relevantCustomDocChunks.length > 0) {
|
|
521
522
|
relevantCustomDocChunks.forEach((chunk, i) => {
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
523
|
+
verboseLog(options, chalk.magenta(` [${i + 1}] Document: "${chunk.document_title}" (Chunk ${chunk.chunk_index + 1})`));
|
|
524
|
+
verboseLog(options, chalk.magenta(` Similarity: ${chunk.similarity?.toFixed(3) || 'N/A'}`));
|
|
525
|
+
verboseLog(options, chalk.gray(` Content: ${chunk.content.substring(0, 100).replace(/\\n/g, ' ')}...`));
|
|
525
526
|
});
|
|
526
527
|
} else {
|
|
527
|
-
|
|
528
|
+
verboseLog(options, chalk.magenta(' (None)'));
|
|
528
529
|
}
|
|
529
|
-
|
|
530
|
+
verboseLog(options, chalk.magenta('---------------------------------'));
|
|
530
531
|
// --- End Logging --->
|
|
531
532
|
|
|
532
533
|
// Prepare context for LLM with the potentially reduced lists
|
|
@@ -552,7 +553,7 @@ async function runAnalysis(filePath, options = {}) {
|
|
|
552
553
|
// Post-process results to filter dismissed issues
|
|
553
554
|
let filteredResults = lowSeverityFiltered;
|
|
554
555
|
if (options.trackFeedback && feedbackData && Object.keys(feedbackData).length > 0) {
|
|
555
|
-
|
|
556
|
+
verboseLog(options, chalk.cyan('--- Filtering Results Based on Feedback ---'));
|
|
556
557
|
filteredResults = await filterAnalysisResults(lowSeverityFiltered, feedbackData, {
|
|
557
558
|
similarityThreshold: options.feedbackThreshold || 0.7,
|
|
558
559
|
verbose: options.verbose,
|
|
@@ -758,11 +759,10 @@ async function callLLMForAnalysis(context, options = {}) {
|
|
|
758
759
|
cacheTtl: options.cacheTtl || '5m', // Pass cache TTL option (default: 5m, no extra cost)
|
|
759
760
|
});
|
|
760
761
|
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
console.log(chalk.gray(`Response content length: ${llmResponse?.content?.length || 0} characters`));
|
|
762
|
+
verboseLog(options, chalk.blue('Received LLM response, attempting to parse...'));
|
|
763
|
+
verboseLog(options, chalk.gray(`Response type: ${typeof llmResponse}`));
|
|
764
|
+
verboseLog(options, chalk.gray(`Response has json: ${!!llmResponse?.json}`));
|
|
765
|
+
verboseLog(options, chalk.gray(`Response content length: ${llmResponse?.content?.length || 0} characters`));
|
|
766
766
|
|
|
767
767
|
// Parse the raw LLM response
|
|
768
768
|
const analysisResponse = parseAnalysisResponse(llmResponse);
|
|
@@ -774,12 +774,12 @@ async function callLLMForAnalysis(context, options = {}) {
|
|
|
774
774
|
return {
|
|
775
775
|
summary: analysisResponse.summary || 'Analysis completed with parsing issues',
|
|
776
776
|
issues: Array.isArray(analysisResponse.issues) ? analysisResponse.issues : [],
|
|
777
|
-
rawResponse: analysisResponse.rawResponse || llmResponse.substring(0, 500),
|
|
777
|
+
rawResponse: analysisResponse.rawResponse || JSON.stringify(llmResponse).substring(0, 500),
|
|
778
778
|
parseWarning: 'Response structure was reconstructed due to parsing issues',
|
|
779
779
|
};
|
|
780
780
|
}
|
|
781
781
|
|
|
782
|
-
|
|
782
|
+
verboseLog(options, chalk.green('Successfully parsed LLM response with expected structure'));
|
|
783
783
|
return analysisResponse;
|
|
784
784
|
} catch (error) {
|
|
785
785
|
console.error(chalk.red(`Error calling LLM for analysis: ${error.message}`));
|
|
@@ -932,10 +932,10 @@ async function sendPromptToLLM(promptConfig, llmOptions) {
|
|
|
932
932
|
};
|
|
933
933
|
|
|
934
934
|
// Debug: Log prompt structure
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
935
|
+
verboseLog(llmOptions, chalk.gray(` Prompt config has systemPrompt: ${!!promptConfig.systemPrompt}`));
|
|
936
|
+
verboseLog(llmOptions, chalk.gray(` Prompt config has userPrompt: ${!!promptConfig.userPrompt}`));
|
|
937
|
+
verboseLog(llmOptions, chalk.gray(` System prompt length: ${promptConfig.systemPrompt?.length || 0} chars`));
|
|
938
|
+
verboseLog(llmOptions, chalk.gray(` User prompt length: ${promptConfig.userPrompt?.length || 0} chars`));
|
|
939
939
|
|
|
940
940
|
// Send prompt with system (cached) and user (dynamic) content
|
|
941
941
|
const response = await llm.sendPromptToClaude(promptConfig.userPrompt, {
|
|
@@ -983,17 +983,17 @@ function generateAnalysisPrompt(context) {
|
|
|
983
983
|
const { context: contextSections } = context;
|
|
984
984
|
let prHistorySection = '';
|
|
985
985
|
|
|
986
|
-
|
|
987
|
-
|
|
986
|
+
verboseLog(context.options, chalk.blue(`🔍 Checking for PR comments in prompt generation...`));
|
|
987
|
+
verboseLog(context.options, chalk.gray(`Context sections available: ${contextSections ? contextSections.length : 0}`));
|
|
988
988
|
|
|
989
989
|
if (contextSections && contextSections.length > 0) {
|
|
990
990
|
contextSections.forEach((section, idx) => {
|
|
991
|
-
|
|
991
|
+
verboseLog(context.options, chalk.gray(` Section ${idx + 1}: ${section.title} (${section.items?.length || 0} items)`));
|
|
992
992
|
});
|
|
993
993
|
|
|
994
994
|
const prComments = contextSections.find((section) => section.title === 'Historical Review Comments');
|
|
995
995
|
if (prComments && prComments.items.length > 0) {
|
|
996
|
-
|
|
996
|
+
verboseLog(context.options, chalk.green(`✅ Adding ${prComments.items.length} PR comments to LLM prompt`));
|
|
997
997
|
prHistorySection += `
|
|
998
998
|
|
|
999
999
|
CONTEXT C: HISTORICAL REVIEW COMMENTS
|
|
@@ -1012,12 +1012,12 @@ Similar code patterns and issues identified by human reviewers in past PRs
|
|
|
1012
1012
|
prHistorySection += `Use these historical patterns to identify DEFINITE issues in the current code. `;
|
|
1013
1013
|
prHistorySection += `Only report issues that EXACTLY match historical patterns with SPECIFIC code fixes.\n\n`;
|
|
1014
1014
|
|
|
1015
|
-
|
|
1015
|
+
verboseLog(context.options, chalk.blue(`PR History section preview: ${prHistorySection.substring(0, 200)}...`));
|
|
1016
1016
|
} else {
|
|
1017
|
-
|
|
1017
|
+
verboseLog(context.options, chalk.yellow(`❌ No PR comments section found in context`));
|
|
1018
1018
|
}
|
|
1019
1019
|
} else {
|
|
1020
|
-
|
|
1020
|
+
verboseLog(context.options, chalk.yellow(`❌ No context sections available for PR comments`));
|
|
1021
1021
|
}
|
|
1022
1022
|
|
|
1023
1023
|
// Detect if this is a diff review
|
|
@@ -1047,10 +1047,10 @@ You have access to TWO pieces of information:
|
|
|
1047
1047
|
- Do NOT flag functions/variables as missing if they exist elsewhere in the full file
|
|
1048
1048
|
- The unchanged code is part of the file - check it before making assumptions
|
|
1049
1049
|
|
|
1050
|
-
**FULL FILE CONTENT (for context - DO NOT review unchanged code):**
|
|
1050
|
+
**FULL FILE CONTENT (for context - DO NOT review unchanged code, line numbers shown for reference):**
|
|
1051
1051
|
|
|
1052
1052
|
\`\`\`${file.language}
|
|
1053
|
-
${file.fullFileContent || file.content}
|
|
1053
|
+
${addLineNumbers(file.fullFileContent || file.content)}
|
|
1054
1054
|
\`\`\`
|
|
1055
1055
|
|
|
1056
1056
|
**GIT DIFF TO REVIEW (critique ONLY these changes):**
|
|
@@ -1063,7 +1063,7 @@ Path: ${file.path}
|
|
|
1063
1063
|
Language: ${file.language}
|
|
1064
1064
|
|
|
1065
1065
|
\`\`\`${file.language}
|
|
1066
|
-
${file.content}
|
|
1066
|
+
${addLineNumbers(file.content)}
|
|
1067
1067
|
\`\`\``;
|
|
1068
1068
|
|
|
1069
1069
|
// Add project architecture context if available
|
|
@@ -1166,10 +1166,10 @@ You have access to TWO pieces of information:
|
|
|
1166
1166
|
- Check the full file for existing test cases before making assumptions
|
|
1167
1167
|
- The unchanged test code is part of the file - review it before suggesting additions
|
|
1168
1168
|
|
|
1169
|
-
**FULL TEST FILE CONTENT (for context - check for existing tests):**
|
|
1169
|
+
**FULL TEST FILE CONTENT (for context - check for existing tests, line numbers shown for reference):**
|
|
1170
1170
|
|
|
1171
1171
|
\`\`\`${file.language}
|
|
1172
|
-
${file.fullFileContent || file.content}
|
|
1172
|
+
${addLineNumbers(file.fullFileContent || file.content)}
|
|
1173
1173
|
\`\`\`
|
|
1174
1174
|
|
|
1175
1175
|
**GIT DIFF TO REVIEW (critique ONLY these changes):**
|
|
@@ -1182,7 +1182,7 @@ Path: ${file.path}
|
|
|
1182
1182
|
Language: ${file.language}
|
|
1183
1183
|
|
|
1184
1184
|
\`\`\`${file.language}
|
|
1185
|
-
${file.content}
|
|
1185
|
+
${addLineNumbers(file.content)}
|
|
1186
1186
|
\`\`\``;
|
|
1187
1187
|
|
|
1188
1188
|
// Use shared helpers for custom docs and role definition
|
|
@@ -1317,9 +1317,9 @@ ${g.content}
|
|
|
1317
1317
|
${prFile.diff}
|
|
1318
1318
|
\`\`\`
|
|
1319
1319
|
|
|
1320
|
-
### Full File Content (For Context):
|
|
1320
|
+
### Full File Content (For Context - line numbers shown for reference):
|
|
1321
1321
|
\`\`\`${prFile.language}
|
|
1322
|
-
${prFile.fullContent}
|
|
1322
|
+
${addLineNumbers(prFile.fullContent)}
|
|
1323
1323
|
\`\`\`
|
|
1324
1324
|
`;
|
|
1325
1325
|
})
|
|
@@ -1470,7 +1470,7 @@ async function getPRCommentContext(filePath, options = {}) {
|
|
|
1470
1470
|
let contentForSearch = '';
|
|
1471
1471
|
|
|
1472
1472
|
if (precomputedQueryEmbedding) {
|
|
1473
|
-
|
|
1473
|
+
verboseLog(options, chalk.blue(`🔍 Using pre-computed query embedding for PR comment search`));
|
|
1474
1474
|
// We still need the file content for the search function, but not for embedding
|
|
1475
1475
|
try {
|
|
1476
1476
|
fileContent = fs.readFileSync(filePath, 'utf8');
|
|
@@ -1512,39 +1512,39 @@ async function getPRCommentContext(filePath, options = {}) {
|
|
|
1512
1512
|
// Use semantic search to find similar PR comments
|
|
1513
1513
|
let relevantComments = [];
|
|
1514
1514
|
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
console.log(chalk.gray(` Using Pre-computed Embedding: ${precomputedQueryEmbedding ? 'Yes' : 'No'}`));
|
|
1515
|
+
verboseLog(options, chalk.blue(`🔍 Searching for PR comments with:`));
|
|
1516
|
+
verboseLog(options, chalk.gray(` Project Path: ${projectPath}`));
|
|
1517
|
+
verboseLog(options, chalk.gray(` File: ${fileName}`));
|
|
1518
|
+
verboseLog(options, chalk.gray(` Similarity Threshold: ${similarityThreshold}`));
|
|
1519
|
+
verboseLog(options, chalk.gray(` Content Length: ${contentForSearch.length} chars`));
|
|
1520
|
+
verboseLog(options, chalk.gray(` Using Pre-computed Embedding: ${precomputedQueryEmbedding ? 'Yes' : 'No'}`));
|
|
1522
1521
|
|
|
1523
1522
|
try {
|
|
1524
|
-
|
|
1523
|
+
verboseLog(options, chalk.blue(`🔍 Attempting hybrid search with chunking...`));
|
|
1525
1524
|
relevantComments = await findRelevantPRComments(contentForSearch, {
|
|
1526
1525
|
projectPath,
|
|
1527
1526
|
limit: maxComments,
|
|
1528
1527
|
isTestFile: isTest, // Pass test file context for filtering
|
|
1529
1528
|
precomputedQueryEmbedding: precomputedQueryEmbedding, // Pass pre-computed embedding if available
|
|
1530
1529
|
});
|
|
1531
|
-
|
|
1530
|
+
verboseLog(options, chalk.green(`✅ Hybrid search returned ${relevantComments.length} comments`));
|
|
1532
1531
|
if (relevantComments.length > 0) {
|
|
1533
|
-
|
|
1532
|
+
verboseLog(options, chalk.blue(`Top comment similarities:`));
|
|
1534
1533
|
relevantComments.slice(0, 3).forEach((comment, idx) => {
|
|
1535
|
-
|
|
1534
|
+
verboseLog(
|
|
1535
|
+
options,
|
|
1536
1536
|
chalk.gray(` ${idx + 1}. Score: ${comment.similarity_score?.toFixed(3)} - ${comment.comment_text?.substring(0, 80)}...`)
|
|
1537
1537
|
);
|
|
1538
1538
|
});
|
|
1539
1539
|
}
|
|
1540
1540
|
} catch (dbError) {
|
|
1541
|
-
console.
|
|
1541
|
+
console.warn(chalk.yellow(`⚠️ Hybrid search failed: ${dbError.message}`));
|
|
1542
1542
|
debug(`[getPRCommentContext] Hybrid search failed: ${dbError.message}`);
|
|
1543
1543
|
// No fallback needed - if hybrid search fails, we just return empty results
|
|
1544
1544
|
relevantComments = [];
|
|
1545
1545
|
}
|
|
1546
1546
|
|
|
1547
|
-
|
|
1547
|
+
verboseLog(options, 'Total relevant comments number:', relevantComments.length);
|
|
1548
1548
|
|
|
1549
1549
|
// Extract patterns and insights
|
|
1550
1550
|
const patterns = extractCommentPatterns(relevantComments);
|
|
@@ -1673,12 +1673,12 @@ async function performHolisticPRAnalysis(options) {
|
|
|
1673
1673
|
try {
|
|
1674
1674
|
const { prFiles, unifiedContext, customDocs } = options;
|
|
1675
1675
|
|
|
1676
|
-
|
|
1676
|
+
verboseLog(options, chalk.blue(`🔍 Performing holistic analysis of ${prFiles.length} files with unified context...`));
|
|
1677
1677
|
|
|
1678
1678
|
// Retrieve project architecture summary
|
|
1679
|
-
|
|
1679
|
+
verboseLog(options, chalk.blue('--- Retrieving Project Architecture Context for Holistic PR Review ---'));
|
|
1680
1680
|
const projectPath = options.projectPath || process.cwd();
|
|
1681
|
-
const projectSummary = await getProjectSummary(projectPath);
|
|
1681
|
+
const projectSummary = await getProjectSummary(projectPath, options);
|
|
1682
1682
|
|
|
1683
1683
|
// Create a synthetic file context for holistic analysis
|
|
1684
1684
|
const holisticContext = {
|
|
@@ -1726,56 +1726,58 @@ async function performHolisticPRAnalysis(options) {
|
|
|
1726
1726
|
};
|
|
1727
1727
|
|
|
1728
1728
|
// Add verbose debug logging similar to individual file reviews
|
|
1729
|
-
|
|
1729
|
+
verboseLog(options, chalk.magenta('--- Holistic PR Review: Guidelines Sent to LLM ---'));
|
|
1730
1730
|
if (unifiedContext.guidelines.length > 0) {
|
|
1731
1731
|
unifiedContext.guidelines.slice(0, 10).forEach((g, i) => {
|
|
1732
|
-
|
|
1732
|
+
verboseLog(
|
|
1733
|
+
options,
|
|
1733
1734
|
chalk.magenta(
|
|
1734
1735
|
` [${i + 1}] Path: ${g.path} ${g.headingText || g.heading_text ? `(Heading: "${g.headingText || g.heading_text}")` : ''}`
|
|
1735
1736
|
)
|
|
1736
1737
|
);
|
|
1737
|
-
|
|
1738
|
+
verboseLog(options, chalk.gray(` Content: ${g.content.substring(0, 100).replace(/\n/g, ' ')}...`));
|
|
1738
1739
|
});
|
|
1739
1740
|
} else {
|
|
1740
|
-
|
|
1741
|
+
verboseLog(options, chalk.magenta(' (None)'));
|
|
1741
1742
|
}
|
|
1742
1743
|
|
|
1743
|
-
|
|
1744
|
+
verboseLog(options, chalk.magenta('--- Holistic PR Review: Code Examples Sent to LLM ---'));
|
|
1744
1745
|
if (unifiedContext.codeExamples.length > 0) {
|
|
1745
1746
|
unifiedContext.codeExamples.slice(0, 10).forEach((ex, i) => {
|
|
1746
|
-
|
|
1747
|
-
|
|
1747
|
+
verboseLog(options, chalk.magenta(` [${i + 1}] Path: ${ex.path} (Similarity: ${ex.similarity?.toFixed(3) || 'N/A'})`));
|
|
1748
|
+
verboseLog(options, chalk.gray(` Content: ${ex.content.substring(0, 100).replace(/\\n/g, ' ')}...`));
|
|
1748
1749
|
});
|
|
1749
1750
|
} else {
|
|
1750
|
-
|
|
1751
|
+
verboseLog(options, chalk.magenta(' (None)'));
|
|
1751
1752
|
}
|
|
1752
1753
|
|
|
1753
|
-
|
|
1754
|
+
verboseLog(options, chalk.magenta('--- Holistic PR Review: Top Historic Comments Sent to LLM ---'));
|
|
1754
1755
|
if (unifiedContext.prComments.length > 0) {
|
|
1755
1756
|
unifiedContext.prComments.slice(0, 5).forEach((comment, i) => {
|
|
1756
|
-
|
|
1757
|
+
verboseLog(
|
|
1758
|
+
options,
|
|
1757
1759
|
chalk.magenta(
|
|
1758
1760
|
` [${i + 1}] PR #${comment.prNumber} by ${comment.author} (Relevance: ${(comment.relevanceScore * 100).toFixed(1)}%)`
|
|
1759
1761
|
)
|
|
1760
1762
|
);
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
+
verboseLog(options, chalk.gray(` File: ${comment.filePath}`));
|
|
1764
|
+
verboseLog(options, chalk.gray(` Comment: ${comment.body.substring(0, 100).replace(/\n/g, ' ')}...`));
|
|
1763
1765
|
});
|
|
1764
1766
|
} else {
|
|
1765
|
-
|
|
1767
|
+
verboseLog(options, chalk.magenta(' (None)'));
|
|
1766
1768
|
}
|
|
1767
1769
|
|
|
1768
|
-
|
|
1770
|
+
verboseLog(options, chalk.magenta('--- Holistic PR Review: Custom Document Chunks Sent to LLM ---'));
|
|
1769
1771
|
if (unifiedContext.customDocChunks && unifiedContext.customDocChunks.length > 0) {
|
|
1770
1772
|
unifiedContext.customDocChunks.forEach((chunk, i) => {
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1773
|
+
verboseLog(options, chalk.magenta(` [${i + 1}] Document: "${chunk.document_title}" (Chunk ${chunk.chunk_index + 1})`));
|
|
1774
|
+
verboseLog(options, chalk.gray(` Similarity: ${chunk.similarity?.toFixed(3) || 'N/A'}`));
|
|
1775
|
+
verboseLog(options, chalk.gray(` Content: ${chunk.content.substring(0, 100).replace(/\n/g, ' ')}...`));
|
|
1774
1776
|
});
|
|
1775
1777
|
} else {
|
|
1776
|
-
|
|
1778
|
+
verboseLog(options, chalk.magenta(' (None)'));
|
|
1777
1779
|
}
|
|
1778
|
-
|
|
1780
|
+
verboseLog(options, chalk.magenta('--- Sending Holistic PR Analysis Prompt to LLM ---'));
|
|
1779
1781
|
|
|
1780
1782
|
// Call the centralized analysis function
|
|
1781
1783
|
const parsedResponse = await callLLMForAnalysis(holisticContext, {
|
|
@@ -1784,11 +1786,11 @@ async function performHolisticPRAnalysis(options) {
|
|
|
1784
1786
|
});
|
|
1785
1787
|
|
|
1786
1788
|
// Debug logging
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1789
|
+
verboseLog(options, chalk.blue(`🐛 Holistic analysis parsed response:`));
|
|
1790
|
+
verboseLog(options, chalk.gray(`Summary: ${parsedResponse.summary?.substring(0, 100)}...`));
|
|
1791
|
+
verboseLog(options, chalk.gray(`Cross-file issues: ${parsedResponse.crossFileIssues?.length || 0}`));
|
|
1792
|
+
verboseLog(options, chalk.gray(`File-specific issues keys: ${Object.keys(parsedResponse.fileSpecificIssues || {}).join(', ')}`));
|
|
1793
|
+
verboseLog(options, chalk.gray(`Recommendations: ${parsedResponse.recommendations?.length || 0}`));
|
|
1792
1794
|
|
|
1793
1795
|
// Filter out low severity issues (formatting/style concerns handled by linters)
|
|
1794
1796
|
// Note: The LLM prompt instructs not to generate low severity issues, but this filter
|
|
@@ -1880,12 +1882,15 @@ async function getContextForFile(filePath, content, options = {}) {
|
|
|
1880
1882
|
guidelineQueryEmbedding = await embeddingsSystem.calculateQueryEmbedding(guidelineQuery);
|
|
1881
1883
|
}
|
|
1882
1884
|
|
|
1883
|
-
|
|
1885
|
+
verboseLog(options, chalk.blue('� Starting parallel context retrieval...'));
|
|
1884
1886
|
// Helper function to process custom documents in parallel (with caching)
|
|
1885
1887
|
const processCustomDocuments = async () => {
|
|
1886
1888
|
// Check if preprocessed chunks are available (from PR-level processing)
|
|
1887
1889
|
if (options.preprocessedCustomDocChunks && options.preprocessedCustomDocChunks.length > 0) {
|
|
1888
|
-
|
|
1890
|
+
verboseLog(
|
|
1891
|
+
options,
|
|
1892
|
+
chalk.blue(`📄 Using preprocessed custom document chunks (${options.preprocessedCustomDocChunks.length} available)`)
|
|
1893
|
+
);
|
|
1889
1894
|
|
|
1890
1895
|
// Use the guideline query for finding relevant custom document chunks
|
|
1891
1896
|
const relevantChunks = await embeddingsSystem.findRelevantCustomDocChunks(guidelineQuery, options.preprocessedCustomDocChunks, {
|
|
@@ -1897,15 +1902,15 @@ async function getContextForFile(filePath, content, options = {}) {
|
|
|
1897
1902
|
queryFilePath: filePath,
|
|
1898
1903
|
});
|
|
1899
1904
|
|
|
1900
|
-
|
|
1905
|
+
verboseLog(options, chalk.green(`📄 Found ${relevantChunks.length} relevant custom document chunks`));
|
|
1901
1906
|
|
|
1902
1907
|
// Log which chunks made the cut
|
|
1903
1908
|
if (relevantChunks.length > 0) {
|
|
1904
|
-
|
|
1909
|
+
verboseLog(options, chalk.cyan('📋 Custom Document Chunks Selected:'));
|
|
1905
1910
|
relevantChunks.forEach((chunk, i) => {
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1911
|
+
verboseLog(options, chalk.cyan(` [${i + 1}] "${chunk.document_title}" (Chunk ${chunk.chunk_index + 1})`));
|
|
1912
|
+
verboseLog(options, chalk.gray(` Similarity: ${chunk.similarity?.toFixed(3) || 'N/A'}`));
|
|
1913
|
+
verboseLog(options, chalk.gray(` Content: ${chunk.content.substring(0, 80).replace(/\n/g, ' ')}...`));
|
|
1909
1914
|
});
|
|
1910
1915
|
}
|
|
1911
1916
|
|
|
@@ -1918,17 +1923,17 @@ async function getContextForFile(filePath, content, options = {}) {
|
|
|
1918
1923
|
}
|
|
1919
1924
|
|
|
1920
1925
|
try {
|
|
1921
|
-
|
|
1926
|
+
verboseLog(options, chalk.blue('📄 Processing custom documents for context...'));
|
|
1922
1927
|
|
|
1923
1928
|
// Check if custom documents are already processed for this project
|
|
1924
1929
|
let processedChunks = await checkExistingCustomDocumentChunks(projectPath);
|
|
1925
1930
|
|
|
1926
1931
|
if (!processedChunks || processedChunks.length === 0) {
|
|
1927
|
-
|
|
1932
|
+
verboseLog(options, chalk.cyan('📄 Custom documents not yet processed for this project, processing now...'));
|
|
1928
1933
|
// Process custom documents into chunks (only if not already processed)
|
|
1929
1934
|
processedChunks = await embeddingsSystem.processCustomDocumentsInMemory(options.customDocs, projectPath);
|
|
1930
1935
|
} else {
|
|
1931
|
-
|
|
1936
|
+
verboseLog(options, chalk.green(`📄 Reusing ${processedChunks.length} already processed custom document chunks`));
|
|
1932
1937
|
}
|
|
1933
1938
|
|
|
1934
1939
|
if (processedChunks.length > 0) {
|
|
@@ -1942,15 +1947,15 @@ async function getContextForFile(filePath, content, options = {}) {
|
|
|
1942
1947
|
queryFilePath: filePath,
|
|
1943
1948
|
});
|
|
1944
1949
|
|
|
1945
|
-
|
|
1950
|
+
verboseLog(options, chalk.green(`📄 Found ${relevantChunks.length} relevant custom document chunks`));
|
|
1946
1951
|
|
|
1947
1952
|
// Log which chunks made the cut
|
|
1948
1953
|
if (relevantChunks.length > 0) {
|
|
1949
|
-
|
|
1954
|
+
verboseLog(options, chalk.cyan('📋 Custom Document Chunks Selected:'));
|
|
1950
1955
|
relevantChunks.forEach((chunk, i) => {
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1956
|
+
verboseLog(options, chalk.cyan(` [${i + 1}] "${chunk.document_title}" (Chunk ${chunk.chunk_index + 1})`));
|
|
1957
|
+
verboseLog(options, chalk.gray(` Similarity: ${chunk.similarity?.toFixed(3) || 'N/A'}`));
|
|
1958
|
+
verboseLog(options, chalk.gray(` Content: ${chunk.content.substring(0, 80).replace(/\n/g, ' ')}...`));
|
|
1954
1959
|
});
|
|
1955
1960
|
}
|
|
1956
1961
|
|
|
@@ -1969,49 +1974,67 @@ async function getContextForFile(filePath, content, options = {}) {
|
|
|
1969
1974
|
// Use the statically imported function
|
|
1970
1975
|
return await embeddingsSystem.getExistingCustomDocumentChunks(projectPath);
|
|
1971
1976
|
} catch {
|
|
1972
|
-
|
|
1977
|
+
verboseLog(options, chalk.gray('No existing custom document chunks found, will process from scratch'));
|
|
1973
1978
|
return [];
|
|
1974
1979
|
}
|
|
1975
1980
|
};
|
|
1976
1981
|
|
|
1982
|
+
const withContextFallback = async (promise, fallbackValue, label) => {
|
|
1983
|
+
try {
|
|
1984
|
+
return await promise;
|
|
1985
|
+
} catch (error) {
|
|
1986
|
+
console.warn(chalk.yellow(`${label} failed: ${error.message}`));
|
|
1987
|
+
return fallbackValue;
|
|
1988
|
+
}
|
|
1989
|
+
};
|
|
1990
|
+
|
|
1977
1991
|
const [prContextResult, guidelineCandidates, codeExampleCandidates, relevantCustomDocChunks] = await Promise.all([
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
1992
|
+
withContextFallback(
|
|
1993
|
+
getPRCommentContext(filePath, {
|
|
1994
|
+
...options,
|
|
1995
|
+
projectPath,
|
|
1996
|
+
precomputedQueryEmbedding: fileContentQueryEmbedding,
|
|
1997
|
+
maxComments: MAX_PR_COMMENTS_FOR_CONTEXT,
|
|
1998
|
+
similarityThreshold: options.prSimilarityThreshold || 0.3,
|
|
1999
|
+
timeout: options.prTimeout || 300000,
|
|
2000
|
+
repository: options.repository || null,
|
|
2001
|
+
}),
|
|
2002
|
+
{ comments: [] },
|
|
2003
|
+
'PR comment context retrieval'
|
|
2004
|
+
),
|
|
2005
|
+
withContextFallback(
|
|
2006
|
+
embeddingsSystem.findRelevantDocs(guidelineQuery, {
|
|
2007
|
+
...options,
|
|
2008
|
+
projectPath,
|
|
2009
|
+
precomputedQueryEmbedding: guidelineQueryEmbedding,
|
|
2010
|
+
limit: GUIDELINE_CANDIDATE_LIMIT,
|
|
2011
|
+
similarityThreshold: 0.05,
|
|
2012
|
+
useReranking: true,
|
|
2013
|
+
queryContextForReranking: reviewedSnippetContext,
|
|
2014
|
+
}),
|
|
2015
|
+
[],
|
|
2016
|
+
'Guideline retrieval'
|
|
2017
|
+
),
|
|
2018
|
+
withContextFallback(
|
|
2019
|
+
embeddingsSystem.findSimilarCode(isTestFile ? `${content}\\n// Looking for similar test files and testing patterns` : content, {
|
|
2020
|
+
...options,
|
|
2021
|
+
projectPath,
|
|
2022
|
+
isTestFile,
|
|
2023
|
+
precomputedQueryEmbedding: fileContentQueryEmbedding,
|
|
2024
|
+
limit: CODE_EXAMPLE_LIMIT,
|
|
2025
|
+
similarityThreshold: 0.3,
|
|
2026
|
+
queryFilePath: filePath,
|
|
2027
|
+
includeProjectStructure: false,
|
|
2028
|
+
}),
|
|
2029
|
+
[],
|
|
2030
|
+
'Code example retrieval'
|
|
2031
|
+
),
|
|
2032
|
+
withContextFallback(processCustomDocuments(), [], 'Custom document retrieval'),
|
|
2033
|
+
]);
|
|
2011
2034
|
|
|
2012
2035
|
const prCommentContext = prContextResult?.comments || [];
|
|
2013
2036
|
const prContextAvailable = prCommentContext.length > 0;
|
|
2014
|
-
|
|
2037
|
+
verboseLog(options, chalk.green(`✅ Found ${prCommentContext.length} relevant PR comments`));
|
|
2015
2038
|
|
|
2016
2039
|
const documentChunks = Array.isArray(guidelineCandidates) ? guidelineCandidates.filter((c) => c.type === 'documentation-chunk') : [];
|
|
2017
2040
|
const chunksByDocument = new Map();
|
|
@@ -2173,21 +2196,21 @@ async function gatherUnifiedContextForPR(prFiles, options = {}) {
|
|
|
2173
2196
|
let globalCustomDocChunks = [];
|
|
2174
2197
|
if (options.customDocs && options.customDocs.length > 0) {
|
|
2175
2198
|
const projectPath = options.projectPath || process.cwd();
|
|
2176
|
-
|
|
2199
|
+
verboseLog(options, chalk.blue('📄 Processing custom documents once for entire PR...'));
|
|
2177
2200
|
|
|
2178
2201
|
try {
|
|
2179
2202
|
// Check if custom documents are already processed for this project
|
|
2180
2203
|
let processedChunks = await embeddingsSystem.getExistingCustomDocumentChunks(projectPath);
|
|
2181
2204
|
|
|
2182
2205
|
if (!processedChunks || processedChunks.length === 0) {
|
|
2183
|
-
|
|
2206
|
+
verboseLog(options, chalk.cyan('📄 Custom documents not yet processed for this project, processing now...'));
|
|
2184
2207
|
processedChunks = await embeddingsSystem.processCustomDocumentsInMemory(options.customDocs, projectPath);
|
|
2185
2208
|
} else {
|
|
2186
|
-
|
|
2209
|
+
verboseLog(options, chalk.green(`📄 Reusing ${processedChunks.length} already processed custom document chunks`));
|
|
2187
2210
|
}
|
|
2188
2211
|
|
|
2189
2212
|
globalCustomDocChunks = processedChunks;
|
|
2190
|
-
|
|
2213
|
+
verboseLog(options, chalk.green(`📄 Custom documents processed: ${globalCustomDocChunks.length} chunks available for PR analysis`));
|
|
2191
2214
|
} catch (error) {
|
|
2192
2215
|
console.error(chalk.red(`Error processing custom documents for PR: ${error.message}`));
|
|
2193
2216
|
}
|
|
@@ -2287,6 +2310,7 @@ async function gatherUnifiedContextForPR(prFiles, options = {}) {
|
|
|
2287
2310
|
*
|
|
2288
2311
|
* @param {Object} analysisResults - Analysis results from LLM
|
|
2289
2312
|
* @param {Object} options - Filtering options
|
|
2313
|
+
* @param {boolean} [options.verbose=false] - Enable verbose logging for filtered issues
|
|
2290
2314
|
* @returns {Object} Filtered analysis results without low severity issues
|
|
2291
2315
|
*/
|
|
2292
2316
|
function filterLowSeverityIssues(analysisResults, options = {}) {
|
|
@@ -2304,9 +2328,7 @@ function filterLowSeverityIssues(analysisResults, options = {}) {
|
|
|
2304
2328
|
analysisResults.issues = analysisResults.issues.filter((issue) => {
|
|
2305
2329
|
const severity = (issue.severity || '').toLowerCase();
|
|
2306
2330
|
if (severity === 'low') {
|
|
2307
|
-
|
|
2308
|
-
console.log(chalk.yellow(` Filtering low severity issue: "${(issue.description || '').substring(0, 50)}..."`));
|
|
2309
|
-
}
|
|
2331
|
+
verboseLog(verbose, chalk.yellow(` Filtering low severity issue: "${(issue.description || '').substring(0, 50)}..."`));
|
|
2310
2332
|
return false;
|
|
2311
2333
|
}
|
|
2312
2334
|
return true;
|
|
@@ -2320,11 +2342,10 @@ function filterLowSeverityIssues(analysisResults, options = {}) {
|
|
|
2320
2342
|
analysisResults.crossFileIssues = analysisResults.crossFileIssues.filter((issue) => {
|
|
2321
2343
|
const severity = (issue.severity || '').toLowerCase();
|
|
2322
2344
|
if (severity === 'low') {
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
}
|
|
2345
|
+
verboseLog(
|
|
2346
|
+
verbose,
|
|
2347
|
+
chalk.yellow(` Filtering low severity cross-file issue: "${(issue.message || issue.description || '').substring(0, 50)}..."`)
|
|
2348
|
+
);
|
|
2328
2349
|
return false;
|
|
2329
2350
|
}
|
|
2330
2351
|
return true;
|
|
@@ -2341,11 +2362,10 @@ function filterLowSeverityIssues(analysisResults, options = {}) {
|
|
|
2341
2362
|
analysisResults.fileSpecificIssues[filePath] = issues.filter((issue) => {
|
|
2342
2363
|
const severity = (issue.severity || '').toLowerCase();
|
|
2343
2364
|
if (severity === 'low') {
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
}
|
|
2365
|
+
verboseLog(
|
|
2366
|
+
verbose,
|
|
2367
|
+
chalk.yellow(` Filtering low severity issue in ${filePath}: "${(issue.description || '').substring(0, 50)}..."`)
|
|
2368
|
+
);
|
|
2349
2369
|
return false;
|
|
2350
2370
|
}
|
|
2351
2371
|
return true;
|
|
@@ -2355,9 +2375,10 @@ function filterLowSeverityIssues(analysisResults, options = {}) {
|
|
|
2355
2375
|
}
|
|
2356
2376
|
}
|
|
2357
2377
|
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2378
|
+
verboseLog(
|
|
2379
|
+
verbose && filteredCount > 0,
|
|
2380
|
+
chalk.cyan(`🔇 Filtered ${filteredCount} low severity issue(s) (formatting/style concerns handled by linters)`)
|
|
2381
|
+
);
|
|
2361
2382
|
|
|
2362
2383
|
return analysisResults;
|
|
2363
2384
|
}
|
|
@@ -2368,6 +2389,8 @@ function filterLowSeverityIssues(analysisResults, options = {}) {
|
|
|
2368
2389
|
* @param {Object} analysisResults - Raw analysis results from LLM
|
|
2369
2390
|
* @param {Object} feedbackData - Loaded feedback data
|
|
2370
2391
|
* @param {Object} options - Filtering options
|
|
2392
|
+
* @param {number} [options.similarityThreshold=0.7] - Threshold for considering issues similar to dismissed feedback
|
|
2393
|
+
* @param {boolean} [options.verbose=false] - Enable verbose similarity and filtering logs
|
|
2371
2394
|
* @returns {Promise<Object>} Filtered analysis results
|
|
2372
2395
|
*/
|
|
2373
2396
|
async function filterAnalysisResults(analysisResults, feedbackData, options = {}) {
|
|
@@ -2383,12 +2406,11 @@ async function filterAnalysisResults(analysisResults, feedbackData, options = {}
|
|
|
2383
2406
|
await ensureSemanticSimilarityInitialized();
|
|
2384
2407
|
|
|
2385
2408
|
// Log whether semantic similarity is available
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
}
|
|
2409
|
+
const usingSemanticSimilarity = isSemanticSimilarityAvailable();
|
|
2410
|
+
verboseLog(
|
|
2411
|
+
verbose,
|
|
2412
|
+
chalk.cyan(`🔍 Filtering issues using ${usingSemanticSimilarity ? 'semantic + word-based similarity' : 'word-based similarity only'}`)
|
|
2413
|
+
);
|
|
2392
2414
|
|
|
2393
2415
|
// Filter issues based on feedback (now async due to semantic similarity)
|
|
2394
2416
|
const filterResults = await Promise.all(
|
|
@@ -2399,9 +2421,7 @@ async function filterAnalysisResults(analysisResults, feedbackData, options = {}
|
|
|
2399
2421
|
verbose,
|
|
2400
2422
|
});
|
|
2401
2423
|
|
|
2402
|
-
|
|
2403
|
-
console.log(chalk.yellow(` Filtered issue ${index + 1}: "${issueDescription.substring(0, 50)}..."`));
|
|
2404
|
-
}
|
|
2424
|
+
verboseLog(shouldSkip && verbose, chalk.yellow(` Filtered issue ${index + 1}: "${issueDescription.substring(0, 50)}..."`));
|
|
2405
2425
|
|
|
2406
2426
|
return { issue, shouldSkip };
|
|
2407
2427
|
})
|
|
@@ -2411,9 +2431,10 @@ async function filterAnalysisResults(analysisResults, feedbackData, options = {}
|
|
|
2411
2431
|
|
|
2412
2432
|
const filteredCount = originalCount - filteredIssues.length;
|
|
2413
2433
|
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2434
|
+
verboseLog(
|
|
2435
|
+
verbose && filteredCount > 0,
|
|
2436
|
+
chalk.green(`✅ Filtered ${filteredCount} dismissed issues, ${filteredIssues.length} remaining`)
|
|
2437
|
+
);
|
|
2417
2438
|
|
|
2418
2439
|
return {
|
|
2419
2440
|
...analysisResults,
|