codecritique 2.0.7 → 2.0.8
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/project-analyzer.js +26 -3
- package/src/rag-analyzer.js +23 -5
package/package.json
CHANGED
package/src/project-analyzer.js
CHANGED
|
@@ -405,8 +405,25 @@ export class ProjectAnalyzer {
|
|
|
405
405
|
return depthA - depthB;
|
|
406
406
|
});
|
|
407
407
|
|
|
408
|
-
//
|
|
409
|
-
|
|
408
|
+
// Dedupe by path before capping: transient duplicate rows can exist for
|
|
409
|
+
// the same file (different content_hash) when a partial run hasn't pruned
|
|
410
|
+
// stale embeddings, and they would otherwise consume candidate slots and
|
|
411
|
+
// bloat the content query. Keep the shallowest occurrence (allFiles is
|
|
412
|
+
// already depth-sorted), then take the first 500 distinct shallow files.
|
|
413
|
+
const seenPaths = new Set();
|
|
414
|
+
const sortedFiles = [];
|
|
415
|
+
for (const file of allFiles) {
|
|
416
|
+
if (file.path) {
|
|
417
|
+
if (seenPaths.has(file.path)) {
|
|
418
|
+
continue;
|
|
419
|
+
}
|
|
420
|
+
seenPaths.add(file.path);
|
|
421
|
+
}
|
|
422
|
+
sortedFiles.push(file);
|
|
423
|
+
if (sortedFiles.length >= TERM_SEARCH_CONTENT_CANDIDATE_LIMIT) {
|
|
424
|
+
break;
|
|
425
|
+
}
|
|
426
|
+
}
|
|
410
427
|
if (sortedFiles.length === 0) {
|
|
411
428
|
return [];
|
|
412
429
|
}
|
|
@@ -415,14 +432,20 @@ export class ProjectAnalyzer {
|
|
|
415
432
|
const contentByPath = new Map();
|
|
416
433
|
if (candidatePaths.length > 0) {
|
|
417
434
|
const pathList = candidatePaths.map((candidatePath) => `'${escapeSqlString(candidatePath)}'`).join(', ');
|
|
435
|
+
// No row limit here: the result set is already bounded by the IN list of
|
|
436
|
+
// distinct candidate paths. A limit could truncate rows when duplicate
|
|
437
|
+
// rows exist for a path, silently dropping content for matched files.
|
|
418
438
|
const contentResults = await table
|
|
419
439
|
.query()
|
|
420
440
|
.select(['path', 'content'])
|
|
421
441
|
.where(`${projectPathFilter} AND path IN (${pathList})`)
|
|
422
|
-
.limit(TERM_SEARCH_CONTENT_CANDIDATE_LIMIT)
|
|
423
442
|
.toArray();
|
|
424
443
|
|
|
425
444
|
for (const result of contentResults) {
|
|
445
|
+
// Prefer non-empty content when duplicate rows exist for a path.
|
|
446
|
+
if (contentByPath.get(result.path)) {
|
|
447
|
+
continue;
|
|
448
|
+
}
|
|
426
449
|
contentByPath.set(result.path, result.content || '');
|
|
427
450
|
}
|
|
428
451
|
}
|
package/src/rag-analyzer.js
CHANGED
|
@@ -471,8 +471,10 @@ async function runAnalysis(filePath, options = {}) {
|
|
|
471
471
|
let fullFileContent;
|
|
472
472
|
if (options.diffOnly && options.diffContent) {
|
|
473
473
|
content = options.diffContent;
|
|
474
|
-
// For PR reviews, always read the full file content for context awareness
|
|
475
|
-
|
|
474
|
+
// For PR reviews, always read the full file content for context awareness.
|
|
475
|
+
// Tolerate a missing/unreadable file (e.g. deleted in the window after the
|
|
476
|
+
// existence check) by degrading to null; downstream falls back to file.content.
|
|
477
|
+
fullFileContent = await readTextFile(filePath).catch(() => null);
|
|
476
478
|
verboseLog(options, chalk.blue(`Analyzing diff only for ${path.basename(filePath)}`));
|
|
477
479
|
}
|
|
478
480
|
else {
|
|
@@ -873,6 +875,22 @@ ${projectArchitectureSection}`);
|
|
|
873
875
|
return blocks;
|
|
874
876
|
}
|
|
875
877
|
|
|
878
|
+
/**
|
|
879
|
+
* Build the user-prompt sentence that points the model at the cached system blocks.
|
|
880
|
+
* Returns an empty string when no cached blocks were emitted, so the prompt never
|
|
881
|
+
* instructs the model to use context that was not sent.
|
|
882
|
+
*
|
|
883
|
+
* @param {string[]} cachedSystemBlocks - Cached blocks produced for this request
|
|
884
|
+
* @param {string} subject - What is being evaluated (e.g. 'this file')
|
|
885
|
+
* @returns {string} Instruction sentence, or '' when there is no cached context
|
|
886
|
+
*/
|
|
887
|
+
function buildCachedContextInstruction(cachedSystemBlocks, subject) {
|
|
888
|
+
if (!cachedSystemBlocks || cachedSystemBlocks.length === 0) {
|
|
889
|
+
return '';
|
|
890
|
+
}
|
|
891
|
+
return `Use the cached project context, project architecture, and custom instructions when evaluating ${subject}.`;
|
|
892
|
+
}
|
|
893
|
+
|
|
876
894
|
// LLM call function - sends prompt with cached system content for cost optimization
|
|
877
895
|
async function sendPromptToLLM(promptConfig, llmOptions) {
|
|
878
896
|
try {
|
|
@@ -1150,7 +1168,7 @@ ${reviewInstructions}
|
|
|
1150
1168
|
|
|
1151
1169
|
${fileSection}
|
|
1152
1170
|
|
|
1153
|
-
|
|
1171
|
+
${buildCachedContextInstruction(cachedSystemBlocks, 'this file')}
|
|
1154
1172
|
|
|
1155
1173
|
CONTEXT A: EXPLICIT GUIDELINES FROM DOCUMENTATION
|
|
1156
1174
|
${formattedGuidelines}
|
|
@@ -1270,7 +1288,7 @@ ${reviewInstructions}
|
|
|
1270
1288
|
${fileSection}
|
|
1271
1289
|
|
|
1272
1290
|
## ANALYSIS CONTEXT
|
|
1273
|
-
|
|
1291
|
+
${buildCachedContextInstruction(cachedSystemBlocks, 'this test file')}
|
|
1274
1292
|
|
|
1275
1293
|
CONTEXT A: TESTING GUIDELINES AND BEST PRACTICES
|
|
1276
1294
|
${formattedGuidelines}
|
|
@@ -1427,7 +1445,7 @@ For each file in this PR, you have access to:
|
|
|
1427
1445
|
- **Test Files**: ${prFiles.filter((f) => f.isTest).length}
|
|
1428
1446
|
|
|
1429
1447
|
## UNIFIED CONTEXT FROM PROJECT
|
|
1430
|
-
|
|
1448
|
+
${buildCachedContextInstruction(cachedSystemBlocks, 'this pull request')}
|
|
1431
1449
|
|
|
1432
1450
|
### PROJECT CODE EXAMPLES
|
|
1433
1451
|
${formattedCodeExamples}
|