git-ai-review 2.3.3 → 2.4.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.
File without changes
package/dist/cli.d.ts CHANGED
@@ -12,4 +12,5 @@ export declare function hasVerboseFlag(args: string[]): boolean;
12
12
  export declare function hasClaudeFlag(args: string[]): boolean;
13
13
  export declare function hasCodexFlag(args: string[]): boolean;
14
14
  export declare function hasCopilotFlag(args: string[]): boolean;
15
+ export declare function hasAllReviewersFlag(args: string[]): boolean;
15
16
  export declare function runCli(argv?: string[], deps?: CliDeps): Promise<number>;
package/dist/cli.js CHANGED
@@ -22,10 +22,11 @@ function printHelp(log) {
22
22
  log(' install Install .githooks/pre-commit and set core.hooksPath');
23
23
  log('');
24
24
  log('Options:');
25
- log(' --codex Force Codex reviewer only (skip Copilot/Claude fallback)');
26
- log(' --copilot Force Copilot reviewer only (skip Codex/Claude)');
27
- log(' --claude Force Claude reviewer only (skip Codex/Copilot)');
28
- log(' --verbose Print full prompt and raw model outputs to stdout');
25
+ log(' --codex Force Codex reviewer only (skip Copilot/Claude fallback)');
26
+ log(' --copilot Force Copilot reviewer only (skip Codex/Claude)');
27
+ log(' --claude Force Claude reviewer only (skip Codex/Copilot)');
28
+ log(' --all-reviewers Run all 3 reviewers sequentially (not just first available)');
29
+ log(' --verbose Write full prompt and raw model outputs to .git/ai-review-verbose.log');
29
30
  }
30
31
  export function hasVerboseFlag(args) {
31
32
  return args.includes('--verbose');
@@ -39,22 +40,34 @@ export function hasCodexFlag(args) {
39
40
  export function hasCopilotFlag(args) {
40
41
  return args.includes('--copilot');
41
42
  }
43
+ export function hasAllReviewersFlag(args) {
44
+ return args.includes('--all-reviewers');
45
+ }
42
46
  export async function runCli(argv = process.argv.slice(2), deps = DEFAULT_CLI_DEPS) {
47
+ if (argv.includes('--help') || argv.includes('-h')) {
48
+ printHelp(deps.log);
49
+ return 0;
50
+ }
43
51
  const command = argv[0] || 'help';
44
52
  const args = argv.slice(1);
45
53
  const verbose = hasVerboseFlag(args);
46
54
  const useClaude = hasClaudeFlag(args);
47
55
  const useCodex = hasCodexFlag(args);
48
56
  const useCopilot = hasCopilotFlag(args);
57
+ const allReviewers = hasAllReviewersFlag(args);
49
58
  const cwd = deps.getCwd();
50
59
  const selectedCount = [useClaude, useCodex, useCopilot].filter(Boolean).length;
51
60
  if (selectedCount > 1) {
52
61
  deps.log('Error: --claude, --codex, and --copilot are mutually exclusive.');
53
62
  return 1;
54
63
  }
64
+ if (allReviewers && selectedCount > 0) {
65
+ deps.log('Error: --all-reviewers cannot be combined with --claude, --codex, or --copilot.');
66
+ return 1;
67
+ }
55
68
  const reviewer = useClaude ? 'claude' : useCodex ? 'codex' : useCopilot ? 'copilot' : undefined;
56
69
  if (command === 'review') {
57
- const result = await deps.runReview(cwd, { verbose, reviewer });
70
+ const result = await deps.runReview(cwd, { verbose, reviewer, allReviewers });
58
71
  return result.pass ? 0 : 1;
59
72
  }
60
73
  if (command === 'diff') {
@@ -65,14 +78,14 @@ export async function runCli(argv = process.argv.slice(2), deps = DEFAULT_CLI_DE
65
78
  deps.log('Error: diff command requires a base branch. Usage: git-ai-review diff <base> [head]');
66
79
  return 1;
67
80
  }
68
- const result = await deps.runReview(cwd, { verbose, reviewer }, {
81
+ const result = await deps.runReview(cwd, { verbose, reviewer, allReviewers }, {
69
82
  getStagedDiff: () => getDiffBetweenRefs(base, head, cwd),
70
83
  buildPrompt: (diff, promptCwd) => buildPrompt(diff, promptCwd, `Branch diff (${base}...${head})`),
71
84
  });
72
85
  return result.pass ? 0 : 1;
73
86
  }
74
87
  if (command === 'pre-commit') {
75
- return await deps.runPreCommit(cwd, { verbose, reviewer });
88
+ return await deps.runPreCommit(cwd, { verbose, reviewer, allReviewers });
76
89
  }
77
90
  if (command === 'install') {
78
91
  deps.installPreCommitHook();
@@ -2,6 +2,7 @@ import { runReview } from './review.js';
2
2
  export interface PreCommitOptions {
3
3
  verbose?: boolean;
4
4
  reviewer?: 'claude' | 'codex' | 'copilot';
5
+ allReviewers?: boolean;
5
6
  }
6
7
  export interface PreCommitDeps {
7
8
  runReviewFn: typeof runReview;
package/dist/precommit.js CHANGED
@@ -42,7 +42,7 @@ export async function runPreCommit(cwd = process.cwd(), options = {}, deps = {})
42
42
  printLastReport(reportPath);
43
43
  return 1;
44
44
  }
45
- const review = await runReviewFn(cwd, { verbose, reviewer: options.reviewer });
45
+ const review = await runReviewFn(cwd, { verbose, reviewer: options.reviewer, allReviewers: options.allReviewers });
46
46
  if (review.pass) {
47
47
  rmSync(failCountPath, { force: true });
48
48
  return 0;
package/dist/review.d.ts CHANGED
@@ -7,6 +7,7 @@ export declare function clearLastUnavailable(cwd: string): void;
7
7
  export interface RunReviewOptions {
8
8
  verbose?: boolean;
9
9
  reviewer?: 'claude' | 'codex' | 'copilot';
10
+ allReviewers?: boolean;
10
11
  }
11
12
  export interface RunReviewDeps {
12
13
  getStagedDiff: () => string;
package/dist/review.js CHANGED
@@ -488,6 +488,13 @@ function printReview(model, result, usage) {
488
488
  export async function runReview(cwd = process.cwd(), options = {}, deps = {}) {
489
489
  const verbose = options.verbose === true;
490
490
  const reviewer = options.reviewer;
491
+ const allReviewers = options.allReviewers === true;
492
+ const verboseLogPath = verbose ? resolve(cwd, getGitPath('ai-review-verbose.log')) : null;
493
+ const verboseLines = [];
494
+ const appendVerbose = (line) => {
495
+ if (verbose)
496
+ verboseLines.push(line);
497
+ };
491
498
  const runtimeDeps = {
492
499
  getStagedDiff,
493
500
  buildPrompt,
@@ -507,6 +514,9 @@ export async function runReview(cwd = process.cwd(), options = {}, deps = {}) {
507
514
  }
508
515
  const prompt = runtimeDeps.buildPrompt(diff, cwd);
509
516
  if (verbose) {
517
+ appendVerbose(`[${new Date().toISOString()}] ----- REVIEW PROMPT (FULL) -----`);
518
+ appendVerbose(prompt);
519
+ appendVerbose('----- END REVIEW PROMPT -----\n');
510
520
  runtimeDeps.log('----- REVIEW PROMPT (FULL) -----');
511
521
  runtimeDeps.log(prompt);
512
522
  runtimeDeps.log('----- END REVIEW PROMPT -----');
@@ -526,6 +536,27 @@ export async function runReview(cwd = process.cwd(), options = {}, deps = {}) {
526
536
  runtimeDeps.log('Running Codex review (--codex)...');
527
537
  codex = await runtimeDeps.runCodex(prompt, verbose, true);
528
538
  }
539
+ else if (allReviewers) {
540
+ const runners = [
541
+ ['codex', 'Codex', runtimeDeps.runCodex],
542
+ ['copilot', 'Copilot', runtimeDeps.runCopilot],
543
+ ['claude', 'Claude', runtimeDeps.runClaude],
544
+ ];
545
+ const results = {
546
+ codex: { available: false },
547
+ copilot: { available: false },
548
+ claude: { available: false },
549
+ };
550
+ for (const [name, label, runner] of runners) {
551
+ runtimeDeps.log(`Running ${label} review...`);
552
+ appendVerbose(`[${new Date().toISOString()}] Running ${label} review...`);
553
+ results[name] = await runner(prompt, verbose);
554
+ appendVerbose(`[${new Date().toISOString()}] ${label}: ${results[name].available ? 'completed' : 'unavailable'}`);
555
+ }
556
+ claude = results.claude;
557
+ codex = results.codex;
558
+ copilot = results.copilot;
559
+ }
529
560
  else {
530
561
  const lastUnavailable = readLastUnavailable(cwd);
531
562
  const fallbackOrder = buildFallbackOrder(lastUnavailable);
@@ -593,6 +624,17 @@ export async function runReview(cwd = process.cwd(), options = {}, deps = {}) {
593
624
  }
594
625
  const verdict = evaluateResults(claude, codex, copilot);
595
626
  console.log(`\n${verdict.reason}`);
627
+ if (verboseLogPath && verboseLines.length > 0) {
628
+ appendVerbose(`\n[${new Date().toISOString()}] Verdict: ${verdict.reason}`);
629
+ appendVerbose(`Report: ${JSON.stringify(report, null, 2)}`);
630
+ try {
631
+ writeFileSync(verboseLogPath, verboseLines.join('\n'), 'utf8');
632
+ console.log(`Verbose log written to: ${verboseLogPath}`);
633
+ }
634
+ catch {
635
+ console.error(`Warning: could not write verbose log to ${verboseLogPath}`);
636
+ }
637
+ }
596
638
  return { pass: verdict.pass, reportPath, reason: verdict.reason };
597
639
  }
598
640
  if (process.argv[1] && import.meta.url === pathToFileURL(resolve(process.argv[1])).href) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "git-ai-review",
3
- "version": "2.3.3",
3
+ "version": "2.4.0",
4
4
  "description": "Review your git diff with local Codex CLI, Copilot CLI, or Claude CLI — run manually in one command or automatically as a git hook",
5
5
  "type": "module",
6
6
  "main": "dist/cli.js",