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 +1 -1
- package/src/commands/run.ts +33 -4
- package/src/lib/agents/models.ts +21 -8
- package/src/lib/diagnostics/checks.ts +92 -1
package/package.json
CHANGED
package/src/commands/run.ts
CHANGED
|
@@ -582,7 +582,26 @@ export async function startReview(
|
|
|
582
582
|
|
|
583
583
|
const loadedConfig = await runtime.loadConfig();
|
|
584
584
|
|
|
585
|
-
|
|
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
|
},
|
package/src/lib/agents/models.ts
CHANGED
|
@@ -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 (
|
|
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 {
|