ralph-review 0.1.1 → 0.1.3

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": "ralph-review",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "A CLI tool that orchestrates agentic review-fix cycles until your code is clean.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -582,7 +582,26 @@ export async function startReview(
582
582
 
583
583
  const loadedConfig = await runtime.loadConfig();
584
584
 
585
- const hasExplicitMode = options.base || options.uncommitted || options.commit || options.custom;
585
+ if (options.custom !== undefined && options.custom.trim().length === 0) {
586
+ runtime.prompt.log.error("--custom cannot be empty");
587
+ runtime.process.exit(1);
588
+ return;
589
+ }
590
+
591
+ if (options.commit !== undefined) {
592
+ options.commit = options.commit.trim();
593
+ if (options.commit.length === 0) {
594
+ runtime.prompt.log.error("--commit cannot be empty");
595
+ runtime.process.exit(1);
596
+ return;
597
+ }
598
+ }
599
+
600
+ const hasExplicitMode =
601
+ options.base !== undefined ||
602
+ options.uncommitted === true ||
603
+ options.commit !== undefined ||
604
+ options.custom !== undefined;
586
605
  if (!hasExplicitMode) {
587
606
  if (loadedConfig?.defaultReview?.type === "base") {
588
607
  options.base = loadedConfig.defaultReview.branch;
@@ -590,11 +609,20 @@ export async function startReview(
590
609
  // else: defaults to uncommitted behavior (no base flag)
591
610
  }
592
611
 
612
+ if (options.base !== undefined) {
613
+ options.base = options.base.trim();
614
+ if (options.base.length === 0) {
615
+ runtime.prompt.log.error("--base cannot be empty");
616
+ runtime.process.exit(1);
617
+ return;
618
+ }
619
+ }
620
+
593
621
  const modeOptions = [
594
- options.base && "--base",
622
+ options.base !== undefined && "--base",
595
623
  options.uncommitted && "--uncommitted",
596
- options.commit && "--commit",
597
- options.custom && "--custom",
624
+ options.commit !== undefined && "--commit",
625
+ options.custom !== undefined && "--custom",
598
626
  ].filter(Boolean);
599
627
 
600
628
  if (modeOptions.length > 1) {
@@ -611,6 +639,7 @@ export async function startReview(
611
639
  projectPath: runtime.process.cwd(),
612
640
  baseBranch: options.base,
613
641
  commitSha: options.commit,
642
+ customInstructions: options.custom,
614
643
  capabilityDiscoveryOptions: {
615
644
  probeAgents: getDynamicProbeAgents(loadedConfig),
616
645
  },
@@ -25,24 +25,33 @@ export const codexModelOptions = [
25
25
  ] as const;
26
26
 
27
27
  export const droidModelOptions = [
28
- { value: "gpt-5.1", label: "GPT-5.1" },
29
- { value: "gpt-5.1-codex", label: "GPT-5.1 Codex" },
30
- { value: "gpt-5.1-codex-max", label: "GPT-5.1 Codex Max" },
31
- { value: "gpt-5.2", label: "GPT-5.2" },
32
- { value: "gpt-5.2-codex", label: "GPT-5.2 Codex" },
33
- { value: "claude-opus-4-6", label: "Claude Opus 4.6" },
34
- { value: "claude-sonnet-4-5-20250929", label: "Claude Sonnet 4.5" },
35
28
  { value: "claude-opus-4-5-20251101", label: "Claude Opus 4.5" },
29
+ { value: "claude-opus-4-6", label: "Claude Opus 4.6 (default)" },
30
+ { value: "claude-opus-4-6-fast", label: "Claude Opus 4.6 Fast Mode" },
31
+ { value: "claude-sonnet-4-5-20250929", label: "Claude Sonnet 4.5" },
32
+ { value: "claude-sonnet-4-6", label: "Claude Sonnet 4.6" },
36
33
  { value: "claude-haiku-4-5-20251001", label: "Claude Haiku 4.5" },
34
+ { value: "gpt-5.1", label: "GPT-5.1" },
35
+ { value: "gpt-5.1-codex", label: "GPT-5.1-Codex" },
36
+ { value: "gpt-5.1-codex-max", label: "GPT-5.1-Codex-Max" },
37
+ { value: "gpt-5.2", label: "GPT-5.2" },
38
+ { value: "gpt-5.2-codex", label: "GPT-5.2-Codex" },
39
+ { value: "gpt-5.3-codex", label: "GPT-5.3-Codex" },
37
40
  { value: "gemini-3-pro-preview", label: "Gemini 3 Pro" },
41
+ { value: "gemini-3.1-pro-preview", label: "Gemini 3.1 Pro" },
38
42
  { value: "gemini-3-flash-preview", label: "Gemini 3 Flash" },
39
43
  { value: "glm-4.7", label: "Droid Core (GLM-4.7)" },
44
+ { value: "glm-5", label: "Droid Core (GLM-5)" },
40
45
  { value: "kimi-k2.5", label: "Droid Core (Kimi K2.5)" },
46
+ { value: "minimax-m2.5", label: "Droid Core (MiniMax M2.5)" },
41
47
  ] as const;
42
48
 
43
49
  export const geminiModelOptions = [
44
- { value: "gemini-3-pro-preview", label: "Gemini 3 Pro" },
50
+ { value: "gemini-3.1-pro-preview", label: "Gemini 3.1 Pro" },
45
51
  { value: "gemini-3-flash-preview", label: "Gemini 3 Flash" },
52
+ { value: "gemini-2.5-pro", label: "Gemini 2.5 Pro" },
53
+ { value: "gemini-2.5-flash", label: "Gemini 2.5 Flash" },
54
+ { value: "gemini-2.5-flash-lite", label: "Gemini 2.5 Flash Lite" },
46
55
  ] as const;
47
56
 
48
57
  const commonReasoningLevels: readonly ReasoningLevel[] = ["low", "medium", "high", "xhigh"];
@@ -53,11 +62,15 @@ const droidReasoningLevelsByModel: Record<string, readonly ReasoningLevel[]> = {
53
62
  "gpt-5.1-codex-max": ["low", "medium", "high", "xhigh"],
54
63
  "gpt-5.2": ["low", "medium", "high", "xhigh"],
55
64
  "gpt-5.2-codex": ["low", "medium", "high", "xhigh"],
65
+ "gpt-5.3-codex": ["low", "medium", "high", "xhigh"],
56
66
  "claude-sonnet-4-5-20250929": ["low", "medium", "high"],
67
+ "claude-sonnet-4-6": ["low", "medium", "high"],
57
68
  "claude-opus-4-5-20251101": ["low", "medium", "high"],
58
69
  "claude-haiku-4-5-20251001": ["low", "medium", "high"],
59
70
  "claude-opus-4-6": ["low", "medium", "high", "max"],
71
+ "claude-opus-4-6-fast": ["low", "medium", "high", "max"],
60
72
  "gemini-3-pro-preview": ["low", "medium", "high"],
73
+ "gemini-3.1-pro-preview": ["low", "medium", "high"],
61
74
  "gemini-3-flash-preview": ["low", "medium", "high"],
62
75
  };
63
76
 
@@ -20,6 +20,7 @@ interface RunDiagnosticsDependencies {
20
20
  ) => Promise<AgentCapabilitiesMap>;
21
21
  isGitRepository?: (path: string) => Promise<boolean>;
22
22
  hasUncommittedChanges?: (path: string) => Promise<boolean>;
23
+ gitRefExists?: (path: string, ref: string) => Promise<boolean>;
23
24
  cleanupStaleLockfile?: typeof cleanupStaleLockfile;
24
25
  hasActiveLockfile?: typeof hasActiveLockfile;
25
26
  isTmuxInstalled?: () => boolean;
@@ -34,6 +35,7 @@ export interface RunDiagnosticsOptions {
34
35
  projectPath?: string;
35
36
  baseBranch?: string;
36
37
  commitSha?: string;
38
+ customInstructions?: string;
37
39
  capabilitiesByAgent?: AgentCapabilitiesMap;
38
40
  capabilityDiscoveryOptions?: CapabilityDiscoveryOptions;
39
41
  dependencies?: RunDiagnosticsDependencies;
@@ -136,6 +138,15 @@ async function hasGitUncommittedChanges(path: string): Promise<boolean> {
136
138
  return result.stdout.trim().length > 0;
137
139
  }
138
140
 
141
+ async function hasGitRef(path: string, ref: string): Promise<boolean> {
142
+ if (ref.trim().length === 0) {
143
+ return false;
144
+ }
145
+
146
+ const result = await runGitInPath(path, ["rev-parse", "--verify", ref]);
147
+ return result.exitCode === 0;
148
+ }
149
+
139
150
  function buildReport(
140
151
  context: DiagnosticContext,
141
152
  items: DiagnosticItem[],
@@ -165,6 +176,7 @@ export async function runDiagnostics(
165
176
  const resolveCapabilityDiscovery = deps.discoverAgentCapabilities ?? discoverAgentCapabilities;
166
177
  const resolveIsGitRepo = deps.isGitRepository ?? isGitRepository;
167
178
  const resolveHasChanges = deps.hasUncommittedChanges ?? hasGitUncommittedChanges;
179
+ const resolveGitRefExists = deps.gitRefExists ?? hasGitRef;
168
180
  const resolveCleanupStaleLockfile = deps.cleanupStaleLockfile ?? cleanupStaleLockfile;
169
181
  const resolveHasActiveLockfile = deps.hasActiveLockfile ?? hasActiveLockfile;
170
182
  const resolveIsTmuxInstalled = deps.isTmuxInstalled ?? isTmuxInstalled;
@@ -423,7 +435,86 @@ export async function runDiagnostics(
423
435
  }
424
436
 
425
437
  if (context === "run") {
426
- if (!options.baseBranch && !options.commitSha && insideGitRepo && !gitRepoError) {
438
+ if (insideGitRepo && !gitRepoError && options.baseBranch) {
439
+ let baseRefExists = false;
440
+ let baseRefError: string | null = null;
441
+ try {
442
+ baseRefExists = await resolveGitRefExists(projectPath, options.baseBranch);
443
+ } catch (error) {
444
+ baseRefError = `${error}`;
445
+ }
446
+
447
+ if (baseRefError) {
448
+ items.push({
449
+ id: "git-base-ref",
450
+ category: "git",
451
+ title: "Base ref",
452
+ severity: "error",
453
+ summary: `Unable to validate base ref '${options.baseBranch}'.`,
454
+ details: baseRefError,
455
+ remediation: [runStep("git branch --all"), thenStep("rr run --base <existing-ref>")],
456
+ });
457
+ } else {
458
+ items.push({
459
+ id: "git-base-ref",
460
+ category: "git",
461
+ title: "Base ref",
462
+ severity: baseRefExists ? "ok" : "error",
463
+ summary: baseRefExists
464
+ ? `Base ref '${options.baseBranch}' exists.`
465
+ : `Base ref '${options.baseBranch}' was not found.`,
466
+ remediation: baseRefExists
467
+ ? []
468
+ : [runStep("git branch --all"), thenStep("rr run --base <existing-ref>")],
469
+ });
470
+ }
471
+ }
472
+
473
+ if (insideGitRepo && !gitRepoError && options.commitSha) {
474
+ let commitRefExists = false;
475
+ let commitRefError: string | null = null;
476
+ try {
477
+ commitRefExists = await resolveGitRefExists(projectPath, options.commitSha);
478
+ } catch (error) {
479
+ commitRefError = `${error}`;
480
+ }
481
+
482
+ if (commitRefError) {
483
+ items.push({
484
+ id: "git-commit-ref",
485
+ category: "git",
486
+ title: "Commit ref",
487
+ severity: "error",
488
+ summary: `Unable to validate commit ref '${options.commitSha}'.`,
489
+ details: commitRefError,
490
+ remediation: [
491
+ runStep("git rev-parse --verify <commit>"),
492
+ thenStep("rr run --commit <sha>"),
493
+ ],
494
+ });
495
+ } else {
496
+ items.push({
497
+ id: "git-commit-ref",
498
+ category: "git",
499
+ title: "Commit ref",
500
+ severity: commitRefExists ? "ok" : "error",
501
+ summary: commitRefExists
502
+ ? `Commit ref '${options.commitSha}' exists.`
503
+ : `Commit ref '${options.commitSha}' was not found.`,
504
+ remediation: commitRefExists
505
+ ? []
506
+ : [runStep("git log --oneline -n 20"), thenStep("rr run --commit <existing-sha>")],
507
+ });
508
+ }
509
+ }
510
+
511
+ if (
512
+ !options.baseBranch &&
513
+ !options.commitSha &&
514
+ !options.customInstructions &&
515
+ insideGitRepo &&
516
+ !gitRepoError
517
+ ) {
427
518
  let hasChanges = false;
428
519
  let hasChangesError: string | null = null;
429
520
  try {