coderev-cli 1.0.26 → 1.3.0

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": "coderev-cli",
3
- "version": "1.0.26",
3
+ "version": "1.3.0",
4
4
  "description": "Multi-agent AI code review for git -- parallel agents with confidence scoring",
5
5
  "main": "src/index.js",
6
6
  "bin": {
package/src/cli.js CHANGED
@@ -43,6 +43,9 @@ program
43
43
  .option('--min-confidence <number>', 'Minimum confidence threshold 0-100 (default: 60)', '60')
44
44
  .option('--agents <list>', 'Comma-separated agent list: security,bugs,quality')
45
45
  .option('--blame', 'Enable git blame context analysis to distinguish new vs pre-existing issues')
46
+ .option('--rag', 'Enable RAG codebase context retrieval (requires coderev index first)')
47
+ .option('--issue <url>', 'Validate that PR addresses a linked GitHub/GitLab issue')
48
+ .option('--verify-issue', 'Auto-verify PR addresses linked issues from commit messages')
46
49
  .action(async (options) => {
47
50
  try {
48
51
  const config = loadConfig(options.config);
@@ -183,8 +186,35 @@ program
183
186
  single: options.single || undefined,
184
187
  minConfidence: parseInt(options.minConfidence) || undefined,
185
188
  blame: options.blame || undefined,
189
+ rag: options.rag || undefined,
190
+ repoRoot: options.repo || options.config ? path.dirname(options.config) : process.cwd(),
186
191
  });
187
192
 
193
+ // ── Issue Validation ──
194
+ if (options.issue) {
195
+ const { validateIssue } = require('./issue-validator');
196
+ try {
197
+ const issueResult = await validateIssue(options.issue, diff, {
198
+ token: options.githubToken || options.gitlabToken,
199
+ repoPath: options.repo || process.cwd(),
200
+ reviewResult: result,
201
+ });
202
+ // Append issue validation to result
203
+ result._issueValidation = issueResult.report;
204
+ result._issueValidationFormatted = issueResult.formatted;
205
+ } catch (err) {
206
+ result._issueValidation = { error: err.message };
207
+ }
208
+ }
209
+
210
+ if (options.verifyIssue) {
211
+ const { findRelatedIssues, parseCommitLog } = require('./issue-validator');
212
+ const repoPath = options.repo || process.cwd();
213
+ const commitLog = parseCommitLog(repoPath);
214
+ const relatedIssues = findRelatedIssues(diff, commitLog);
215
+ result._relatedIssuesFound = relatedIssues;
216
+ }
217
+
188
218
  let output;
189
219
  if (options.output === 'json') {
190
220
  output = JSON.stringify(result, null, 2);
@@ -325,6 +355,12 @@ program
325
355
  return;
326
356
  }
327
357
 
358
+ // ── Print issue validation report if available ──
359
+ if (result._issueValidationFormatted) {
360
+ // Print after main output (sent to console.error so it's visible even with piped output)
361
+ console.error(result._issueValidationFormatted);
362
+ }
363
+
328
364
  // ── CI Mode ──
329
365
  if (options.ci && (result.issues || []).length > 0) {
330
366
  const errorIssues = result.issues.filter(i => i.type === 'error');
@@ -888,9 +924,11 @@ program
888
924
  .option('--agent-security <name>', 'Model for security agent')
889
925
  .option('--agent-bugs <name>', 'Model for bug detection agent')
890
926
  .option('--agent-quality <name>', 'Model for quality agent')
927
+ .option('--auto', 'Auto-detect available providers from environment variables')
928
+ .option('--quick', 'Alias for --auto')
891
929
  .action((options) => {
892
930
  const fs = require('fs');
893
- const { resolveTemplate } = require('./models');
931
+ const { resolveTemplate, autoDetectProvider, listTemplates } = require('./models');
894
932
  const configPath = path.join(process.cwd(), '.coderevrc.json');
895
933
 
896
934
  let config = {};
@@ -900,6 +938,86 @@ program
900
938
 
901
939
  if (!config.ai) config.ai = {};
902
940
 
941
+ // ── Auto-detect mode ──
942
+ if (options.auto || options.quick) {
943
+ console.log(chalk.bold('\n🔍 Auto-detecting AI providers...\n'));
944
+
945
+ const result = autoDetectProvider();
946
+
947
+ if (!result) {
948
+ console.log(chalk.yellow('⚠ No API key environment variables detected.'));
949
+ console.log(chalk.gray('\n Supported providers and their env vars:'));
950
+ const allTemplates = listTemplates();
951
+ const seen = new Set();
952
+ for (const t of allTemplates) {
953
+ if (!seen.has(t.apiKeyEnv)) {
954
+ seen.add(t.apiKeyEnv);
955
+ console.log(chalk.gray(` ${t.apiKeyEnv} → ${t.name} (${t.desc})`));
956
+ }
957
+ }
958
+ console.log(chalk.blue('\n 💡 Set one of these env vars and run `coderev setup --auto` again.'));
959
+ console.log(chalk.blue(' Or use `coderev setup --model <name>` to pick manually.'));
960
+ return;
961
+ }
962
+
963
+ // Show detection summary
964
+ console.log(chalk.bold('📋 Detection Results / 检测结果:'));
965
+ console.log('━'.repeat(50));
966
+
967
+ // List all detected keys
968
+ for (const name of result.allDetected) {
969
+ const t = result.allDetected.includes(name) ? require('./models').getTemplate(name) : null;
970
+ if (t) {
971
+ const masked = process.env[t.apiKeyEnv].slice(0, 6) + '...' + process.env[t.apiKeyEnv].slice(-4);
972
+ console.log(chalk.green(` ✔ ${t.apiKeyEnv}`) + chalk.gray(` → ${name} (${masked})`));
973
+ }
974
+ }
975
+
976
+ // Show providers that were NOT detected
977
+ const allNames = Object.keys(require('./models').BUILTIN_TEMPLATES);
978
+ const uniqueNotDetected = [...new Set(
979
+ allNames.filter(n => !result.allDetected.includes(n))
980
+ )];
981
+
982
+ console.log('');
983
+ console.log(chalk.bold(`✨ Selected: ${chalk.green(result.chosen)}`));
984
+ console.log(chalk.gray(` Provider: ${result.template.provider}`));
985
+ console.log(chalk.gray(` Model: ${result.template.model}`));
986
+ console.log(chalk.gray(` API Key: ${result.template.apiKeyEnv}`));
987
+ console.log('');
988
+
989
+ // Write config
990
+ try {
991
+ const resolved = resolveTemplate(result.chosen);
992
+ Object.assign(config.ai, resolved);
993
+
994
+ // Also add fallback if second provider is available
995
+ const remaining = result.allDetected.filter(n => n !== result.chosen);
996
+ if (remaining.length > 0) {
997
+ const fallbackName = remaining[0];
998
+ const fallbackResolved = resolveTemplate(fallbackName);
999
+ config.ai.fallback = {
1000
+ enabled: true,
1001
+ provider: fallbackResolved.provider,
1002
+ baseURL: fallbackResolved.baseURL,
1003
+ model: fallbackResolved.model,
1004
+ apiKeyEnv: fallbackResolved.apiKeyEnv,
1005
+ temperature: fallbackResolved.temperature,
1006
+ maxTokens: fallbackResolved.maxTokens,
1007
+ };
1008
+ console.log(chalk.blue(` Fallback: ${fallbackName} (${fallbackResolved.model})`));
1009
+ }
1010
+
1011
+ fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
1012
+ console.log(chalk.green(`\n✔ Config saved to ${configPath}`));
1013
+ console.log(chalk.blue(' Run `coderev review` to start reviewing code!'));
1014
+ } catch (err) {
1015
+ console.error(chalk.red(`✖ ${err.message}`));
1016
+ process.exit(1);
1017
+ }
1018
+ return;
1019
+ }
1020
+
903
1021
  // Set primary model
904
1022
  if (options.model) {
905
1023
  try {
@@ -1105,6 +1223,45 @@ program
1105
1223
  }
1106
1224
  });
1107
1225
 
1226
+ program
1227
+ .command('index')
1228
+ .description('Build codebase index for RAG-powered code reviews')
1229
+ .option('-r, --repo <path>', 'Path to git repository', '.')
1230
+ .option('--max-files <number>', 'Maximum files to index (default: 500)', '500')
1231
+ .option('--json', 'Output as JSON')
1232
+ .action(async (options) => {
1233
+ try {
1234
+ const { buildIndex, loadIndex, INDEX_DIR } = require('./rag-indexer');
1235
+ const repoRoot = path.resolve(options.repo);
1236
+
1237
+ console.error(chalk.blue(`📚 Building codebase index for ${repoRoot}...`));
1238
+ console.error(chalk.gray(` Max files: ${options.maxFiles}`));
1239
+
1240
+ const index = buildIndex(repoRoot, { maxFiles: parseInt(options.maxFiles) });
1241
+
1242
+ if (options.json) {
1243
+ console.log(JSON.stringify(index.stats, null, 2));
1244
+ } else {
1245
+ console.log(chalk.green(`\n✅ Index built successfully!`));
1246
+ console.log(chalk.bold(`\n📊 Statistics:`));
1247
+ console.log(` Files scanned: ${index.stats.filesScanned}`);
1248
+ console.log(` Symbols extracted: ${index.stats.symbolsExtracted}`);
1249
+ console.log(` Time: ${index.stats.timeMs}ms`);
1250
+ console.log(` Stored at: ${path.join(repoRoot, INDEX_DIR)}`);
1251
+ if (Object.keys(index.stats.languageBreakdown).length > 0) {
1252
+ console.log(chalk.bold(`\n🔤 Language breakdown:`));
1253
+ for (const [lang, count] of Object.entries(index.stats.languageBreakdown).sort((a, b) => b[1] - a[1])) {
1254
+ console.log(` ${lang}: ${count} symbols`);
1255
+ }
1256
+ }
1257
+ console.log(chalk.blue(`\n💡 Tip: Run \`coderev review --rag\` to use codebase context in reviews.`));
1258
+ }
1259
+ } catch (err) {
1260
+ console.error(chalk.red(`✖ ${err.message}`));
1261
+ process.exit(1);
1262
+ }
1263
+ });
1264
+
1108
1265
  program.parse(process.argv);
1109
1266
 
1110
1267
  // ── Helpers ───────────────────────────────────────────────────
@@ -1204,6 +1361,10 @@ function formatTerminal(result) {
1204
1361
  enLines.push(' ' + chalk.cyan(' Files analyzed: ') + bc.filesAnalyzed);
1205
1362
  }
1206
1363
  }
1364
+ if (result._rag) {
1365
+ enLines.push('\n' + chalk.bold('RAG Context:'));
1366
+ enLines.push(' ' + chalk.magenta(`Indexed ${result._rag.indexedSymbols} symbols from codebase`));
1367
+ }
1207
1368
  enLines.push('\n' + '━'.repeat(50));
1208
1369
 
1209
1370
  return cnLines.join('\n') + '\n' + enLines.join('\n');