@gobing-ai/spur 0.4.10 → 0.4.11
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/.claude-plugin/marketplace.json +1 -1
- package/config/workflows/pr-review.yaml +72 -224
- package/package.json +1 -1
- package/plugins/sp/commands/dev-pr-review.md +27 -17
- package/plugins/sp/plugin.json +1 -1
- package/plugins/sp/skills/code-verification/references/secu-review.md +7 -3
- package/plugins/sp/skills/pr-reviewing/SKILL.md +157 -210
- package/plugins/sp/skills/source-driven-development/SKILL.md +14 -6
- package/plugins/sp/skills/spur-cli/SKILL.md +1 -1
- package/plugins/sp/skills/spur-cli/references/builder.md +86 -13
- package/plugins/sp/skills/spur-dev/references/flag-glossary.md +3 -2
- package/spur.js +904 -8
|
@@ -3,13 +3,14 @@
|
|
|
3
3
|
# (initialState / states[].id / onEnter / top-level transitions), same family as
|
|
4
4
|
# basic.yaml / task-pipeline.yaml. Seeded into projects by `spur init` (scaffold manifest).
|
|
5
5
|
#
|
|
6
|
-
# Shape:
|
|
6
|
+
# Shape: check → precheck → open → review → done
|
|
7
7
|
# (soft probes everywhere: red gates route to `failed`/`pending` via transitions,
|
|
8
8
|
# never a raw lifecycle abort — same reliability contract as basic.yaml)
|
|
9
9
|
#
|
|
10
|
-
# This YAML is the SSOT for the review spine's state order and guards. Every state shells
|
|
11
|
-
#
|
|
12
|
-
#
|
|
10
|
+
# This YAML is the SSOT for the review spine's state order and guards. Every state shells out to
|
|
11
|
+
# the four flat `spur builder pr-<verb>` verbs (task 1219); the op sequencing
|
|
12
|
+
# (preflight→hygiene, push→ensure, request→wait→collect) lives in `runPrVerb`, not in this file's
|
|
13
|
+
# shell, and no gh/git logic lives in this file. All model-bearing work — finding triage, `fix`
|
|
13
14
|
# mode — stays in the sp:pr-reviewing skill and never enters this machine.
|
|
14
15
|
#
|
|
15
16
|
# Non-negotiable: the external review goes through the GitHub PR + an `@codex review`
|
|
@@ -21,7 +22,7 @@
|
|
|
21
22
|
# submit: request and stop at `pending` (no wait)
|
|
22
23
|
# rerun: request with --force even when HEAD is already reviewed
|
|
23
24
|
# baseBranch — base for a newly created PR (default: existing PR base, else repo default)
|
|
24
|
-
# focus — extra review focus appended to the @codex request
|
|
25
|
+
# focus — extra review focus appended to the @codex review request
|
|
25
26
|
# noWait — "true" returns pending right after the request instead of polling
|
|
26
27
|
# waitTimeoutSec — poll budget for Codex output (default 600)
|
|
27
28
|
# waitIntervalSec — poll interval (default 30; do not poll aggressively)
|
|
@@ -34,9 +35,9 @@
|
|
|
34
35
|
version: "1"
|
|
35
36
|
name: pr-review
|
|
36
37
|
kind: state-machine
|
|
37
|
-
description: GitHub Codex PR-review spine —
|
|
38
|
+
description: GitHub Codex PR-review spine — check → precheck → open → review
|
|
38
39
|
iterationBound: 16
|
|
39
|
-
initialState:
|
|
40
|
+
initialState: check
|
|
40
41
|
terminalStates:
|
|
41
42
|
- done
|
|
42
43
|
- pending
|
|
@@ -53,50 +54,27 @@ vars:
|
|
|
53
54
|
spurBin: "spur"
|
|
54
55
|
preReviewCmd: ""
|
|
55
56
|
__runId: ""
|
|
56
|
-
# Runtime-set by file.read.into-var (0771): the request record's requestedAt|head,
|
|
57
|
-
# extracted once by `request` and read per state — no repeated JSON parsing in shell.
|
|
58
|
-
prSince: ""
|
|
59
|
-
prHead: ""
|
|
60
57
|
|
|
61
58
|
states:
|
|
62
|
-
- id:
|
|
59
|
+
- id: check
|
|
63
60
|
description: >
|
|
64
|
-
Soft probe: git/gh/repo checks
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
Writes PASS|FAIL to .spur/run/${vars.__runId}-pr-
|
|
61
|
+
Soft probe: preflight (git/gh/repo checks, detached HEAD, dirty tree, base-branch refusal —
|
|
62
|
+
refused before any push can publish it) followed by the hygiene scan of base...HEAD
|
|
63
|
+
(secrets/.env/conflict markers BLOCK; debug residue WARNs). A preflight failure stops before
|
|
64
|
+
hygiene. Writes PASS|WARN|BLOCK|FAIL to .spur/run/${vars.__runId}-pr-check.status; always
|
|
65
|
+
exit 0, so a red gate routes through the transitions below.
|
|
68
66
|
onEnter:
|
|
69
|
-
# script-root retired for this workflow: actions call `$spurBin builder pr
|
|
67
|
+
# script-root retired for this workflow: actions call `$spurBin builder pr-<verb>` directly.
|
|
70
68
|
- kind: shell
|
|
71
69
|
options:
|
|
72
|
-
# (e) 0825:
|
|
73
|
-
#
|
|
70
|
+
# (e) 0825: 7 lines — preflight+hygiene and their status fallback must land together
|
|
71
|
+
# (one verb now owns the whole probe; a split would half-probe the submission gate).
|
|
74
72
|
command: >-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
if [ "$rc" -eq 0 ]; then printf 'PASS\n' > "$STATUS_FILE"; else printf 'FAIL\n' > "$STATUS_FILE"; fi &&
|
|
81
|
-
exit 0
|
|
82
|
-
|
|
83
|
-
- id: hygiene
|
|
84
|
-
description: >
|
|
85
|
-
Submission sanity scan of base...HEAD: secrets/.env/conflict markers BLOCK the run;
|
|
86
|
-
debug residue only WARNs. Not a replacement for the independent Codex review.
|
|
87
|
-
onEnter:
|
|
88
|
-
- kind: shell
|
|
89
|
-
options:
|
|
90
|
-
# (e) 0825: 9 lines — hygiene verdict + json capture land together; the status-file
|
|
91
|
-
# fallback keeps a silent driver from passing the gate.
|
|
92
|
-
command: >-
|
|
93
|
-
|
|
94
|
-
STATUS_FILE=".spur/run/$__runId-pr-hygiene.status" &&
|
|
95
|
-
set +e &&
|
|
96
|
-
$spurBin builder pr hygiene --base "$baseBranch" --json
|
|
97
|
-
--status-file "$STATUS_FILE" > ".spur/run/$__runId-pr-hygiene.json";
|
|
98
|
-
rc=$?; set -e &&
|
|
99
|
-
if [ ! -f "$STATUS_FILE" ]; then printf 'FAIL\n' > "$STATUS_FILE"; fi &&
|
|
73
|
+
STATUS_FILE=".spur/run/$__runId-pr-check.status";
|
|
74
|
+
mkdir -p .spur/run;
|
|
75
|
+
$spurBin builder pr-check --base "$baseBranch" --json --status-file "$STATUS_FILE"
|
|
76
|
+
> ".spur/run/$__runId-pr-check.json";
|
|
77
|
+
if [ ! -f "$STATUS_FILE" ]; then printf 'FAIL\n' > "$STATUS_FILE"; fi;
|
|
100
78
|
exit 0
|
|
101
79
|
|
|
102
80
|
- id: precheck
|
|
@@ -111,141 +89,78 @@ states:
|
|
|
111
89
|
command: >-
|
|
112
90
|
mkdir -p .spur/run; if [ -z "$preReviewCmd" ]; then R=SKIP; elif sh -c "$preReviewCmd"; then R=PASS; else R=FAIL; fi; printf '%s\n' "$R" > ".spur/run/$__runId-pr-precheck.status"
|
|
113
91
|
|
|
114
|
-
- id:
|
|
92
|
+
- id: open
|
|
115
93
|
description: >
|
|
116
|
-
Publish the branch (normal push only, never force; sets upstream when missing)
|
|
117
|
-
|
|
94
|
+
Publish the branch (normal push only, never force; sets upstream when missing) and reuse or
|
|
95
|
+
create its PR — a rejected push stops before any PR work. Writes FOUND|CREATED|FAIL to
|
|
96
|
+
.spur/run/${vars.__runId}-pr-open.status and the composed record (push + pr) to
|
|
97
|
+
.spur/run/${vars.__runId}-pr-open.json.
|
|
118
98
|
onEnter:
|
|
119
99
|
- kind: shell
|
|
120
100
|
options:
|
|
121
|
-
# (e) 0825:
|
|
122
|
-
#
|
|
101
|
+
# (e) 0825: 7 lines — publish + PR dedupe are one verb now; the status fallback keeps a
|
|
102
|
+
# silent driver from passing the gate.
|
|
123
103
|
command: >-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
> "
|
|
129
|
-
rc=$?; set -e &&
|
|
130
|
-
if [ ! -f "$STATUS_FILE" ]; then printf 'FAIL\n' > "$STATUS_FILE"; fi &&
|
|
104
|
+
STATUS_FILE=".spur/run/$__runId-pr-open.status";
|
|
105
|
+
mkdir -p .spur/run;
|
|
106
|
+
$spurBin builder pr-open --base "$baseBranch" --json --status-file "$STATUS_FILE"
|
|
107
|
+
> ".spur/run/$__runId-pr-open.json";
|
|
108
|
+
if [ ! -f "$STATUS_FILE" ]; then printf 'FAIL\n' > "$STATUS_FILE"; fi;
|
|
131
109
|
exit 0
|
|
132
110
|
|
|
133
|
-
- id:
|
|
134
|
-
description: Find or create the GitHub PR for the current branch — never a duplicate.
|
|
135
|
-
onEnter:
|
|
136
|
-
- kind: shell
|
|
137
|
-
options:
|
|
138
|
-
# (e) 0825: 9 lines — PR dedupe lookup captures its record for the downstream
|
|
139
|
-
# review link; ensure + capture must not race.
|
|
140
|
-
command: >-
|
|
141
|
-
mkdir -p .spur/run &&
|
|
142
|
-
STATUS_FILE=".spur/run/$__runId-pr-ensure.status" &&
|
|
143
|
-
set +e &&
|
|
144
|
-
$spurBin builder pr ensure-pr --base "$baseBranch" --json
|
|
145
|
-
--status-file "$STATUS_FILE" > ".spur/run/$__runId-pr.json";
|
|
146
|
-
rc=$?; set -e &&
|
|
147
|
-
if [ ! -f "$STATUS_FILE" ]; then printf 'FAIL\n' > "$STATUS_FILE"; fi &&
|
|
148
|
-
exit 0
|
|
149
|
-
|
|
150
|
-
- id: request
|
|
151
|
-
description: >
|
|
152
|
-
Post the `@codex review` request (per-HEAD dedupe unless mode=rerun forces).
|
|
153
|
-
Records the request under .spur/run/${vars.__runId}-pr-request.json.
|
|
154
|
-
onEnter:
|
|
155
|
-
- kind: shell
|
|
156
|
-
options:
|
|
157
|
-
# (e) 0825: 10 lines — the write-then-read split is the file-var transport feeding
|
|
158
|
-
# wait/collect and must stay one action (same record, same run scope).
|
|
159
|
-
command: >-
|
|
160
|
-
F="$([ "$mode" = rerun ] && echo --force)"; $spurBin builder pr request --focus "$focus" $F --json --status-file ".spur/run/$__runId-pr-request.status" > ".spur/run/$__runId-pr-request.json"; [ -f ".spur/run/$__runId-pr-request.status" ] || echo FAIL > ".spur/run/$__runId-pr-request.status"; jq -e . ".spur/run/$__runId-pr-request.json" >/dev/null 2>&1 && jq -r '.requestedAt // ""' ".spur/run/$__runId-pr-request.json" > ".spur/run/$__runId-pr-since.txt" && jq -r '.head // ""' ".spur/run/$__runId-pr-request.json" > ".spur/run/$__runId-pr-head.txt"; exit 0
|
|
161
|
-
|
|
162
|
-
- id: wait
|
|
111
|
+
- id: review
|
|
163
112
|
description: >
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
113
|
+
Request, wait for and collect the `@codex review` result on the current pushed HEAD
|
|
114
|
+
(per-HEAD dedupe unless mode=rerun; mode=submit or noWait=true stop at PENDING). Writes
|
|
115
|
+
CLEAN|FINDINGS|PENDING|FAIL to .spur/run/${vars.__runId}-pr-review.status — a wait timeout
|
|
116
|
+
is PENDING, never a failure — plus the composed findings record and the composite status.
|
|
168
117
|
onEnter:
|
|
169
|
-
- kind: file.read.into-var
|
|
170
|
-
options:
|
|
171
|
-
path: .spur/run/${vars.__runId}-pr-since.txt
|
|
172
|
-
var: prSince
|
|
173
|
-
- kind: file.read.into-var
|
|
174
|
-
options:
|
|
175
|
-
path: .spur/run/${vars.__runId}-pr-head.txt
|
|
176
|
-
var: prHead
|
|
177
118
|
- kind: shell
|
|
178
119
|
options:
|
|
179
|
-
# (e) 0825:
|
|
180
|
-
#
|
|
120
|
+
# (e) 0825: 10 lines — request/wait/collect are one verb now, so their result lands
|
|
121
|
+
# atomically; the status fallback covers a crashed driver. `mode=submit` or `noWait=true`
|
|
122
|
+
# pass --no-wait and `mode=rerun` passes --force, each as its own space-separated word
|
|
123
|
+
# (two substitutions, never concatenated). `/bin/sh -c` carries no `-e`, so a failed
|
|
124
|
+
# substitution is ignored.
|
|
181
125
|
command: >-
|
|
182
|
-
|
|
183
|
-
STATUS_FILE=".spur/run/$__runId-pr-
|
|
184
|
-
|
|
185
|
-
$spurBin builder pr
|
|
186
|
-
--json --status-file "$STATUS_FILE" > ".spur/run/$__runId-pr-
|
|
187
|
-
|
|
188
|
-
if [ ! -f "$STATUS_FILE" ]; then printf 'FAIL\n' > "$STATUS_FILE"; fi &&
|
|
126
|
+
O="$([ "$mode" = submit ] || [ "$noWait" = "true" ] && echo --no-wait) $([ "$mode" = rerun ] && echo --force)";
|
|
127
|
+
STATUS_FILE=".spur/run/$__runId-pr-review.status";
|
|
128
|
+
mkdir -p .spur/run;
|
|
129
|
+
$spurBin builder pr-review --focus "$focus" $O --timeout "$waitTimeoutSec" --interval "$waitIntervalSec"
|
|
130
|
+
--json --status-file "$STATUS_FILE" > ".spur/run/$__runId-pr-findings.json";
|
|
131
|
+
if [ ! -f "$STATUS_FILE" ]; then printf 'FAIL\n' > "$STATUS_FILE"; fi;
|
|
189
132
|
exit 0
|
|
190
|
-
|
|
191
|
-
- id: collect
|
|
192
|
-
description: >-
|
|
193
|
-
Normalize the latest current-HEAD Codex result and record composite PR/CI status.
|
|
194
|
-
Request record read via file.read.into-var vars (0771, see wait).
|
|
195
|
-
onEnter:
|
|
196
|
-
- kind: file.read.into-var
|
|
197
|
-
options:
|
|
198
|
-
path: .spur/run/${vars.__runId}-pr-since.txt
|
|
199
|
-
var: prSince
|
|
200
|
-
- kind: file.read.into-var
|
|
201
|
-
options:
|
|
202
|
-
path: .spur/run/${vars.__runId}-pr-head.txt
|
|
203
|
-
var: prHead
|
|
204
133
|
- kind: shell
|
|
205
134
|
options:
|
|
206
|
-
#
|
|
207
|
-
#
|
|
135
|
+
# The report reads the same composite PR/CI/Codex artifact as before (read-only, so a
|
|
136
|
+
# failed probe records its own error object instead of failing the collected run).
|
|
208
137
|
command: >-
|
|
209
|
-
|
|
138
|
+
$spurBin builder pr-status --json > ".spur/run/$__runId-pr-status.json" || true
|
|
210
139
|
|
|
211
140
|
- id: done
|
|
212
141
|
description: Terminal — review collected; findings artifact at .spur/run/${vars.__runId}-pr-findings.json
|
|
213
142
|
- id: pending
|
|
214
|
-
description: Terminal — review requested (or timed out) but not yet collected; run /sp:dev-pr-review
|
|
143
|
+
description: Terminal — review requested (or timed out) but not yet collected; run /sp:dev-pr-review status later
|
|
215
144
|
- id: failed
|
|
216
145
|
description: Terminal — a gate went red; the stage .status/.json artifacts under .spur/run name the blocker
|
|
217
146
|
|
|
218
147
|
transitions:
|
|
219
|
-
- from:
|
|
220
|
-
to: hygiene
|
|
221
|
-
description: Repo, gh auth, and clean tree verified
|
|
222
|
-
guard:
|
|
223
|
-
kind: shell
|
|
224
|
-
options:
|
|
225
|
-
command: 'test "$(cat .spur/run/$__runId-pr-preflight.status 2>/dev/null)" = PASS'
|
|
226
|
-
- from: preflight
|
|
227
|
-
to: failed
|
|
228
|
-
terminalReason: failed-check
|
|
229
|
-
description: Preflight red (detached HEAD, dirty tree, gh auth, no GitHub remote, or the current branch being the base branch) — stop before any publishing
|
|
230
|
-
guard:
|
|
231
|
-
kind: always
|
|
232
|
-
|
|
233
|
-
- from: hygiene
|
|
148
|
+
- from: check
|
|
234
149
|
to: precheck
|
|
235
150
|
description: Clean or warn-only — warnings ride along to the report
|
|
236
151
|
guard:
|
|
237
152
|
kind: shell
|
|
238
153
|
options:
|
|
239
|
-
command: 'grep -qE "^(PASS|WARN)$" .spur/run/$__runId-pr-
|
|
240
|
-
- from:
|
|
154
|
+
command: 'grep -qE "^(PASS|WARN)$" .spur/run/$__runId-pr-check.status 2>/dev/null'
|
|
155
|
+
- from: check
|
|
241
156
|
to: failed
|
|
242
157
|
terminalReason: failed-check
|
|
243
|
-
description: BLOCK (secrets/.env/conflict markers) or probe failure —
|
|
158
|
+
description: BLOCK (secrets/.env/conflict markers), a preflight failure (detached HEAD, dirty tree, gh auth, no GitHub remote, or the current branch being the base branch), or a probe failure — stop before any publishing
|
|
244
159
|
guard:
|
|
245
160
|
kind: always
|
|
246
161
|
|
|
247
162
|
- from: precheck
|
|
248
|
-
to:
|
|
163
|
+
to: open
|
|
249
164
|
description: Project fast check passed or was skipped
|
|
250
165
|
guard:
|
|
251
166
|
kind: shell
|
|
@@ -258,104 +173,37 @@ transitions:
|
|
|
258
173
|
guard:
|
|
259
174
|
kind: always
|
|
260
175
|
|
|
261
|
-
- from:
|
|
262
|
-
to:
|
|
263
|
-
description: Branch published
|
|
264
|
-
guard:
|
|
265
|
-
kind: shell
|
|
266
|
-
options:
|
|
267
|
-
command: 'grep -qE "^(PUSHED|UP_TO_DATE)$" .spur/run/$__runId-pr-push.status 2>/dev/null'
|
|
268
|
-
- from: push
|
|
269
|
-
to: failed
|
|
270
|
-
terminalReason: failed-check
|
|
271
|
-
description: Push rejected (e.g. non-fast-forward) — report; never force-push as a workaround
|
|
272
|
-
guard:
|
|
273
|
-
kind: always
|
|
274
|
-
|
|
275
|
-
- from: ensure-pr
|
|
276
|
-
to: request
|
|
277
|
-
description: PR found or created
|
|
278
|
-
guard:
|
|
279
|
-
kind: shell
|
|
280
|
-
options:
|
|
281
|
-
command: 'grep -qE "^(FOUND|CREATED)$" .spur/run/$__runId-pr-ensure.status 2>/dev/null'
|
|
282
|
-
- from: ensure-pr
|
|
283
|
-
to: failed
|
|
284
|
-
terminalReason: failed-check
|
|
285
|
-
description: No reviewable delta or PR creation failed — report the actual git/GitHub state
|
|
286
|
-
guard:
|
|
287
|
-
kind: always
|
|
288
|
-
|
|
289
|
-
# Declaration order matters: ALREADY_REVIEWED routes straight to collect (full mode dedupe);
|
|
290
|
-
# submit/noWait stop at pending; everything else waits.
|
|
291
|
-
- from: request
|
|
292
|
-
to: collect
|
|
293
|
-
description: Current HEAD already carries a Codex review — collect instead of duplicating
|
|
294
|
-
guard:
|
|
295
|
-
kind: shell
|
|
296
|
-
options:
|
|
297
|
-
command: 'test "$(cat .spur/run/$__runId-pr-request.status 2>/dev/null)" = ALREADY_REVIEWED'
|
|
298
|
-
- from: request
|
|
299
|
-
to: pending
|
|
300
|
-
description: Request already in flight, or mode=submit/--no-wait after posting — return pending
|
|
301
|
-
guard:
|
|
302
|
-
kind: shell
|
|
303
|
-
options:
|
|
304
|
-
# (e) 0825: 4-predicate guard — ALREADY_REQUESTED/REQUESTED pair state is the
|
|
305
|
-
# fallback route when the review driver stalls (mode/noWait honored).
|
|
306
|
-
command: 'test "$(cat .spur/run/$__runId-pr-request.status 2>/dev/null)" = ALREADY_REQUESTED || { test "$(cat .spur/run/$__runId-pr-request.status 2>/dev/null)" = REQUESTED && { test "$mode" = "submit" || test "$noWait" = "true"; }; }'
|
|
307
|
-
- from: request
|
|
308
|
-
to: wait
|
|
309
|
-
description: Request posted — poll for the result
|
|
310
|
-
guard:
|
|
311
|
-
kind: shell
|
|
312
|
-
options:
|
|
313
|
-
command: 'test "$(cat .spur/run/$__runId-pr-request.status 2>/dev/null)" = REQUESTED'
|
|
314
|
-
- from: request
|
|
315
|
-
to: failed
|
|
316
|
-
terminalReason: failed-check
|
|
317
|
-
description: Request failed (no PR, gh error) — report the preserved error
|
|
318
|
-
guard:
|
|
319
|
-
kind: always
|
|
320
|
-
|
|
321
|
-
- from: wait
|
|
322
|
-
to: collect
|
|
323
|
-
description: Codex findings or an explicit clean result on the current HEAD found
|
|
324
|
-
guard:
|
|
325
|
-
kind: shell
|
|
326
|
-
options:
|
|
327
|
-
command: 'grep -qE "^(FOUND|CLEAN)$" .spur/run/$__runId-pr-wait.status 2>/dev/null'
|
|
328
|
-
- from: wait
|
|
329
|
-
to: pending
|
|
330
|
-
description: Poll budget exhausted — pending, not failed
|
|
176
|
+
- from: open
|
|
177
|
+
to: review
|
|
178
|
+
description: Branch published and PR found or created
|
|
331
179
|
guard:
|
|
332
180
|
kind: shell
|
|
333
181
|
options:
|
|
334
|
-
command: '
|
|
335
|
-
- from:
|
|
182
|
+
command: 'grep -qE "^(FOUND|CREATED)$" .spur/run/$__runId-pr-open.status 2>/dev/null'
|
|
183
|
+
- from: open
|
|
336
184
|
to: failed
|
|
337
185
|
terminalReason: failed-check
|
|
338
|
-
description:
|
|
186
|
+
description: Push rejected (e.g. non-fast-forward — report; never force-push as a workaround) or no reviewable delta or PR creation failed
|
|
339
187
|
guard:
|
|
340
188
|
kind: always
|
|
341
189
|
|
|
342
|
-
- from:
|
|
190
|
+
- from: review
|
|
343
191
|
to: done
|
|
344
192
|
description: Current-HEAD review collected (CLEAN/FINDINGS)
|
|
345
193
|
guard:
|
|
346
194
|
kind: shell
|
|
347
195
|
options:
|
|
348
|
-
command: 'grep -qE "^(FINDINGS|CLEAN)$" .spur/run/$__runId-pr-
|
|
349
|
-
- from:
|
|
196
|
+
command: 'grep -qE "^(FINDINGS|CLEAN)$" .spur/run/$__runId-pr-review.status 2>/dev/null'
|
|
197
|
+
- from: review
|
|
350
198
|
to: pending
|
|
351
|
-
description:
|
|
199
|
+
description: Request in flight, mode=submit/noWait, or a wait timeout — pending, not failed (collect later)
|
|
352
200
|
guard:
|
|
353
201
|
kind: shell
|
|
354
202
|
options:
|
|
355
|
-
command: 'test "$(cat .spur/run/$__runId-pr-
|
|
356
|
-
- from:
|
|
203
|
+
command: 'test "$(cat .spur/run/$__runId-pr-review.status 2>/dev/null)" = PENDING'
|
|
204
|
+
- from: review
|
|
357
205
|
to: failed
|
|
358
206
|
terminalReason: failed-check
|
|
359
|
-
description:
|
|
207
|
+
description: Review probe itself failed (no PR, gh error, moved HEAD)
|
|
360
208
|
guard:
|
|
361
209
|
kind: always
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gobing-ai/spur",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.11",
|
|
4
4
|
"description": "Spur CLI — local-first harness for mainstream coding agents: constraint checking, workflow orchestration, agent health, and history analytics. Bun-native; exposes the `spur` command.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"spur",
|
|
@@ -1,39 +1,49 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: "
|
|
2
|
+
description: "Local-first PR review — status, a local branch-diff review, and validated fixes; --codex opts into the GitHub Codex review spine."
|
|
3
3
|
role: reviewer
|
|
4
|
-
argument-hint: "[
|
|
4
|
+
argument-hint: "[status|review|fix] [--codex] [--force] [--no-wait] [--base <branch>] [--agent <inline|auto|name>] [<focus>]"
|
|
5
5
|
allowed-tools: ["Bash", "Read", "Skill"]
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# Dev PR Review
|
|
9
9
|
|
|
10
|
-
Wraps the **sp:pr-reviewing** skill. The
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
Wraps the **sp:pr-reviewing** skill. The default `status` mode is a read-only snapshot of the
|
|
11
|
+
branch, its base and the pull request; `review` runs a **local** review of the branch diff unless
|
|
12
|
+
`--codex` opts into the GitHub `@codex review` spine; `fix` triages findings from either reviewer
|
|
13
|
+
and commits the verified ones. The invoking agent orchestrates.
|
|
13
14
|
|
|
14
15
|
## Argument Flags
|
|
15
16
|
|
|
16
17
|
| Flag | Description | Default |
|
|
17
18
|
| --- | --- | --- |
|
|
18
|
-
| `<mode>` | `
|
|
19
|
-
| `--
|
|
20
|
-
| `--
|
|
21
|
-
| `--
|
|
22
|
-
|
|
|
19
|
+
| `<mode>` | `status\|review\|fix` — status: read-only branch + PR state; review: local branch-diff review (or the GitHub Codex spine with `--codex`); fix: validate, fix and commit findings from either reviewer. | status |
|
|
20
|
+
| `--codex` | `review` only — route through the GitHub Codex spine (`pr-review.yaml`) instead of the local `/sp:dev-review`. | off |
|
|
21
|
+
| `--force` | `review --codex` only — request a fresh review of the current HEAD even when it was already reviewed (`mode=rerun`). | off |
|
|
22
|
+
| `--no-wait` | `review --codex` only — post the request and return `pending` without polling (`mode=submit`). | off |
|
|
23
|
+
| `--base <branch>` | Base branch for the branch report, the `pr-check` diff and a newly created PR. | existing PR base, else repo default |
|
|
24
|
+
| `--agent <inline\|auto\|name>` | Who performs the model-bearing triage/fix. Omit uses the current agent, `inline` forbids dispatch, `auto` resolves the declared role, and a name pins that executor. This does not change the deterministic route. | omit |
|
|
25
|
+
| `<focus>` | Remaining free text — extra review focus for the local review or the Codex request (e.g. `security boundaries and transaction idempotency`). | none |
|
|
23
26
|
|
|
24
27
|
For shared semantics, see the [flag glossary](../skills/spur-dev/references/flag-glossary.md).
|
|
25
28
|
|
|
26
29
|
## Usage
|
|
27
30
|
|
|
28
|
-
|
|
31
|
+
```
|
|
32
|
+
/sp:dev-pr-review [status|review|fix] [--codex] [--force] [--no-wait] [--base <branch>] [--agent <inline|auto|name>] [review focus]
|
|
33
|
+
```
|
|
29
34
|
|
|
30
35
|
## Implementation
|
|
31
36
|
|
|
32
37
|
- Apply the [inline-default execution-surface contract](../skills/spur-dev/references/cross-cutting.md#inline-default-execution-surface).
|
|
33
38
|
- Delegate everything: `Skill(skill="sp:pr-reviewing", args="$ARGUMENTS")`. The skill owns mode
|
|
34
|
-
routing,
|
|
35
|
-
|
|
36
|
-
deterministic git/gh core every spine step shells out to.
|
|
37
|
-
-
|
|
38
|
-
|
|
39
|
-
|
|
39
|
+
routing, the local `/sp:dev-review` dispatch, the Codex spine and finding triage/fix. On the
|
|
40
|
+
Codex path `pr-review.yaml` is the SSOT for state order and guards, and the four flat
|
|
41
|
+
`spur builder pr-<verb>` verbs are the deterministic git/gh core every spine step shells out to.
|
|
42
|
+
- Retired modes print one notice line and run their replacement: `full` → `review --codex`;
|
|
43
|
+
`submit` → `review --codex --no-wait`; `rerun` → `review --codex --force`; `collect` → `status`.
|
|
44
|
+
`rules` prints that `AGENTS.md` review-rule authoring belongs to `cc:cc-magents` (the `cc` plugin)
|
|
45
|
+
and exits without running a review.
|
|
46
|
+
- Never force-push, rewrite history, close or integrate the PR, or discard unrelated changes.
|
|
47
|
+
`review` without `--codex` makes no GitHub writes. `fix` pre-authorizes one focused commit for the
|
|
48
|
+
verified findings — stop if unrelated uncommitted changes make safe separation or committing
|
|
49
|
+
ambiguous.
|
package/plugins/sp/plugin.json
CHANGED
|
@@ -90,9 +90,13 @@ Before declaring a task `done`, run this lightweight checklist. It catches the m
|
|
|
90
90
|
- [ ] No `console.log` / `console.error` in production code (use the project logger).
|
|
91
91
|
- [ ] No `--no-verify`, `--force`, or new suppression comments added to bypass gates.
|
|
92
92
|
- [ ] Solution section contains `file:line` citations for every changed file.
|
|
93
|
-
- [ ] API/library
|
|
94
|
-
|
|
95
|
-
|
|
93
|
+
- [ ] Every API/library claim in changed docs carries BOTH a source citation (URL or `path:line`) and
|
|
94
|
+
an EARNED level `HIGH|MEDIUM|LOW` — `HIGH` cites (a URL, `path:line`, or a path with a
|
|
95
|
+
code/doc extension), `MEDIUM`/`LOW` cite or name what is unverified. That is the
|
|
96
|
+
`| Claim | Source | Local verification | Confidence |` contract of
|
|
97
|
+
`.spur/rules/structure/api-library-claim-citation.yaml`; ADR-138 still forbids a confidence
|
|
98
|
+
label on the VERDICT or verify answer. The deterministic advisory backstop for the citation
|
|
99
|
+
half is the `cited-version-strings` / `cited-api-claims` rules
|
|
96
100
|
(`.spur/rules/docs/sourced-api-claims.yaml`, run ad hoc — not wired into a preset).
|
|
97
101
|
- [ ] Review section has P1–P4 findings table (even if all rows are empty — the table itself proves review happened).
|
|
98
102
|
|