ralph-review 0.2.2 → 0.2.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/README.md +123 -16
- package/package.json +6 -4
- package/src/cli-core.ts +51 -88
- package/src/cli-rrr.ts +1 -4
- package/src/cli.ts +1 -2
- package/src/commands/apply.ts +6 -14
- package/src/commands/config-handlers.ts +68 -69
- package/src/commands/config-model.ts +147 -125
- package/src/commands/doctor.ts +2 -4
- package/src/commands/fix.ts +73 -51
- package/src/commands/handoff-selection.ts +6 -8
- package/src/commands/interactive-deps.ts +18 -0
- package/src/commands/log.ts +12 -12
- package/src/commands/run.ts +32 -33
- package/src/commands/stop.ts +6 -13
- package/src/commands/update.ts +2 -4
- package/src/lib/agents/claude.ts +4 -16
- package/src/lib/agents/core.ts +16 -0
- package/src/lib/agents/droid.ts +4 -15
- package/src/lib/cli-parser.ts +19 -14
- package/src/lib/handoff.ts +16 -7
- package/src/lib/logging/session-log.ts +2 -1
- package/src/lib/prompts/defaults/review.md +1 -1
- package/src/lib/prompts/protocol.ts +2 -1
- package/src/lib/review-workflow/findings/artifact.ts +3 -1
- package/src/lib/review-workflow/findings/types.ts +1 -1
- package/src/lib/review-workflow/remediation/prompt.ts +7 -7
- package/src/lib/review-workflow/remediation/run-batch-fix-phase.ts +30 -20
- package/src/lib/review-workflow/remediation/run-fix-session.ts +70 -68
- package/src/lib/review-workflow/results/finalize-result.ts +20 -3
- package/src/lib/review-workflow/run-review-cycle.ts +1 -12
- package/src/lib/review-workflow/session-status.ts +13 -0
- package/src/lib/review-workflow/shared/framed-json.ts +2 -47
- package/src/lib/session/state.ts +50 -38
- package/src/lib/structured-output.ts +24 -9
- package/src/lib/tui/dashboard/HelpOverlay.tsx +13 -57
- package/src/lib/tui/dashboard/StatusBar.tsx +12 -50
- package/src/lib/tui/dashboard/StopSessionPickerOverlay.tsx +4 -22
- package/src/lib/tui/sessions/detail/DetailPane.tsx +6 -64
- package/src/lib/tui/sessions/detail/IdleStateView.tsx +10 -12
- package/src/lib/tui/sessions/detail/SessionDetailView.tsx +1 -1
- package/src/lib/tui/sessions/detail/session-detail-parts.tsx +66 -87
- package/src/lib/tui/sessions/history/SessionListOverlay.tsx +17 -75
- package/src/lib/tui/sessions/review-summary-parser.ts +2 -68
- package/src/lib/tui/shared/CenteredModal.tsx +44 -0
- package/src/lib/tui/shared/KeyboardShortcutsModal.tsx +14 -0
- package/src/lib/tui/shared/ShortcutHint.tsx +33 -0
- package/src/lib/tui/workspace/Workspace.tsx +6 -91
- package/src/lib/tui/workspace/use-workspace-state.ts +44 -37
- package/src/lib/types/fix.ts +15 -48
- package/src/lib/types/guards.ts +47 -0
- package/src/lib/types/review.ts +5 -39
package/README.md
CHANGED
|
@@ -13,7 +13,7 @@ Orchestrating coding agents for code review, verification, and fixing via the Ra
|
|
|
13
13
|
|
|
14
14
|
- [How It Works](#how-it-works)
|
|
15
15
|
- [Reviewer and Fixer Flow](#reviewer-and-fixer-flow)
|
|
16
|
-
- [
|
|
16
|
+
- [Workflows](#workflows)
|
|
17
17
|
- [Prerequisites](#prerequisites)
|
|
18
18
|
- [Installation](#installation)
|
|
19
19
|
- [Quick Start](#quick-start)
|
|
@@ -97,24 +97,131 @@ findings remain unresolved, Ralph Review keeps the remediation worktree availabl
|
|
|
97
97
|
|
|
98
98
|
---
|
|
99
99
|
|
|
100
|
-
##
|
|
100
|
+
## Workflows
|
|
101
101
|
|
|
102
|
-
|
|
103
|
-
|
|
102
|
+
Ralph Review fits into a few common loops. Each workflow below shows the scenario
|
|
103
|
+
first, then the exact commands.
|
|
104
104
|
|
|
105
|
-
|
|
105
|
+
### 1. Review what you are about to commit
|
|
106
106
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
107
|
+
You just finished a feature on a working branch. Before you stage and push, you
|
|
108
|
+
want a second pair of eyes on staged, unstaged, and untracked changes.
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
rr run --uncommitted
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Findings are printed and persisted. You can keep coding while the reviewer runs
|
|
115
|
+
in the background and come back to triage when it is done.
|
|
116
|
+
|
|
117
|
+
### 2. Review your branch against the base
|
|
118
|
+
|
|
119
|
+
You are preparing a pull request against `main` and want the reviewer to look at
|
|
120
|
+
the full diff, not just your last commit.
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
rr run --base main
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Add a focus instruction when the diff is large:
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
rr run --base main "focus on auth boundaries and input validation"
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### 3. Re-review a single commit
|
|
133
|
+
|
|
134
|
+
CI flagged something on a specific commit, or you are auditing a hotfix in
|
|
135
|
+
isolation.
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
rr run --commit 9f3a2b1
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### 4. Review now, fix later (the default loop)
|
|
142
|
+
|
|
143
|
+
`rr run` only reviews. Findings get a stable ID (`F001`, `F002`, ...) and a
|
|
144
|
+
priority (`P0` through `P3`). Triage them, then fix exactly what you want:
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
rr run
|
|
148
|
+
rr fix --session SESSION --priority P0,P1
|
|
149
|
+
# or pick by ID
|
|
150
|
+
rr fix --session SESSION --id F003 --id F007
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
When the fixer is done it either applies the patch to your working tree
|
|
154
|
+
automatically, or leaves a pending handoff that you apply explicitly:
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
rr apply
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### 5. Review and auto-fix in one shot
|
|
161
|
+
|
|
162
|
+
Trusted change, low risk, you want the loop to close itself:
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
rr run --auto --priority P0,P1
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
The reviewer runs first, then the fixer immediately remediates only matching
|
|
169
|
+
priorities in a disposable worktree and hands the result back to you.
|
|
170
|
+
|
|
171
|
+
> Auto Setup picks the reviewer model based on Factory.ai's public code review
|
|
172
|
+
> benchmark. See [Why these models?](#why-these-models-auto-setup-model-selection)
|
|
173
|
+
> below.
|
|
174
|
+
|
|
175
|
+
### 6. Pre-PR / team review workflow
|
|
176
|
+
|
|
177
|
+
Before opening a PR, run a base-branch review and let auto-fix clean up the
|
|
178
|
+
obvious stuff. Anything left becomes review notes you can paste into the PR
|
|
179
|
+
description.
|
|
180
|
+
|
|
181
|
+
```bash
|
|
182
|
+
rr run --base main --auto --priority P0
|
|
183
|
+
rr log -n 1 # grab the latest review log
|
|
184
|
+
rr log --json # or pipe into your own tooling
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
For an org-wide loop, persisted review logs (`rr log --json --global`) make it
|
|
188
|
+
easy to feed findings into dashboards or follow-up issues.
|
|
189
|
+
|
|
190
|
+
### 7. Triage with Interactive Mode
|
|
191
|
+
|
|
192
|
+
Run `rr` with no arguments to open Interactive Mode. It shows active sessions,
|
|
193
|
+
recent history, review output, findings, fix results, and handoff status in a
|
|
194
|
+
single view, which is convenient when several reviews are in flight.
|
|
195
|
+
|
|
196
|
+
### Why these models? (Auto Setup model selection)
|
|
197
|
+
|
|
198
|
+
`rr init` with Auto Setup chooses your reviewer and fixer's model. That list is
|
|
199
|
+
informed by Factory.ai's public code review benchmark, which scored 13 frontier
|
|
200
|
+
and open-weight models against a golden set of 50 human-curated bugs across
|
|
201
|
+
five real-world repositories (Sentry, Grafana, Keycloak, Discourse, and
|
|
202
|
+
cal.com).
|
|
203
|
+
|
|
204
|
+
The reviewer model priority (highest first) is currently:
|
|
205
|
+
|
|
206
|
+
1. GPT-5.2
|
|
207
|
+
2. Claude Opus 4.6
|
|
208
|
+
3. Claude Sonnet 4.6
|
|
209
|
+
4. Claude Opus 4.7
|
|
210
|
+
5. GLM 5.1
|
|
211
|
+
6. GPT-5.3 Codex
|
|
212
|
+
7. Gemini 3.1 Pro Preview
|
|
213
|
+
8. Kimi K2.6
|
|
214
|
+
|
|
215
|
+
Models near the top scored highest on the benchmark for finding real bugs at a
|
|
216
|
+
reasonable cost. The fixer priority is tuned separately and favors models that
|
|
217
|
+
are strong at code edits rather than at finding issues.
|
|
218
|
+
|
|
219
|
+
For the methodology and full results, see Factory.ai's writeup:
|
|
220
|
+
[Which Model Reviews Code Best?](https://factory.ai/news/code-review-benchmark).
|
|
221
|
+
|
|
222
|
+
You can always override the auto selection by running `rr init` and choosing
|
|
223
|
+
Customize Setup, or by editing `reviewer.model` and `fixer.model` in your
|
|
224
|
+
configuration directly.
|
|
118
225
|
|
|
119
226
|
---
|
|
120
227
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ralph-review",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.3",
|
|
4
4
|
"description": "Orchestrating coding agents for code review, verification and fixing via the ralph loop.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -53,15 +53,16 @@
|
|
|
53
53
|
"publish:recover": "bun run scripts/publish.ts --recover",
|
|
54
54
|
"publish:recover:execute": "bun run scripts/publish.ts --recover --execute",
|
|
55
55
|
"rr": "bun run src/cli.ts",
|
|
56
|
-
"test": "bun test",
|
|
56
|
+
"test": "AGENT=1 bun test",
|
|
57
57
|
"prepublishOnly": "bun test",
|
|
58
58
|
"typecheck": "tsc --noEmit",
|
|
59
59
|
"knip": "knip-bun",
|
|
60
|
+
"check-duplicates": "bunx jscpd src tests --exitCode 1 --reporters ai",
|
|
60
61
|
"lint": "biome check --write .",
|
|
61
62
|
"lint:ci": "biome ci .",
|
|
62
63
|
"lint-staged": "lint-staged",
|
|
63
|
-
"check": "bun run typecheck && bun run knip && bun run lint && AGENT=1 bun test --coverage",
|
|
64
|
-
"check:ci": "bun run typecheck && bun run knip && bun run lint:ci && AGENT=1 bun test --coverage",
|
|
64
|
+
"check": "bun run typecheck && bun run knip && bun run lint && bun run check-duplicates && AGENT=1 bun test --coverage",
|
|
65
|
+
"check:ci": "bun run typecheck && bun run knip && bun run lint:ci && bun run check-duplicates && AGENT=1 bun test --coverage",
|
|
65
66
|
"prepare": "husky && bun run setup-hooks",
|
|
66
67
|
"setup-hooks": "bun -e 'await Bun.write(\".husky/pre-commit\", \"#!/usr/bin/env sh\\n\\nbun run knip && bun run lint-staged\\n\")' && chmod +x .husky/pre-commit"
|
|
67
68
|
},
|
|
@@ -70,6 +71,7 @@
|
|
|
70
71
|
"@types/bun": "latest",
|
|
71
72
|
"@types/react": "^19.0.0",
|
|
72
73
|
"husky": "^9.1.7",
|
|
74
|
+
"jscpd": "^4.0.9",
|
|
73
75
|
"knip": "^5.82.1",
|
|
74
76
|
"lint-staged": "^16.2.7",
|
|
75
77
|
"typescript": "^5",
|
package/src/cli-core.ts
CHANGED
|
@@ -1,5 +1,52 @@
|
|
|
1
1
|
import { type CommandDef, formatCommandHelp, formatMainHelp } from "./lib/cli-parser";
|
|
2
2
|
|
|
3
|
+
const RUN_REVIEW_OPTIONS: CommandDef["options"] = [
|
|
4
|
+
{ name: "max", alias: "m", type: "number", description: "Max iterations" },
|
|
5
|
+
{
|
|
6
|
+
name: "force",
|
|
7
|
+
alias: "f",
|
|
8
|
+
type: "boolean",
|
|
9
|
+
description: "Run full max iterations even if no issues are found",
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
name: "auto",
|
|
13
|
+
type: "boolean",
|
|
14
|
+
description: "Automatically run remediation after review completes",
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
name: "priority",
|
|
18
|
+
type: "string",
|
|
19
|
+
placeholder: "P0,P1",
|
|
20
|
+
description: "Priority filter for --auto using comma-separated values",
|
|
21
|
+
},
|
|
22
|
+
];
|
|
23
|
+
|
|
24
|
+
const FIX_SELECTION_OPTIONS: CommandDef["options"] = [
|
|
25
|
+
{
|
|
26
|
+
name: "session",
|
|
27
|
+
alias: "s",
|
|
28
|
+
type: "string",
|
|
29
|
+
description: "Session ID whose persisted findings should be fixed",
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
name: "all",
|
|
33
|
+
type: "boolean",
|
|
34
|
+
description: "Select all persisted findings for remediation",
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
name: "priority",
|
|
38
|
+
type: "string",
|
|
39
|
+
placeholder: "P0,P1,P2,P3",
|
|
40
|
+
description: "Select findings by priority (comma-separated values)",
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
name: "id",
|
|
44
|
+
type: "string",
|
|
45
|
+
placeholder: "F001",
|
|
46
|
+
description: "Select findings by ID (repeatable)",
|
|
47
|
+
},
|
|
48
|
+
];
|
|
49
|
+
|
|
3
50
|
export const COMMANDS: CommandDef[] = [
|
|
4
51
|
{
|
|
5
52
|
name: "init",
|
|
@@ -67,24 +114,7 @@ export const COMMANDS: CommandDef[] = [
|
|
|
67
114
|
name: "run",
|
|
68
115
|
description: "Run review only and persist findings for later fixing",
|
|
69
116
|
options: [
|
|
70
|
-
|
|
71
|
-
{
|
|
72
|
-
name: "force",
|
|
73
|
-
alias: "f",
|
|
74
|
-
type: "boolean",
|
|
75
|
-
description: "Run full max iterations even if no issues are found",
|
|
76
|
-
},
|
|
77
|
-
{
|
|
78
|
-
name: "auto",
|
|
79
|
-
type: "boolean",
|
|
80
|
-
description: "Automatically run remediation after review completes",
|
|
81
|
-
},
|
|
82
|
-
{
|
|
83
|
-
name: "priority",
|
|
84
|
-
type: "string",
|
|
85
|
-
placeholder: "P0,P1",
|
|
86
|
-
description: "Priority filter for --auto using comma-separated values",
|
|
87
|
-
},
|
|
117
|
+
...RUN_REVIEW_OPTIONS,
|
|
88
118
|
{
|
|
89
119
|
name: "base",
|
|
90
120
|
type: "string",
|
|
@@ -130,31 +160,7 @@ export const COMMANDS: CommandDef[] = [
|
|
|
130
160
|
{
|
|
131
161
|
name: "fix",
|
|
132
162
|
description: "Fix selected findings from a persisted review session",
|
|
133
|
-
options:
|
|
134
|
-
{
|
|
135
|
-
name: "session",
|
|
136
|
-
alias: "s",
|
|
137
|
-
type: "string",
|
|
138
|
-
description: "Session ID whose persisted findings should be fixed",
|
|
139
|
-
},
|
|
140
|
-
{
|
|
141
|
-
name: "all",
|
|
142
|
-
type: "boolean",
|
|
143
|
-
description: "Select all persisted findings for remediation",
|
|
144
|
-
},
|
|
145
|
-
{
|
|
146
|
-
name: "priority",
|
|
147
|
-
type: "string",
|
|
148
|
-
placeholder: "P0|P1|P2|P3",
|
|
149
|
-
description: "Select findings by priority (repeatable)",
|
|
150
|
-
},
|
|
151
|
-
{
|
|
152
|
-
name: "id",
|
|
153
|
-
type: "string",
|
|
154
|
-
placeholder: "F001",
|
|
155
|
-
description: "Select findings by ID (repeatable)",
|
|
156
|
-
},
|
|
157
|
-
],
|
|
163
|
+
options: FIX_SELECTION_OPTIONS,
|
|
158
164
|
examples: [
|
|
159
165
|
"rr fix --session session-123 --all",
|
|
160
166
|
"rr fix --session session-123 --priority P0,P1",
|
|
@@ -312,56 +318,13 @@ export const COMMANDS: CommandDef[] = [
|
|
|
312
318
|
name: "_run-foreground",
|
|
313
319
|
description: "Internal: run review session in tmux foreground",
|
|
314
320
|
hidden: true,
|
|
315
|
-
options:
|
|
316
|
-
{ name: "max", type: "number", description: "Max iterations" },
|
|
317
|
-
{
|
|
318
|
-
name: "force",
|
|
319
|
-
alias: "f",
|
|
320
|
-
type: "boolean",
|
|
321
|
-
description: "Run full max iterations even if no issues are found",
|
|
322
|
-
},
|
|
323
|
-
{
|
|
324
|
-
name: "auto",
|
|
325
|
-
type: "boolean",
|
|
326
|
-
description: "Automatically run remediation after review completes",
|
|
327
|
-
},
|
|
328
|
-
{
|
|
329
|
-
name: "priority",
|
|
330
|
-
type: "string",
|
|
331
|
-
placeholder: "P0,P1",
|
|
332
|
-
description: "Priority filter for --auto using comma-separated values",
|
|
333
|
-
},
|
|
334
|
-
],
|
|
321
|
+
options: RUN_REVIEW_OPTIONS,
|
|
335
322
|
},
|
|
336
323
|
{
|
|
337
324
|
name: "_fix-foreground",
|
|
338
325
|
description: "Internal: run fixer session in tmux foreground",
|
|
339
326
|
hidden: true,
|
|
340
|
-
options:
|
|
341
|
-
{
|
|
342
|
-
name: "session",
|
|
343
|
-
alias: "s",
|
|
344
|
-
type: "string",
|
|
345
|
-
description: "Session ID whose persisted findings should be fixed",
|
|
346
|
-
},
|
|
347
|
-
{
|
|
348
|
-
name: "all",
|
|
349
|
-
type: "boolean",
|
|
350
|
-
description: "Select all persisted findings for remediation",
|
|
351
|
-
},
|
|
352
|
-
{
|
|
353
|
-
name: "priority",
|
|
354
|
-
type: "string",
|
|
355
|
-
placeholder: "P0|P1|P2|P3",
|
|
356
|
-
description: "Select findings by priority (repeatable)",
|
|
357
|
-
},
|
|
358
|
-
{
|
|
359
|
-
name: "id",
|
|
360
|
-
type: "string",
|
|
361
|
-
placeholder: "F001",
|
|
362
|
-
description: "Select findings by ID (repeatable)",
|
|
363
|
-
},
|
|
364
|
-
],
|
|
327
|
+
options: FIX_SELECTION_OPTIONS,
|
|
365
328
|
},
|
|
366
329
|
];
|
|
367
330
|
|
package/src/cli-rrr.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
|
|
3
3
|
import { printCommandHelp } from "@/cli-core";
|
|
4
|
+
import { CONSOLE_ERROR, CONSOLE_LOG, PROCESS_EXIT } from "@/cli-io";
|
|
4
5
|
import { startReview } from "@/commands/run";
|
|
5
6
|
|
|
6
7
|
export interface RrrDeps {
|
|
@@ -11,10 +12,6 @@ export interface RrrDeps {
|
|
|
11
12
|
exit: (code: number) => void;
|
|
12
13
|
}
|
|
13
14
|
|
|
14
|
-
const CONSOLE_LOG = console.log.bind(console) as (message: string) => void;
|
|
15
|
-
const CONSOLE_ERROR = console.error.bind(console) as (message: string) => void;
|
|
16
|
-
const PROCESS_EXIT = process.exit.bind(process) as (code: number) => void;
|
|
17
|
-
|
|
18
15
|
const DEFAULT_RRR_DEPS: RrrDeps = {
|
|
19
16
|
printCommandHelp,
|
|
20
17
|
startReview,
|
package/src/cli.ts
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import * as p from "@clack/prompts";
|
|
4
4
|
import { getCommandDef, getVersion, parseArgs, printCommandHelp, printUsage } from "./cli-core";
|
|
5
|
+
import { CONSOLE_LOG, PROCESS_EXIT } from "./cli-io";
|
|
5
6
|
import { runApply } from "./commands/apply";
|
|
6
7
|
import { runConfig } from "./commands/config";
|
|
7
8
|
import { runDoctor } from "./commands/doctor";
|
|
@@ -55,10 +56,8 @@ export interface CliDeps {
|
|
|
55
56
|
exit: (code: number) => void;
|
|
56
57
|
}
|
|
57
58
|
|
|
58
|
-
const CONSOLE_LOG = console.log.bind(console) as (message: string) => void;
|
|
59
59
|
const CLACK_ERROR = p.log.error.bind(p.log) as (message: string) => void;
|
|
60
60
|
const CLACK_MESSAGE = p.log.message.bind(p.log) as (message: string) => void;
|
|
61
|
-
const PROCESS_EXIT = process.exit.bind(process) as (code: number) => void;
|
|
62
61
|
const IS_INTERACTIVE_TERMINAL = (): boolean =>
|
|
63
62
|
process.stdin.isTTY === true && process.stdout.isTTY === true;
|
|
64
63
|
|
package/src/commands/apply.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import * as p from "@clack/prompts";
|
|
2
|
-
import { getCommandDef } from "@/cli";
|
|
3
2
|
import { resolvePendingHandoffSelection } from "@/commands/handoff-selection";
|
|
4
|
-
import
|
|
3
|
+
import {
|
|
4
|
+
createInteractiveCommandDeps,
|
|
5
|
+
type InteractiveCommandDeps,
|
|
6
|
+
} from "@/commands/interactive-deps";
|
|
5
7
|
import { parseCommand } from "@/lib/cli-parser";
|
|
6
8
|
import { applyPendingHandoff, listProjectPendingHandoffs } from "@/lib/handoff";
|
|
7
9
|
import { appendLog } from "@/lib/logger";
|
|
@@ -10,19 +12,9 @@ interface ApplyOptions {
|
|
|
10
12
|
session?: string;
|
|
11
13
|
}
|
|
12
14
|
|
|
13
|
-
|
|
14
|
-
getCommandDef: (name: string) => CommandDef | undefined;
|
|
15
|
-
logError: (message: string) => void;
|
|
16
|
-
exit: (code: number) => void;
|
|
17
|
-
isTTY: () => boolean;
|
|
18
|
-
}
|
|
15
|
+
type ApplyDeps = InteractiveCommandDeps;
|
|
19
16
|
|
|
20
|
-
const DEFAULT_APPLY_DEPS
|
|
21
|
-
getCommandDef,
|
|
22
|
-
logError: (message: string) => p.log.error(message),
|
|
23
|
-
exit: (code: number) => process.exit(code),
|
|
24
|
-
isTTY: () => process.stdout.isTTY === true,
|
|
25
|
-
};
|
|
17
|
+
const DEFAULT_APPLY_DEPS = createInteractiveCommandDeps();
|
|
26
18
|
|
|
27
19
|
const NO_PENDING_HANDOFFS_MESSAGE = "No pending review handoffs for current working directory.";
|
|
28
20
|
|
|
@@ -39,6 +39,11 @@ interface ParsedShowArgs extends ParsedScopedArgs {
|
|
|
39
39
|
verbose: boolean;
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
+
type LoadedEffectiveConfig = Awaited<
|
|
43
|
+
ReturnType<ConfigCommandDeps["loadEffectiveConfigWithDiagnostics"]>
|
|
44
|
+
>;
|
|
45
|
+
type ValidEffectiveConfig = LoadedEffectiveConfig & { config: Config };
|
|
46
|
+
|
|
42
47
|
const SHOW_USAGE = "Usage: rr config show [--local|--global] [--json] [--verbose]";
|
|
43
48
|
|
|
44
49
|
function shellQuote(value: string): string {
|
|
@@ -61,61 +66,72 @@ function printValue(value: unknown, print: (value: string) => void): void {
|
|
|
61
66
|
print(String(value));
|
|
62
67
|
}
|
|
63
68
|
|
|
69
|
+
async function warnIfEffectiveConfigInvalid(
|
|
70
|
+
deps: ConfigCommandDeps,
|
|
71
|
+
remediation: string
|
|
72
|
+
): Promise<ValidEffectiveConfig | null> {
|
|
73
|
+
const effective = await deps.loadEffectiveConfigWithDiagnostics(deps.cwd());
|
|
74
|
+
const errors = collectEffectiveConfigValidationErrors(effective);
|
|
75
|
+
if (!effective.config || errors.length > 0) {
|
|
76
|
+
deps.log.warn(
|
|
77
|
+
formatConfigValidationMessage(
|
|
78
|
+
getEffectiveConfigErrorHeader(effective),
|
|
79
|
+
errors.length > 0 ? errors : ["Configuration format is invalid."],
|
|
80
|
+
remediation
|
|
81
|
+
)
|
|
82
|
+
);
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return { ...effective, config: effective.config };
|
|
87
|
+
}
|
|
88
|
+
|
|
64
89
|
function parseScopedArgs(args: string[], defaultScope: ResolvedReadScope): ParsedScopedArgs {
|
|
65
90
|
const positional: string[] = [];
|
|
66
|
-
|
|
67
|
-
let sawLocal = false;
|
|
68
|
-
let sawGlobal = false;
|
|
91
|
+
const state = { scope: defaultScope, sawLocal: false, sawGlobal: false };
|
|
69
92
|
|
|
70
93
|
for (const arg of args) {
|
|
71
|
-
if (arg
|
|
72
|
-
|
|
73
|
-
throw new Error("Cannot use --local and --global together.");
|
|
74
|
-
}
|
|
75
|
-
sawLocal = true;
|
|
76
|
-
scope = "local";
|
|
77
|
-
continue;
|
|
94
|
+
if (!parseScopeFlag(arg, state)) {
|
|
95
|
+
positional.push(arg);
|
|
78
96
|
}
|
|
97
|
+
}
|
|
79
98
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
99
|
+
return { scope: state.scope, positional };
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function parseScopeFlag(
|
|
103
|
+
arg: string,
|
|
104
|
+
state: { scope: ResolvedReadScope; sawLocal: boolean; sawGlobal: boolean }
|
|
105
|
+
): boolean {
|
|
106
|
+
if (arg === "--local") {
|
|
107
|
+
if (state.sawGlobal) {
|
|
108
|
+
throw new Error("Cannot use --local and --global together.");
|
|
87
109
|
}
|
|
110
|
+
state.sawLocal = true;
|
|
111
|
+
state.scope = "local";
|
|
112
|
+
return true;
|
|
113
|
+
}
|
|
88
114
|
|
|
89
|
-
|
|
115
|
+
if (arg === "--global") {
|
|
116
|
+
if (state.sawLocal) {
|
|
117
|
+
throw new Error("Cannot use --local and --global together.");
|
|
118
|
+
}
|
|
119
|
+
state.sawGlobal = true;
|
|
120
|
+
state.scope = "global";
|
|
121
|
+
return true;
|
|
90
122
|
}
|
|
91
123
|
|
|
92
|
-
return
|
|
124
|
+
return false;
|
|
93
125
|
}
|
|
94
126
|
|
|
95
127
|
function parseShowArgs(args: string[]): ParsedShowArgs {
|
|
96
128
|
const positional: string[] = [];
|
|
97
|
-
|
|
98
|
-
let sawLocal = false;
|
|
99
|
-
let sawGlobal = false;
|
|
129
|
+
const scopeState = { scope: "effective" as ResolvedReadScope, sawLocal: false, sawGlobal: false };
|
|
100
130
|
let json = false;
|
|
101
131
|
let verbose = false;
|
|
102
132
|
|
|
103
133
|
for (const arg of args) {
|
|
104
|
-
if (arg
|
|
105
|
-
if (sawGlobal) {
|
|
106
|
-
throw new Error("Cannot use --local and --global together.");
|
|
107
|
-
}
|
|
108
|
-
sawLocal = true;
|
|
109
|
-
scope = "local";
|
|
110
|
-
continue;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
if (arg === "--global") {
|
|
114
|
-
if (sawLocal) {
|
|
115
|
-
throw new Error("Cannot use --local and --global together.");
|
|
116
|
-
}
|
|
117
|
-
sawGlobal = true;
|
|
118
|
-
scope = "global";
|
|
134
|
+
if (parseScopeFlag(arg, scopeState)) {
|
|
119
135
|
continue;
|
|
120
136
|
}
|
|
121
137
|
|
|
@@ -132,7 +148,7 @@ function parseShowArgs(args: string[]): ParsedShowArgs {
|
|
|
132
148
|
positional.push(arg);
|
|
133
149
|
}
|
|
134
150
|
|
|
135
|
-
return { scope, positional, json, verbose };
|
|
151
|
+
return { scope: scopeState.scope, positional, json, verbose };
|
|
136
152
|
}
|
|
137
153
|
|
|
138
154
|
export async function runShow(args: string[], deps: ConfigCommandDeps): Promise<void> {
|
|
@@ -296,17 +312,10 @@ export async function runSet(args: string[], deps: ConfigCommandDeps): Promise<v
|
|
|
296
312
|
await deps.saveConfig(normalized.config);
|
|
297
313
|
deps.log.success(`Updated "${key}" to ${formatValue(parsedValue)}.`);
|
|
298
314
|
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
formatConfigValidationMessage(
|
|
304
|
-
getEffectiveConfigErrorHeader(effective),
|
|
305
|
-
effectiveErrors.length > 0 ? effectiveErrors : ["Configuration format is invalid."],
|
|
306
|
-
"Fix the repo-local override or restore compatible global values, then try again."
|
|
307
|
-
)
|
|
308
|
-
);
|
|
309
|
-
}
|
|
315
|
+
await warnIfEffectiveConfigInvalid(
|
|
316
|
+
deps,
|
|
317
|
+
"Fix the repo-local override or restore compatible global values, then try again."
|
|
318
|
+
);
|
|
310
319
|
}
|
|
311
320
|
|
|
312
321
|
export async function runEdit(args: string[], deps: ConfigCommandDeps): Promise<void> {
|
|
@@ -347,16 +356,11 @@ export async function runEdit(args: string[], deps: ConfigCommandDeps): Promise<
|
|
|
347
356
|
}
|
|
348
357
|
|
|
349
358
|
if (parsed.scope === "local") {
|
|
350
|
-
const effective = await
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
getEffectiveConfigErrorHeader(effective),
|
|
356
|
-
errors.length > 0 ? errors : ["Configuration format is invalid."],
|
|
357
|
-
'Run "rr init" and choose Repo-local config to regenerate the file, or fix it manually.'
|
|
358
|
-
)
|
|
359
|
-
);
|
|
359
|
+
const effective = await warnIfEffectiveConfigInvalid(
|
|
360
|
+
deps,
|
|
361
|
+
'Run "rr init" and choose Repo-local config to regenerate the file, or fix it manually.'
|
|
362
|
+
);
|
|
363
|
+
if (!effective) {
|
|
360
364
|
return;
|
|
361
365
|
}
|
|
362
366
|
|
|
@@ -378,16 +382,11 @@ export async function runEdit(args: string[], deps: ConfigCommandDeps): Promise<
|
|
|
378
382
|
return;
|
|
379
383
|
}
|
|
380
384
|
|
|
381
|
-
const effective = await
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
getEffectiveConfigErrorHeader(effective),
|
|
387
|
-
effectiveErrors.length > 0 ? effectiveErrors : ["Configuration format is invalid."],
|
|
388
|
-
"Fix the repo-local override or restore compatible global values, then try again."
|
|
389
|
-
)
|
|
390
|
-
);
|
|
385
|
+
const effective = await warnIfEffectiveConfigInvalid(
|
|
386
|
+
deps,
|
|
387
|
+
"Fix the repo-local override or restore compatible global values, then try again."
|
|
388
|
+
);
|
|
389
|
+
if (!effective) {
|
|
391
390
|
return;
|
|
392
391
|
}
|
|
393
392
|
|