codecritique 2.0.2 → 2.0.4

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/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  [![npm version](https://img.shields.io/npm/v/codecritique.svg)](https://www.npmjs.com/package/codecritique)
4
4
  [![npm downloads](https://img.shields.io/npm/dm/codecritique.svg)](https://www.npmjs.com/package/codecritique)
5
5
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
6
- [![Node.js](https://img.shields.io/badge/node-%3E%3D24.15.0-brightgreen.svg)](https://nodejs.org/)
6
+ [![Node.js](https://img.shields.io/badge/node-%3E%3D24-brightgreen.svg)](https://nodejs.org/)
7
7
  [![CI](https://github.com/cosmocoder/CodeCritique/actions/workflows/release.yml/badge.svg)](https://github.com/cosmocoder/CodeCritique/actions/workflows/release.yml)
8
8
 
9
9
  **AI-Powered Code Review. Context-Aware. Privacy-First.**
@@ -96,7 +96,7 @@ This RAG-based approach provides more accurate, project-specific code reviews co
96
96
 
97
97
  ### Prerequisites
98
98
 
99
- - **Node.js** v24.15.0 or higher
99
+ - **Node.js** 24 or newer
100
100
  - **Git** (for diff-based analysis)
101
101
  - **Anthropic API key** (for LLM analysis)
102
102
 
@@ -199,7 +199,7 @@ For easier integration with non-JavaScript projects, you can use the provided sh
199
199
 
200
200
  3. **Environment setup** (the script handles this automatically):
201
201
  - Creates/uses `.env` file in your project directory
202
- - Validates Node.js v24.15.0+ requirement
202
+ - Validates Node.js 24+ requirement
203
203
  - Provides helpful error messages for missing dependencies
204
204
 
205
205
  ## Quick Start
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codecritique",
3
- "version": "2.0.2",
3
+ "version": "2.0.4",
4
4
  "description": "AI-powered code review tool for any programming language",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
@@ -90,8 +90,8 @@
90
90
  "npm": "11.12.1"
91
91
  },
92
92
  "engines": {
93
- "node": ">=24.15.0",
94
- "npm": ">=11.12.1 <12"
93
+ "node": ">=24",
94
+ "npm": ">=11"
95
95
  },
96
96
  "engine-strict": true,
97
97
  "publishConfig": {
package/src/index.js CHANGED
@@ -31,6 +31,7 @@ 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
33
  import { verboseLog } from './utils/logging.js';
34
+ import { collectPRLevelFindings, getFileLevelIssueCount, hasPRLevelFindings } from './utils/review-results.js';
34
35
 
35
36
  // Create a default embeddings system instance
36
37
  const embeddingsSystem = getDefaultEmbeddingsSystem();
@@ -424,7 +425,7 @@ async function runCodeReview(options) {
424
425
  // Determine output function based on format option
425
426
  const outputFn = options.output === 'json' ? outputJson : options.output === 'markdown' ? outputMarkdown : outputText;
426
427
  // Pass the detailed results array to the output function
427
- outputFn(reviewResult.results, options);
428
+ outputFn(reviewResult.results, options, reviewResult);
428
429
  console.log(chalk.bold.green(`\nAnalysis complete for ${operationDescription}! (${duration}s)`));
429
430
  }
430
431
  else {
@@ -435,7 +436,7 @@ async function runCodeReview(options) {
435
436
  // Still output empty results if outputFile is specified (for CI/CD pipelines)
436
437
  if (options.outputFile) {
437
438
  const outputFn = options.output === 'json' ? outputJson : options.output === 'markdown' ? outputMarkdown : outputText;
438
- outputFn([], options);
439
+ outputFn([], options, reviewResult);
439
440
  console.log(chalk.yellow(`Empty results written to: ${options.outputFile}`));
440
441
  }
441
442
 
@@ -1013,14 +1014,20 @@ function getChangedFiles(branch, workingDir = process.cwd()) {
1013
1014
  *
1014
1015
  * @param {Array<Object>} reviewResults - Array of individual file review results
1015
1016
  * @param {Object} options - Command line options
1017
+ * @param {Object} aggregateResult - Full review result with PR-level context
1016
1018
  */
1017
- function outputJson(reviewResults, options) {
1019
+ function outputJson(reviewResults, options, aggregateResult = {}) {
1020
+ const prLevelFindings = collectPRLevelFindings(reviewResults, aggregateResult);
1021
+ const fileLevelIssues = getFileLevelIssueCount(reviewResults);
1022
+
1018
1023
  // Structure the output to be informative
1019
1024
  const output = {
1020
1025
  summary: {
1021
1026
  totalFilesReviewed: reviewResults.length,
1022
1027
  filesWithIssues: reviewResults.filter((r) => r.success && !r.skipped && r.results?.issues?.length > 0).length,
1023
- totalIssues: reviewResults.reduce((sum, r) => sum + (r.results?.issues?.length || 0), 0),
1028
+ totalIssues: fileLevelIssues + prLevelFindings.issues.length,
1029
+ fileLevelIssues,
1030
+ prLevelIssues: prLevelFindings.issues.length,
1024
1031
  issuesWithCodeSuggestions: reviewResults.reduce((sum, r) => {
1025
1032
  if (!r.success || r.skipped || !r.results?.issues) {
1026
1033
  return sum;
@@ -1049,6 +1056,10 @@ function outputJson(reviewResults, options) {
1049
1056
  }),
1050
1057
  };
1051
1058
 
1059
+ if (hasPRLevelFindings(prLevelFindings)) {
1060
+ output.prLevelFindings = prLevelFindings;
1061
+ }
1062
+
1052
1063
  const jsonOutput = JSON.stringify(output, null, 2);
1053
1064
 
1054
1065
  // If output-file is specified, write to file instead of stdout
@@ -1067,13 +1078,15 @@ function outputJson(reviewResults, options) {
1067
1078
  *
1068
1079
  * @param {Array<Object>} reviewResults - Array of individual file review results
1069
1080
  * @param {Object} options - Command line options
1081
+ * @param {Object} aggregateResult - Full review result with PR-level context
1070
1082
  */
1071
- function outputMarkdown(reviewResults, options) {
1083
+ function outputMarkdown(reviewResults, options, aggregateResult = {}) {
1072
1084
  const totalFiles = reviewResults.length;
1073
1085
  const filesWithIssues = reviewResults.filter((r) => r.success && !r.skipped && r.results?.issues?.length > 0).length;
1074
- const totalIssues = reviewResults.reduce((sum, r) => sum + (r.results?.issues?.length || 0), 0);
1075
1086
  const skippedFiles = reviewResults.filter((r) => r.skipped).length;
1076
1087
  const errorFiles = reviewResults.filter((r) => !r.success).length;
1088
+ const prLevelFindings = collectPRLevelFindings(reviewResults, aggregateResult);
1089
+ const totalIssues = getFileLevelIssueCount(reviewResults) + prLevelFindings.issues.length;
1077
1090
 
1078
1091
  const lines = [
1079
1092
  '# AI Code Review Results (RAG Approach)',
@@ -1092,6 +1105,8 @@ function outputMarkdown(reviewResults, options) {
1092
1105
  lines.push(`- **Errors:** ${errorFiles}`);
1093
1106
  }
1094
1107
 
1108
+ appendPRLevelFindingsMarkdown(lines, prLevelFindings);
1109
+
1095
1110
  lines.push('', '## Detailed Review per File', '');
1096
1111
 
1097
1112
  reviewResults.forEach((fileResult) => {
@@ -1157,13 +1172,15 @@ function outputMarkdown(reviewResults, options) {
1157
1172
  *
1158
1173
  * @param {Array<Object>} reviewResults - Array of individual file review results
1159
1174
  * @param {Object} cliOptions - Command line options
1175
+ * @param {Object} aggregateResult - Full review result with PR-level context
1160
1176
  */
1161
- function outputText(reviewResults, cliOptions) {
1177
+ function outputText(reviewResults, cliOptions, aggregateResult = {}) {
1162
1178
  const totalFiles = reviewResults.length;
1163
1179
  const filesWithIssues = reviewResults.filter((r) => r.success && !r.skipped && r.results?.issues?.length > 0).length;
1164
- const totalIssues = reviewResults.reduce((sum, r) => sum + (r.results?.issues?.length || 0), 0);
1165
1180
  const skippedFiles = reviewResults.filter((r) => r.skipped).length;
1166
1181
  const errorFiles = reviewResults.filter((r) => !r.success).length;
1182
+ const prLevelFindings = collectPRLevelFindings(reviewResults, aggregateResult);
1183
+ const totalIssues = getFileLevelIssueCount(reviewResults) + prLevelFindings.issues.length;
1167
1184
 
1168
1185
  console.log(chalk.bold.blue('\n===== AI Code Review Summary ====='));
1169
1186
  console.log(`Files Analyzed: ${chalk.bold(totalFiles)}`);
@@ -1177,6 +1194,8 @@ function outputText(reviewResults, cliOptions) {
1177
1194
  }
1178
1195
  console.log(chalk.bold.blue('================================================'));
1179
1196
 
1197
+ outputPRLevelFindingsText(prLevelFindings);
1198
+
1180
1199
  reviewResults.forEach((fileResult) => {
1181
1200
  if (!fileResult.success) {
1182
1201
  console.log(chalk.bold.red(`\n===== Error reviewing ${fileResult.filePath} =====`));
@@ -1230,6 +1249,68 @@ function outputText(reviewResults, cliOptions) {
1230
1249
  });
1231
1250
  }
1232
1251
 
1252
+ function appendPRLevelFindingsMarkdown(lines, prLevelFindings) {
1253
+ if (!hasPRLevelFindings(prLevelFindings)) {
1254
+ return;
1255
+ }
1256
+
1257
+ lines.push('', '## PR-Level Findings', '');
1258
+
1259
+ if (prLevelFindings.summary) {
1260
+ lines.push(`**Summary:** ${prLevelFindings.summary}`, '');
1261
+ }
1262
+
1263
+ for (const issue of prLevelFindings.issues) {
1264
+ const severity = issue.severity || 'info';
1265
+ const severityEmoji = getSeverityEmoji(severity);
1266
+ lines.push(`- **[${severity.toUpperCase()}] ${severityEmoji}** ${issue.description}`);
1267
+ if (issue.files?.length > 0) {
1268
+ lines.push(` - Affected files: ${issue.files.join(', ')}`);
1269
+ }
1270
+ if (issue.suggestion) {
1271
+ lines.push(` - Suggestion: ${issue.suggestion}`);
1272
+ }
1273
+ }
1274
+
1275
+ if (prLevelFindings.recommendations.length > 0) {
1276
+ lines.push('', '**Recommendations:**', '');
1277
+ for (const recommendation of prLevelFindings.recommendations) {
1278
+ lines.push(`- ${recommendation}`);
1279
+ }
1280
+ }
1281
+ }
1282
+
1283
+ function outputPRLevelFindingsText(prLevelFindings) {
1284
+ if (!hasPRLevelFindings(prLevelFindings)) {
1285
+ return;
1286
+ }
1287
+
1288
+ console.log(chalk.bold.underline('\n===== PR-Level Findings ====='));
1289
+ if (prLevelFindings.summary) {
1290
+ console.log(chalk.bold.cyan(`Summary: ${prLevelFindings.summary}`));
1291
+ }
1292
+
1293
+ for (const issue of prLevelFindings.issues) {
1294
+ const severity = issue.severity || 'info';
1295
+ const severityColor = getSeverityColor(severity);
1296
+ console.log(` ${severityColor(`[${severity.toUpperCase()}]`)} ${issue.description}`);
1297
+ if (issue.files?.length > 0) {
1298
+ console.log(` Affected files: ${issue.files.join(', ')}`);
1299
+ }
1300
+ if (issue.suggestion) {
1301
+ console.log(` ${chalk.green(`Suggestion: ${issue.suggestion}`)}`);
1302
+ }
1303
+ console.log('');
1304
+ }
1305
+
1306
+ if (prLevelFindings.recommendations.length > 0) {
1307
+ console.log(chalk.bold.yellow('Recommendations:'));
1308
+ for (const recommendation of prLevelFindings.recommendations) {
1309
+ console.log(` - ${recommendation}`);
1310
+ }
1311
+ }
1312
+ }
1313
+
1233
1314
  // --- Severity Helpers (Remain Unchanged) --- //
1234
1315
 
1235
1316
  /**