codecritique 1.2.1 → 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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codecritique",
3
- "version": "1.2.1",
3
+ "version": "1.2.2",
4
4
  "description": "AI-powered code review tool for any programming language",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
@@ -350,7 +350,7 @@ export class CustomDocumentProcessor {
350
350
  }
351
351
 
352
352
  // Try cache manager
353
- const cachedChunks = await this.cacheManager.getCustomDocuments(resolvedProjectPath);
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
- await this.cacheManager.clearCustomDocuments(resolvedProjectPath);
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
- getCustomDocuments = vi.fn().mockResolvedValue([]);
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
- getCustomDocuments: vi.fn().mockResolvedValue([]),
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.getCustomDocuments.mockResolvedValue(cachedChunks);
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.getCustomDocuments.mockResolvedValue(cachedChunks);
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.getCustomDocuments.mockResolvedValue([]);
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 from cache', async () => {
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(mockCacheManager.clearCustomDocuments).toHaveBeenCalled();
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
@@ -576,35 +576,37 @@ async function generateEmbeddings(options) {
576
576
 
577
577
  // Start the progress update interval
578
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
+ }
579
600
 
580
- const results = await embeddingsSystem.processBatchEmbeddings(filesToProcess, {
581
- concurrency,
582
- verbose: options.verbose,
583
- excludePatterns,
584
- respectGitignore: options.gitignore !== false,
585
- baseDir: baseDir,
586
- batchSize: 100, // Set a reasonable batch size
587
- maxLines: parseInt(options.maxLines || '1000', 10),
588
- onProgress: (status) => {
589
- // Update counters based on status
590
- if (status === 'processed') {
591
- processedCount++;
592
- } else if (status === 'skipped') {
593
- skippedCount++;
594
- } else if (status === 'failed') {
595
- failedCount++;
596
- } else if (status === 'excluded') {
597
- excludedCount++;
598
- }
599
-
600
- // Update the spinner with new progress information
601
- updateSpinner();
602
- },
603
- });
604
-
605
- // Clean up the progress display
606
- clearInterval(progressInterval);
607
- 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
+ }
608
610
 
609
611
  console.log(chalk.green(`\nEmbedding generation complete!`));
610
612
  console.log(chalk.cyan(`Processed: ${results.processed} files`));
@@ -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
@@ -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
  })
@@ -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');