@gobing-ai/spur 0.3.93 → 0.3.95
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/README.md +42 -0
- package/board/index.d.ts +38 -0
- package/config/plugin-scripts.json +5 -0
- 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/feature-verification.yaml +7 -4
- package/config/workflows/idea-pipeline.yaml +10 -4
- package/config/workflows/task-pipeline.yaml +71 -52
- package/config/workflows/wrapup-pipeline.yaml +92 -17
- package/package.json +15 -11
- 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/commands/dev-verify.md +7 -4
- package/plugins/sp/commands/dev-verifyall.md +4 -3
- package/plugins/sp/commands/dev-wrap.md +1 -1
- package/plugins/sp/hooks/context-post-tool.ts +2 -2
- package/plugins/sp/hooks/context-session-start.ts +2 -1
- package/plugins/sp/hooks/context-session-stop.ts +22 -14
- package/plugins/sp/hooks/pi/guard-extension.ts +112 -172
- package/plugins/sp/lib/idea-handoff.generated.mjs +2 -2
- package/plugins/sp/lib/inline-run.generated.d.mts +4 -2
- package/plugins/sp/lib/inline-run.generated.mjs +35 -14
- package/plugins/sp/plugin.json +1 -1
- package/plugins/sp/scripts/daily-summary/daily-summary.mjs +3 -3
- package/plugins/sp/scripts/daily-summary/daily-summary.ts +3 -3
- package/plugins/sp/scripts/dogfood-testing/detect-pipeline-driving.mjs +2 -0
- package/plugins/sp/scripts/dogfood-testing/detect-pipeline-driving.ts +1 -0
- package/plugins/sp/scripts/dogfood-testing/validate-report.mjs +9 -7
- package/plugins/sp/scripts/dogfood-testing/validate-report.ts +8 -7
- package/plugins/sp/scripts/feature-sync-bounded.mjs +5 -2
- package/plugins/sp/scripts/feature-sync-bounded.ts +3 -1
- package/plugins/sp/scripts/feature-verification-steps.ts +6 -6
- package/plugins/sp/scripts/history-anatomy-cache.mjs +3 -3
- package/plugins/sp/scripts/history-anatomy-cache.ts +4 -4
- package/plugins/sp/scripts/inline-run-setup.mjs +60 -12
- package/plugins/sp/scripts/inline-run-setup.ts +122 -15
- package/plugins/sp/scripts/pr-reviewing.mjs +2 -0
- package/plugins/sp/scripts/pr-reviewing.ts +1 -0
- package/plugins/sp/scripts/quality-gate.mjs +15 -6
- package/plugins/sp/scripts/quality-gate.ts +30 -9
- package/plugins/sp/scripts/residual-scan.mjs +4 -4
- package/plugins/sp/scripts/residual-scan.ts +5 -4
- package/plugins/sp/scripts/script-contract-check.ts +175 -46
- package/plugins/sp/scripts/script-root.mjs +125 -0
- package/plugins/sp/scripts/script-root.ts +195 -0
- package/plugins/sp/scripts/stage-registry-adapter.ts +1 -1
- package/plugins/sp/scripts/task-evidence-precheck.ts +4 -2
- package/plugins/sp/scripts/task-size-precheck.ts +3 -1
- package/plugins/sp/scripts/validate-commands.ts +12 -12
- package/plugins/sp/scripts/validate-flag-contracts.ts +42 -30
- package/plugins/sp/scripts/verify-answer-lint.ts +3 -1
- package/plugins/sp/scripts/workflow-step-profile.mjs +5 -2
- package/plugins/sp/scripts/workflow-step-profile.ts +3 -1
- package/plugins/sp/scripts/wrapup-drift-probe.mjs +5 -2
- package/plugins/sp/scripts/wrapup-drift-probe.ts +4 -2
- package/plugins/sp/scripts/wrapup-steps.mjs +32 -8
- package/plugins/sp/scripts/wrapup-steps.ts +56 -10
- package/plugins/sp/skills/code-verification/SKILL.md +12 -7
- package/plugins/sp/skills/code-verification/references/verdict-schema.md +11 -0
- package/plugins/sp/skills/functional-review/SKILL.md +1 -1
- package/plugins/sp/skills/next-router/references/routing-table.md +1 -1
- package/plugins/sp/skills/session-review/SKILL.md +11 -9
- package/plugins/sp/skills/spur-cli/references/tasks/verbs.md +9 -1
- package/plugins/sp/skills/spur-dev/references/dev-operations.md +8 -2
- package/plugins/sp/skills/spur-dev/references/execution-batch.md +65 -17
- package/plugins/sp/skills/spur-dev/references/flag-glossary.md +16 -3
- package/plugins/sp/skills/spur-dev/references/inline-pipeline-driver.md +22 -5
- package/plugins/sp/tsconfig.json +8 -0
- package/spur.js +20934 -21087
- package/web/_astro/{BoardApp.CerSBgis.js → BoardApp.BxJuwD7I.js} +151 -152
- package/web/_astro/BoardApp.Cpxntzad.js +1 -0
- package/web/_astro/TaskDetail.C5bW4WGV.js +1 -0
- package/web/_astro/_commonjsHelpers.CqkleIqs.js +1 -0
- package/web/_astro/arc.CyjRvNMY.js +1 -0
- package/web/_astro/architectureDiagram-3BPJPVTR.B4lWRzJA.js +36 -0
- package/web/_astro/{blockDiagram-GPEHLZMM.DXVIiv0p.js → blockDiagram-GPEHLZMM.Be38USQ2.js} +1 -1
- package/web/_astro/board-facade-react-dom-client.B2U5wMvk.js +1 -0
- package/web/_astro/board-facade-react-dom.Cd2MuMhF.js +1 -0
- package/web/_astro/board-facade-react-jsx-dev-runtime.B_L3JzfC.js +1 -0
- package/web/_astro/board-facade-react-jsx-runtime.BY2yfHRi.js +1 -0
- package/web/_astro/board-facade-react-router-dom.Dy5pjzOw.js +1 -0
- package/web/_astro/board-facade-react-router.DjjqU27_.js +1 -0
- package/web/_astro/board-facade-react.Dgf4GRyj.js +1 -0
- package/web/_astro/{c4Diagram-AAUBKEIU.BbF_zCxW.js → c4Diagram-AAUBKEIU.DT9Fj5Qx.js} +1 -1
- package/web/_astro/channel.CI6N_tCg.js +1 -0
- package/web/_astro/{chunk-2J33WTMH.CAgQHpPC.js → chunk-2J33WTMH.SeSBWLg5.js} +1 -1
- package/web/_astro/{chunk-4BX2VUAB.BN-5tpw4.js → chunk-4BX2VUAB.DoO4VE14.js} +1 -1
- package/web/_astro/{chunk-55IACEB6.CnPkEEr0.js → chunk-55IACEB6.DrmTFA53.js} +1 -1
- package/web/_astro/{chunk-727SXJPM.BQzQeMVm.js → chunk-727SXJPM.Bi-TUb_V.js} +4 -4
- package/web/_astro/{chunk-AQP2D5EJ.B6xNyDnL.js → chunk-AQP2D5EJ.B-xp8Uhi.js} +1 -1
- package/web/_astro/{chunk-FMBD7UC4.C7f9Ih78.js → chunk-FMBD7UC4.DGWrDnUU.js} +1 -1
- package/web/_astro/chunk-JMJ3UQ3L.BQJEJHu4.js +34 -0
- package/web/_astro/{chunk-ND2GUHAM.CNV1dFXT.js → chunk-ND2GUHAM._BagvPDy.js} +1 -1
- package/web/_astro/{chunk-QZHKN3VN.Cudn2TkJ.js → chunk-QZHKN3VN.VOboYWQ0.js} +1 -1
- package/web/_astro/chunk-YNUBSHFH.CxeAygfi.js +7 -0
- package/web/_astro/{classDiagram-4FO5ZUOK.D1NwP50q.js → classDiagram-4FO5ZUOK.D_apfHS7.js} +1 -1
- package/web/_astro/{classDiagram-v2-Q7XG4LA2.D1NwP50q.js → classDiagram-v2-Q7XG4LA2.D_apfHS7.js} +1 -1
- package/web/_astro/client.BhluLQqe.js +1 -0
- package/web/_astro/client.DBpSRLnx.js +9 -0
- package/web/_astro/cose-bilkent-S5V4N54A.DNLU_L-x.js +1 -0
- package/web/_astro/{cynefin-OW5HDTMX.BmK52w8G.js → cynefin-OW5HDTMX.D8borhV-.js} +1 -1
- package/web/_astro/{dagre-BM42HDAG.Bfy5CTDT.js → dagre-BM42HDAG.Uc0lvjcV.js} +2 -2
- package/web/_astro/diagram-2AECGRRQ.DDIxtDBo.js +43 -0
- package/web/_astro/diagram-5GNKFQAL.DVoDnD3H.js +10 -0
- package/web/_astro/{diagram-KO2AKTUF.CW_vMJ4z.js → diagram-KO2AKTUF.CRubTJ-0.js} +3 -3
- package/web/_astro/{diagram-LMA3HP47.B_8ZGF67.js → diagram-LMA3HP47.m9SeYcXQ.js} +1 -1
- package/web/_astro/{diagram-OG6HWLK6.BppnHsdS.js → diagram-OG6HWLK6.C6s2ZjV5.js} +1 -1
- package/web/_astro/{erDiagram-TEJ5UH35.BEuHXcjJ.js → erDiagram-TEJ5UH35.nSzr5KTj.js} +5 -5
- package/web/_astro/{flowDiagram-I6XJVG4X.CH-UlnGr.js → flowDiagram-I6XJVG4X.DrZcys16.js} +4 -4
- package/web/_astro/ganttDiagram-6RSMTGT7.Do4M1b-K.js +292 -0
- package/web/_astro/{gitGraphDiagram-PVQCEYII.XnPxPPZN.js → gitGraphDiagram-PVQCEYII.BWc9oJ4l.js} +1 -1
- package/web/_astro/index.5dn-Zm-2.js +1 -0
- package/web/_astro/index.C-IqPpW2.js +1 -0
- package/web/_astro/index.CENnIEqT.css +1 -0
- package/web/_astro/index.Cyc_GA4f.js +1 -0
- package/web/_astro/index.tZfqwSut.js +1 -0
- package/web/_astro/{infoDiagram-5YYISTIA.JyjYRu_T.js → infoDiagram-5YYISTIA.BIdOctFk.js} +1 -1
- package/web/_astro/{ishikawaDiagram-YF4QCWOH.BBRBF-Fo.js → ishikawaDiagram-YF4QCWOH.CV5SuG2t.js} +5 -5
- package/web/_astro/{journeyDiagram-JHISSGLW.C_iymSyp.js → journeyDiagram-JHISSGLW.CxE-rEJ-.js} +1 -1
- package/web/_astro/jsx-runtime.DWOiEOII.js +1 -0
- package/web/_astro/{kanban-definition-UN3LZRKU.DdfW-Oqt.js → kanban-definition-UN3LZRKU.BPRdKWs_.js} +7 -7
- package/web/_astro/{linear.C2_IkbZT.js → linear.zRsuuDTE.js} +1 -1
- package/web/_astro/mermaid.core.jAJTcMKc.js +301 -0
- package/web/_astro/{mindmap-definition-RKZ34NQL.DAZIxQSK.js → mindmap-definition-RKZ34NQL.BffJxfCr.js} +2 -2
- package/web/_astro/{pieDiagram-4H26LBE5.CN8sIhKM.js → pieDiagram-4H26LBE5.CacwWaE3.js} +3 -3
- package/web/_astro/{quadrantDiagram-W4KKPZXB.3dGcX5GP.js → quadrantDiagram-W4KKPZXB.hXELD06-.js} +1 -1
- package/web/_astro/{requirementDiagram-4Y6WPE33.BV2y4dd6.js → requirementDiagram-4Y6WPE33.DdEbPcqT.js} +3 -3
- package/web/_astro/{sankeyDiagram-5OEKKPKP.Cqo15Tvo.js → sankeyDiagram-5OEKKPKP.DgaDocKv.js} +4 -4
- package/web/_astro/{sequenceDiagram-3UESZ5HK.CROCPMJB.js → sequenceDiagram-3UESZ5HK.B0KScnAu.js} +1 -1
- package/web/_astro/{stateDiagram-AJRCARHV.RfXZrkFE.js → stateDiagram-AJRCARHV.CV9M_WNc.js} +1 -1
- package/web/_astro/{stateDiagram-v2-BHNVJYJU.CPXmbBs9.js → stateDiagram-v2-BHNVJYJU.BOAo74Et.js} +1 -1
- package/web/_astro/{timeline-definition-PNZ67QCA.DdgKTiO8.js → timeline-definition-PNZ67QCA.CqqepUMe.js} +3 -3
- package/web/_astro/{vennDiagram-CIIHVFJN.CPNVSHF1.js → vennDiagram-CIIHVFJN.DDqe4dOG.js} +5 -5
- package/web/_astro/{wardleyDiagram-YWT4CUSO.CQhA0Jyr.js → wardleyDiagram-YWT4CUSO.B3R9OPdO.js} +3 -3
- package/web/_astro/{xychartDiagram-2RQKCTM6.n61BWyy4.js → xychartDiagram-2RQKCTM6.B3SvgXpQ.js} +1 -1
- package/web/board-runtime.json +69 -0
- package/web/index.html +2 -2
- package/web/_astro/BoardApp.eoTz0pZs.js +0 -1
- package/web/_astro/TaskDetail.DCqiC-OZ.js +0 -1
- package/web/_astro/arc.CPwg6Rw0.js +0 -1
- package/web/_astro/architectureDiagram-3BPJPVTR.DM_vp_hO.js +0 -36
- package/web/_astro/channel.MYZLKNwy.js +0 -1
- package/web/_astro/client.yhYJvxCU.js +0 -9
- package/web/_astro/cose-bilkent-S5V4N54A.B1wSL-Xb.js +0 -1
- package/web/_astro/diagram-2AECGRRQ.DhNnvUvX.js +0 -43
- package/web/_astro/diagram-5GNKFQAL.lTX5KwnS.js +0 -10
- package/web/_astro/ganttDiagram-6RSMTGT7.BO81S85v.js +0 -292
- package/web/_astro/index.De90oHcH.js +0 -1
- package/web/_astro/index.Hjbr15fG.css +0 -1
- package/web/_astro/mermaid.core.GAOYeSR0.js +0 -303
|
@@ -13,13 +13,18 @@
|
|
|
13
13
|
# pauses,
|
|
14
14
|
# even under --auto (Iron Law #6: irreversible action -> surface to human).
|
|
15
15
|
#
|
|
16
|
-
# Shape: start -> task-resolve -> doc-sync -> learnings-
|
|
16
|
+
# Shape: start -> task-resolve -> doc-sync -> learnings-validate -> learnings-append
|
|
17
|
+
# -> metrics-record
|
|
17
18
|
# -> feature-transition (conditional: if vars.feature set)
|
|
18
19
|
# -> feature-verify (post-transition feature check gate, 0915 F1)
|
|
19
20
|
# -> branch-cleanup (conditional: if vars.merge=true)
|
|
20
21
|
# -> done
|
|
21
22
|
# doc-sync routes a contract violation to `repair` (cheap, no
|
|
22
23
|
# re-dispatch — ADR-118 pilot) and an executor failure to `failed`;
|
|
24
|
+
# learnings-validate (0986) rejects a nonempty narration-only capture to
|
|
25
|
+
# the same `repair` edge with a distinct invalid-learnings-shape status —
|
|
26
|
+
# expectFile proves existence, never shape — while empty/missing captures
|
|
27
|
+
# keep the soft skip by passing through to learnings-append;
|
|
23
28
|
# task-resolve with a VALIDATED empty list short-circuits to `skipped`;
|
|
24
29
|
# invalid wrap input, an unresolved/non-completed task, a failed metrics
|
|
25
30
|
# capture, a failed required sync, or a failed feature gate routes to
|
|
@@ -75,7 +80,7 @@
|
|
|
75
80
|
"$schema": "@gobing-ai/spur/schemas/state-machine-workflow.schema.json"
|
|
76
81
|
kind: state-machine
|
|
77
82
|
name: wrapup-pipeline
|
|
78
|
-
version: "
|
|
83
|
+
version: "6"
|
|
79
84
|
description: "Post-execution wrap-up: doc-sync (doc drift + learning capture), metrics, feature-transition, branch-cleanup"
|
|
80
85
|
iterationBound: 10
|
|
81
86
|
initialState: start
|
|
@@ -108,6 +113,12 @@ states:
|
|
|
108
113
|
Pipeline start. Task statuses are NOT mutated by wrap-up — wrap-up consumes
|
|
109
114
|
completed tasks and produces learning/metrics/doc artifacts only.
|
|
110
115
|
onEnter:
|
|
116
|
+
# 0960 R2: run-start script-resolution identity — record which tree this run
|
|
117
|
+
# resolved (source-repo vs installed) before any step executes. Never aborts.
|
|
118
|
+
- kind: shell
|
|
119
|
+
options:
|
|
120
|
+
command: >-
|
|
121
|
+
S=plugins/sp/scripts/script-root.ts; [ -f config/plugin-scripts.json -a -f "$S" ] || S="$(superskill script path sp script-root.mjs 2>/dev/null)"; RUNNER=bun; case "$S" in *.mjs) RUNNER=node ;; esac; [ -f "$S" ] && "$RUNNER" "$S" --run-id "$__runId" --spur-bin "$spurBin" || echo "script-root failed closed — run 'superskill install sp --marketplace gobing-ai/spur'" >&2; exit 0
|
|
111
122
|
- kind: note
|
|
112
123
|
options:
|
|
113
124
|
message: "Wrap-up pipeline start for tasks: ${vars.tasks}. Task statuses are NOT mutated."
|
|
@@ -143,12 +154,12 @@ states:
|
|
|
143
154
|
options:
|
|
144
155
|
command: >-
|
|
145
156
|
mkdir -p .spur/run &&
|
|
146
|
-
if [ -f plugins/sp/scripts/wrapup-steps.ts ]; then
|
|
157
|
+
if [ -f config/plugin-scripts.json -a -f plugins/sp/scripts/wrapup-steps.ts ]; then
|
|
147
158
|
bun plugins/sp/scripts/wrapup-steps.ts resolve;
|
|
148
159
|
elif W="$(superskill script path sp wrapup-steps.mjs 2>/dev/null)" && [ -f "$W" ]; then
|
|
149
160
|
node "$W" resolve;
|
|
150
161
|
else
|
|
151
|
-
echo "wrapup-steps failed closed — script not found — run 'superskill install sp'" >&2;
|
|
162
|
+
echo "wrapup-steps failed closed — script not found — run 'superskill install sp --marketplace gobing-ai/spur'" >&2;
|
|
152
163
|
printf 'FAIL\n' > ".spur/run/$__runId-wrapup-resolve.status";
|
|
153
164
|
fi
|
|
154
165
|
# (0944 R1-R3) deterministic drift probe — runs ONLY when the caller left mode
|
|
@@ -163,7 +174,7 @@ states:
|
|
|
163
174
|
command: >-
|
|
164
175
|
if [ -n "$mode" ]; then
|
|
165
176
|
printf '%s\n' "$mode" > ".spur/run/$__runId-mode.txt";
|
|
166
|
-
elif [ -f plugins/sp/scripts/wrapup-drift-probe.ts ]; then
|
|
177
|
+
elif [ -f config/plugin-scripts.json -a -f plugins/sp/scripts/wrapup-drift-probe.ts ]; then
|
|
167
178
|
bun plugins/sp/scripts/wrapup-drift-probe.ts;
|
|
168
179
|
elif W="$(superskill script path sp wrapup-drift-probe.mjs 2>/dev/null)" && [ -f "$W" ]; then
|
|
169
180
|
node "$W";
|
|
@@ -190,12 +201,12 @@ states:
|
|
|
190
201
|
options:
|
|
191
202
|
command: >-
|
|
192
203
|
mkdir -p .spur/run .spur/memory &&
|
|
193
|
-
if [ -f plugins/sp/scripts/wrapup-steps.ts ]; then
|
|
204
|
+
if [ -f config/plugin-scripts.json -a -f plugins/sp/scripts/wrapup-steps.ts ]; then
|
|
194
205
|
bun plugins/sp/scripts/wrapup-steps.ts route-reason;
|
|
195
206
|
elif W="$(superskill script path sp wrapup-steps.mjs 2>/dev/null)" && [ -f "$W" ]; then
|
|
196
207
|
node "$W" route-reason;
|
|
197
208
|
else
|
|
198
|
-
echo "wrapup-steps failed closed — script not found — run 'superskill install sp'" >&2;
|
|
209
|
+
echo "wrapup-steps failed closed — script not found — run 'superskill install sp --marketplace gobing-ai/spur'" >&2;
|
|
199
210
|
printf 'FAIL\n' > ".spur/run/$__runId-wrapup-resolve.status";
|
|
200
211
|
fi
|
|
201
212
|
|
|
@@ -234,6 +245,34 @@ states:
|
|
|
234
245
|
expectFile: .spur/run/${vars.__runId}-wrapup-learnings.md
|
|
235
246
|
timeoutMs: ${vars.stepTimeoutMs}
|
|
236
247
|
|
|
248
|
+
- id: learnings-validate
|
|
249
|
+
description: >
|
|
250
|
+
Structural shape gate between doc-sync and the append (0986): expectFile
|
|
251
|
+
asserts the capture EXISTS and nothing about its shape, so a
|
|
252
|
+
stream-of-thought capture could be appended verbatim to the tracked
|
|
253
|
+
memory file. This state classifies a NONEMPTY capture — a date, a
|
|
254
|
+
four-digit task WBS, and at least one markdown bullet — and writes PASS
|
|
255
|
+
or invalid-learnings-shape to a run-scoped status. An empty or missing
|
|
256
|
+
capture keeps the existing soft skip (PASS) so learnings-append still
|
|
257
|
+
performs its no-op; an unclassifiable status fails loud instead of
|
|
258
|
+
appending it. Structural check only — the doc-sync prompt owns prose
|
|
259
|
+
quality; no new engine-level content contract.
|
|
260
|
+
onEnter:
|
|
261
|
+
# (warn) structural check only; the doc-sync prompt owns prose quality (0986 Design).
|
|
262
|
+
# The WBS predicate excludes digit/dash neighbours so a date's year cannot satisfy it.
|
|
263
|
+
- kind: shell
|
|
264
|
+
options:
|
|
265
|
+
command: >-
|
|
266
|
+
mkdir -p .spur/run &&
|
|
267
|
+
S=".spur/run/$__runId-wrapup-learnings.status" &&
|
|
268
|
+
F=".spur/run/$__runId-wrapup-learnings.md" &&
|
|
269
|
+
if [ ! -s "$F" ] || awk '/[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]/{d=1} /(^|[^0-9-])[0-9][0-9][0-9][0-9]([^0-9-]|$)/{w=1} /^[[:space:]]*[-*] /{b=1} END{exit !(d+w+b==3)}' "$F"; then
|
|
270
|
+
printf 'PASS\n' > "$S";
|
|
271
|
+
else
|
|
272
|
+
printf 'invalid-learnings-shape\n' > "$S";
|
|
273
|
+
fi &&
|
|
274
|
+
exit 0
|
|
275
|
+
|
|
237
276
|
- id: learnings-append
|
|
238
277
|
description: >
|
|
239
278
|
Best-effort soft append of the captured learnings to .spur/memory/learnings.md
|
|
@@ -259,15 +298,22 @@ states:
|
|
|
259
298
|
description: >
|
|
260
299
|
Contract-violation repair (ADR-118 pilot, 0871): doc-sync's agent.run missed
|
|
261
300
|
its declared post-condition after a clean exit, so wrap-up does NOT re-dispatch
|
|
262
|
-
the expensive agent on this attempt.
|
|
263
|
-
|
|
264
|
-
|
|
301
|
+
the expensive agent on this attempt. Also the destination for a nonempty
|
|
302
|
+
capture that fails learnings-validate's shape gate (0986). The miss is
|
|
303
|
+
recorded to a run-scoped repair status — text distinguishing a narration-only
|
|
304
|
+
shape failure from the missing-file contract violation — and wrap-up proceeds
|
|
305
|
+
to metrics-record; the run log and action trace already name the violated
|
|
306
|
+
contract and the observed value.
|
|
265
307
|
onEnter:
|
|
266
308
|
- kind: shell
|
|
267
309
|
options:
|
|
268
310
|
command: >-
|
|
269
311
|
mkdir -p .spur/run &&
|
|
270
|
-
|
|
312
|
+
R=".spur/run/$__runId-wrapup-repair.status" &&
|
|
313
|
+
V="$(cat .spur/run/$__runId-wrapup-learnings.status 2>/dev/null)" &&
|
|
314
|
+
if [ "$V" = invalid-learnings-shape ]; then printf 'invalid-learnings-shape: narration-only learnings capture — append skipped, repair recorded (0986)\n' > "$R";
|
|
315
|
+
else printf 'contract-violation: doc-sync learnings capture missed its declared contract — repair skipped re-dispatch (ADR-118 pilot)\n' > "$R";
|
|
316
|
+
fi &&
|
|
271
317
|
exit 0
|
|
272
318
|
|
|
273
319
|
- id: metrics-record
|
|
@@ -288,12 +334,12 @@ states:
|
|
|
288
334
|
options:
|
|
289
335
|
command: >-
|
|
290
336
|
mkdir -p .spur/run &&
|
|
291
|
-
if [ -f plugins/sp/scripts/wrapup-steps.ts ]; then
|
|
337
|
+
if [ -f config/plugin-scripts.json -a -f plugins/sp/scripts/wrapup-steps.ts ]; then
|
|
292
338
|
bun plugins/sp/scripts/wrapup-steps.ts metrics;
|
|
293
339
|
elif W="$(superskill script path sp wrapup-steps.mjs 2>/dev/null)" && [ -f "$W" ]; then
|
|
294
340
|
node "$W" metrics;
|
|
295
341
|
else
|
|
296
|
-
echo "wrapup-steps failed closed — script not found — run 'superskill install sp'" >&2;
|
|
342
|
+
echo "wrapup-steps failed closed — script not found — run 'superskill install sp --marketplace gobing-ai/spur'" >&2;
|
|
297
343
|
printf 'FAIL\n' > ".spur/run/$__runId-wrapup-metrics.status";
|
|
298
344
|
fi
|
|
299
345
|
|
|
@@ -322,12 +368,12 @@ states:
|
|
|
322
368
|
options:
|
|
323
369
|
command: >-
|
|
324
370
|
mkdir -p .spur/run &&
|
|
325
|
-
if [ -f plugins/sp/scripts/wrapup-steps.ts ]; then
|
|
371
|
+
if [ -f config/plugin-scripts.json -a -f plugins/sp/scripts/wrapup-steps.ts ]; then
|
|
326
372
|
bun plugins/sp/scripts/wrapup-steps.ts feature-transition;
|
|
327
373
|
elif W="$(superskill script path sp wrapup-steps.mjs 2>/dev/null)" && [ -f "$W" ]; then
|
|
328
374
|
node "$W" feature-transition;
|
|
329
375
|
else
|
|
330
|
-
echo "wrapup-steps failed closed — script not found — run 'superskill install sp'" >&2;
|
|
376
|
+
echo "wrapup-steps failed closed — script not found — run 'superskill install sp --marketplace gobing-ai/spur'" >&2;
|
|
331
377
|
printf 'FAIL\n' > ".spur/run/$__runId-wrapup-sync.status";
|
|
332
378
|
fi
|
|
333
379
|
|
|
@@ -460,8 +506,8 @@ transitions:
|
|
|
460
506
|
guard:
|
|
461
507
|
kind: contract-violation
|
|
462
508
|
- from: doc-sync
|
|
463
|
-
to: learnings-
|
|
464
|
-
description: Doc drift repair + learning capture succeeded —
|
|
509
|
+
to: learnings-validate
|
|
510
|
+
description: Doc drift repair + learning capture succeeded — validate the capture's shape before appending.
|
|
465
511
|
guard:
|
|
466
512
|
kind: action-ok
|
|
467
513
|
- from: doc-sync
|
|
@@ -475,6 +521,35 @@ transitions:
|
|
|
475
521
|
guard:
|
|
476
522
|
kind: always
|
|
477
523
|
|
|
524
|
+
# ── learnings-validate: shape gate (0986) — invalid-shape to repair, PASS to append ──
|
|
525
|
+
# Declaration order matters: the discriminating invalid-shape edge is tried
|
|
526
|
+
# first, then PASS; an unreadable status fails loud rather than appending an
|
|
527
|
+
# unclassified capture.
|
|
528
|
+
- from: learnings-validate
|
|
529
|
+
to: repair
|
|
530
|
+
description: >
|
|
531
|
+
Nonempty capture failed the structural shape gate (no date/WBS/bullet) —
|
|
532
|
+
route to the cheap repair edge and never append narration (0986 R2).
|
|
533
|
+
guard:
|
|
534
|
+
kind: shell
|
|
535
|
+
options:
|
|
536
|
+
command: 'test "$(cat .spur/run/$__runId-wrapup-learnings.status 2>/dev/null)" = invalid-learnings-shape'
|
|
537
|
+
- from: learnings-validate
|
|
538
|
+
to: learnings-append
|
|
539
|
+
description: Capture is shape-valid (or empty/missing, keeping the soft skip) — append.
|
|
540
|
+
guard:
|
|
541
|
+
kind: shell
|
|
542
|
+
options:
|
|
543
|
+
command: 'test "$(cat .spur/run/$__runId-wrapup-learnings.status 2>/dev/null)" = PASS'
|
|
544
|
+
- from: learnings-validate
|
|
545
|
+
to: failed
|
|
546
|
+
terminalReason: failed-check
|
|
547
|
+
description: >
|
|
548
|
+
Validation status missing or unreadable — fail loud instead of appending
|
|
549
|
+
a capture whose shape was never classified.
|
|
550
|
+
guard:
|
|
551
|
+
kind: always
|
|
552
|
+
|
|
478
553
|
# ── linear body: learnings-append -> metrics-record ──
|
|
479
554
|
- from: learnings-append
|
|
480
555
|
to: metrics-record
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gobing-ai/spur",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.95",
|
|
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",
|
|
@@ -24,12 +24,16 @@
|
|
|
24
24
|
"spur": "spur.js"
|
|
25
25
|
},
|
|
26
26
|
"exports": {
|
|
27
|
-
"./schemas/*": "./schemas/*"
|
|
27
|
+
"./schemas/*": "./schemas/*",
|
|
28
|
+
"./board": {
|
|
29
|
+
"types": "./board/index.d.ts"
|
|
30
|
+
}
|
|
28
31
|
},
|
|
29
32
|
"files": [
|
|
30
33
|
"spur.js",
|
|
31
34
|
"config",
|
|
32
35
|
"schemas",
|
|
36
|
+
"board",
|
|
33
37
|
"web",
|
|
34
38
|
"README.md",
|
|
35
39
|
"plugins/",
|
|
@@ -45,7 +49,7 @@
|
|
|
45
49
|
"dev": "bun run src/index.ts",
|
|
46
50
|
"build": "bun run ../../scripts/spur-dev.ts build-cli",
|
|
47
51
|
"build:binaries": "bun run ../../scripts/spur-dev.ts build-binaries",
|
|
48
|
-
"build:bundle": "bun run ../../scripts/spur-dev.ts bundle-web && bun build src/index.ts --target=bun --outfile spur.js && bun run ../../scripts/spur-dev.ts bundle-config config && bun run ../../scripts/spur-dev.ts bundle-plugins",
|
|
52
|
+
"build:bundle": "bun run ../../scripts/spur-dev.ts bundle-web && bun run ../../scripts/spur-dev.ts emit-board-types && bun build src/index.ts --target=bun --outfile spur.js && bun run ../../scripts/spur-dev.ts bundle-config config && bun run ../../scripts/spur-dev.ts bundle-plugins",
|
|
49
53
|
"test": "bun test tests --coverage --coverage-dir=../../.coverage --pass-with-no-tests",
|
|
50
54
|
"typecheck": "tsc --noEmit",
|
|
51
55
|
"prepack": "bun run build:bundle",
|
|
@@ -53,14 +57,14 @@
|
|
|
53
57
|
},
|
|
54
58
|
"devDependencies": {
|
|
55
59
|
"@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.
|
|
60
|
+
"@gobing-ai/ts-db": "^0.5.11",
|
|
61
|
+
"@gobing-ai/ts-ai-runner": "^0.5.11",
|
|
62
|
+
"@gobing-ai/ts-dual-workflow-engine": "^0.5.11",
|
|
63
|
+
"@gobing-ai/ts-infra": "^0.5.11",
|
|
64
|
+
"@gobing-ai/ts-llm-jsonl-importer": "^0.5.11",
|
|
65
|
+
"@gobing-ai/ts-rule-engine": "^0.5.11",
|
|
66
|
+
"@gobing-ai/ts-runtime": "^0.5.11",
|
|
67
|
+
"@gobing-ai/ts-utils": "^0.5.11",
|
|
64
68
|
"@types/bun": "1.3.14",
|
|
65
69
|
"@types/figlet": "^1.7.0",
|
|
66
70
|
"@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.**
|
|
@@ -40,10 +40,13 @@ See `sp:code-verification` § Shippable readiness gate.
|
|
|
40
40
|
|
|
41
41
|
- Apply the [inline-default execution-surface contract](../skills/spur-dev/references/cross-cutting.md#inline-default-execution-surface).
|
|
42
42
|
- `Skill(skill="sp:code-verification", args="verify $ARGUMENTS")`
|
|
43
|
-
- **Residual sweep (F96 R1):** after
|
|
44
|
-
`.spur/run/<wbs>-verdict.json
|
|
45
|
-
|
|
46
|
-
|
|
43
|
+
- **Residual sweep (F96 R1, post-record since 0987):** after `task verdict` derives
|
|
44
|
+
`.spur/run/<wbs>-verdict.json` and `spur task record` has flipped the verdict-proven R/AC boxes,
|
|
45
|
+
run `residual-scan scan` + `fold` (observe-only; folds the verdict's `residual-sweep` check,
|
|
46
|
+
PASS → PARTIAL when a blocking residual exists) — under every `--fix` mode — then, only on a
|
|
47
|
+
downgrade, re-run `spur task record <wbs> --verdict-file .spur/run/<wbs>-verdict.json` so Testing
|
|
48
|
+
carries the final verdict. Same order as the pipeline `record` state (0983): scanning before
|
|
49
|
+
record reads unticked-but-proven boxes and folds a clean PASS to PARTIAL. Resolve the script via `superskill script path sp
|
|
47
50
|
residual-scan.mjs`; never reference `plugins/sp/scripts/` in shipped surfaces
|
|
48
51
|
(script-contract-check rule 4). When the task reaches `done` through `--next`, run
|
|
49
52
|
`residual-scan settle` (best-effort). On a folded PARTIAL: the bounded remediation loop has
|
|
@@ -72,8 +72,9 @@ Without `--fix all`, shippable is not evaluated (optional note: use `--fix all`
|
|
|
72
72
|
- Batch orchestration: `Skill(skill="sp:spur-dev", args="verifyall $ARGUMENTS")`
|
|
73
73
|
- Per-task verification (inner): `Skill(skill="sp:code-verification", args="verify <wbs> $SHARED_FLAGS")`
|
|
74
74
|
- Shippable gate: once after the batch (same skill § Shippable readiness gate)
|
|
75
|
-
- **Residual sweep (F96 R1, per task):** same contract as
|
|
76
|
-
verdict artifact is derived
|
|
77
|
-
|
|
75
|
+
- **Residual sweep (F96 R1, per task, post-record since 0987):** same contract as
|
|
76
|
+
`/sp:dev-verify` — after each task's verdict artifact is derived and `spur task record` has
|
|
77
|
+
flipped its verdict-proven boxes, run `residual-scan scan` + `fold` (under every `--fix` mode),
|
|
78
|
+
and re-record only on a downgrade; on reaching `done` via `--next`, run `settle`. Script resolution:
|
|
78
79
|
`superskill script path sp residual-scan.mjs` (never `plugins/sp/scripts/` —
|
|
79
80
|
script-contract-check rule 4).
|
|
@@ -17,7 +17,7 @@ Wraps the **wrapup-pipeline.yaml** workflow.
|
|
|
17
17
|
| `--agent` `<inline\|auto\|name>` | Who runs the wrap's model-bearing steps. Wrap is workflow-backed (headless): `omit` and `--agent inline` resolve identically per task 0687 — tier substitution under objective trigger 3 (durable auditable run record required) with a warning naming the substituted executor; `auto` tier-resolves an executor; a name pins that executor into `vars.agent`. | inline |
|
|
18
18
|
| `--auto` | Skip objective HITL gates. | off |
|
|
19
19
|
| `--merge` | Merge the wrap branch. | off |
|
|
20
|
-
| `--dry-run` |
|
|
20
|
+
| `--dry-run` | Validate and walk transitions without executing actions. Conditional states therefore resolve to their declared failure edges (their guard files are never written), so a render ending in `failed` is **not** a run failure — it reports "this state's route cannot be decided without running it". Use a real run to preview a route. | off |
|
|
21
21
|
|
|
22
22
|
For shared semantics, see the [flag glossary](../skills/spur-dev/references/flag-glossary.md).
|
|
23
23
|
|
|
@@ -34,7 +34,7 @@ export const REDACTION_CAP_BYTES = 4096;
|
|
|
34
34
|
/** Max command/pattern summary length before ellipsis (chars). */
|
|
35
35
|
export const SUMMARY_MAX_CHARS = 200;
|
|
36
36
|
|
|
37
|
-
interface ToolPayload {
|
|
37
|
+
export interface ToolPayload {
|
|
38
38
|
session_id?: string;
|
|
39
39
|
tool_name?: string;
|
|
40
40
|
tool_input?: {
|
|
@@ -115,7 +115,7 @@ export function redactText(text: string, capBytes: number = REDACTION_CAP_BYTES)
|
|
|
115
115
|
if (bytes.length <= capBytes) return s;
|
|
116
116
|
// Truncate on byte boundary then append marker
|
|
117
117
|
let end = capBytes;
|
|
118
|
-
while (end > 0 && (bytes[end] & 0xc0) === 0x80) end--;
|
|
118
|
+
while (end > 0 && ((bytes[end] ?? 0) & 0xc0) === 0x80) end--;
|
|
119
119
|
return `${new TextDecoder().decode(bytes.subarray(0, end))}…[truncated]`;
|
|
120
120
|
}
|
|
121
121
|
|
|
@@ -126,6 +126,7 @@ export function recordSessionStart(
|
|
|
126
126
|
dir: string,
|
|
127
127
|
env: NodeJS.ProcessEnv = getEnvVars(),
|
|
128
128
|
now: () => Date = () => new Date(),
|
|
129
|
+
agentFallback?: string,
|
|
129
130
|
): string | null {
|
|
130
131
|
try {
|
|
131
132
|
mkdirSync(dir, { recursive: true });
|
|
@@ -140,7 +141,7 @@ export function recordSessionStart(
|
|
|
140
141
|
const pad = (n: number) => String(n).padStart(2, '0');
|
|
141
142
|
const sessionId = `session-${at.toISOString().slice(0, 10)}-${pad(at.getHours())}${pad(at.getMinutes())}`;
|
|
142
143
|
const ts = at.toISOString();
|
|
143
|
-
const agent = resolveAgentHint(env);
|
|
144
|
+
const agent = resolveAgentHint(env, agentFallback);
|
|
144
145
|
const model = resolveModelHint(env);
|
|
145
146
|
|
|
146
147
|
const sessionBody: Record<string, unknown> = {
|
|
@@ -49,30 +49,30 @@ function computeSessionTotals(
|
|
|
49
49
|
return { reads, writes, tokens };
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
52
|
+
/**
|
|
53
|
+
* Finalize one session (task 0969 R3): read the `.session.json` pointer, compute the session's
|
|
54
|
+
* rollup totals, append the `session_end` row, remove the pointer (best-effort) and return the
|
|
55
|
+
* event. Shared by this hook's entrypoint and Pi's `session_shutdown` so both hosts roll up
|
|
56
|
+
* identically. Returns null on every fail-open path.
|
|
57
|
+
*/
|
|
58
|
+
export function recordSessionEnd(dir: string, now: () => Date = () => new Date()): Record<string, unknown> | null {
|
|
59
59
|
const sessionFile = join(dir, '.session.json');
|
|
60
|
-
if (!existsSync(sessionFile))
|
|
60
|
+
if (!existsSync(sessionFile)) return null;
|
|
61
61
|
|
|
62
62
|
let sessionId = '';
|
|
63
63
|
try {
|
|
64
64
|
const session = JSON.parse(readFileSync(sessionFile, 'utf-8')) as { session?: string };
|
|
65
65
|
sessionId = session.session ?? '';
|
|
66
66
|
} catch {
|
|
67
|
-
|
|
67
|
+
return null;
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
-
if (!sessionId)
|
|
70
|
+
if (!sessionId) return null;
|
|
71
71
|
|
|
72
72
|
const totals = computeSessionTotals(join(dir, 'token-ledger.jsonl'), sessionId);
|
|
73
73
|
|
|
74
74
|
const event = {
|
|
75
|
-
ts:
|
|
75
|
+
ts: now().toISOString(),
|
|
76
76
|
session: sessionId,
|
|
77
77
|
type: 'session_end' as const,
|
|
78
78
|
totals,
|
|
@@ -81,7 +81,7 @@ async function main(): Promise<void> {
|
|
|
81
81
|
try {
|
|
82
82
|
appendFileSync(join(dir, 'token-ledger.jsonl'), `${JSON.stringify(event)}\n`);
|
|
83
83
|
} catch {
|
|
84
|
-
|
|
84
|
+
return null;
|
|
85
85
|
}
|
|
86
86
|
|
|
87
87
|
try {
|
|
@@ -90,7 +90,15 @@ async function main(): Promise<void> {
|
|
|
90
90
|
// cleanup is best-effort
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
-
|
|
93
|
+
return event;
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
-
|
|
96
|
+
// Entrypoint: run the shared core, then exit 0 (fail-open) as this hook always has.
|
|
97
|
+
if (import.meta.main) {
|
|
98
|
+
try {
|
|
99
|
+
recordSessionEnd(join(getEnvVar('CLAUDE_PROJECT_DIR') ?? process.cwd(), '.spur', 'context'));
|
|
100
|
+
} catch {
|
|
101
|
+
/* fail-open: every error path exits 0 */
|
|
102
|
+
}
|
|
103
|
+
process.exit(0);
|
|
104
|
+
}
|