@gobing-ai/spur 0.3.87 → 0.3.89
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/config.example.yaml +9 -0
- package/config/rules/boundary/env-var-hygiene.yaml +13 -5
- package/config/rules/strict/runtime-boundaries.yaml +1 -0
- package/config/workflows/idea-pipeline.yaml +17 -0
- package/config/workflows/task-pipeline.yaml +25 -0
- package/package.json +9 -9
- package/plugins/sp/README.md +2 -2
- package/plugins/sp/commands/dev-refine.md +25 -7
- package/plugins/sp/commands/dev-refineall.md +9 -6
- package/plugins/sp/hooks/agent-hint.ts +1 -1
- package/plugins/sp/hooks/careful-guard.ts +1 -1
- package/plugins/sp/hooks/context-post-tool.ts +1 -1
- package/plugins/sp/hooks/context-session-start.ts +1 -1
- package/plugins/sp/hooks/context-session-stop.ts +1 -1
- package/plugins/sp/hooks/pi/guard-extension.ts +1 -1
- package/plugins/sp/hooks/task-write-guard.ts +1 -1
- package/plugins/sp/lib/env.ts +46 -0
- package/plugins/sp/lib/idea-handoff.generated.mjs +160 -152
- package/plugins/sp/plugin.json +1 -1
- package/plugins/sp/scripts/daily-summary/daily-summary.mjs +1 -1
- package/plugins/sp/scripts/daily-summary/daily-summary.ts +1 -1
- package/plugins/sp/scripts/feature-sync-bounded.mjs +1 -1
- package/plugins/sp/scripts/feature-sync-bounded.ts +1 -1
- package/plugins/sp/scripts/idea-handoff.mjs +1 -1
- package/plugins/sp/scripts/idea-handoff.ts +1 -1
- package/plugins/sp/scripts/inline-run-setup.ts +1 -1
- package/plugins/sp/scripts/pr-reviewing.mjs +1 -1
- package/plugins/sp/scripts/pr-reviewing.ts +1 -1
- package/plugins/sp/scripts/quality-gate.mjs +1 -1
- package/plugins/sp/scripts/quality-gate.ts +1 -1
- package/plugins/sp/scripts/task-evidence-precheck.ts +1 -1
- package/plugins/sp/scripts/task-size-precheck.ts +1 -1
- package/plugins/sp/scripts/verify-answer-lint.ts +1 -1
- package/plugins/sp/scripts/workflow-step-profile.mjs +1 -1
- package/plugins/sp/scripts/workflow-step-profile.ts +1 -1
- package/plugins/sp/scripts/wrapup-steps.mjs +1 -1
- package/plugins/sp/scripts/wrapup-steps.ts +1 -1
- package/plugins/sp/skills/spur-cli/references/agent.md +92 -9
- package/plugins/sp/skills/spur-dev/SKILL.md +33 -33
- package/plugins/sp/skills/spur-dev/references/dev-operations.md +81 -24
- package/plugins/sp/skills/spur-dev/references/flag-glossary.md +6 -3
- package/plugins/sp/skills/spur-dev/references/planning-workflow.md +10 -6
- package/schemas/spur-config.schema.json +14 -2
- package/spur.js +1889 -465
- package/web/_astro/{BoardApp.yBBcFXWP.js → BoardApp.CDUcHlTJ.js} +67 -67
- package/web/_astro/BoardApp.CaCGU_uX.js +1 -0
- package/web/_astro/{TaskDetail.DqFJbRFc.js → TaskDetail.DwTmbQp5.js} +1 -1
- package/web/_astro/{arc.DL-BpHoi.js → arc.BzF71EFI.js} +1 -1
- package/web/_astro/{architectureDiagram-3BPJPVTR.9IdQYyDq.js → architectureDiagram-3BPJPVTR.jvdDahWM.js} +1 -1
- package/web/_astro/{blockDiagram-GPEHLZMM.BKsFCqTl.js → blockDiagram-GPEHLZMM.zSg4AmFD.js} +1 -1
- package/web/_astro/{c4Diagram-AAUBKEIU.DhwI0dh1.js → c4Diagram-AAUBKEIU.BkUIUQWH.js} +1 -1
- package/web/_astro/channel.SSVY0JPQ.js +1 -0
- package/web/_astro/{chunk-2J33WTMH.B3QVmQ9S.js → chunk-2J33WTMH.DvfQ_f50.js} +1 -1
- package/web/_astro/{chunk-4BX2VUAB.DBSuqs9F.js → chunk-4BX2VUAB.DuI4gQqX.js} +1 -1
- package/web/_astro/{chunk-55IACEB6.BSYTWAYD.js → chunk-55IACEB6.D3BWBOpF.js} +1 -1
- package/web/_astro/{chunk-727SXJPM.cWVuxXfS.js → chunk-727SXJPM.3QSi0a9M.js} +1 -1
- package/web/_astro/{chunk-AQP2D5EJ.DpU_Ob3d.js → chunk-AQP2D5EJ.xazCQrAF.js} +1 -1
- package/web/_astro/{chunk-FMBD7UC4.BykFkyji.js → chunk-FMBD7UC4.B2g6u4rA.js} +1 -1
- package/web/_astro/{chunk-ND2GUHAM.DwgHlMdY.js → chunk-ND2GUHAM.wWwWs99t.js} +1 -1
- package/web/_astro/{chunk-QZHKN3VN.CusXUGWM.js → chunk-QZHKN3VN.BD5g3qa9.js} +1 -1
- package/web/_astro/{classDiagram-4FO5ZUOK.fx0ObzkN.js → classDiagram-4FO5ZUOK.C7CzCdsX.js} +1 -1
- package/web/_astro/{classDiagram-v2-Q7XG4LA2.fx0ObzkN.js → classDiagram-v2-Q7XG4LA2.C7CzCdsX.js} +1 -1
- package/web/_astro/{cose-bilkent-S5V4N54A.Z4HgOlsd.js → cose-bilkent-S5V4N54A.Xyiau0gw.js} +1 -1
- package/web/_astro/{cynefin-OW5HDTMX.B5dIZHJu.js → cynefin-OW5HDTMX.BeC5MWas.js} +1 -1
- package/web/_astro/{dagre-BM42HDAG.DT70Q_Yw.js → dagre-BM42HDAG.yZbMN9vc.js} +1 -1
- package/web/_astro/{diagram-2AECGRRQ.DqHA3XBF.js → diagram-2AECGRRQ.Cmo2zQM-.js} +1 -1
- package/web/_astro/{diagram-5GNKFQAL.BmCem957.js → diagram-5GNKFQAL.D033eSVi.js} +1 -1
- package/web/_astro/{diagram-KO2AKTUF.sn0-hrE0.js → diagram-KO2AKTUF.CR6k3Y3G.js} +1 -1
- package/web/_astro/{diagram-LMA3HP47.BSHe9tVc.js → diagram-LMA3HP47.x7mwu8jz.js} +1 -1
- package/web/_astro/{diagram-OG6HWLK6.DHIc-86k.js → diagram-OG6HWLK6.D8aTTvUr.js} +1 -1
- package/web/_astro/{erDiagram-TEJ5UH35.Bxayrs7v.js → erDiagram-TEJ5UH35.BoBqcKXQ.js} +1 -1
- package/web/_astro/{flowDiagram-I6XJVG4X.BkzoE_5I.js → flowDiagram-I6XJVG4X.D3mTQdrU.js} +1 -1
- package/web/_astro/{ganttDiagram-6RSMTGT7.okT6CvTo.js → ganttDiagram-6RSMTGT7.H-cqgIh-.js} +1 -1
- package/web/_astro/{gitGraphDiagram-PVQCEYII.CJuYbhC7.js → gitGraphDiagram-PVQCEYII.B6s9zbfC.js} +1 -1
- package/web/_astro/{infoDiagram-5YYISTIA.RqLy7nBo.js → infoDiagram-5YYISTIA.BzgCoV6P.js} +1 -1
- package/web/_astro/{ishikawaDiagram-YF4QCWOH.BwIcoagw.js → ishikawaDiagram-YF4QCWOH.BZzVhy1-.js} +1 -1
- package/web/_astro/{journeyDiagram-JHISSGLW.UB1VbWtH.js → journeyDiagram-JHISSGLW.BV3195Py.js} +1 -1
- package/web/_astro/{kanban-definition-UN3LZRKU.AaxMKpTk.js → kanban-definition-UN3LZRKU.BjRd2DWz.js} +1 -1
- package/web/_astro/{linear.Nv_xOUjP.js → linear.BILTgS5N.js} +1 -1
- package/web/_astro/{mermaid.core.Bc4LqQgX.js → mermaid.core.DBy_WKeW.js} +4 -4
- package/web/_astro/{mindmap-definition-RKZ34NQL.oKUvU_qi.js → mindmap-definition-RKZ34NQL.BiEjaI4-.js} +1 -1
- package/web/_astro/{pieDiagram-4H26LBE5.DQk0oo03.js → pieDiagram-4H26LBE5.i_8V5pIn.js} +1 -1
- package/web/_astro/{quadrantDiagram-W4KKPZXB.BdDjESDa.js → quadrantDiagram-W4KKPZXB.BWaW3MHn.js} +1 -1
- package/web/_astro/{requirementDiagram-4Y6WPE33.C2u9hUeH.js → requirementDiagram-4Y6WPE33.CzddBbtg.js} +1 -1
- package/web/_astro/{sankeyDiagram-5OEKKPKP.CDEoiJST.js → sankeyDiagram-5OEKKPKP.X2ww0e-D.js} +1 -1
- package/web/_astro/{sequenceDiagram-3UESZ5HK.D_hT_GAT.js → sequenceDiagram-3UESZ5HK.DSA4kTcc.js} +1 -1
- package/web/_astro/{stateDiagram-AJRCARHV.DI8RYG0b.js → stateDiagram-AJRCARHV.D0DtFSpR.js} +1 -1
- package/web/_astro/{stateDiagram-v2-BHNVJYJU.Bkxz4DnP.js → stateDiagram-v2-BHNVJYJU.BfQq0zQv.js} +1 -1
- package/web/_astro/{timeline-definition-PNZ67QCA.DSY-kH3-.js → timeline-definition-PNZ67QCA.Dmlrgi1m.js} +1 -1
- package/web/_astro/{vennDiagram-CIIHVFJN.CpaDtuGr.js → vennDiagram-CIIHVFJN.D5mpl00Z.js} +1 -1
- package/web/_astro/{wardleyDiagram-YWT4CUSO.DujQWvo8.js → wardleyDiagram-YWT4CUSO.Df4BdzO4.js} +1 -1
- package/web/_astro/{xychartDiagram-2RQKCTM6.DcM5Y4b9.js → xychartDiagram-2RQKCTM6.DiTRreKN.js} +1 -1
- package/web/index.html +1 -1
- package/web/_astro/BoardApp.CJiqp5pS.js +0 -1
- package/web/_astro/channel.CX5453qQ.js +0 -1
|
@@ -133,9 +133,18 @@ agent:
|
|
|
133
133
|
# A disabled profile stays visible to `spur agent doctor` but never routes
|
|
134
134
|
# (no role, team, stage, or explicit selection). Flip back to enabled by
|
|
135
135
|
# removing the line or setting disabled: false.
|
|
136
|
+
# Quota refresh is external: schedule `spur agent usage` (cron/launchd) to
|
|
137
|
+
# capture provider usage and record quota-owned availability observations.
|
|
138
|
+
# spur serve never runs it (B6 0892; docs/design/session-pinned-dispatch.md §3.4).
|
|
139
|
+
# A bare `true` is operator-owned (only humans write booleans). Automatic
|
|
140
|
+
# writers (quota/probe checks) emit the ownership object instead; an
|
|
141
|
+
# operator-owned disable is never auto-re-enabled (B6 0890).
|
|
136
142
|
# - name: retired-profile
|
|
137
143
|
# agent: omp
|
|
138
144
|
# disabled: true
|
|
145
|
+
# - name: quota-limited
|
|
146
|
+
# agent: omp
|
|
147
|
+
# disabled: { owner: quota, since: 2026-02-14T09:30:00.000Z, reason: agent.quota.exhausted quota-limited }
|
|
139
148
|
- name: pi-dsv4-flash-volc
|
|
140
149
|
agent: pi
|
|
141
150
|
# tier: standard
|
|
@@ -9,16 +9,18 @@ $schema: "@gobing-ai/spur/schemas/rule-file.schema.json"
|
|
|
9
9
|
# Env flows into the app as typed config or injected `env` records (ADR-027). Runtime
|
|
10
10
|
# options belong in `.spur/config.yaml` `bootstrap.options` via `getAppOptions`;
|
|
11
11
|
# dev-only knobs belong in consts.
|
|
12
|
-
# - Plugin scripts/hooks import
|
|
13
|
-
#
|
|
14
|
-
#
|
|
12
|
+
# - Plugin scripts/hooks import `plugins/sp/lib/env.ts` — the vendored standalone
|
|
13
|
+
# gateway — because `superskill install` bundles them on targets with no
|
|
14
|
+
# node_modules, where any `@gobing-ai/*` import fails ("Bundle failed", task
|
|
15
|
+
# 0669). It mirrors the upstream semantics and is the one exempt file.
|
|
16
|
+
# The `superskill script convert` `.mjs` twins remain self-contained by inlining.
|
|
15
17
|
# - `apps/web` uses `import.meta.env` (Vite browser plane) — not process env, never matches.
|
|
16
18
|
# - Which variables exist and who consumes them is documented in `.env.example`.
|
|
17
19
|
# (The former per-var lease table moved there; the rule now enforces the funnel,
|
|
18
20
|
# not a file-by-var inventory.)
|
|
19
21
|
#
|
|
20
|
-
# Scope: apps/**, packages/**, scripts/**, plugins/sp/** — src AND tests.
|
|
21
|
-
#
|
|
22
|
+
# Scope: apps/**, packages/**, scripts/**, plugins/sp/** — src AND tests. One exclusion:
|
|
23
|
+
# the plugin's vendored gateway, whose sole job is direct `process.env` access.
|
|
22
24
|
include:
|
|
23
25
|
- "apps/**/src/**/*.{ts,tsx}"
|
|
24
26
|
- "apps/**/tests/**/*.{ts,tsx}"
|
|
@@ -26,6 +28,12 @@ include:
|
|
|
26
28
|
- "packages/**/tests/**/*.{ts,tsx}"
|
|
27
29
|
- "scripts/**/*.ts"
|
|
28
30
|
- "plugins/sp/**/*.ts"
|
|
31
|
+
exclude:
|
|
32
|
+
# plugins/sp/lib/env.ts is the vendored standalone env gateway for plugin
|
|
33
|
+
# hooks/scripts (bundled by superskill onto targets without node_modules);
|
|
34
|
+
# direct process.env access is its entire purpose. Mirrors the upstream
|
|
35
|
+
# ts-utils gateway — keep in sync (verified 2026-09).
|
|
36
|
+
- "plugins/sp/lib/env.ts"
|
|
29
37
|
rules:
|
|
30
38
|
- id: env-var-hygiene
|
|
31
39
|
description: >
|
|
@@ -58,6 +58,7 @@ rules:
|
|
|
58
58
|
- "packages/app/src/services/token-ledger-watcher.ts" # node:fs watch() live watcher
|
|
59
59
|
- "packages/app/src/services/project-registry.ts" # atomic projects.json persistence
|
|
60
60
|
- "packages/app/src/services/slash-commands-service.ts" # synchronous ~/.config/spur/slash_commands.json persistence (mirrors project-registry.ts)
|
|
61
|
+
- "packages/app/src/services/agent-usage-producer.ts" # atomic agent-usage snapshot write (tmp + rename, mirrors project-registry.ts; task 0892 R1)
|
|
61
62
|
- "packages/app/src/services/history-service.ts" # versioned analyze artifact + bounded-errors sidecar + latest.json symlink pointer (task 0474); ts-runtime FileSystem seam has no symlink, so the pointer uses node:fs directly (mirrors project-registry.ts persistence exemption)
|
|
62
63
|
- "packages/app/src/observability/workflow-run-log-sink.ts" # sync FD append for mid-run tail-able all-in-one run log (task 0426 / feature D2); append() is sync from the observability bus
|
|
63
64
|
- "apps/cli/src/commands/workflow.ts" # FD byte-window tail of the mid-run run log for `workflow trace --follow` streaming (task 0428 / feature D2); readSync at offset over the observability sink's FDs
|
|
@@ -94,6 +94,10 @@ states:
|
|
|
94
94
|
agent: "${vars.agent}"
|
|
95
95
|
role: planner
|
|
96
96
|
resolvedAgentVar: planningAgent
|
|
97
|
+
# B7 R1 (0894): also pin the role once — __executor.planner feeds stage
|
|
98
|
+
# dispatch so no later idea stage re-walks the doctor ladder.
|
|
99
|
+
roles:
|
|
100
|
+
planner: "${vars.agent}"
|
|
97
101
|
- kind: shell
|
|
98
102
|
options:
|
|
99
103
|
command: >-
|
|
@@ -122,6 +126,9 @@ states:
|
|
|
122
126
|
input: "The operator's ask of record is .spur/run/${vars.__runId}-idea-input.md — idea persisted verbatim at start; authoritative, read it first (${vars.idea} is a convenience echo). Run sp:brainstorm. The skill owns the approach-generation, design summary, and `needs_design` signal criteria; emit .spur/run/${vars.__runId}-idea-needs-design.json ({\"needs_design\": true|false}) plus the design summary per its `Design Approval Gate` / `The needs_design signal` sections. Also emit the idea-evaluation report to .spur/run/${vars.__runId}-idea-eval-report.md per the `sp:spur-dev` skill's `idea-evaluation` reference (urgency/necessity 0–5, premises, pros/cons, alternatives, enhanced idea, recommendation, plus mandatory `## Requirement inventory`: numbered I<n> items quoting/paraphrasing idea-input lines; `[unclear: ...]` marks ambiguity, `[deferred: <reason>]` marks out-of-scope). End with this exact footer: `---\\nrun_id: ${vars.__runId}\\ngenerated_at: <RFC3339>\\n---` (omit if run_id is empty)."
|
|
123
127
|
# Declared Layer-1 role (0538 R2): routing reason beside the agent: pin.
|
|
124
128
|
role: planner
|
|
129
|
+
# B7 R6 (0894): planner stages declare fresh — the role default is stated
|
|
130
|
+
# explicitly so the definition is self-documenting.
|
|
131
|
+
session: fresh
|
|
125
132
|
expectFile: .spur/run/${vars.__runId}-idea-eval-report.md
|
|
126
133
|
timeoutMs: ${vars.stepTimeoutMs}
|
|
127
134
|
|
|
@@ -158,6 +165,8 @@ states:
|
|
|
158
165
|
input: 'Create a feature for the idea. The operator''s ask of record is .spur/run/${vars.__runId}-idea-input.md — the idea argument persisted verbatim at start; treat it as the authoritative ask. Read .spur/run/${vars.__runId}-idea-eval-report.md if present for the enhanced idea and scores. Use ''spur feature create "<name>" --json'' to create it; with --json the feature id is returned under the .ref.id envelope (n), not a top-level .id — read it from there. Write the feature id to .spur/run/${vars.__runId}-idea-feature-id.txt. If an existing feature is appropriate, use its id instead. Also write two body-only intent artifacts: .spur/run/${vars.__runId}-idea-goal.md with concise Goal intent only (a short statement of what the feature achieves; never task breakdowns, checklists, or how-to steps), and .spur/run/${vars.__runId}-idea-scope.md with explicit in-scope and out-of-scope boundary bullets.'
|
|
159
166
|
# Declared Layer-1 role (0538 R2): routing reason beside the agent: pin.
|
|
160
167
|
role: planner
|
|
168
|
+
# B7 R6 (0894): declared fresh policy (planner default).
|
|
169
|
+
session: fresh
|
|
161
170
|
expectFile: .spur/run/${vars.__runId}-idea-feature-id.txt
|
|
162
171
|
timeoutMs: ${vars.stepTimeoutMs}
|
|
163
172
|
- kind: file.read.into-var
|
|
@@ -202,6 +211,8 @@ states:
|
|
|
202
211
|
input: "Generate acceptance criteria for feature ${vars.featureId}. The operator's ask of record is .spur/run/${vars.__runId}-idea-input.md — idea persisted verbatim at start; authoritative. Read the feature file and author R-numbered BDD Gherkin scenarios per ac-style-guide.md. Tie every scenario to the `## Requirement inventory` of .spur/run/${vars.__runId}-idea-eval-report.md: under each `Scenario:` heading add a comment `# covers: I1, I3` listing covered ids — every non-`[deferred: ...]` item needs at least one covering scenario. Two `spur task check` rules bind AC text: (1) AC bullets copy scenario titles verbatim — the title is the byte-identical identity key of its bullet; (2) gate-language words (HITL, approval/approved, merged/merge event, content-gate, GATED, capstone standalone) are forbidden in titles, bodies, and enum values (L4.gate-language) — rephrase around them. Output only the Acceptance Criteria section body (gherkin fence included), no commentary, no heading."
|
|
203
212
|
# Declared Layer-1 role (0538 R2): routing reason beside the agent: pin.
|
|
204
213
|
role: planner
|
|
214
|
+
# B7 R6 (0894): declared fresh policy (planner default).
|
|
215
|
+
session: fresh
|
|
205
216
|
answerFile: .spur/run/${vars.__runId}-idea-ac-content.md
|
|
206
217
|
expectFile: .spur/run/${vars.__runId}-idea-ac-content.md
|
|
207
218
|
timeoutMs: ${vars.stepTimeoutMs}
|
|
@@ -298,6 +309,8 @@ states:
|
|
|
298
309
|
input: 'The operator''s ask of record is .spur/run/${vars.__runId}-idea-input.md (idea persisted verbatim at start; authoritative). Run sp:sys-architecture for feature ${vars.featureId}. Read the brainstorm artifact, feature AC, and .spur/run/${vars.__runId}-idea-design-review.md. Produce ADR entries, architecture updates, and design satellites (docs/design/<slug>.md) per the constitution edit rules; never write task or feature corpus files directly. Design-review contract (fixed headings `## Proposed design`, `## Operator feedback`, `## Reconciliation`): first pass — write the proposed summary under `## Proposed design`, leave `## Operator feedback` empty; retry after operator feedback — revise the design/ADR artifacts, document changes under `## Reconciliation`, and when feedback invalidates an Acceptance Criteria scenario write the revised AC section body to a file, persist via `$spurBin feature update "$featureId" --section "Acceptance Criteria" --from-file <file>`.'
|
|
299
310
|
# Declared Layer-1 role (0538 R2): routing reason beside the agent: pin.
|
|
300
311
|
role: planner
|
|
312
|
+
# B7 R6 (0894): declared fresh policy (planner default).
|
|
313
|
+
session: fresh
|
|
301
314
|
expectFile: .spur/run/${vars.__runId}-idea-design-review.md
|
|
302
315
|
timeoutMs: ${vars.stepTimeoutMs}
|
|
303
316
|
# expectFile proves existence only, and the onEnter skeleton pre-creates the file — so an
|
|
@@ -365,6 +378,8 @@ states:
|
|
|
365
378
|
input: "The operator's ask of record is .spur/run/${vars.__runId}-idea-input.md (the idea argument persisted verbatim at start; authoritative). Run sp:spec-decomposition for feature ${vars.featureId} per skill references/decomposition.md § Idea-pipeline emission: sizing first, then the batch JSON at .spur/run/${vars.__runId}-idea-task-batch.json and the private task-order sidecar at .spur/run/${vars.__runId}-idea-task-order.json."
|
|
366
379
|
# Declared Layer-1 role (0538 R2): routing reason beside the agent: pin.
|
|
367
380
|
role: planner
|
|
381
|
+
# B7 R6 (0894): declared fresh policy (planner default).
|
|
382
|
+
session: fresh
|
|
368
383
|
expectFile: .spur/run/${vars.__runId}-idea-task-batch.json
|
|
369
384
|
timeoutMs: ${vars.stepTimeoutMs}
|
|
370
385
|
# R1 (0518): the task-order sidecar is the ordering contract for handoff-finalize.
|
|
@@ -448,6 +463,8 @@ states:
|
|
|
448
463
|
agent: ${vars.planningAgent}
|
|
449
464
|
# Declared Layer-1 role (0538 R2), same planner executor as decompose.
|
|
450
465
|
role: planner
|
|
466
|
+
# B7 R6 (0894): declared fresh policy (planner default).
|
|
467
|
+
session: fresh
|
|
451
468
|
timeoutMs: ${vars.stepTimeoutMs}
|
|
452
469
|
# (warn) non-slash pointer: the 0788 checklist is per-checkout (digest via the
|
|
453
470
|
# project's own computePlanningDigest), so the bounded prompt pins the skill
|
|
@@ -196,6 +196,18 @@ states:
|
|
|
196
196
|
command: >-
|
|
197
197
|
mkdir -p .spur/run .spur/memory; RUN_ID="$__runId"; [ -n "$RUN_ID" ] || RUN_ID="pipeline-$wbs"; case "$RUN_ID" in *'$'*|*'{'*|*'}'*|*vars.*|*/*|*'\'*|*..*) echo "route-reason: refusing unsafe run id: $RUN_ID" >&2; exit 1 ;; esac; REASON_FILE=".spur/run/$RUN_ID-route-reason.txt"; jq -rn --arg m "$mode" '{"fast":"fast:evidence complete+consistent","":"safety:standard verification","unknown":"safety:unknown evidence quality","conflict":"safety:conflicting evidence"}[$m] // "safety:unrecognized evidence (mode=\($m))"' > "$REASON_FILE"; printf '%s %s %s\n' "$RUN_ID" "$wbs" "$(cat "$REASON_FILE")" >> .spur/memory/task-pipeline-routes.log; exit 0
|
|
198
198
|
|
|
199
|
+
# (e) B7 R1 (0894): resolve every declared role ONCE at precheck — pins land
|
|
200
|
+
# in __executor.<role> run vars so stage dispatch performs no doctor call.
|
|
201
|
+
# Soft probe: failures mark the status file and stages degrade to their own
|
|
202
|
+
# resolution; nothing here aborts the run.
|
|
203
|
+
- kind: doctor.probe
|
|
204
|
+
options:
|
|
205
|
+
resultFile: ".spur/run/${vars.__runId}-precheck-roles.status"
|
|
206
|
+
spurBin: "${vars.spurBin}"
|
|
207
|
+
roles:
|
|
208
|
+
coder: "${vars.implementAgent}"
|
|
209
|
+
reviewer: "${vars.agent}"
|
|
210
|
+
|
|
199
211
|
- id: implement
|
|
200
212
|
description: >
|
|
201
213
|
Run agent-driven implementation via /sp:dev-run --mode implement, THEN move the
|
|
@@ -216,6 +228,9 @@ states:
|
|
|
216
228
|
# Pure slash command only (ADR-043). Anti-recursion / implement discipline
|
|
217
229
|
# Declared Layer-1 role (0538 R2/0710 R7): routing reason; no executor pin — role routing feeds the distinctness gate.
|
|
218
230
|
role: coder
|
|
231
|
+
# B7 R3/R6 (0894): coder stages reuse the role session — the test-fix hop
|
|
232
|
+
# resumes the implement session instead of re-reading the task cold.
|
|
233
|
+
session: reuse
|
|
219
234
|
# lives in /sp:dev-run --mode implement → sp:code-implementation, not YAML prose.
|
|
220
235
|
input: /sp:dev-run --mode implement ${vars.wbs} --auto
|
|
221
236
|
timeoutMs: ${vars.implementTimeoutMs}
|
|
@@ -356,6 +371,9 @@ states:
|
|
|
356
371
|
# R3 (0482): `--findings` names the failing anchors inline; `--gate-log` remains
|
|
357
372
|
# Declared Layer-1 role (0538 R2/0710 R7): routing reason; no executor pin — role routing feeds the distinctness gate.
|
|
358
373
|
role: coder
|
|
374
|
+
# B7 R3/R6 (0894): coder stages reuse the role session — the fix hop
|
|
375
|
+
# continues the implement session it is repairing.
|
|
376
|
+
session: reuse
|
|
359
377
|
# the full-context escape hatch when the digest is not enough.
|
|
360
378
|
input: /sp:dev-fixall "${vars.qualityGateCmd}" --gate-log .spur/run/${vars.wbs}-test-gate.log --findings "${vars.gateFindings}"
|
|
361
379
|
timeoutMs: ${vars.stepTimeoutMs}
|
|
@@ -417,7 +435,11 @@ states:
|
|
|
417
435
|
expectFile: .spur/run/${vars.__runId}-review-answer.txt
|
|
418
436
|
# Declared Layer-1 role (0538 R2/0710 R7): routing reason; no executor pin — role routing feeds the distinctness gate.
|
|
419
437
|
role: reviewer
|
|
438
|
+
# B7 R3/R6 (0894): declared fresh policy (reviewer default) — the action
|
|
439
|
+
# result records this as declared, not defaulted. `freshSession: true` is
|
|
440
|
+
# kept as the action-level hard guarantee (0710 R2 structural assertion).
|
|
420
441
|
freshSession: true
|
|
442
|
+
session: fresh
|
|
421
443
|
priority: ${vars.taskPriority}
|
|
422
444
|
compareExecutorWith: implement
|
|
423
445
|
timeoutMs: ${vars.stepTimeoutMs}
|
|
@@ -477,7 +499,10 @@ states:
|
|
|
477
499
|
input: /sp:dev-verify ${vars.wbs} --auto --fix none --focus all
|
|
478
500
|
# Declared Layer-1 role (0538 R2/0710 R7): routing reason; no executor pin — role routing feeds the distinctness gate.
|
|
479
501
|
role: reviewer
|
|
502
|
+
# B7 R3/R6 (0894): declared fresh policy (reviewer default).
|
|
503
|
+
# `freshSession: true` kept as the action-level hard guarantee (0710 R2).
|
|
480
504
|
freshSession: true
|
|
505
|
+
session: fresh
|
|
481
506
|
priority: ${vars.taskPriority}
|
|
482
507
|
compareExecutorWith: implement
|
|
483
508
|
timeoutMs: ${vars.stepTimeoutMs}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gobing-ai/spur",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.89",
|
|
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.4.
|
|
57
|
-
"@gobing-ai/ts-ai-runner": "^0.4.
|
|
58
|
-
"@gobing-ai/ts-dual-workflow-engine": "^0.4.
|
|
59
|
-
"@gobing-ai/ts-infra": "^0.4.
|
|
60
|
-
"@gobing-ai/ts-llm-jsonl-importer": "^0.4.
|
|
61
|
-
"@gobing-ai/ts-rule-engine": "^0.4.
|
|
62
|
-
"@gobing-ai/ts-runtime": "^0.4.
|
|
63
|
-
"@gobing-ai/ts-utils": "^0.4.
|
|
56
|
+
"@gobing-ai/ts-db": "^0.4.69",
|
|
57
|
+
"@gobing-ai/ts-ai-runner": "^0.4.69",
|
|
58
|
+
"@gobing-ai/ts-dual-workflow-engine": "^0.4.69",
|
|
59
|
+
"@gobing-ai/ts-infra": "^0.4.69",
|
|
60
|
+
"@gobing-ai/ts-llm-jsonl-importer": "^0.4.69",
|
|
61
|
+
"@gobing-ai/ts-rule-engine": "^0.4.69",
|
|
62
|
+
"@gobing-ai/ts-runtime": "^0.4.69",
|
|
63
|
+
"@gobing-ai/ts-utils": "^0.4.69",
|
|
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
|
@@ -94,7 +94,7 @@ list this README is checked against.
|
|
|
94
94
|
| `dev-idea` | Turn a vague idea into a feature with AC and a decomposed task batch — discovery, idea-eval, feature-create, AC, feature-check, system-design, decompose, batch-create, handoff |
|
|
95
95
|
| `dev-plan` | Plan a feature from a written description — intake → feature create → AC generation → feature check gate → decomposition → batch-create |
|
|
96
96
|
| `dev-brainstorm` | Interactive solution design — heuristic discovery interview followed by structured ideation with trade-offs and confidence scoring |
|
|
97
|
-
| `dev-refine` | Refine task requirements via structured Q&A — clarify scope, elicit missing details, tighten acceptance criteria; `--depth ready`
|
|
97
|
+
| `dev-refine` | Refine task requirements via structured Q&A — clarify scope, elicit missing details, tighten acceptance criteria; `--depth ready` audits and corrects existing claims, freezes the spec, promotes to todo |
|
|
98
98
|
|
|
99
99
|
#### Lifecycle — execution
|
|
100
100
|
|
|
@@ -116,7 +116,7 @@ list this README is checked against.
|
|
|
116
116
|
| --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
117
117
|
| `dev-runall` | Run a batch of tasks through their pipelines in dependency-correct order — resolve a set, topo-sort, run each via task-pipeline.yaml, emit a batch report |
|
|
118
118
|
| `dev-parallel` | Fan out independent tasks or investigations in parallel via subagents — choose the right pattern and synthesize results |
|
|
119
|
-
| `dev-refineall` | Batch-refine tasks (feature or selector) — planning-half bulk fill of Background/Requirements/AC/Design/Plan before runall; `--depth ready`
|
|
119
|
+
| `dev-refineall` | Batch-refine tasks (feature or selector) — planning-half bulk fill of Background/Requirements/AC/Design/Plan before runall; `--depth ready` audits, corrects, freezes and promotes each task |
|
|
120
120
|
| `dev-verifyall` | Batch-verify tasks against requirements and AC — resolves a set, runs per-task verification, produces consolidated PASS/PARTIAL/FAIL summary report |
|
|
121
121
|
| `dev-wrapall` | Wrap up a batch of completed tasks — learnings, metrics, doc-sync, feature transition, optional branch cleanup |
|
|
122
122
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: Refine task
|
|
2
|
+
description: Refine or re-audit a task — fill missing sections via Q&A, or with --depth ready verify existing claims and fixes against the tree, correct them, and promote to todo
|
|
3
3
|
role: planner
|
|
4
4
|
argument-hint: "<wbs> [--focus <mode>] [--description <text>] [--depth <standard|ready>] [--agent <inline|auto|name>] [--auto] [--next]"
|
|
5
5
|
allowed-tools: ["Bash", "Read", "Skill", "AskUserQuestion"]
|
|
@@ -7,17 +7,24 @@ allowed-tools: ["Bash", "Read", "Skill", "AskUserQuestion"]
|
|
|
7
7
|
|
|
8
8
|
# Dev Refine
|
|
9
9
|
|
|
10
|
-
Wraps the **sp:spur-dev** skill.
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
Wraps the **sp:spur-dev** skill. Three jobs:
|
|
11
|
+
|
|
12
|
+
| Job | Invocation |
|
|
13
|
+
| --- | --- |
|
|
14
|
+
| Fill blank Design/AC/Plan after `--skip-design` or an incomplete create (fallback; Design normally comes from plan/create) | `/sp:dev-refine <wbs> --auto` |
|
|
15
|
+
| Evaluate and correct an existing task: a review-triage filing, a stale backlog item, anything whose claims or proposed fixes may no longer hold | `/sp:dev-refine <wbs> --depth ready` (add `--auto` to skip Q&A) |
|
|
16
|
+
| Freeze a spec for another implementer; also the recovery command when `spur task create` ready preparation fails | `/sp:dev-refine <wbs> --auto --depth ready` |
|
|
17
|
+
|
|
18
|
+
`standard` checks only that target sections are structurally complete. It never re-checks what
|
|
19
|
+
they say. Use `ready` whenever the existing content itself is in doubt.
|
|
13
20
|
|
|
14
21
|
## Argument Flags
|
|
15
22
|
|
|
16
23
|
| Flag | Description | Default |
|
|
17
24
|
| --- | --- | --- |
|
|
18
25
|
| `<wbs>` | Task WBS to refine. | required |
|
|
19
|
-
| `--focus` `<mode>` |
|
|
20
|
-
| `--description` `<text>` |
|
|
26
|
+
| `--focus` `<mode>` | Gap-analysis focus: `all\|requirements\|background\|constraints\|acceptance\|quick`. | `all` |
|
|
27
|
+
| `--description` `<text>` | Operator framing injected into Q&A/synthesis. | omitted |
|
|
21
28
|
| `--depth` `<standard\|ready>` | Spec depth bar (see flag glossary). | `standard` |
|
|
22
29
|
| `--agent` `<inline\|auto\|name>` | Who runs the model-bearing refinement. | omit |
|
|
23
30
|
| `--auto` | Skip objective HITL gates. | off |
|
|
@@ -35,7 +42,18 @@ Under `--auto` with **`--depth standard`** (default), SKIP only when target sect
|
|
|
35
42
|
findings: Background, Requirements, Acceptance Criteria, Design, Plan. Solution is not a refine
|
|
36
43
|
target. Under **`--depth ready`**, do **not** SKIP on L3-clean alone — run the implement-ready
|
|
37
44
|
checklist (dev-operations § refine) and rewrite Design/Requirements/Plan until another agent can
|
|
38
|
-
implement without inventing design
|
|
45
|
+
implement without inventing design:
|
|
46
|
+
|
|
47
|
+
1. Audit every existing claim against the current tree: facts, cited sources, fix soundness across
|
|
48
|
+
all callers, test observability, environment, concurrent work, and scope.
|
|
49
|
+
2. Correct what is wrong and record each change in a dated `**Refine corrections (<date>)**` block
|
|
50
|
+
in Background.
|
|
51
|
+
3. Run `spur task check <wbs> --as todo --json`, fill unset priority/estimate, and promote
|
|
52
|
+
`backlog → todo`.
|
|
53
|
+
4. Report checklist rows `{id, pass, evidence}`.
|
|
54
|
+
|
|
55
|
+
Refine targets `backlog`/`todo` tasks; a `wip`-or-later task needs explicit operator consent and is
|
|
56
|
+
never demoted. Stage floor: the `planner` role per
|
|
39
57
|
[`roles.md`](../references/roles.md) — this command names roles, never tiers (0538 R4);
|
|
40
58
|
ready synthesis may use a higher tier when the task spans packages/seams.
|
|
41
59
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: Refine a batch of tasks via structured Q&A — resolve a set (feature or selector), refine each in dependency-correct order, emit a batch report;
|
|
2
|
+
description: Refine a batch of tasks via structured Q&A — resolve a set (feature or selector), refine each in dependency-correct order, emit a batch report; --depth ready audits, corrects and promotes each task
|
|
3
3
|
role: planner
|
|
4
4
|
argument-hint: "--feature <id> | --tasks <selector> [--focus <mode>] [--description <text>] [--depth <standard|ready>] [--agent <inline|auto|name>] [--auto] [--keep-going] [--status <s>] [--json] [--worktree [<name>]]"
|
|
5
5
|
allowed-tools: ["Bash", "Read", "Skill", "AskUserQuestion"]
|
|
@@ -9,7 +9,9 @@ allowed-tools: ["Bash", "Read", "Skill", "AskUserQuestion"]
|
|
|
9
9
|
|
|
10
10
|
Wraps the **sp:spur-dev** skill. Batch counterpart of `/sp:dev-refine` — same per-task refine
|
|
11
11
|
operation, applied to a resolved set (typically every task under a feature). Pass
|
|
12
|
-
`--depth ready` to force an implement-ready freeze on every task (does not L3-SKIP).
|
|
12
|
+
`--depth ready` to force an implement-ready freeze on every task (does not L3-SKIP). Each task's
|
|
13
|
+
existing claims are audited against the current tree and corrected, and passing tasks are promoted
|
|
14
|
+
`backlog → todo`. Use it to re-audit a feature's filed or stale tasks as well as before handoff.
|
|
13
15
|
|
|
14
16
|
## Argument Flags
|
|
15
17
|
|
|
@@ -17,8 +19,8 @@ operation, applied to a resolved set (typically every task under a feature). Pas
|
|
|
17
19
|
| --- | --- | --- |
|
|
18
20
|
| `--feature` `<id>` | Refine all tasks in a feature. | required (one of `--feature` / `--tasks`) |
|
|
19
21
|
| `--tasks` `<selector>` | Task selector to refine (alternative to `--feature`). | required (one of `--feature` / `--tasks`) |
|
|
20
|
-
| `--focus` `<mode>` |
|
|
21
|
-
| `--description` `<text>` |
|
|
22
|
+
| `--focus` `<mode>` | Gap-analysis focus: `all\|requirements\|background\|constraints\|acceptance\|quick`. | `all` |
|
|
23
|
+
| `--description` `<text>` | Operator framing injected into each task's Q&A/synthesis. | omitted |
|
|
22
24
|
| `--depth` `<standard\|ready>` | Spec depth bar (see flag glossary). | `standard` |
|
|
23
25
|
| `--agent` `<inline\|auto\|name>` | Who runs the model-bearing refinement. | omit |
|
|
24
26
|
| `--auto` | Skip objective HITL gates. | off |
|
|
@@ -50,8 +52,9 @@ worktree copy; your main tree still shows pre-run statuses until the FF-merge on
|
|
|
50
52
|
not a bug.
|
|
51
53
|
|
|
52
54
|
**Depth:** default `standard` keeps the cheap L3 SKIP gate under `--auto`. Use
|
|
53
|
-
`--depth ready` when handing a feature to another implementer (frozen Design/Requirements/Plan)
|
|
54
|
-
thread `--depth` into each per-task refine.
|
|
55
|
+
`--depth ready` when handing a feature to another implementer (frozen Design/Requirements/Plan),
|
|
56
|
+
or when the tasks' existing content is in doubt; thread `--depth` into each per-task refine. Ready
|
|
57
|
+
report rows add corrections count, status before → after, and failed checklist ids.
|
|
55
58
|
|
|
56
59
|
> **`--next` dropped** (feature H8, 2026-07-31). Batch-level chaining was a token bomb — each refine
|
|
57
60
|
> hop is an LLM call, and a large feature means N refine chains fanned out at once. For batch
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
* adapter. This file owns the Claude Code payload/decision shape only.
|
|
23
23
|
*/
|
|
24
24
|
|
|
25
|
-
import { getEnvVar } from '
|
|
25
|
+
import { getEnvVar } from '../lib/env';
|
|
26
26
|
import { classifyCommand } from './destructive-policy';
|
|
27
27
|
|
|
28
28
|
interface ToolPayload {
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
import { execSync } from 'node:child_process';
|
|
24
24
|
import { appendFileSync, existsSync, readFileSync, statSync, writeFileSync } from 'node:fs';
|
|
25
25
|
import { join, resolve, sep } from 'node:path';
|
|
26
|
-
import { getEnvVar } from '
|
|
26
|
+
import { getEnvVar } from '../lib/env';
|
|
27
27
|
|
|
28
28
|
/** Tools recorded by this hook (must match hooks.json PostToolUse matcher). */
|
|
29
29
|
export const ALLOWED_TOOLS = new Set(['Bash', 'Grep', 'Glob', 'Read', 'Write', 'Edit']);
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
import { execSync } from 'node:child_process';
|
|
16
16
|
import { appendFileSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
17
17
|
import { join } from 'node:path';
|
|
18
|
-
import { getEnvVar, getEnvVars } from '
|
|
18
|
+
import { getEnvVar, getEnvVars } from '../lib/env';
|
|
19
19
|
import { resolveAgentHint, resolveModelHint } from './agent-hint';
|
|
20
20
|
import { checkContextFreshness } from './context-post-tool';
|
|
21
21
|
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
|
|
18
18
|
import { appendFileSync, existsSync, readFileSync, rmSync } from 'node:fs';
|
|
19
19
|
import { join } from 'node:path';
|
|
20
|
-
import { getEnvVar } from '
|
|
20
|
+
import { getEnvVar } from '../lib/env';
|
|
21
21
|
|
|
22
22
|
interface LedgerEvent {
|
|
23
23
|
session: string;
|
|
@@ -23,7 +23,7 @@ import { appendFileSync, existsSync, mkdirSync, readFileSync, realpathSync, rmSy
|
|
|
23
23
|
import { homedir } from 'node:os';
|
|
24
24
|
import { isAbsolute, join, resolve } from 'node:path';
|
|
25
25
|
import type { ExtensionAPI } from '@earendil-works/pi-coding-agent';
|
|
26
|
-
import { getEnvVar, getEnvVars } from '
|
|
26
|
+
import { getEnvVar, getEnvVars } from '../../lib/env';
|
|
27
27
|
import { resolveAgentHint as resolveAgentHintShared, resolveModelHint as resolveModelHintShared } from '../agent-hint';
|
|
28
28
|
import { classifyCommand } from '../destructive-policy';
|
|
29
29
|
import { couldBeTaskFile } from '../task-file-policy';
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vendored env gateway for the sp plugin.
|
|
3
|
+
*
|
|
4
|
+
* WHY this copy exists: hooks and scripts in this plugin are bundled
|
|
5
|
+
* standalone by `superskill install` on targets that have no node_modules —
|
|
6
|
+
* any `@gobing-ai/*` import fails to resolve there and aborts the install
|
|
7
|
+
* ("Bundle failed", task 0669). This file mirrors the semantics of the
|
|
8
|
+
* canonical gateway (`@gobing-ai/ts-utils` dist/env, ts-libs) so the plugin
|
|
9
|
+
* stays self-contained. Keep in sync when the upstream gateway changes.
|
|
10
|
+
*
|
|
11
|
+
* These accessors are node-bun only: on `cloudflare-workers` there is no
|
|
12
|
+
* `process`; inject config explicitly rather than calling them (ADR-008).
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Read one environment variable. Only an unset variable yields `fallback`;
|
|
17
|
+
* an empty string is a set value and is returned as-is.
|
|
18
|
+
*/
|
|
19
|
+
export function getEnvVar(name: string, fallback?: string): string | undefined {
|
|
20
|
+
const raw = process.env[name];
|
|
21
|
+
return raw === undefined ? fallback : raw;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Read the live environment as a record. The returned object IS `process.env`
|
|
26
|
+
* (not a copy): whole-record operations see later mutations, which child-spawn
|
|
27
|
+
* composition relies on. For single variables prefer {@link getEnvVar}.
|
|
28
|
+
*/
|
|
29
|
+
export function getEnvVars(): Record<string, string | undefined> {
|
|
30
|
+
return process.env;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Set one environment variable through the gateway. Passing `undefined` removes
|
|
35
|
+
* the key, so the save/restore idiom (`const prev = getEnvVar(k); …
|
|
36
|
+
* setEnvVar(k, prev)`) restores absence exactly.
|
|
37
|
+
*/
|
|
38
|
+
export function setEnvVar(name: string, value: string | undefined): void {
|
|
39
|
+
if (value === undefined) delete process.env[name];
|
|
40
|
+
else process.env[name] = value;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Remove one environment variable; a no-op when the key is absent. */
|
|
44
|
+
export function removeEnvVar(name: string): void {
|
|
45
|
+
delete process.env[name];
|
|
46
|
+
}
|