codecritique 1.2.0 → 1.2.2
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/custom-documents.js +2 -2
- package/src/custom-documents.test.js +11 -9
- package/src/index.js +41 -40
- package/src/prompt-cache.js +1 -0
- package/src/rag-analyzer.js +10 -9
- package/src/rag-analyzer.test.js +11 -0
package/package.json
CHANGED
package/src/custom-documents.js
CHANGED
|
@@ -350,7 +350,7 @@ export class CustomDocumentProcessor {
|
|
|
350
350
|
}
|
|
351
351
|
|
|
352
352
|
// Try cache manager
|
|
353
|
-
const cachedChunks =
|
|
353
|
+
const cachedChunks = this.cacheManager.getCustomDocumentChunks(resolvedProjectPath);
|
|
354
354
|
if (cachedChunks && cachedChunks.length > 0) {
|
|
355
355
|
// Restore to memory
|
|
356
356
|
this.customDocumentChunks.set(resolvedProjectPath, cachedChunks);
|
|
@@ -520,7 +520,7 @@ export class CustomDocumentProcessor {
|
|
|
520
520
|
try {
|
|
521
521
|
const resolvedProjectPath = path.resolve(projectPath);
|
|
522
522
|
this.customDocumentChunks.delete(resolvedProjectPath);
|
|
523
|
-
|
|
523
|
+
this.cacheManager.customDocumentChunks.delete(resolvedProjectPath);
|
|
524
524
|
console.log(chalk.green(`Cleared custom document chunks for project: ${resolvedProjectPath}`));
|
|
525
525
|
} catch (error) {
|
|
526
526
|
console.error(chalk.red(`Error clearing project chunks: ${error.message}`));
|
|
@@ -11,8 +11,7 @@ vi.mock('./embeddings/model-manager.js', () => ({
|
|
|
11
11
|
vi.mock('./embeddings/cache-manager.js', () => ({
|
|
12
12
|
CacheManager: class {
|
|
13
13
|
storeCustomDocuments = vi.fn().mockResolvedValue(undefined);
|
|
14
|
-
|
|
15
|
-
clearCustomDocuments = vi.fn().mockResolvedValue(undefined);
|
|
14
|
+
getCustomDocumentChunks = vi.fn().mockReturnValue([]);
|
|
16
15
|
},
|
|
17
16
|
}));
|
|
18
17
|
|
|
@@ -32,8 +31,7 @@ describe('CustomDocumentProcessor', () => {
|
|
|
32
31
|
|
|
33
32
|
mockCacheManager = {
|
|
34
33
|
storeCustomDocuments: vi.fn().mockResolvedValue(undefined),
|
|
35
|
-
|
|
36
|
-
clearCustomDocuments: vi.fn().mockResolvedValue(undefined),
|
|
34
|
+
getCustomDocumentChunks: vi.fn().mockReturnValue([]),
|
|
37
35
|
};
|
|
38
36
|
|
|
39
37
|
processor = new CustomDocumentProcessor({
|
|
@@ -319,7 +317,7 @@ describe('CustomDocumentProcessor', () => {
|
|
|
319
317
|
|
|
320
318
|
it('should return chunks from cache if not in memory', async () => {
|
|
321
319
|
const cachedChunks = [{ id: 'cached', content: 'from cache' }];
|
|
322
|
-
mockCacheManager.
|
|
320
|
+
mockCacheManager.getCustomDocumentChunks.mockReturnValue(cachedChunks);
|
|
323
321
|
|
|
324
322
|
const result = await processor.getExistingChunks('/project');
|
|
325
323
|
|
|
@@ -328,7 +326,7 @@ describe('CustomDocumentProcessor', () => {
|
|
|
328
326
|
|
|
329
327
|
it('should restore cached chunks to memory', async () => {
|
|
330
328
|
const cachedChunks = [{ id: 'cached', content: 'from cache' }];
|
|
331
|
-
mockCacheManager.
|
|
329
|
+
mockCacheManager.getCustomDocumentChunks.mockReturnValue(cachedChunks);
|
|
332
330
|
|
|
333
331
|
await processor.getExistingChunks('/project');
|
|
334
332
|
|
|
@@ -336,7 +334,7 @@ describe('CustomDocumentProcessor', () => {
|
|
|
336
334
|
});
|
|
337
335
|
|
|
338
336
|
it('should return empty array when no chunks exist', async () => {
|
|
339
|
-
mockCacheManager.
|
|
337
|
+
mockCacheManager.getCustomDocumentChunks.mockReturnValue([]);
|
|
340
338
|
|
|
341
339
|
const result = await processor.getExistingChunks('/project');
|
|
342
340
|
|
|
@@ -353,10 +351,14 @@ describe('CustomDocumentProcessor', () => {
|
|
|
353
351
|
expect(processor.customDocumentChunks.has('/project')).toBe(false);
|
|
354
352
|
});
|
|
355
353
|
|
|
356
|
-
it('should clear chunks
|
|
354
|
+
it('should clear chunks for only the selected project', async () => {
|
|
355
|
+
processor.customDocumentChunks.set('/project', [{ id: 'chunk-a' }]);
|
|
356
|
+
processor.customDocumentChunks.set('/other', [{ id: 'chunk-b' }]);
|
|
357
|
+
|
|
357
358
|
await processor.clearProjectChunks('/project');
|
|
358
359
|
|
|
359
|
-
expect(
|
|
360
|
+
expect(processor.customDocumentChunks.has('/project')).toBe(false);
|
|
361
|
+
expect(processor.customDocumentChunks.has('/other')).toBe(true);
|
|
360
362
|
});
|
|
361
363
|
});
|
|
362
364
|
|
package/src/index.js
CHANGED
|
@@ -420,19 +420,18 @@ async function runCodeReview(options) {
|
|
|
420
420
|
outputFn(reviewResult.results, options);
|
|
421
421
|
console.log(chalk.bold.green(`\nAnalysis complete for ${operationDescription}! (${duration}s)`));
|
|
422
422
|
} else {
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
);
|
|
433
|
-
}
|
|
423
|
+
// No results to display (e.g., all files were excluded/skipped)
|
|
424
|
+
const message = reviewResult.message || 'All files were excluded from review (e.g., config files, lock files).';
|
|
425
|
+
console.log(chalk.yellow(message));
|
|
426
|
+
|
|
427
|
+
// Still output empty results if outputFile is specified (for CI/CD pipelines)
|
|
428
|
+
if (options.outputFile) {
|
|
429
|
+
const outputFn = options.output === 'json' ? outputJson : options.output === 'markdown' ? outputMarkdown : outputText;
|
|
430
|
+
outputFn([], options);
|
|
431
|
+
console.log(chalk.yellow(`Empty results written to: ${options.outputFile}`));
|
|
434
432
|
}
|
|
435
|
-
|
|
433
|
+
|
|
434
|
+
console.log(chalk.bold.yellow(`\nReview complete for ${operationDescription} - no reviewable files found (${duration}s)`));
|
|
436
435
|
}
|
|
437
436
|
} else {
|
|
438
437
|
console.error(chalk.red('\nCode review process failed.'));
|
|
@@ -577,35 +576,37 @@ async function generateEmbeddings(options) {
|
|
|
577
576
|
|
|
578
577
|
// Start the progress update interval
|
|
579
578
|
const progressInterval = setInterval(updateSpinner, 100);
|
|
579
|
+
let results;
|
|
580
|
+
try {
|
|
581
|
+
results = await embeddingsSystem.processBatchEmbeddings(filesToProcess, {
|
|
582
|
+
concurrency,
|
|
583
|
+
verbose: options.verbose,
|
|
584
|
+
excludePatterns,
|
|
585
|
+
respectGitignore: options.gitignore !== false,
|
|
586
|
+
baseDir: baseDir,
|
|
587
|
+
batchSize: 100, // Set a reasonable batch size
|
|
588
|
+
maxLines: parseInt(options.maxLines || '1000', 10),
|
|
589
|
+
onProgress: (status) => {
|
|
590
|
+
// Update counters based on status
|
|
591
|
+
if (status === 'processed') {
|
|
592
|
+
processedCount++;
|
|
593
|
+
} else if (status === 'skipped') {
|
|
594
|
+
skippedCount++;
|
|
595
|
+
} else if (status === 'failed') {
|
|
596
|
+
failedCount++;
|
|
597
|
+
} else if (status === 'excluded') {
|
|
598
|
+
excludedCount++;
|
|
599
|
+
}
|
|
580
600
|
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
// Update counters based on status
|
|
591
|
-
if (status === 'processed') {
|
|
592
|
-
processedCount++;
|
|
593
|
-
} else if (status === 'skipped') {
|
|
594
|
-
skippedCount++;
|
|
595
|
-
} else if (status === 'failed') {
|
|
596
|
-
failedCount++;
|
|
597
|
-
} else if (status === 'excluded') {
|
|
598
|
-
excludedCount++;
|
|
599
|
-
}
|
|
600
|
-
|
|
601
|
-
// Update the spinner with new progress information
|
|
602
|
-
updateSpinner();
|
|
603
|
-
},
|
|
604
|
-
});
|
|
605
|
-
|
|
606
|
-
// Clean up the progress display
|
|
607
|
-
clearInterval(progressInterval);
|
|
608
|
-
spinner.stop(true);
|
|
601
|
+
// Update the spinner with new progress information
|
|
602
|
+
updateSpinner();
|
|
603
|
+
},
|
|
604
|
+
});
|
|
605
|
+
} finally {
|
|
606
|
+
// Clean up the progress display even if embedding generation fails.
|
|
607
|
+
clearInterval(progressInterval);
|
|
608
|
+
spinner.stop(true);
|
|
609
|
+
}
|
|
609
610
|
|
|
610
611
|
console.log(chalk.green(`\nEmbedding generation complete!`));
|
|
611
612
|
console.log(chalk.cyan(`Processed: ${results.processed} files`));
|
package/src/prompt-cache.js
CHANGED
|
@@ -195,6 +195,7 @@ Code suggestions enable reviewers to apply fixes directly as GitHub suggestions.
|
|
|
195
195
|
*/
|
|
196
196
|
const LINE_NUMBERS_RULE = `**CRITICAL 'lineNumbers' RULE - MANDATORY COMPLIANCE**:
|
|
197
197
|
- **ALWAYS provide line numbers** - this field is REQUIRED for every issue
|
|
198
|
+
- **Line numbers are shown in the file content** as a prefix on each line (e.g., " 42 | const x = 1;"). Use THESE line numbers directly - do NOT try to count lines yourself.
|
|
198
199
|
- If you can identify specific lines, provide them (max 3-5 for repeated issues)
|
|
199
200
|
- If the issue affects the entire file or cannot be pinpointed, provide [1] or relevant section line numbers
|
|
200
201
|
- For ANY issue that occurs multiple times in a file, list ONLY the first 3-5 occurrences maximum
|
package/src/rag-analyzer.js
CHANGED
|
@@ -27,6 +27,7 @@ import { isGenericDocument, getGenericDocumentContext } from './utils/document-d
|
|
|
27
27
|
import { isTestFile, shouldProcessFile } from './utils/file-validation.js';
|
|
28
28
|
import { detectFileType, detectLanguageFromExtension } from './utils/language-detection.js';
|
|
29
29
|
import { debug } 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;
|
|
@@ -774,7 +775,7 @@ async function callLLMForAnalysis(context, options = {}) {
|
|
|
774
775
|
return {
|
|
775
776
|
summary: analysisResponse.summary || 'Analysis completed with parsing issues',
|
|
776
777
|
issues: Array.isArray(analysisResponse.issues) ? analysisResponse.issues : [],
|
|
777
|
-
rawResponse: analysisResponse.rawResponse || llmResponse.substring(0, 500),
|
|
778
|
+
rawResponse: analysisResponse.rawResponse || JSON.stringify(llmResponse).substring(0, 500),
|
|
778
779
|
parseWarning: 'Response structure was reconstructed due to parsing issues',
|
|
779
780
|
};
|
|
780
781
|
}
|
|
@@ -1047,10 +1048,10 @@ You have access to TWO pieces of information:
|
|
|
1047
1048
|
- Do NOT flag functions/variables as missing if they exist elsewhere in the full file
|
|
1048
1049
|
- The unchanged code is part of the file - check it before making assumptions
|
|
1049
1050
|
|
|
1050
|
-
**FULL FILE CONTENT (for context - DO NOT review unchanged code):**
|
|
1051
|
+
**FULL FILE CONTENT (for context - DO NOT review unchanged code, line numbers shown for reference):**
|
|
1051
1052
|
|
|
1052
1053
|
\`\`\`${file.language}
|
|
1053
|
-
${file.fullFileContent || file.content}
|
|
1054
|
+
${addLineNumbers(file.fullFileContent || file.content)}
|
|
1054
1055
|
\`\`\`
|
|
1055
1056
|
|
|
1056
1057
|
**GIT DIFF TO REVIEW (critique ONLY these changes):**
|
|
@@ -1063,7 +1064,7 @@ Path: ${file.path}
|
|
|
1063
1064
|
Language: ${file.language}
|
|
1064
1065
|
|
|
1065
1066
|
\`\`\`${file.language}
|
|
1066
|
-
${file.content}
|
|
1067
|
+
${addLineNumbers(file.content)}
|
|
1067
1068
|
\`\`\``;
|
|
1068
1069
|
|
|
1069
1070
|
// Add project architecture context if available
|
|
@@ -1166,10 +1167,10 @@ You have access to TWO pieces of information:
|
|
|
1166
1167
|
- Check the full file for existing test cases before making assumptions
|
|
1167
1168
|
- The unchanged test code is part of the file - review it before suggesting additions
|
|
1168
1169
|
|
|
1169
|
-
**FULL TEST FILE CONTENT (for context - check for existing tests):**
|
|
1170
|
+
**FULL TEST FILE CONTENT (for context - check for existing tests, line numbers shown for reference):**
|
|
1170
1171
|
|
|
1171
1172
|
\`\`\`${file.language}
|
|
1172
|
-
${file.fullFileContent || file.content}
|
|
1173
|
+
${addLineNumbers(file.fullFileContent || file.content)}
|
|
1173
1174
|
\`\`\`
|
|
1174
1175
|
|
|
1175
1176
|
**GIT DIFF TO REVIEW (critique ONLY these changes):**
|
|
@@ -1182,7 +1183,7 @@ Path: ${file.path}
|
|
|
1182
1183
|
Language: ${file.language}
|
|
1183
1184
|
|
|
1184
1185
|
\`\`\`${file.language}
|
|
1185
|
-
${file.content}
|
|
1186
|
+
${addLineNumbers(file.content)}
|
|
1186
1187
|
\`\`\``;
|
|
1187
1188
|
|
|
1188
1189
|
// Use shared helpers for custom docs and role definition
|
|
@@ -1317,9 +1318,9 @@ ${g.content}
|
|
|
1317
1318
|
${prFile.diff}
|
|
1318
1319
|
\`\`\`
|
|
1319
1320
|
|
|
1320
|
-
### Full File Content (For Context):
|
|
1321
|
+
### Full File Content (For Context - line numbers shown for reference):
|
|
1321
1322
|
\`\`\`${prFile.language}
|
|
1322
|
-
${prFile.fullContent}
|
|
1323
|
+
${addLineNumbers(prFile.fullContent)}
|
|
1323
1324
|
\`\`\`
|
|
1324
1325
|
`;
|
|
1325
1326
|
})
|
package/src/rag-analyzer.test.js
CHANGED
|
@@ -385,6 +385,17 @@ describe('rag-analyzer', () => {
|
|
|
385
385
|
expect(result.results).toBeDefined();
|
|
386
386
|
});
|
|
387
387
|
|
|
388
|
+
it('should reconstruct malformed responses without crashing', async () => {
|
|
389
|
+
llm.sendPromptToClaude.mockResolvedValue({
|
|
390
|
+
content: 'non-json fallback text',
|
|
391
|
+
json: { foo: 'bar' },
|
|
392
|
+
});
|
|
393
|
+
const result = await runAnalysis('/test/file.js');
|
|
394
|
+
expect(result.success).toBe(true);
|
|
395
|
+
expect(result.results.summary).toBeDefined();
|
|
396
|
+
expect(Array.isArray(result.results.issues)).toBe(true);
|
|
397
|
+
});
|
|
398
|
+
|
|
388
399
|
it('should handle LLM response with null json', async () => {
|
|
389
400
|
llm.sendPromptToClaude.mockResolvedValue({ json: null });
|
|
390
401
|
const result = await runAnalysis('/test/file.js');
|