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 +3 -3
- package/package.json +3 -3
- package/src/index.js +89 -8
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
[](https://www.npmjs.com/package/codecritique)
|
|
4
4
|
[](https://www.npmjs.com/package/codecritique)
|
|
5
5
|
[](https://opensource.org/licenses/MIT)
|
|
6
|
-
[](https://nodejs.org/)
|
|
7
7
|
[](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**
|
|
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
|
|
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.
|
|
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
|
|
94
|
-
"npm": ">=11
|
|
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:
|
|
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
|
/**
|