@gobing-ai/spur 0.3.93 → 0.3.94
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/rules/boundary/test-subpath-boundary.yaml +48 -0
- package/config/rules/strict/runtime-boundaries.yaml +2 -1
- package/config/rules/structure/protected-files.yaml +4 -0
- package/config/workflows/task-pipeline.yaml +17 -26
- package/package.json +9 -9
- package/plugins/sp/README.md +1 -1
- package/plugins/sp/commands/dev-review-session.md +4 -4
- package/plugins/sp/commands/dev-review.md +16 -5
- package/plugins/sp/hooks/pi/guard-extension.ts +17 -36
- package/plugins/sp/lib/idea-handoff.generated.mjs +1 -1
- package/plugins/sp/lib/inline-run.generated.d.mts +2 -0
- package/plugins/sp/lib/inline-run.generated.mjs +29 -8
- package/plugins/sp/plugin.json +1 -1
- package/plugins/sp/scripts/inline-run-setup.mjs +42 -4
- package/plugins/sp/scripts/inline-run-setup.ts +78 -4
- package/plugins/sp/scripts/quality-gate.mjs +11 -4
- package/plugins/sp/scripts/quality-gate.ts +22 -7
- package/plugins/sp/scripts/residual-scan.mjs +1 -1
- package/plugins/sp/scripts/residual-scan.ts +2 -1
- package/plugins/sp/skills/session-review/SKILL.md +11 -9
- package/plugins/sp/skills/spur-dev/references/dev-operations.md +8 -2
- package/plugins/sp/skills/spur-dev/references/execution-batch.md +45 -17
- package/plugins/sp/skills/spur-dev/references/flag-glossary.md +16 -3
- package/plugins/sp/skills/spur-dev/references/inline-pipeline-driver.md +16 -2
- package/spur.js +19288 -20178
- package/web/_astro/BoardApp.C02hAHPO.js +1 -0
- package/web/_astro/{BoardApp.CerSBgis.js → BoardApp.FTEs3-N8.js} +107 -105
- package/web/_astro/{TaskDetail.DCqiC-OZ.js → TaskDetail.C-GdsS-t.js} +1 -1
- package/web/_astro/arc.uuAf51IT.js +1 -0
- package/web/_astro/{architectureDiagram-3BPJPVTR.DM_vp_hO.js → architectureDiagram-3BPJPVTR.CGe629A1.js} +1 -1
- package/web/_astro/{blockDiagram-GPEHLZMM.DXVIiv0p.js → blockDiagram-GPEHLZMM.D1mGCq3p.js} +1 -1
- package/web/_astro/{c4Diagram-AAUBKEIU.BbF_zCxW.js → c4Diagram-AAUBKEIU.CMsolcde.js} +1 -1
- package/web/_astro/channel.fsgl7o5j.js +1 -0
- package/web/_astro/{chunk-2J33WTMH.CAgQHpPC.js → chunk-2J33WTMH.CrGA3fik.js} +1 -1
- package/web/_astro/{chunk-4BX2VUAB.BN-5tpw4.js → chunk-4BX2VUAB.DsLVVla0.js} +1 -1
- package/web/_astro/{chunk-55IACEB6.CnPkEEr0.js → chunk-55IACEB6.Dxc59Tfi.js} +1 -1
- package/web/_astro/{chunk-727SXJPM.BQzQeMVm.js → chunk-727SXJPM.CaEVE2Wy.js} +4 -4
- package/web/_astro/{chunk-AQP2D5EJ.B6xNyDnL.js → chunk-AQP2D5EJ.BiJ4HXeI.js} +1 -1
- package/web/_astro/{chunk-FMBD7UC4.C7f9Ih78.js → chunk-FMBD7UC4.Mxf1fru5.js} +1 -1
- package/web/_astro/{chunk-ND2GUHAM.CNV1dFXT.js → chunk-ND2GUHAM.pyOWQixH.js} +1 -1
- package/web/_astro/{chunk-QZHKN3VN.Cudn2TkJ.js → chunk-QZHKN3VN.BmEsg4vr.js} +1 -1
- package/web/_astro/{classDiagram-4FO5ZUOK.D1NwP50q.js → classDiagram-4FO5ZUOK.BHhhMFTO.js} +1 -1
- package/web/_astro/{classDiagram-v2-Q7XG4LA2.D1NwP50q.js → classDiagram-v2-Q7XG4LA2.BHhhMFTO.js} +1 -1
- package/web/_astro/{cose-bilkent-S5V4N54A.B1wSL-Xb.js → cose-bilkent-S5V4N54A.2fH4YOlp.js} +1 -1
- package/web/_astro/{cynefin-OW5HDTMX.BmK52w8G.js → cynefin-OW5HDTMX.C2j1_lKL.js} +1 -1
- package/web/_astro/{dagre-BM42HDAG.Bfy5CTDT.js → dagre-BM42HDAG.hZ2NCTdT.js} +2 -2
- package/web/_astro/diagram-2AECGRRQ.Bfw5_EzK.js +43 -0
- package/web/_astro/diagram-5GNKFQAL.Bt1V_Tmk.js +10 -0
- package/web/_astro/{diagram-KO2AKTUF.CW_vMJ4z.js → diagram-KO2AKTUF.DEk-YFwp.js} +3 -3
- package/web/_astro/{diagram-LMA3HP47.B_8ZGF67.js → diagram-LMA3HP47.CDjndtQm.js} +1 -1
- package/web/_astro/{diagram-OG6HWLK6.BppnHsdS.js → diagram-OG6HWLK6.cFnUHScG.js} +1 -1
- package/web/_astro/{erDiagram-TEJ5UH35.BEuHXcjJ.js → erDiagram-TEJ5UH35.DlhYp7NV.js} +5 -5
- package/web/_astro/{flowDiagram-I6XJVG4X.CH-UlnGr.js → flowDiagram-I6XJVG4X.DNTpsxfx.js} +4 -4
- package/web/_astro/{ganttDiagram-6RSMTGT7.BO81S85v.js → ganttDiagram-6RSMTGT7.DC_p36PI.js} +1 -1
- package/web/_astro/{gitGraphDiagram-PVQCEYII.XnPxPPZN.js → gitGraphDiagram-PVQCEYII.DkEqNI0P.js} +1 -1
- package/web/_astro/{infoDiagram-5YYISTIA.JyjYRu_T.js → infoDiagram-5YYISTIA.CrjioCTG.js} +1 -1
- package/web/_astro/{ishikawaDiagram-YF4QCWOH.BBRBF-Fo.js → ishikawaDiagram-YF4QCWOH.BcK0CN8n.js} +5 -5
- package/web/_astro/{journeyDiagram-JHISSGLW.C_iymSyp.js → journeyDiagram-JHISSGLW.p0CbBDX1.js} +1 -1
- package/web/_astro/{kanban-definition-UN3LZRKU.DdfW-Oqt.js → kanban-definition-UN3LZRKU.puHIFt6J.js} +7 -7
- package/web/_astro/{linear.C2_IkbZT.js → linear.Bb-3a1d7.js} +1 -1
- package/web/_astro/mermaid.core.CJDgXOJs.js +301 -0
- package/web/_astro/{mindmap-definition-RKZ34NQL.DAZIxQSK.js → mindmap-definition-RKZ34NQL.BoAZEI9t.js} +2 -2
- package/web/_astro/{pieDiagram-4H26LBE5.CN8sIhKM.js → pieDiagram-4H26LBE5.CJNSdqY7.js} +3 -3
- package/web/_astro/{quadrantDiagram-W4KKPZXB.3dGcX5GP.js → quadrantDiagram-W4KKPZXB.DtTy7_0Z.js} +1 -1
- package/web/_astro/{requirementDiagram-4Y6WPE33.BV2y4dd6.js → requirementDiagram-4Y6WPE33.BNYV98Xg.js} +3 -3
- package/web/_astro/{sankeyDiagram-5OEKKPKP.Cqo15Tvo.js → sankeyDiagram-5OEKKPKP.DRq9DGHp.js} +4 -4
- package/web/_astro/{sequenceDiagram-3UESZ5HK.CROCPMJB.js → sequenceDiagram-3UESZ5HK.B7KdBFCm.js} +1 -1
- package/web/_astro/{stateDiagram-AJRCARHV.RfXZrkFE.js → stateDiagram-AJRCARHV.CD62ZJ2H.js} +1 -1
- package/web/_astro/{stateDiagram-v2-BHNVJYJU.CPXmbBs9.js → stateDiagram-v2-BHNVJYJU.CeVk1TAj.js} +1 -1
- package/web/_astro/{timeline-definition-PNZ67QCA.DdgKTiO8.js → timeline-definition-PNZ67QCA.C_ZPA5tT.js} +3 -3
- package/web/_astro/{vennDiagram-CIIHVFJN.CPNVSHF1.js → vennDiagram-CIIHVFJN.CrThO_FQ.js} +5 -5
- package/web/_astro/{wardleyDiagram-YWT4CUSO.CQhA0Jyr.js → wardleyDiagram-YWT4CUSO.DSZCA5nl.js} +3 -3
- package/web/_astro/{xychartDiagram-2RQKCTM6.n61BWyy4.js → xychartDiagram-2RQKCTM6.KI7baTuk.js} +1 -1
- package/web/index.html +1 -1
- package/web/_astro/BoardApp.eoTz0pZs.js +0 -1
- package/web/_astro/arc.CPwg6Rw0.js +0 -1
- package/web/_astro/channel.MYZLKNwy.js +0 -1
- package/web/_astro/diagram-2AECGRRQ.DhNnvUvX.js +0 -43
- package/web/_astro/diagram-5GNKFQAL.lTX5KwnS.js +0 -10
- package/web/_astro/mermaid.core.GAOYeSR0.js +0 -303
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
$schema: "@gobing-ai/spur/schemas/rule-file.schema.json"
|
|
2
|
+
# Test-only subpath boundary — @gobing-ai/spur-app/testing and the app's testing/
|
|
3
|
+
# module are for tests and the mock itself, never production sources (task 0975 R3).
|
|
4
|
+
#
|
|
5
|
+
# WHY: `@gobing-ai/spur-app/testing` (packages/app/src/testing/history-board-mock.ts)
|
|
6
|
+
# is a test-only mock subpath (E82). A production import couples shipped code to a
|
|
7
|
+
# fake, and it crept toward the server transport before 0961 severed the seam. The
|
|
8
|
+
# evaluator's glob matcher is segment-aware with `*`/`**` only (no brace groups), so
|
|
9
|
+
# the ts/tsx globs would need separate entries — the repo carries .ts sources only,
|
|
10
|
+
# matching the dao-boundary precedent.
|
|
11
|
+
#
|
|
12
|
+
# Scope covers src trees only: `<workspace>/tests/**` directories stay outside, and
|
|
13
|
+
# `packages/app/src/testing/**` (the mock's own home) is exempted explicitly.
|
|
14
|
+
rules:
|
|
15
|
+
- id: no-testing-subpath-import
|
|
16
|
+
description: >
|
|
17
|
+
Production sources must not import the @gobing-ai/spur-app/testing subpath
|
|
18
|
+
(history-board mock, E82/0961). Tests under <workspace>/tests/** and the
|
|
19
|
+
packages/app/src/testing/** module itself are exempt.
|
|
20
|
+
severity: error
|
|
21
|
+
evaluator:
|
|
22
|
+
type: forbidden-import
|
|
23
|
+
config:
|
|
24
|
+
forbidden:
|
|
25
|
+
- specifier: "@gobing-ai/spur-app/testing"
|
|
26
|
+
scope:
|
|
27
|
+
include:
|
|
28
|
+
- "apps/**/src/**/*.ts"
|
|
29
|
+
- "packages/**/src/**/*.ts"
|
|
30
|
+
exclude:
|
|
31
|
+
- "packages/app/src/testing/**"
|
|
32
|
+
|
|
33
|
+
- id: no-relative-testing-module-import
|
|
34
|
+
description: >
|
|
35
|
+
packages/app production sources must not reach the testing/ module through
|
|
36
|
+
relative imports (from './testing/…' or '../…/testing…'). The testing/**
|
|
37
|
+
module itself and workspace tests/ directories are exempt.
|
|
38
|
+
severity: error
|
|
39
|
+
evaluator:
|
|
40
|
+
type: forbidden-import
|
|
41
|
+
config:
|
|
42
|
+
forbidden:
|
|
43
|
+
- pattern: "(?:from\\s+|import\\(\\s*)['\"](\\.\\.?/)+(?:[^'\"]*/)?testing(?:/|['\"])"
|
|
44
|
+
scope:
|
|
45
|
+
include:
|
|
46
|
+
- "packages/app/src/**/*.ts"
|
|
47
|
+
exclude:
|
|
48
|
+
- "packages/app/src/testing/**"
|
|
@@ -73,6 +73,7 @@ rules:
|
|
|
73
73
|
|
|
74
74
|
# Synchronous template & workflow resolvers:
|
|
75
75
|
- "apps/cli/src/commands/task.ts" # sync loadTemplateContent/Bodies callbacks
|
|
76
|
-
- "packages/app/src/services/workflow-service.ts" #
|
|
76
|
+
- "packages/app/src/services/workflow-service.ts" # run-log/checkpoint reclamation realpath confinement (:988, :1014)
|
|
77
|
+
- "packages/app/src/workflow/run-record.ts" # confined sync run-record reads: realpath + fstat byte-window (moved from workflow-service.ts, 0962)
|
|
77
78
|
- "apps/cli/src/release-ops.ts" # sync manifest/workspace reads + Bun.write rewrites inside the git release flow (task 0617); mirrors task.ts sync-template exemption
|
|
78
79
|
- "apps/cli/src/version-carriers.ts" # sync manifest reads + Bun.write rewrites for registered release carriers (task 0854); mirrors release-ops.ts sync exemption
|
|
@@ -37,6 +37,10 @@ rules:
|
|
|
37
37
|
# this rule's include scope — scanning generated copies only adds noise
|
|
38
38
|
# (e.g. `id: task-implement-transition` trips the sk- token shape).
|
|
39
39
|
- "apps/cli/config/"
|
|
40
|
+
# `apps/cli/plugins/` is the same kind of gitignored build:bundle output — a staged
|
|
41
|
+
# npm copy of `plugins/`, itself outside the include scope. Minified generated libs
|
|
42
|
+
# there trip the api-key shape (0976: HEAD's inline-run.generated.mjs fires too).
|
|
43
|
+
- "apps/cli/plugins/"
|
|
40
44
|
|
|
41
45
|
- id: no-unsafe-git-commands
|
|
42
46
|
description: "Unsafe git commands (force-push, hard reset) can overwrite history or cause data loss (AGENTS.md CRITICAL safety)."
|
|
@@ -286,6 +286,13 @@ states:
|
|
|
286
286
|
# a silent no-op routes the run to `failed` here instead of drifting
|
|
287
287
|
# into test/review and being caught a full pass later.
|
|
288
288
|
requireDiff: true
|
|
289
|
+
# Verification-only re-run admission (task 0976): when requireDiff
|
|
290
|
+
# sees zero non-corpus changes because the implementation already
|
|
291
|
+
# landed through another path, `spur task check <wbs>` PASS admits
|
|
292
|
+
# the empty implement as a certification pass; a check failure
|
|
293
|
+
# keeps the 0424 R3 empty-implement violation. Static option —
|
|
294
|
+
# deliberately no run-var knob (ADR-115).
|
|
295
|
+
requireDiffAllowCleanCheck: true
|
|
289
296
|
# 0933 R26: escalation contract — the agent.run option names the QUESTION
|
|
290
297
|
# file (the pause signal, deleted pre-dispatch for freshness); a paused
|
|
291
298
|
# attempt (agent wrote its question there and exited 0) succeeds with
|
|
@@ -577,31 +584,24 @@ states:
|
|
|
577
584
|
Deterministic failure-class router on a red gate (0943 R3). The engine has no onEnter
|
|
578
585
|
conditionals, so the FAIL branch from `test`/`test-recheck` lands here as its own
|
|
579
586
|
deterministic state (the sanctioned alternative), while the green branch goes to
|
|
580
|
-
`triage`. `decide failure-class` reads the bounded gate findings and routes:
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
587
|
+
`triage`. `decide failure-class` reads the bounded gate findings and routes: fix →
|
|
588
|
+
test-fix (the bounded repair hop; its default, recorded as `source: default` when no
|
|
589
|
+
backend decides — 0976 R2), stop → failed with terminalReason failed-check. A
|
|
590
|
+
missing/corrupt decision fails closed to failed-check. (0976 R2 collapsed the former
|
|
591
|
+
`retryable` lane: the declared default made it unreachable on the inline path.)
|
|
584
592
|
onEnter:
|
|
585
593
|
- kind: decide
|
|
586
594
|
options:
|
|
587
595
|
id: failure-class
|
|
588
596
|
method: choice
|
|
589
597
|
question: >-
|
|
590
|
-
The quality gate failed. Is the failure
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
choices: [retryable, fix, stop]
|
|
598
|
+
The quality gate failed. Is the failure fixable by the repair agent, or should
|
|
599
|
+
the run stop here?
|
|
600
|
+
choices: [fix, stop]
|
|
594
601
|
default: fix
|
|
595
602
|
evidence:
|
|
596
603
|
- .spur/run/${vars.wbs}-test-gate.findings
|
|
597
604
|
resultFile: .spur/run/${vars.wbs}-failure-class.decision
|
|
598
|
-
# A retryable classification still counts an attempt so the existing
|
|
599
|
-
# qualityGateMaxFixAttempts cap bounds the recheck loop (0943 R3; the cap edge reads
|
|
600
|
-
# the same counter the fixall path uses).
|
|
601
|
-
- kind: shell
|
|
602
|
-
options:
|
|
603
|
-
command: >-
|
|
604
|
-
A=".spur/run/$wbs-test-fix-attempt"; [ "$(jq -r '.value // ""' ".spur/run/$wbs-failure-class.decision" 2>/dev/null)" = retryable ] && { n="$(cat "$A" 2>/dev/null || echo 0)"; printf '%s\n' "$((n + 1))" > "$A"; }; exit 0
|
|
605
605
|
|
|
606
606
|
- id: review
|
|
607
607
|
description: Three-dimensional code review via /sp:dev-review (functional requirements traceability + SECUA framework (Security, Efficiency, Correctness, Usability, Architecture) + architecture depth), findings written to `## Review`.
|
|
@@ -1007,8 +1007,7 @@ transitions:
|
|
|
1007
1007
|
options:
|
|
1008
1008
|
command: 'test "$mode" != fast'
|
|
1009
1009
|
# ── failure-class routing (0943 R3): stop first (never mislabeled by the cap), then the
|
|
1010
|
-
# attempt cap
|
|
1011
|
-
# decision lanes, then the fail-closed defense. Declaration order matters.
|
|
1010
|
+
# attempt cap, then the fix lane, then the fail-closed defense. Declaration order matters.
|
|
1012
1011
|
- from: test-fail-triage
|
|
1013
1012
|
to: failed
|
|
1014
1013
|
terminalReason: failed-check
|
|
@@ -1021,7 +1020,7 @@ transitions:
|
|
|
1021
1020
|
- from: test-fail-triage
|
|
1022
1021
|
to: failed
|
|
1023
1022
|
terminalReason: retry-exhausted
|
|
1024
|
-
description: Attempt cap reached — qualityGateMaxFixAttempts bounds the
|
|
1023
|
+
description: Attempt cap reached — qualityGateMaxFixAttempts bounds the fix lane.
|
|
1025
1024
|
# (warn) 5 commands: named fix attempts (legibility, 0874).
|
|
1026
1025
|
guard:
|
|
1027
1026
|
kind: shell
|
|
@@ -1029,14 +1028,6 @@ transitions:
|
|
|
1029
1028
|
command: >-
|
|
1030
1029
|
fix_attempts="$(cat .spur/run/$wbs-test-fix-attempt 2>/dev/null || echo 0)";
|
|
1031
1030
|
test "$fix_attempts" -ge "$qualityGateMaxFixAttempts"
|
|
1032
|
-
- from: test-fail-triage
|
|
1033
|
-
to: test-recheck
|
|
1034
|
-
description: Failure class retryable and under the cap — re-run the gate without fixall; the attempt was counted on entry.
|
|
1035
|
-
guard:
|
|
1036
|
-
kind: shell
|
|
1037
|
-
options:
|
|
1038
|
-
command: >-
|
|
1039
|
-
jq -e '.value == "retryable"' ".spur/run/$wbs-failure-class.decision" >/dev/null 2>&1
|
|
1040
1031
|
- from: test-fail-triage
|
|
1041
1032
|
to: test-fix
|
|
1042
1033
|
description: Failure class fix and under the cap — the bounded repair hop, as before.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gobing-ai/spur",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.94",
|
|
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",
|
|
@@ -53,14 +53,14 @@
|
|
|
53
53
|
},
|
|
54
54
|
"devDependencies": {
|
|
55
55
|
"@commander-js/extra-typings": "^14.0.0",
|
|
56
|
-
"@gobing-ai/ts-db": "^0.5.
|
|
57
|
-
"@gobing-ai/ts-ai-runner": "^0.5.
|
|
58
|
-
"@gobing-ai/ts-dual-workflow-engine": "^0.5.
|
|
59
|
-
"@gobing-ai/ts-infra": "^0.5.
|
|
60
|
-
"@gobing-ai/ts-llm-jsonl-importer": "^0.5.
|
|
61
|
-
"@gobing-ai/ts-rule-engine": "^0.5.
|
|
62
|
-
"@gobing-ai/ts-runtime": "^0.5.
|
|
63
|
-
"@gobing-ai/ts-utils": "^0.5.
|
|
56
|
+
"@gobing-ai/ts-db": "^0.5.9",
|
|
57
|
+
"@gobing-ai/ts-ai-runner": "^0.5.9",
|
|
58
|
+
"@gobing-ai/ts-dual-workflow-engine": "^0.5.9",
|
|
59
|
+
"@gobing-ai/ts-infra": "^0.5.9",
|
|
60
|
+
"@gobing-ai/ts-llm-jsonl-importer": "^0.5.9",
|
|
61
|
+
"@gobing-ai/ts-rule-engine": "^0.5.9",
|
|
62
|
+
"@gobing-ai/ts-runtime": "^0.5.9",
|
|
63
|
+
"@gobing-ai/ts-utils": "^0.5.9",
|
|
64
64
|
"@types/bun": "1.3.14",
|
|
65
65
|
"@types/figlet": "^1.7.0",
|
|
66
66
|
"@types/node-notifier": "8.0.5",
|
package/plugins/sp/README.md
CHANGED
|
@@ -102,7 +102,7 @@ list this README is checked against.
|
|
|
102
102
|
| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
103
103
|
| `dev-next` | Status-aware router — inspect a task (or next frontier under a feature), dispatch the best existing `/sp:dev-*` command, chain on clean success |
|
|
104
104
|
| `dev-run` | Run a task — full pipeline (precheck→implement→test→review→approve→verify→record→done) or single-step (implement) |
|
|
105
|
-
| `dev-review` | Multi-dimensional review for a task or path
|
|
105
|
+
| `dev-review` | Multi-dimensional review for a task or path (traceability, SECUA, architecture); `--triage` fixes small findings, files the rest as tasks |
|
|
106
106
|
| `dev-pr-review` | GitHub Codex PR review — prepare/reuse a PR, request `@codex review`, collect findings, validate/fix/re-review (fix mode); workflow-backed spine |
|
|
107
107
|
| `dev-verify` | Verify a task against its requirements and Acceptance Criteria — traceability check producing a PASS/PARTIAL/FAIL verdict with evidence |
|
|
108
108
|
| `dev-unit` | Generate or extend tests until the unit target is met |
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: "Review the active coding-agent session immediately: summarize outcomes, distinguish resolved and open issues with evidence, and propose bounded improvements. With --triage: apply pure-doc / one-to-two-line fixes inline, then file remaining findings as one
|
|
2
|
+
description: "Review the active coding-agent session immediately: summarize outcomes, distinguish resolved and open issues with evidence, and propose bounded improvements. With --triage: apply pure-doc / one-to-two-line fixes inline, then file remaining findings as one or more implement-ready tasks. Triggers: review this session, session wrap-up, immediate retrospective, what happened, what was resolved, triage findings"
|
|
3
3
|
role: reviewer
|
|
4
4
|
argument-hint: "[<focus>] [--triage]"
|
|
5
5
|
allowed-tools: ["Bash", "Read", "Grep", "Glob", "Skill"]
|
|
@@ -11,8 +11,8 @@ Wraps the **sp:session-review** skill for a lightweight review of the active hos
|
|
|
11
11
|
default it is report-only: current conversation plus read-only repository evidence, run inline so
|
|
12
12
|
the session context is preserved — no workflow launch, history import, task creation, or
|
|
13
13
|
remediation. With `--triage`, it first triages the findings, then applies direct fixes (pure
|
|
14
|
-
documentation work and one-to-two-line fixes) inline and files everything remaining as
|
|
15
|
-
|
|
14
|
+
documentation work and one-to-two-line fixes) inline and files everything remaining as one or more
|
|
15
|
+
implement-ready tasks for further fixing. The result includes a non-overlapping time breakdown with durations in
|
|
16
16
|
`M:SS` or `H:MM:SS` form and `n/a` for unavailable measurements.
|
|
17
17
|
|
|
18
18
|
## Argument Flags
|
|
@@ -20,7 +20,7 @@ new task for further fixing. The result includes a non-overlapping time breakdow
|
|
|
20
20
|
| Flag | Description | Default |
|
|
21
21
|
| --- | --- | --- |
|
|
22
22
|
| `[<focus>]` | Optional question or operation to emphasize without excluding material session outcomes. | full active session |
|
|
23
|
-
| `--triage` | After the report: bucket findings → apply pure-doc / 1–2-line fixes inline →
|
|
23
|
+
| `--triage` | After the report: bucket findings → apply pure-doc / 1–2-line fixes inline → file the remainder as one or more implement-ready tasks. | off (report-only) |
|
|
24
24
|
|
|
25
25
|
For shared semantics, see the [flag glossary](../skills/spur-dev/references/flag-glossary.md).
|
|
26
26
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: "Review code for a task or path — multi-dimensional review across functional traceability, SECUA quality, and architectural depth. Triggers: \"review this\", \"check the code\", \"SECUA review\", \"dev review\", \"audit this\"."
|
|
2
|
+
description: "Review code for a task or path — multi-dimensional review across functional traceability, SECUA quality, and architectural depth. With --triage: fix small findings directly, then file the rest as implement-ready tasks. Triggers: \"review this\", \"check the code\", \"SECUA review\", \"dev review\", \"audit this\", \"triage review findings\"."
|
|
3
3
|
role: reviewer
|
|
4
|
-
argument-hint: "[<wbs|path>] [--agent <inline|auto|name>] [--focus <dims>] [--
|
|
4
|
+
argument-hint: "[<wbs|path>] [--agent <inline|auto|name>] [--focus <dims>] [--triage] [--worktree [<name>]]"
|
|
5
5
|
allowed-tools: ["Bash", "Read", "Skill"]
|
|
6
6
|
---
|
|
7
7
|
|
|
@@ -16,17 +16,28 @@ Wraps the **sp:functional-review**, **sp:code-verification**, and **sp:code-impr
|
|
|
16
16
|
| `[<wbs\|path>]` | Task WBS or source path to review. | cwd |
|
|
17
17
|
| `--agent` `<inline\|auto\|name>` | Who runs the model-bearing review. | omit |
|
|
18
18
|
| `--focus` `<dims>` | Review dimensions (functional / SECUA / architecture). | all |
|
|
19
|
-
| `--
|
|
19
|
+
| `--triage` | After the review: fix small findings directly, file the rest as one or more implement-ready tasks. | off (report-only) |
|
|
20
|
+
| `--worktree` `[<name>]` | Run the triage in an isolated git worktree; FF-merge on success, retain on failure. Bare `--worktree` creates a fresh tree; `--worktree <name>` adopts an existing worktree by name/path/branch. Requires `--triage`. | off |
|
|
20
21
|
|
|
21
22
|
For shared semantics, see the [flag glossary](../skills/spur-dev/references/flag-glossary.md).
|
|
22
23
|
|
|
23
24
|
## Usage
|
|
24
25
|
|
|
25
|
-
|
|
26
|
+
```
|
|
27
|
+
/sp:dev-review [<wbs|path>] [--agent <inline|auto|name>] [--focus <dims>] [--triage] [--worktree [<name>]]
|
|
28
|
+
/sp:dev-review scripts --triage
|
|
29
|
+
/sp:dev-review 0962 --triage --worktree
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Put the target first: a bare token after `--worktree` is read as the worktree name.
|
|
26
33
|
|
|
27
34
|
## Implementation
|
|
28
35
|
|
|
29
36
|
- Apply the [inline-default execution-surface contract](../skills/spur-dev/references/cross-cutting.md#inline-default-execution-surface).
|
|
30
37
|
- WBS mode (`<wbs>`): `Skill(skill="sp:functional-review", args="<wbs> $ARGUMENTS")` + `Skill(skill="sp:code-verification", args="review $ARGUMENTS")` + `Skill(skill="sp:code-improvement", args="<wbs> $ARGUMENTS")` (functional traceability + SECUA framework + architectural depth; the three skills return review fragments and the coordinator (`sp:super-reviewer`) writes the combined `## Review` — F92 0593 R1)
|
|
31
38
|
- Path mode (`<path>`): `Skill(skill="sp:code-verification", args="review $ARGUMENTS")` + `Skill(skill="sp:code-improvement", args="<path> $ARGUMENTS")` (advisory SECUA quality + architectural depth; performs no task mutation)
|
|
32
|
-
- `--
|
|
39
|
+
- `--triage`: after the review returns, run the triage protocol in [dev-operations.md § 2. review](../skills/spur-dev/references/dev-operations.md#2-review) **inline in this session** — the review skills and `sp:super-reviewer` stay report-only.
|
|
40
|
+
- Without `--worktree`, every write lands in the current working tree on the current branch: no branch creation, no checkout, no commit.
|
|
41
|
+
- `--worktree [<name>]`: the [execution-batch.md § Worktree isolation](../skills/spur-dev/references/execution-batch.md#worktree-isolation---worktree-name) lifecycle as a run of one (marker `command` = `dev-review`, `selector` = the target). Rejected without `--triage`.
|
|
42
|
+
While it runs, fixes and filed tasks land in the worktree copy; the main tree shows them only after the FF-merge.
|
|
43
|
+
- `--fix`: Deprecated (still accepted: no-op + warning; route remediation to `--triage` or `/sp:dev-verify --fix`). **`--next` removed** (feature H8, 2026-07-31) — it was a deprecated no-op; route progression to `/sp:dev-next`. **was: `--next` deprecated no-op.**
|
|
@@ -25,6 +25,7 @@ import { isAbsolute, join, resolve } from 'node:path';
|
|
|
25
25
|
import type { ExtensionAPI } from '@earendil-works/pi-coding-agent';
|
|
26
26
|
import { getEnvVar, getEnvVars } from '../../lib/env';
|
|
27
27
|
import { resolveAgentHint as resolveAgentHintShared, resolveModelHint as resolveModelHintShared } from '../agent-hint';
|
|
28
|
+
import { cappedByteLength, truncateSummary } from '../context-post-tool';
|
|
28
29
|
import { classifyCommand } from '../destructive-policy';
|
|
29
30
|
import { couldBeTaskFile } from '../task-file-policy';
|
|
30
31
|
|
|
@@ -36,8 +37,6 @@ import { couldBeTaskFile } from '../task-file-policy';
|
|
|
36
37
|
const spurContextDir = (): string => join(process.cwd(), '.spur', 'context');
|
|
37
38
|
const sessionFilePath = (): string => join(spurContextDir(), '.session.json');
|
|
38
39
|
const ledgerFilePath = (): string => join(spurContextDir(), 'token-ledger.jsonl');
|
|
39
|
-
const REDACTION_CAP = 4096;
|
|
40
|
-
const SUMMARY_MAX_CHARS = 200;
|
|
41
40
|
|
|
42
41
|
// ─── Helpers ─────────────────────────────────────────────────────────────
|
|
43
42
|
|
|
@@ -122,39 +121,17 @@ interface ToolEvent {
|
|
|
122
121
|
tool_input?: Record<string, unknown>;
|
|
123
122
|
}
|
|
124
123
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
// Rough estimate: ~4 chars per token for English text
|
|
128
|
-
return Math.ceil(text.length / 4);
|
|
129
|
-
}
|
|
130
|
-
|
|
124
|
+
// Summaries persist to disk, so they go through the same scrubber as the Claude hook —
|
|
125
|
+
// the local copy this replaced stored the raw command (secrets included) as `summary`.
|
|
131
126
|
function summarizeToolEvent(event: ToolEvent): string {
|
|
132
127
|
const input = event.tool_input ?? {};
|
|
133
128
|
const candidates = [input.file_path, input.command, input.pattern, input.glob_pattern, input.glob, input.path];
|
|
134
129
|
for (const c of candidates) {
|
|
135
|
-
if (typeof c === 'string' && c.trim())
|
|
136
|
-
const s = c.trim();
|
|
137
|
-
return s.length > SUMMARY_MAX_CHARS ? `${s.slice(0, SUMMARY_MAX_CHARS - 3)}...` : s;
|
|
138
|
-
}
|
|
130
|
+
if (typeof c === 'string' && c.trim()) return truncateSummary(c);
|
|
139
131
|
}
|
|
140
132
|
return `(${event.tool_name ?? 'unknown'})`;
|
|
141
133
|
}
|
|
142
134
|
|
|
143
|
-
function redactText(text: string): string {
|
|
144
|
-
// Strip obvious secret patterns
|
|
145
|
-
let result = text.slice(0, REDACTION_CAP);
|
|
146
|
-
result = result.replace(
|
|
147
|
-
/-----BEGIN\s+(?:RSA\s+)?PRIVATE\s+KEY-----[\s\S]*?-----END\s+(?:RSA\s+)?PRIVATE\s+KEY-----/g,
|
|
148
|
-
'[REDACTED: PRIVATE KEY]',
|
|
149
|
-
);
|
|
150
|
-
result = result.replace(/ghp_[A-Za-z0-9]{36}/g, '[REDACTED: GITHUB_TOKEN]');
|
|
151
|
-
result = result.replace(/gho_[A-Za-z0-9]{36}/g, '[REDACTED: GITHUB_TOKEN]');
|
|
152
|
-
result = result.replace(/sk-[A-Za-z0-9]{20,}/g, '[REDACTED: API_KEY]');
|
|
153
|
-
result = result.replace(/api[_-]key['"]?\s*[:=]\s*['"][A-Za-z0-9_-]{16,}['"]/gi, '[REDACTED: API_KEY]');
|
|
154
|
-
result = result.replace(/AKIA[0-9A-Z]{16}/g, '[REDACTED: AWS_KEY]');
|
|
155
|
-
return result;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
135
|
function appendToLedger(event: ToolEvent, command: string | undefined): void {
|
|
159
136
|
try {
|
|
160
137
|
if (!existsSync(spurContextDir())) return;
|
|
@@ -162,16 +139,19 @@ function appendToLedger(event: ToolEvent, command: string | undefined): void {
|
|
|
162
139
|
if (!sessionId) return;
|
|
163
140
|
|
|
164
141
|
const summary = summarizeToolEvent(event);
|
|
165
|
-
const tokens = command ?
|
|
142
|
+
const tokens = command ? Math.ceil(cappedByteLength(command) / 4) : 0;
|
|
143
|
+
const input = event.tool_input ?? {};
|
|
144
|
+
// Pi tool names are lowercase (`read`) and its write/edit tools take `path`, not `file_path`.
|
|
145
|
+
const path = typeof input.file_path === 'string' ? input.file_path : input.path;
|
|
166
146
|
|
|
167
147
|
const ledgerEntry = JSON.stringify({
|
|
168
148
|
session: sessionId,
|
|
169
|
-
type: event.tool_name === '
|
|
149
|
+
type: event.tool_name?.toLowerCase() === 'read' ? 'read' : 'write',
|
|
170
150
|
tool: event.tool_name,
|
|
171
|
-
|
|
151
|
+
file: typeof path === 'string' ? path : undefined,
|
|
172
152
|
summary,
|
|
173
153
|
tokens,
|
|
174
|
-
|
|
154
|
+
ts: new Date().toISOString(),
|
|
175
155
|
});
|
|
176
156
|
|
|
177
157
|
appendFileSync(ledgerFilePath(), `${ledgerEntry}\n`);
|
|
@@ -216,7 +196,7 @@ function initSession(): void {
|
|
|
216
196
|
type: 'session_start',
|
|
217
197
|
agent: session.agent,
|
|
218
198
|
model: session.model,
|
|
219
|
-
|
|
199
|
+
ts: session.started_at,
|
|
220
200
|
});
|
|
221
201
|
appendFileSync(ledgerFilePath(), `${startEntry}\n`);
|
|
222
202
|
} catch {
|
|
@@ -256,7 +236,7 @@ function cleanupSession(): void {
|
|
|
256
236
|
reads,
|
|
257
237
|
writes,
|
|
258
238
|
tokens,
|
|
259
|
-
|
|
239
|
+
ts: new Date().toISOString(),
|
|
260
240
|
});
|
|
261
241
|
appendFileSync(ledgerFilePath(), `${endEntry}\n`);
|
|
262
242
|
|
|
@@ -272,8 +252,9 @@ function cleanupSession(): void {
|
|
|
272
252
|
export default function (pi: ExtensionAPI): void {
|
|
273
253
|
// ── task-write-guard + careful-guard ──────────────────────────────
|
|
274
254
|
pi.on('tool_call', async (event, ctx) => {
|
|
275
|
-
// task-write-guard: block Write/Edit to Spur task files
|
|
276
|
-
|
|
255
|
+
// task-write-guard: block Write/Edit to Spur task files.
|
|
256
|
+
// Both guards honor the same `off` escape hatches as the Claude hooks.
|
|
257
|
+
if ((event.toolName === 'write' || event.toolName === 'edit') && getEnvVar('SPUR_WRITE_GUARD') !== 'off') {
|
|
277
258
|
const input = event.input as Record<string, unknown> | undefined;
|
|
278
259
|
// Pi's write/edit tools use `path` (not Claude Code's `file_path`)
|
|
279
260
|
const filePath = resolveInputPath(input);
|
|
@@ -287,7 +268,7 @@ export default function (pi: ExtensionAPI): void {
|
|
|
287
268
|
}
|
|
288
269
|
|
|
289
270
|
// careful-guard: warn on destructive Bash commands
|
|
290
|
-
if (event.toolName === 'bash') {
|
|
271
|
+
if (event.toolName === 'bash' && getEnvVar('SPUR_CAREFUL') !== 'off') {
|
|
291
272
|
const input = event.input as Record<string, unknown> | undefined;
|
|
292
273
|
const command = typeof input?.command === 'string' ? input.command : '';
|
|
293
274
|
const hit = command ? classifyCommand(command) : null;
|
|
@@ -322,7 +322,7 @@ END`;var MEASURE_COLS="messages, tool_calls, skill_calls, fresh_input_tokens, ca
|
|
|
322
322
|
''
|
|
323
323
|
)
|
|
324
324
|
ELSE ''
|
|
325
|
-
END`;var POST_WATERMARK_TABLES=[...KEYED_ROLLUP_TABLES,...GLOBAL_RANKED_ROLLUP_TABLES,"history_daily_stats","history_board_source_daily"];var HISTORY_IMPORT_SCHEMA_VERSION="0.5.
|
|
325
|
+
END`;var POST_WATERMARK_TABLES=[...KEYED_ROLLUP_TABLES,...GLOBAL_RANKED_ROLLUP_TABLES,"history_daily_stats","history_board_source_daily"];var HISTORY_IMPORT_SCHEMA_VERSION="0.5.9",HISTORY_IMPORT_SCHEMA_SQL=`
|
|
326
326
|
CREATE TABLE IF NOT EXISTS history_import_checkpoint (
|
|
327
327
|
source TEXT NOT NULL,
|
|
328
328
|
source_file TEXT NOT NULL,
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
/** GENERATED by scripts/commands/bundle-plugin-lib.ts — do not edit. Requires Bun for SQLite. */
|
|
2
2
|
export declare const createOrAttachInlineRun: typeof import('@gobing-ai/spur-app').createOrAttachInlineRun;
|
|
3
3
|
export declare const openInlineRunProjectDb: typeof import('@gobing-ai/spur-app').openInlineRunProjectDb;
|
|
4
|
+
export declare const persistWorktreeRuns: typeof import('@gobing-ai/spur-app').persistWorktreeRuns;
|
|
5
|
+
export declare const runDecideForInlineRun: typeof import('@gobing-ai/spur-app').runDecideForInlineRun;
|
|
4
6
|
export declare const computeProofInputFingerprint: typeof import('@gobing-ai/spur-app').computeProofInputFingerprint;
|
|
5
7
|
export declare const readProofInputContents: typeof import('@gobing-ai/spur-app').readProofInputContents;
|
|
6
8
|
export declare const createWorkflowActionTraceWriter: typeof import('@gobing-ai/spur-app').createWorkflowActionTraceWriter;
|