@gobing-ai/spur 0.2.9 → 0.2.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gobing-ai/spur",
3
- "version": "0.2.9",
3
+ "version": "0.2.11",
4
4
  "description": "Spur CLI — local-first harness for mainstream coding agents: constraint checking, workflow orchestration, agent health, and history analytics. Bun-native; exposes the `spur` command.",
5
5
  "keywords": [
6
6
  "spur",
@@ -26,6 +26,7 @@ rules:
26
26
  - ".test.ts"
27
27
  - "/node_modules/"
28
28
  - "/dist/"
29
+ - "vendors/"
29
30
 
30
31
  - id: no-unsafe-git-commands
31
32
  description: "Unsafe git commands (force-push, hard reset) can overwrite history or cause data loss (AGENTS.md CRITICAL safety)."
@@ -14,22 +14,19 @@ variants:
14
14
  standard:
15
15
  backlog:
16
16
  required: [Background]
17
- forbidden: [Solution, Review, Testing]
17
+ optional: [Requirements, Acceptance Criteria, Q&A, Design, Plan, Solution, Testing, Review]
18
18
  # todo = "ready to execute" (§2.3): fully specified and HITL-reviewable.
19
- # Design + Plan + Acceptance Criteria are required so a task can be reviewed
20
- # and approved before any code is written. Solution stays forbidden — it is
21
- # the implementation change-map (file:line), authored at wip/testing.
19
+ # Templates intentionally carry the full lifecycle scaffold; required lists
20
+ # express readiness, while optional lists tolerate empty future-stage placeholders.
22
21
  todo:
23
22
  required: [Background, Acceptance Criteria, Design, Plan]
24
- optional: [Q&A, Requirements]
25
- forbidden: [Solution, Review, Testing]
23
+ optional: [Q&A, Requirements, Solution, Testing, Review]
26
24
  wip:
27
25
  required: [Background, Acceptance Criteria, Design, Plan]
28
- optional: [Q&A, Requirements, Solution]
29
- forbidden: [Review]
26
+ optional: [Q&A, Requirements, Solution, Testing, Review]
30
27
  testing:
31
28
  required: [Solution, Testing]
32
- optional: [Background, Review]
29
+ optional: [Background, Requirements, Acceptance Criteria, Q&A, Design, Plan, Review]
33
30
  # done REQUIRES the evidence trio (Solution/Testing/Review). The spec sections
34
31
  # a task was built from (Design/Plan/AC/…) are OPTIONAL, not forbidden, so a
35
32
  # finished task keeps its design history rather than being stripped to pass the
@@ -40,27 +37,25 @@ variants:
40
37
  gate: true
41
38
  blocked:
42
39
  required: [Background]
43
- optional: [Plan, Solution]
40
+ optional: [Requirements, Acceptance Criteria, Q&A, Design, Plan, Solution, Testing, Review]
44
41
  cancelled:
45
42
  required: []
46
- optional: [Background]
43
+ optional: [Background, Requirements, Acceptance Criteria, Q&A, Design, Plan, Solution, Testing, Review]
47
44
 
48
45
  # Feature implementation — AC pre-seeded from the linked feature's scenarios.
49
46
  feature-impl:
50
47
  backlog:
51
48
  required: [Background]
52
- forbidden: [Solution, Review, Testing]
49
+ optional: [Requirements, Acceptance Criteria, Q&A, Design, Plan, Solution, Testing, Review]
53
50
  todo:
54
51
  required: [Background, Acceptance Criteria, Design, Plan]
55
- optional: [Q&A, Requirements]
56
- forbidden: [Solution, Review, Testing]
52
+ optional: [Q&A, Requirements, Solution, Testing, Review]
57
53
  wip:
58
54
  required: [Background, Acceptance Criteria, Design, Plan]
59
- optional: [Q&A, Requirements, Solution]
60
- forbidden: [Review]
55
+ optional: [Q&A, Requirements, Solution, Testing, Review]
61
56
  testing:
62
57
  required: [Solution, Testing]
63
- optional: [Background, Requirements, Acceptance Criteria, Design, Plan, Review]
58
+ optional: [Background, Requirements, Acceptance Criteria, Q&A, Design, Plan, Review]
64
59
  # done keeps the spec sections optional (see `standard` rationale above) so a
65
60
  # finished feature-impl task retains its Design/Plan/AC history.
66
61
  done:
@@ -69,37 +64,35 @@ variants:
69
64
  gate: true
70
65
  blocked:
71
66
  required: [Background]
72
- optional: [Plan, Solution]
67
+ optional: [Requirements, Acceptance Criteria, Q&A, Design, Plan, Solution, Testing, Review]
73
68
  cancelled:
74
69
  required: []
75
- optional: [Background]
70
+ optional: [Background, Requirements, Acceptance Criteria, Q&A, Design, Plan, Solution, Testing, Review]
76
71
 
77
72
  # Bug / issue report — repro in Background, verified Root Cause, then fix.
78
73
  issue:
79
74
  backlog:
80
75
  required: [Background]
81
- forbidden: [Solution, Review]
76
+ optional: [Requirements, Acceptance Criteria, Q&A, Design, Plan, Root Cause, Solution, Testing, Review]
82
77
  todo:
83
78
  required: [Background, Root Cause, Plan]
84
- optional: [Acceptance Criteria, Design, Q&A]
85
- forbidden: [Solution, Review]
79
+ optional: [Requirements, Acceptance Criteria, Design, Q&A, Solution, Testing, Review]
86
80
  wip:
87
81
  required: [Background, Root Cause, Plan]
88
- optional: [Design, Solution, Q&A]
89
- forbidden: [Review]
82
+ optional: [Requirements, Acceptance Criteria, Design, Solution, Testing, Review, Q&A]
90
83
  testing:
91
84
  required: [Root Cause, Solution, Testing]
92
- optional: [Background, Review]
85
+ optional: [Background, Requirements, Acceptance Criteria, Q&A, Design, Plan, Review]
93
86
  done:
94
87
  required: [Root Cause, Solution, Testing, Review]
95
- optional: [Background, Acceptance Criteria, Design, Plan, Q&A]
88
+ optional: [Background, Requirements, Acceptance Criteria, Q&A, Design, Plan]
96
89
  gate: true
97
90
  blocked:
98
91
  required: [Background]
99
- optional: [Root Cause, Plan]
92
+ optional: [Requirements, Acceptance Criteria, Q&A, Design, Plan, Root Cause, Solution, Testing, Review]
100
93
  cancelled:
101
94
  required: []
102
- optional: [Background]
95
+ optional: [Background, Requirements, Acceptance Criteria, Q&A, Design, Plan, Root Cause, Solution, Testing, Review]
103
96
 
104
97
  # Code-review result task — the deliverable is the P1–P4 findings table.
105
98
  #
@@ -117,74 +110,71 @@ variants:
117
110
  review:
118
111
  backlog:
119
112
  required: [Background]
120
- optional: [Plan, Review]
121
- forbidden: [Testing]
113
+ optional: [Requirements, Acceptance Criteria, Q&A, Design, Plan, Solution, Testing, Review]
122
114
  todo:
123
115
  required: [Background, Plan]
124
- optional: [Q&A, Requirements, Review]
125
- forbidden: [Testing]
116
+ optional: [Q&A, Requirements, Acceptance Criteria, Design, Solution, Testing, Review]
126
117
  wip:
127
118
  required: [Background, Review]
128
- optional: [Plan, Q&A, Requirements, Solution, Testing]
119
+ optional: [Requirements, Acceptance Criteria, Q&A, Design, Plan, Solution, Testing]
129
120
  testing:
130
121
  required: [Background, Review]
131
- optional: [Plan, Requirements, Solution, Testing]
122
+ optional: [Requirements, Acceptance Criteria, Q&A, Design, Plan, Solution, Testing]
132
123
  done:
133
124
  required: [Background, Review]
134
- optional: [Plan, Q&A, Requirements, Solution, Testing]
125
+ optional: [Requirements, Acceptance Criteria, Q&A, Design, Plan, Solution, Testing]
135
126
  gate: true
136
127
  blocked:
137
128
  required: [Background]
138
- optional: [Plan, Review, Solution]
129
+ optional: [Requirements, Acceptance Criteria, Q&A, Design, Plan, Solution, Testing, Review]
139
130
  cancelled:
140
131
  required: []
141
- optional: [Background]
132
+ optional: [Background, Requirements, Acceptance Criteria, Q&A, Design, Plan, Solution, Testing, Review]
142
133
 
143
- # Process / docs / chore — lightweight: Background + Plan, no Solution/AC gate.
134
+ # Process / docs / chore — same scaffold, lighter required gates.
144
135
  meta:
145
136
  backlog:
146
137
  required: [Background]
147
- forbidden: [Review]
138
+ optional: [Requirements, Acceptance Criteria, Q&A, Design, Plan, Solution, Testing, Review]
148
139
  todo:
149
140
  required: [Background, Plan]
150
- optional: [Q&A, Design]
151
- forbidden: [Review]
141
+ optional: [Requirements, Acceptance Criteria, Q&A, Design, Solution, Testing, Review]
152
142
  wip:
153
143
  required: [Background, Plan]
154
- optional: [Q&A, Design, Solution]
155
- forbidden: [Review]
144
+ optional: [Requirements, Acceptance Criteria, Q&A, Design, Solution, Testing, Review]
156
145
  testing:
157
146
  required: [Background, Plan]
158
- optional: [Solution, Testing]
147
+ optional: [Requirements, Acceptance Criteria, Q&A, Design, Solution, Testing, Review]
159
148
  done:
160
149
  required: [Background, Plan]
161
- optional: [Solution, Testing, Review]
150
+ optional: [Requirements, Acceptance Criteria, Q&A, Design, Solution, Testing, Review]
162
151
  blocked:
163
152
  required: [Background]
164
- optional: [Plan]
153
+ optional: [Requirements, Acceptance Criteria, Q&A, Design, Plan, Solution, Testing, Review]
165
154
  cancelled:
166
155
  required: []
167
- optional: [Background]
156
+ optional: [Background, Requirements, Acceptance Criteria, Q&A, Design, Plan, Solution, Testing, Review]
168
157
 
169
158
  # Brainstorm template (minimal idea capture).
170
159
  brainstorm:
171
160
  backlog:
172
161
  required: [Background]
173
- forbidden: [Solution, Review, Testing]
162
+ optional: [Requirements, Acceptance Criteria, Q&A, Design, Plan, Solution, Testing, Review]
174
163
  todo:
175
164
  required: [Background]
176
- optional: [Q&A]
165
+ optional: [Requirements, Acceptance Criteria, Q&A, Design, Plan, Solution, Testing, Review]
177
166
  wip:
178
167
  required: [Background, Plan]
179
- optional: [Q&A, Design]
180
- forbidden: [Review]
168
+ optional: [Requirements, Acceptance Criteria, Q&A, Design, Solution, Testing, Review]
181
169
  testing:
182
170
  required: [Background, Solution]
183
- optional: [Testing]
184
- forbidden: [Review]
171
+ optional: [Requirements, Acceptance Criteria, Q&A, Design, Plan, Testing, Review]
185
172
  done:
186
173
  required: [Background, Solution]
174
+ optional: [Requirements, Acceptance Criteria, Q&A, Design, Plan, Testing, Review]
187
175
  blocked:
188
176
  required: [Background]
177
+ optional: [Requirements, Acceptance Criteria, Q&A, Design, Plan, Solution, Testing, Review]
189
178
  cancelled:
190
179
  required: []
180
+ optional: [Background, Requirements, Acceptance Criteria, Q&A, Design, Plan, Solution, Testing, Review]
@@ -20,10 +20,40 @@ updated_at: "{{ CREATED_AT }}"
20
20
 
21
21
  {{ BACKGROUND }}
22
22
 
23
+ ### Requirements
24
+
25
+ <!-- Constraints the eventual direction must satisfy, if known. -->
26
+
27
+ ### Acceptance Criteria
28
+
29
+ <!-- Decision criteria or success checks for the brainstorm output. Keep empty if not applicable. -->
30
+
23
31
  ### Q&A
24
32
 
33
+ <!-- Questions, answers, assumptions, and decision notes from the brainstorm. -->
34
+
35
+ ### Design
36
+
37
+ <!-- Candidate approaches, tradeoffs, and selected direction. -->
38
+
25
39
  ### Plan
26
40
 
41
+ <!-- Follow-up steps or task/feature creation plan once the idea is ready to execute. -->
42
+
43
+ ### Solution
44
+
45
+ <!-- Final synthesized recommendation or output from the brainstorm. -->
46
+
47
+ ### Testing
48
+
49
+ <!-- Validation performed for claims, links, or feasibility. Use N/A when not applicable. -->
50
+
51
+ ### Review
52
+
53
+ <!-- Risks, open concerns, and follow-up review notes. -->
54
+
27
55
  ### References
28
56
 
57
+ <!-- Links to docs, examples, related tasks/features, or external references. -->
58
+
29
59
  ### History
@@ -20,31 +20,42 @@ updated_at: "{{ CREATED_AT }}"
20
20
 
21
21
  {{ BACKGROUND }}
22
22
 
23
+ ### Requirements
24
+
25
+ <!-- R-numbered list derived from the linked feature or refined task scope. -->
26
+
23
27
  ### Acceptance Criteria
24
28
 
25
- ```gherkin
26
- Feature: {{ NAME }}
29
+ <!-- Copy or derive real scenarios from the linked feature. Do not leave placeholder AC here. -->
27
30
 
28
- Scenario: Basic acceptance
29
- Given a precondition
30
- When an action is taken
31
- Then an expected result occurs
32
- ```
31
+ ### Q&A
33
32
 
34
- - [ ] Acceptance checklist item
33
+ <!-- Clarifications and decisions made during refinement. Keep empty if none. -->
35
34
 
36
35
  ### Design
37
36
 
37
+ <!-- Chosen implementation approach, key tradeoffs, invariants, and impacted surfaces. -->
38
+
38
39
  ### Plan
39
40
 
40
- - [ ] Implementation step
41
+ <!-- Ordered implementation checklist. Fill before moving to todo/wip. -->
41
42
 
42
43
  ### Solution
43
44
 
45
+ <!-- Filled during implementation: file:line change map and concise rationale. -->
46
+
44
47
  ### Testing
45
48
 
49
+ <!-- Filled during verification: commands run, outcomes, coverage claim or N/A. -->
50
+
46
51
  ### Review
47
52
 
53
+ <!-- Filled during review: P1-P4 findings, residual risk, and final disposition. -->
54
+
48
55
  ### References
49
56
 
57
+ {{ FEATURE_ID }}
58
+
59
+ <!-- Links to the parent feature, design docs, related tasks, or external references. -->
60
+
50
61
  ### History
@@ -20,14 +20,44 @@ updated_at: "{{ CREATED_AT }}"
20
20
 
21
21
  {{ BACKGROUND }}
22
22
 
23
+ ### Requirements
24
+
25
+ <!-- R-numbered expectations for the fix. Include repro/expected behavior if it helps traceability. -->
26
+
27
+ ### Acceptance Criteria
28
+
29
+ <!-- Given/When/Then regression scenario or checklist proving the bug is fixed. -->
30
+
31
+ ### Q&A
32
+
33
+ <!-- Clarifications and triage decisions. Keep empty if none. -->
34
+
35
+ ### Design
36
+
37
+ <!-- Fix approach and tradeoffs. Keep this short unless the issue changes architecture. -->
38
+
39
+ ### Plan
40
+
41
+ <!-- Ordered debugging/fix checklist. Fill before moving to todo/wip. -->
42
+
23
43
  ### Root Cause
24
44
 
45
+ <!-- Verified underlying cause with file:line evidence. Fill once reproduced/isolated. -->
46
+
25
47
  ### Solution
26
48
 
49
+ <!-- Filled during implementation: file:line change map and concise rationale. -->
50
+
27
51
  ### Testing
28
52
 
53
+ <!-- Filled during verification: regression command(s), outcomes, coverage claim or N/A. -->
54
+
29
55
  ### Review
30
56
 
57
+ <!-- Filled during review: P1-P4 findings, residual risk, and final disposition. -->
58
+
31
59
  ### References
32
60
 
61
+ <!-- Links to failing logs, related issues, tasks, docs, or external references. -->
62
+
33
63
  ### History
@@ -20,12 +20,40 @@ updated_at: "{{ CREATED_AT }}"
20
20
 
21
21
  {{ BACKGROUND }}
22
22
 
23
+ ### Requirements
24
+
25
+ <!-- R-numbered expectations for the process/docs/chore outcome. Keep empty if not applicable. -->
26
+
27
+ ### Acceptance Criteria
28
+
29
+ <!-- Lightweight checklist or Given/When/Then if there is an observable completion condition. -->
30
+
31
+ ### Q&A
32
+
33
+ <!-- Clarifications and decisions made during refinement. Keep empty if none. -->
34
+
35
+ ### Design
36
+
37
+ <!-- Approach and tradeoffs for process/docs/config changes. Keep this short. -->
38
+
23
39
  ### Plan
24
40
 
25
- - [ ] Implementation step
41
+ <!-- Ordered checklist. Fill before moving to todo/wip. -->
42
+
43
+ ### Solution
44
+
45
+ <!-- Filled during implementation: changed files/sections and concise rationale. -->
26
46
 
27
47
  ### Testing
28
48
 
49
+ <!-- Filled during verification: commands/checks run, outcomes, coverage claim or N/A. -->
50
+
51
+ ### Review
52
+
53
+ <!-- Filled during review: P1-P4 findings, residual risk, and final disposition. -->
54
+
29
55
  ### References
30
56
 
57
+ <!-- Links to docs, tasks, decisions, or external references. -->
58
+
31
59
  ### History
@@ -4,7 +4,6 @@ name: "{{ NAME }}"
4
4
  description: ""
5
5
  status: backlog
6
6
  type: review
7
- template: review
8
7
  profile: standard
9
8
  feature_id: null
10
9
  parent_wbs: null
@@ -31,12 +30,37 @@ in the reviewed PR/commit/diff). Fix in priority order (P1 → P2 → …); re-r
31
30
  | P1 | | | |
32
31
  | P2 | | | |
33
32
 
33
+ ### Requirements
34
+
35
+ <!-- R-numbered fix requirements derived from the findings. Fill after triage/refinement. -->
36
+
37
+ ### Acceptance Criteria
38
+
39
+ <!-- Checks that prove the findings were addressed. Keep empty until the review task becomes executable work. -->
40
+
41
+ ### Q&A
42
+
43
+ <!-- Clarifications, false positives, accepted risk, and triage decisions. -->
44
+
45
+ ### Design
46
+
47
+ <!-- Fix approach and tradeoffs if the findings require design judgment. -->
48
+
34
49
  ### Plan
35
50
 
36
51
  - [ ] Fix P1 findings
37
52
  - [ ] Fix P2 findings
53
+ - [ ] Fix all the remaining findings if any
38
54
  - [ ] Re-review the changed code
39
55
 
56
+ ### Solution
57
+
58
+ <!-- Filled during implementation: file:line change map and concise rationale. -->
59
+
60
+ ### Testing
61
+
62
+ <!-- Filled during verification: commands/checks run, outcomes, coverage claim or N/A. -->
63
+
40
64
  ### Review
41
65
 
42
66
  Post-implementation reflection — filled **after** the first fix round: what went wrong, what
@@ -49,4 +73,6 @@ remains to fix before closing, and any **back-issues** (new findings surfaced by
49
73
 
50
74
  ### References
51
75
 
76
+ <!-- Links to source review, dogfood report, PR/diff, related tasks, or external references. -->
77
+
52
78
  ### History
@@ -20,29 +20,40 @@ updated_at: "{{ CREATED_AT }}"
20
20
 
21
21
  {{ BACKGROUND }}
22
22
 
23
+ ### Requirements
24
+
25
+ <!-- R-numbered list of what must be true when this task is complete. Keep empty until requirements are known. -->
26
+
23
27
  ### Acceptance Criteria
24
28
 
25
- ```gherkin
26
- Feature: {{ NAME }}
29
+ <!-- Given/When/Then scenarios or a checklist derived from Requirements. Keep empty if this task has no objective AC yet. -->
30
+
31
+ ### Q&A
32
+
33
+ <!-- Clarifications and decisions made during refinement. Keep empty if none. -->
27
34
 
28
- Scenario: Basic acceptance
29
- Given a precondition
30
- When an action is taken
31
- Then an expected result occurs
32
- ```
35
+ ### Design
33
36
 
34
- - [ ] Acceptance checklist item
37
+ <!-- Chosen approach, key tradeoffs, invariants, and impacted surfaces. Keep snippets short. -->
35
38
 
36
39
  ### Plan
37
40
 
38
- - [ ] Implementation step
41
+ <!-- Ordered implementation checklist. Fill before moving to todo/wip. -->
39
42
 
40
43
  ### Solution
41
44
 
45
+ <!-- Filled during implementation: file:line change map and concise rationale. -->
46
+
42
47
  ### Testing
43
48
 
49
+ <!-- Filled during verification: commands run, outcomes, coverage claim or N/A. -->
50
+
44
51
  ### Review
45
52
 
53
+ <!-- Filled during review: P1-P4 findings, residual risk, and final disposition. -->
54
+
46
55
  ### References
47
56
 
57
+ <!-- Links to features, docs, ADRs, related tasks, or external references. -->
58
+
48
59
  ### History
@@ -1,8 +1,8 @@
1
- $schema: "@gobing-ai/spur/schemas/state-machine-workflow.schema.json"
2
1
  # Canonical basic workflow: implement -> check -> fix until the check passes or the
3
2
  # iteration bound is exhausted. Authored for the @gobing-ai/ts-dual-workflow-engine
4
3
  # state-machine schema (initialState / states[].id / onEnter / top-level transitions).
5
4
  # See Architecture Section 5 and Design Section 3.3.
5
+ "$schema": "@gobing-ai/spur/schemas/state-machine-workflow.schema.json"
6
6
  name: basic
7
7
  kind: state-machine
8
8
  description: The canonical implement-check-fix-until-pass loop
@@ -1,4 +1,3 @@
1
- $schema: "@gobing-ai/spur/schemas/state-machine-workflow.schema.json"
2
1
  "$schema": "@gobing-ai/spur/schemas/state-machine-workflow.schema.json"
3
2
  kind: state-machine
4
3
  name: feature-dev
@@ -1,4 +1,3 @@
1
- $schema: "@gobing-ai/spur/schemas/state-machine-workflow.schema.json"
2
1
  "$schema": "@gobing-ai/spur/schemas/state-machine-workflow.schema.json"
3
2
  kind: state-machine
4
3
  name: feature-lifecycle
@@ -1,4 +1,3 @@
1
- $schema: "@gobing-ai/spur/schemas/state-machine-workflow.schema.json"
2
1
  # Planning pipeline — front-half of the development workflow (task 0088, design §6).
3
2
  #
4
3
  # Orchestration is configuration (ADR-022 / §3.2): this is YAML over the existing
@@ -1,4 +1,3 @@
1
- $schema: "@gobing-ai/spur/schemas/state-machine-workflow.schema.json"
2
1
  "$schema": "@gobing-ai/spur/schemas/state-machine-workflow.schema.json"
3
2
  kind: state-machine
4
3
  name: task-lifecycle
@@ -1,4 +1,3 @@
1
- $schema: "@gobing-ai/spur/schemas/state-machine-workflow.schema.json"
2
1
  # Task execution pipeline — one run per execution attempt of a task (design §6).
3
2
  #
4
3
  # Orchestration is configuration (ADR-022 / §3.2): this is YAML over the existing
@@ -98,10 +97,9 @@ states:
98
97
  - id: approve
99
98
  description: >
100
99
  Human-in-the-loop approval gate. Under `profile=auto` this state is never entered
101
- (review routes around it straight to verify). NOTE: to make this state PAUSE for `spur workflow continue`
102
- (E3), add `pause: true` here deferred until the globally-installed `@gobing-ai/spur`
103
- schema (currently a stale 0.2.5 that lacks the `pause` field) is refreshed, so
104
- full `spur workflow validate` stays green. The workspace schema already supports it.
100
+ (review routes around it straight to verify). In interactive mode this state pauses
101
+ the run for `spur workflow continue` (E3), making approval an explicit operator action.
102
+ pause: true
105
103
  onEnter:
106
104
  - kind: hitl.confirm
107
105
  options:
package/spur.js CHANGED
@@ -32493,7 +32493,40 @@ var init_template_renderer = () => {};
32493
32493
  // ../../packages/config/src/loader.ts
32494
32494
  import { existsSync as existsSync3 } from "fs";
32495
32495
  import { homedir } from "os";
32496
- import { join as join2 } from "path";
32496
+ import { dirname as dirname3, join as join2 } from "path";
32497
+ import { fileURLToPath as fileURLToPath3 } from "url";
32498
+ function embeddedSchemasId(embeddedSchemas) {
32499
+ if (embeddedSchemas === undefined)
32500
+ return "disk";
32501
+ let id = embeddedSchemasIds.get(embeddedSchemas);
32502
+ if (id === undefined) {
32503
+ id = nextEmbeddedSchemasId;
32504
+ nextEmbeddedSchemasId += 1;
32505
+ embeddedSchemasIds.set(embeddedSchemas, id);
32506
+ }
32507
+ return `embedded:${id}`;
32508
+ }
32509
+ function cacheKey(configPath, opts, validateJsonSchema2) {
32510
+ return [
32511
+ configPath,
32512
+ validateJsonSchema2 ? "schema" : "zod",
32513
+ opts?.schemaManifestSpecifier ?? "@gobing-ai/spur/package.json",
32514
+ embeddedSchemasId(opts?.embeddedSchemas)
32515
+ ].join("\x00");
32516
+ }
32517
+ function resolveSchemaSpecifier(specifier, manifestSpecifier) {
32518
+ if (specifier === manifestSpecifier) {
32519
+ const workspaceManifest = join2(LOADER_DIR, "..", "..", "..", "apps", "cli", "package.json");
32520
+ if (existsSync3(workspaceManifest))
32521
+ return workspaceManifest;
32522
+ }
32523
+ try {
32524
+ const resolved = import.meta.resolve(specifier);
32525
+ return resolved.startsWith("file:") ? fileURLToPath3(resolved) : resolved;
32526
+ } catch {
32527
+ return specifier;
32528
+ }
32529
+ }
32497
32530
  function resolveConfigFile(cwd) {
32498
32531
  const projectConfig = join2(cwd ?? process.cwd(), SPUR_CONFIG_DIR, SPUR_CONFIG_FILE);
32499
32532
  if (existsSync3(projectConfig))
@@ -32518,11 +32551,21 @@ function makeEmbeddedReader(embeddedSchemas) {
32518
32551
  };
32519
32552
  }
32520
32553
  async function loadSpurConfig(cwd = process.cwd(), opts) {
32521
- const configPath = join2(cwd, SPUR_CONFIG_DIR, SPUR_CONFIG_FILE);
32522
- if (!existsSync3(configPath)) {
32554
+ const configPath = resolveConfigFile(cwd);
32555
+ if (configPath === undefined) {
32523
32556
  return spurConfigSchema.parse({});
32524
32557
  }
32525
32558
  const validateJsonSchema2 = opts?.validateJsonSchema ?? true;
32559
+ const key = cacheKey(configPath, opts, validateJsonSchema2);
32560
+ const cached2 = spurConfigCache.get(key);
32561
+ if (cached2 !== undefined)
32562
+ return cached2;
32563
+ const promise2 = loadSpurConfigFile(configPath, opts, validateJsonSchema2);
32564
+ spurConfigCache.set(key, promise2);
32565
+ promise2.catch(() => spurConfigCache.delete(key));
32566
+ return promise2;
32567
+ }
32568
+ async function loadSpurConfigFile(configPath, opts, validateJsonSchema2) {
32526
32569
  let raw;
32527
32570
  if (validateJsonSchema2) {
32528
32571
  const embeddedSchemas = opts?.embeddedSchemas;
@@ -32531,7 +32574,7 @@ async function loadSpurConfig(cwd = process.cwd(), opts) {
32531
32574
  if (embeddedSchemas !== undefined && specifier === manifestSpecifier) {
32532
32575
  return `${EMBEDDED_PREFIX}/package.json`;
32533
32576
  }
32534
- return specifier;
32577
+ return resolveSchemaSpecifier(specifier, manifestSpecifier);
32535
32578
  };
32536
32579
  const fileSystem = embeddedSchemas ? { readFile: makeEmbeddedReader(embeddedSchemas) } : undefined;
32537
32580
  raw = await loadStructuredConfig(configPath, {
@@ -32561,7 +32604,7 @@ async function loadStructuredSpurConfig(configPath, opts) {
32561
32604
  if (embeddedSchemas !== undefined && specifier === manifestSpecifier) {
32562
32605
  return `${EMBEDDED_PREFIX}/package.json`;
32563
32606
  }
32564
- return specifier;
32607
+ return resolveSchemaSpecifier(specifier, manifestSpecifier);
32565
32608
  };
32566
32609
  const fileSystem = embeddedSchemas ? { readFile: makeEmbeddedReader(embeddedSchemas) } : undefined;
32567
32610
  return await loadStructuredConfig(configPath, {
@@ -32584,6 +32627,14 @@ function defaultPlanningFolders() {
32584
32627
  };
32585
32628
  }
32586
32629
  async function resolvePlanningFolders(fs) {
32630
+ const cached2 = planningFoldersCache.get(fs);
32631
+ if (cached2 !== undefined)
32632
+ return cached2;
32633
+ const promise2 = resolvePlanningFoldersUncached(fs);
32634
+ planningFoldersCache.set(fs, promise2);
32635
+ return promise2;
32636
+ }
32637
+ async function resolvePlanningFoldersUncached(fs) {
32587
32638
  const configPath = fs.resolve(join2(SPUR_CONFIG_DIR, SPUR_CONFIG_FILE));
32588
32639
  if (!await fs.exists(configPath))
32589
32640
  return defaultPlanningFolders();
@@ -32614,7 +32665,7 @@ async function resolvePlanningFolders(fs) {
32614
32665
  return defaultPlanningFolders();
32615
32666
  }
32616
32667
  }
32617
- var SPUR_CONFIG_DIR = ".spur", SPUR_CONFIG_FILE = "config.yaml", GLOBAL_CONFIG_FILE, EMBEDDED_PREFIX = "\x00embedded-spur";
32668
+ var SPUR_CONFIG_DIR = ".spur", SPUR_CONFIG_FILE = "config.yaml", GLOBAL_CONFIG_FILE, LOADER_DIR, nextEmbeddedSchemasId = 1, embeddedSchemasIds, spurConfigCache, planningFoldersCache, EMBEDDED_PREFIX = "\x00embedded-spur";
32618
32669
  var init_loader = __esm(() => {
32619
32670
  init_dist3();
32620
32671
  init_dist2();
@@ -32622,6 +32673,10 @@ var init_loader = __esm(() => {
32622
32673
  init_bundled_config();
32623
32674
  init_template_renderer();
32624
32675
  GLOBAL_CONFIG_FILE = join2(homedir(), ".config", "spur", "config.yaml");
32676
+ LOADER_DIR = dirname3(fileURLToPath3(import.meta.url));
32677
+ embeddedSchemasIds = new WeakMap;
32678
+ spurConfigCache = new Map;
32679
+ planningFoldersCache = new WeakMap;
32625
32680
  });
32626
32681
 
32627
32682
  // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.2/node_modules/@gobing-ai/ts-runtime/dist/bun-sqlite.js
@@ -55614,7 +55669,7 @@ var init_db2 = __esm(() => {
55614
55669
 
55615
55670
  // ../../packages/domain/src/planning/locks.ts
55616
55671
  import { closeSync, fsyncSync, openSync, renameSync as renameSync2 } from "fs";
55617
- import { dirname as dirname5, join as join5 } from "path";
55672
+ import { dirname as dirname6, join as join5 } from "path";
55618
55673
  function lockPathForEntity(folder, entityId) {
55619
55674
  return join5(folder, `.${entityId}.lock`);
55620
55675
  }
@@ -55701,7 +55756,7 @@ function fsyncPath(path8, swallowErrors = false) {
55701
55756
  }
55702
55757
  }
55703
55758
  function atomicWrite(dest, content, entityId, fs3, projectName = "spur") {
55704
- const dir = dirname5(dest);
55759
+ const dir = dirname6(dest);
55705
55760
  const rand = Math.random().toString(36).slice(2, 10);
55706
55761
  const tempPath = join5(dir, `.${projectName}.${entityId}.${rand}.tmp`);
55707
55762
  fs3.writeFile(tempPath, content);
@@ -55710,7 +55765,7 @@ function atomicWrite(dest, content, entityId, fs3, projectName = "spur") {
55710
55765
  fsyncPath(dir, true);
55711
55766
  }
55712
55767
  async function atomicWriteAsync(dest, content, entityId, fs3, projectName = "spur") {
55713
- const dir = dirname5(dest);
55768
+ const dir = dirname6(dest);
55714
55769
  const rand = Math.random().toString(36).slice(2, 10);
55715
55770
  const tempPath = join5(dir, `.${projectName}.${entityId}.${rand}.tmp`);
55716
55771
  await fs3.writeFile(tempPath, content);
@@ -55774,6 +55829,19 @@ function findHeadings(body, hashes) {
55774
55829
  }
55775
55830
  return hits;
55776
55831
  }
55832
+ function normalizeYamlScalars(data) {
55833
+ const out = {};
55834
+ for (const [key, value] of Object.entries(data)) {
55835
+ if (value instanceof Date) {
55836
+ out[key] = value.toISOString();
55837
+ } else if (value !== null && typeof value === "object" && !Array.isArray(value)) {
55838
+ out[key] = normalizeYamlScalars(value);
55839
+ } else {
55840
+ out[key] = value;
55841
+ }
55842
+ }
55843
+ return out;
55844
+ }
55777
55845
 
55778
55846
  class MarkdownDocument {
55779
55847
  _domain;
@@ -55804,7 +55872,7 @@ class MarkdownDocument {
55804
55872
  try {
55805
55873
  const parsed = $parse(raw);
55806
55874
  if (parsed !== null && typeof parsed === "object" && !Array.isArray(parsed)) {
55807
- data = parsed;
55875
+ data = normalizeYamlScalars(parsed);
55808
55876
  }
55809
55877
  } catch {}
55810
55878
  frontmatter = { raw, data };
@@ -55971,8 +56039,19 @@ ${body.trimEnd()}
55971
56039
  ${content}
55972
56040
  ${END_MARKER}${after}`;
55973
56041
  }
56042
+ yamlSafeValue(value) {
56043
+ if (value.startsWith('"') && value.endsWith('"'))
56044
+ return value;
56045
+ if (/[:{}[\],&*?|>!%@`#"'\\\n\r]/.test(value))
56046
+ return `"${value}"`;
56047
+ if (/^(null|true|false|yes|no|on|off)$/i.test(value))
56048
+ return `"${value}"`;
56049
+ if (/^\d/.test(value) && !/^\d{4}-\d{2}-\d{2}/.test(value))
56050
+ return `"${value}"`;
56051
+ return value;
56052
+ }
55974
56053
  setFrontmatterField(key, value) {
55975
- const fieldLine = `${key}: ${value}`;
56054
+ const fieldLine = `${key}: ${this.yamlSafeValue(value)}`;
55976
56055
  if (this._frontmatter === null) {
55977
56056
  const raw2 = fieldLine;
55978
56057
  this._frontmatter = { raw: raw2, data: { [key]: value } };
@@ -58476,7 +58555,7 @@ var init_rule_service = __esm(() => {
58476
58555
  });
58477
58556
 
58478
58557
  // ../../packages/app/src/services/task-check.ts
58479
- import { dirname as dirname6, join as join8 } from "path";
58558
+ import { dirname as dirname7, join as join8 } from "path";
58480
58559
  function isPlaceholderBody(body) {
58481
58560
  const stripped = body.replace(/<!--[\s\S]*?-->/g, "").replace(/^\s*>\s*TBD\s*$/gim, "").trim();
58482
58561
  return stripped.length === 0;
@@ -58526,6 +58605,29 @@ function isProseOnlyReview(body) {
58526
58605
  return !stripped.split(`
58527
58606
  `).some((l) => l.includes("|"));
58528
58607
  }
58608
+ function hasAdjacentFileLineColumns(body) {
58609
+ const lines = body.split(`
58610
+ `);
58611
+ for (const line of lines) {
58612
+ if (!line.includes("|"))
58613
+ continue;
58614
+ const cells = line.split("|").map((c3) => c3.trim());
58615
+ if (cells.length < 3)
58616
+ continue;
58617
+ for (let i2 = 0;i2 < cells.length - 1; i2++) {
58618
+ const a2 = cells[i2];
58619
+ const b = cells[i2 + 1];
58620
+ if (a2 === undefined || b === undefined)
58621
+ continue;
58622
+ const fileText = a2.replace(/`/g, "");
58623
+ const fileCell = /\.(tsx?|jsx?|mjs|cjs|md|ya?ml|json|css|html|sql|sh|toml)$/i.test(fileText);
58624
+ const lineCell = /^\d+(-\d+)?$/.test(b);
58625
+ if (fileCell && lineCell)
58626
+ return true;
58627
+ }
58628
+ }
58629
+ return false;
58630
+ }
58529
58631
  var TaskCheckService;
58530
58632
  var init_task_check = __esm(() => {
58531
58633
  init_src2();
@@ -58554,8 +58656,8 @@ var init_task_check = __esm(() => {
58554
58656
  const entry = this.resolveMatrixEntry(variant, status);
58555
58657
  this.runL2(doc2, entry, findings);
58556
58658
  this.runL3(doc2, entry, findings);
58557
- const tasksDir = dirname6(filePath);
58558
- const featuresDir = join8(dirname6(tasksDir), "features");
58659
+ const tasksDir = dirname7(filePath);
58660
+ const featuresDir = join8(dirname7(tasksDir), "features");
58559
58661
  await this.runL4(doc2, fm, findings, featuresDir, tasksDir);
58560
58662
  await this.runL4Rollup(doc2, wbs, status, findings, tasksDir);
58561
58663
  return { wbs, ...this.summarizeWithStatus(status, findings, strict) };
@@ -58586,7 +58688,8 @@ var init_task_check = __esm(() => {
58586
58688
  const solBody = doc2.getSection("Solution");
58587
58689
  if (solBody !== null && !isPlaceholderBody(solBody)) {
58588
58690
  const hasFileLine = /`[^`]+?:\d+(-\d+)?`/.test(solBody) || /[^\s`]\.\w+:\d+/.test(solBody);
58589
- if (!hasFileLine) {
58691
+ const hasTableFileLine = hasFileLine || hasAdjacentFileLineColumns(solBody);
58692
+ if (!hasTableFileLine) {
58590
58693
  findings.push({
58591
58694
  layer: "L3",
58592
58695
  severity: "error",
@@ -58899,7 +59002,7 @@ function renderTesting(v) {
58899
59002
  lines.push(`| ${req.id} | ${req.status} | ${evidence} |`);
58900
59003
  }
58901
59004
  }
58902
- lines.push("");
59005
+ lines.push("- Coverage: N/A (verdict-based; verify pipeline does not measure code coverage)");
58903
59006
  return lines.join(`
58904
59007
  `);
58905
59008
  }
@@ -58997,7 +59100,7 @@ function gitDiffU0(cwd) {
58997
59100
  var init_task_record = () => {};
58998
59101
 
58999
59102
  // ../../packages/app/src/services/task-service.ts
59000
- import { dirname as dirname7, isAbsolute, relative as relative2, resolve as resolve2 } from "path";
59103
+ import { dirname as dirname8, isAbsolute, relative as relative2, resolve as resolve2 } from "path";
59001
59104
  function patchFrontmatterField(rendered, key, value) {
59002
59105
  const existingRe = new RegExp(`^(?=\\s*${escapeRegex2(key)}:)`, "m");
59003
59106
  if (existingRe.test(rendered)) {
@@ -59054,7 +59157,7 @@ function sectionIsBare(doc2, name) {
59054
59157
  const body = doc2.getSection(name);
59055
59158
  if (body === null)
59056
59159
  return true;
59057
- const trimmed = body.trim();
59160
+ const trimmed = body.replace(/<!--[\s\S]*?-->/g, "").trim();
59058
59161
  if (trimmed === "")
59059
59162
  return true;
59060
59163
  if (/^Pipeline run \d{4}\b/.test(trimmed))
@@ -59590,7 +59693,7 @@ ${block}` : block);
59590
59693
  resolveListDir(folder) {
59591
59694
  if (folder === undefined)
59592
59695
  return this.ctx.tasksDir;
59593
- const root = resolve2(dirname7(this.ctx.tasksDir));
59696
+ const root = resolve2(dirname8(this.ctx.tasksDir));
59594
59697
  const candidate = resolve2(root, folder);
59595
59698
  const rel = relative2(root, candidate);
59596
59699
  if (rel.startsWith("..") || isAbsolute(rel)) {
@@ -59864,7 +59967,7 @@ var init_team_service = __esm(() => {
59864
59967
 
59865
59968
  // ../../packages/app/src/workflow/actions/agent-run.ts
59866
59969
  import { mkdir, writeFile } from "fs/promises";
59867
- import { dirname as dirname8, isAbsolute as isAbsolute2, join as join10 } from "path";
59970
+ import { dirname as dirname9, isAbsolute as isAbsolute2, join as join10 } from "path";
59868
59971
 
59869
59972
  class AgentRunActionRunner {
59870
59973
  kind = KIND;
@@ -59916,7 +60019,7 @@ class AgentRunActionRunner {
59916
60019
  const ok2 = exitCode2 === 0;
59917
60020
  if (answerFile !== undefined) {
59918
60021
  const target = isAbsolute2(answerFile) ? answerFile : join10(cwd, answerFile);
59919
- await mkdir(dirname8(target), { recursive: true });
60022
+ await mkdir(dirname9(target), { recursive: true });
59920
60023
  await writeFile(target, answer, "utf8");
59921
60024
  }
59922
60025
  return {
@@ -61232,7 +61335,7 @@ init_loader();
61232
61335
  init_dist4();
61233
61336
  init_dist3();
61234
61337
  import { appendFileSync as appendFileSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync4 } from "fs";
61235
- import { dirname as dirname3 } from "path";
61338
+ import { dirname as dirname4 } from "path";
61236
61339
 
61237
61340
  // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.2+be3e9d72efacb189/node_modules/@gobing-ai/ts-infra/dist/application/index.js
61238
61341
  init_default_observers();
@@ -64634,7 +64737,7 @@ function validateAppConfig(validator, raw, section, filePath) {
64634
64737
  function createFileSink(filePath) {
64635
64738
  return (line) => {
64636
64739
  try {
64637
- mkdirSync2(dirname3(filePath), { recursive: true });
64740
+ mkdirSync2(dirname4(filePath), { recursive: true });
64638
64741
  appendFileSync3(filePath, line);
64639
64742
  } catch {}
64640
64743
  };
@@ -66052,8 +66155,8 @@ var figlet = (() => {
66052
66155
  })();
66053
66156
 
66054
66157
  // ../../node_modules/.bun/figlet@1.11.0/node_modules/figlet/dist/node-figlet.mjs
66055
- import { fileURLToPath as fileURLToPath3 } from "url";
66056
- var __filename2 = fileURLToPath3(import.meta.url);
66158
+ import { fileURLToPath as fileURLToPath4 } from "url";
66159
+ var __filename2 = fileURLToPath4(import.meta.url);
66057
66160
  var __dirname2 = path7.dirname(__filename2);
66058
66161
  var fontPath = path7.join(__dirname2, "/../fonts/");
66059
66162
  var nodeFiglet = figlet;
@@ -68864,7 +68967,7 @@ import { join as join13, resolve as resolve4 } from "path";
68864
68967
  var CLI_CONFIG = {
68865
68968
  binaryName: "spur",
68866
68969
  binaryLabel: "spur",
68867
- binaryVersion: "0.2.9",
68970
+ binaryVersion: "0.2.11",
68868
68971
  configDir: ".spur",
68869
68972
  configFile: ".spur/config.yaml",
68870
68973
  databaseFile: ".spur/spur.db"
@@ -68874,21 +68977,21 @@ var CLI_CONFIG = {
68874
68977
  var SCAFFOLD_MANIFEST = [
68875
68978
  { source: "rules/recommended-pre-check.yaml", target: "rules/recommended-pre-check.yaml" },
68876
68979
  { source: "rules/recommended-post-check.yaml", target: "rules/recommended-post-check.yaml" },
68877
- { source: "workflows/basic.yaml", target: "config/workflows/basic.yaml" },
68878
- { source: "workflows/task-lifecycle.yaml", target: "config/workflows/task-lifecycle.yaml" },
68879
- { source: "workflows/feature-lifecycle.yaml", target: "config/workflows/feature-lifecycle.yaml" },
68880
- { source: "workflows/feature-dev.yaml", target: "config/workflows/feature-dev.yaml" },
68881
- { source: "workflows/task-pipeline.yaml", target: "config/workflows/task-pipeline.yaml" },
68882
- { source: "workflows/planning-pipeline.yaml", target: "config/workflows/planning-pipeline.yaml" },
68980
+ { source: "workflows/basic.yaml", target: "workflows/basic.yaml" },
68981
+ { source: "workflows/task-lifecycle.yaml", target: "workflows/task-lifecycle.yaml" },
68982
+ { source: "workflows/feature-lifecycle.yaml", target: "workflows/feature-lifecycle.yaml" },
68983
+ { source: "workflows/feature-dev.yaml", target: "workflows/feature-dev.yaml" },
68984
+ { source: "workflows/task-pipeline.yaml", target: "workflows/task-pipeline.yaml" },
68985
+ { source: "workflows/planning-pipeline.yaml", target: "workflows/planning-pipeline.yaml" },
68883
68986
  { source: "tasks/section-matrix.yaml", target: "tasks/section-matrix.yaml" },
68884
68987
  { source: "templates/task/standard.md", target: "tasks/templates/standard.md" },
68885
68988
  { source: "templates/task/feature-impl.md", target: "tasks/templates/feature-impl.md" },
68886
68989
  { source: "templates/task/issue.md", target: "tasks/templates/issue.md" },
68887
68990
  { source: "templates/task/review.md", target: "tasks/templates/review.md" },
68888
68991
  { source: "templates/task/meta.md", target: "tasks/templates/meta.md" },
68889
- { source: "templates/feature/default.md", target: "config/templates/feature/default.md" },
68890
- { source: "templates/bdd/gherkin.md", target: "config/templates/bdd/gherkin.md" },
68891
- { source: "templates/bdd/checklist.md", target: "config/templates/bdd/checklist.md" },
68992
+ { source: "templates/feature/default.md", target: "templates/feature/default.md" },
68993
+ { source: "templates/bdd/gherkin.md", target: "templates/bdd/gherkin.md" },
68994
+ { source: "templates/bdd/checklist.md", target: "templates/bdd/checklist.md" },
68892
68995
  {
68893
68996
  source: "templates/docs/99_PROJECT_CONSTITUTION.md",
68894
68997
  target: "docs/99_PROJECT_CONSTITUTION.md",
@@ -68901,13 +69004,13 @@ var SCAFFOLD_MANIFEST = [
68901
69004
  { source: "templates/docs/03_ARCHITECTURE.md", target: "docs/03_ARCHITECTURE.md", root: true, preserve: true },
68902
69005
  { source: "templates/docs/04_DESIGN.md", target: "docs/04_DESIGN.md", root: true, preserve: true },
68903
69006
  { source: "templates/docs/05_FEATURES.md", target: "docs/05_FEATURES.md", root: true, preserve: true },
68904
- { source: "templates/docs/99_PROJECT_CONSTITUTION.md", target: "config/templates/docs/99_PROJECT_CONSTITUTION.md" },
68905
- { source: "templates/docs/00_ADR.md", target: "config/templates/docs/00_ADR.md" },
68906
- { source: "templates/docs/01_PRD.md", target: "config/templates/docs/01_PRD.md" },
68907
- { source: "templates/docs/02_ROADMAP.md", target: "config/templates/docs/02_ROADMAP.md" },
68908
- { source: "templates/docs/03_ARCHITECTURE.md", target: "config/templates/docs/03_ARCHITECTURE.md" },
68909
- { source: "templates/docs/04_DESIGN.md", target: "config/templates/docs/04_DESIGN.md" },
68910
- { source: "templates/docs/05_FEATURES.md", target: "config/templates/docs/05_FEATURES.md" }
69007
+ { source: "templates/docs/99_PROJECT_CONSTITUTION.md", target: "templates/docs/99_PROJECT_CONSTITUTION.md" },
69008
+ { source: "templates/docs/00_ADR.md", target: "templates/docs/00_ADR.md" },
69009
+ { source: "templates/docs/01_PRD.md", target: "templates/docs/01_PRD.md" },
69010
+ { source: "templates/docs/02_ROADMAP.md", target: "templates/docs/02_ROADMAP.md" },
69011
+ { source: "templates/docs/03_ARCHITECTURE.md", target: "templates/docs/03_ARCHITECTURE.md" },
69012
+ { source: "templates/docs/04_DESIGN.md", target: "templates/docs/04_DESIGN.md" },
69013
+ { source: "templates/docs/05_FEATURES.md", target: "templates/docs/05_FEATURES.md" }
68911
69014
  ];
68912
69015
 
68913
69016
  // src/commands/init.ts
@@ -69394,7 +69497,7 @@ init_src();
69394
69497
 
69395
69498
  // ../server/src/serve.ts
69396
69499
  init_src3();
69397
- import { dirname as dirname9, isAbsolute as isAbsolute3, join as join16 } from "path";
69500
+ import { dirname as dirname10, isAbsolute as isAbsolute3, join as join16 } from "path";
69398
69501
  init_dist3();
69399
69502
 
69400
69503
  // ../server/src/bootstrap.ts
@@ -72635,15 +72738,15 @@ var getPattern = (label, next) => {
72635
72738
  }
72636
72739
  const match = label.match(/^\:([^\{\}]+)(?:\{(.+)\})?$/);
72637
72740
  if (match) {
72638
- const cacheKey = `${label}#${next}`;
72639
- if (!patternCache[cacheKey]) {
72741
+ const cacheKey2 = `${label}#${next}`;
72742
+ if (!patternCache[cacheKey2]) {
72640
72743
  if (match[2]) {
72641
- patternCache[cacheKey] = next && next[0] !== ":" && next[0] !== "*" ? [cacheKey, match[1], new RegExp(`^${match[2]}(?=/${next})`)] : [label, match[1], new RegExp(`^${match[2]}$`)];
72744
+ patternCache[cacheKey2] = next && next[0] !== ":" && next[0] !== "*" ? [cacheKey2, match[1], new RegExp(`^${match[2]}(?=/${next})`)] : [label, match[1], new RegExp(`^${match[2]}$`)];
72642
72745
  } else {
72643
- patternCache[cacheKey] = [label, match[1], true];
72746
+ patternCache[cacheKey2] = [label, match[1], true];
72644
72747
  }
72645
72748
  }
72646
- return patternCache[cacheKey];
72749
+ return patternCache[cacheKey2];
72647
72750
  }
72648
72751
  return null;
72649
72752
  };
@@ -77367,7 +77470,7 @@ var defaultDeps = {
77367
77470
  async function resolveWebDistPath(configuredPath) {
77368
77471
  const candidates = configuredPath && configuredPath.trim() !== "" ? [isAbsolute3(configuredPath) ? configuredPath : join16(process.cwd(), configuredPath)] : [
77369
77472
  join16(process.cwd(), "dist/web"),
77370
- join16(dirname9(process.execPath), "../web"),
77473
+ join16(dirname10(process.execPath), "../web"),
77371
77474
  join16(import.meta.dir, "../../../dist/web")
77372
77475
  ];
77373
77476
  for (const candidate of candidates) {
@@ -78135,6 +78238,15 @@ ${result.content}`);
78135
78238
  } else {
78136
78239
  context3.output.write(`${result.ref.id}: ${result.fromStatus} \u2192 ${result.toStatus}`);
78137
78240
  }
78241
+ if (status === "done" && !options.json) {
78242
+ const task2 = await svc.show(wbs);
78243
+ const featureId2 = task2.frontmatter.feature_id;
78244
+ if (!featureId2 || featureId2.length === 0) {
78245
+ context3.output.write(`\u24D8 Task ${wbs} is now done but has no feature_id.
78246
+ ` + ` Link it with: spur task update ${wbs} --feature <id>
78247
+ ` + " (Advisory only \u2014 the task is already done.)");
78248
+ }
78249
+ }
78138
78250
  } else {
78139
78251
  context3.output.error("Either <status>, --section/--from-file, or --feature/--priority is required");
78140
78252
  context3.setExitCode(2);
@@ -78436,7 +78548,17 @@ async function runDoneGateCheck(context3, wbs, folderOverride) {
78436
78548
  const result = await svc.check(`${tasksDir}/${fileName}`, wbs, { strict: false });
78437
78549
  return result.pass;
78438
78550
  }
78551
+ var sectionMatrixCache = new Map;
78439
78552
  async function loadSectionMatrix(projectRoot) {
78553
+ const cached2 = sectionMatrixCache.get(projectRoot);
78554
+ if (cached2 !== undefined)
78555
+ return cached2;
78556
+ const promise2 = loadSectionMatrixUncached(projectRoot);
78557
+ sectionMatrixCache.set(projectRoot, promise2);
78558
+ promise2.catch(() => sectionMatrixCache.delete(projectRoot));
78559
+ return promise2;
78560
+ }
78561
+ async function loadSectionMatrixUncached(projectRoot) {
78440
78562
  const localPath = join18(projectRoot, ".spur", "tasks", "section-matrix.yaml");
78441
78563
  if (existsSync5(localPath)) {
78442
78564
  const data = await loadStructuredSpurConfig(localPath, {
@@ -78826,7 +78948,7 @@ init_src3();
78826
78948
  init_src();
78827
78949
  init_src2();
78828
78950
  init_dist3();
78829
- import { dirname as dirname10, join as join19, resolve as resolve6 } from "path";
78951
+ import { dirname as dirname11, join as join19, resolve as resolve6 } from "path";
78830
78952
  import { isatty as isatty3 } from "tty";
78831
78953
 
78832
78954
  // ../../node_modules/.bun/@clack+core@1.4.1/node_modules/@clack/core/dist/index.mjs
@@ -80238,7 +80360,7 @@ async function createMigratedDbAdapter(cwd = process.cwd(), env = process.env, d
80238
80360
  const configuredUrl = env.DATABASE_URL === undefined ? join19(cwd, CLI_CONFIG.databaseFile) : config3.database.url;
80239
80361
  const url2 = dbUrl ?? configuredUrl;
80240
80362
  if (url2 !== ":memory:") {
80241
- await createNodeFileSystem().ensureDir(dirname10(url2));
80363
+ await createNodeFileSystem().ensureDir(dirname11(url2));
80242
80364
  }
80243
80365
  return createMigratedDb({ url: url2 });
80244
80366
  }
@@ -1,29 +0,0 @@
1
- ---
2
- schema_version: 1
3
- name: "{{ NAME }}"
4
- description: ""
5
- status: backlog
6
- type: brainstorm
7
- profile: standard
8
- feature_id: null
9
- parent_wbs: null
10
- priority: P2
11
- tags: []
12
- dependencies: []
13
- created_at: "{{ CREATED_AT }}"
14
- updated_at: "{{ CREATED_AT }}"
15
- ---
16
-
17
- ## {{ WBS }}. {{ NAME }}
18
-
19
- ### Background
20
-
21
- {{ BACKGROUND }}
22
-
23
- ### Q&A
24
-
25
- ### Plan
26
-
27
- ### References
28
-
29
- ### History
@@ -1,50 +0,0 @@
1
- ---
2
- schema_version: 1
3
- name: "{{ NAME }}"
4
- description: ""
5
- status: backlog
6
- type: task
7
- profile: standard
8
- feature_id: null
9
- parent_wbs: null
10
- priority: P2
11
- tags: []
12
- dependencies: []
13
- created_at: "{{ CREATED_AT }}"
14
- updated_at: "{{ CREATED_AT }}"
15
- ---
16
-
17
- ## {{ WBS }}. {{ NAME }}
18
-
19
- ### Background
20
-
21
- {{ BACKGROUND }}
22
-
23
- ### Acceptance Criteria
24
-
25
- ```gherkin
26
- Feature: {{ NAME }}
27
-
28
- Scenario: Basic acceptance
29
- Given a precondition
30
- When an action is taken
31
- Then an expected result occurs
32
- ```
33
-
34
- - [ ] Acceptance checklist item
35
-
36
- ### Design
37
-
38
- ### Plan
39
-
40
- - [ ] Implementation step
41
-
42
- ### Solution
43
-
44
- ### Testing
45
-
46
- ### Review
47
-
48
- ### References
49
-
50
- ### History
@@ -1,33 +0,0 @@
1
- ---
2
- schema_version: 1
3
- name: "{{ NAME }}"
4
- description: ""
5
- status: backlog
6
- type: issue
7
- profile: standard
8
- feature_id: null
9
- parent_wbs: null
10
- priority: P2
11
- tags: ["bug"]
12
- dependencies: []
13
- created_at: "{{ CREATED_AT }}"
14
- updated_at: "{{ CREATED_AT }}"
15
- ---
16
-
17
- ## {{ WBS }}. {{ NAME }}
18
-
19
- ### Background
20
-
21
- {{ BACKGROUND }}
22
-
23
- ### Root Cause
24
-
25
- ### Solution
26
-
27
- ### Testing
28
-
29
- ### Review
30
-
31
- ### References
32
-
33
- ### History
@@ -1,31 +0,0 @@
1
- ---
2
- schema_version: 1
3
- name: "{{ NAME }}"
4
- description: ""
5
- status: backlog
6
- type: meta
7
- profile: standard
8
- feature_id: null
9
- parent_wbs: null
10
- priority: P2
11
- tags: ["meta"]
12
- dependencies: []
13
- created_at: "{{ CREATED_AT }}"
14
- updated_at: "{{ CREATED_AT }}"
15
- ---
16
-
17
- ## {{ WBS }}. {{ NAME }}
18
-
19
- ### Background
20
-
21
- {{ BACKGROUND }}
22
-
23
- ### Plan
24
-
25
- - [ ] Implementation step
26
-
27
- ### Testing
28
-
29
- ### References
30
-
31
- ### History
@@ -1,53 +0,0 @@
1
- ---
2
- schema_version: 1
3
- name: "{{ NAME }}"
4
- description: ""
5
- status: backlog
6
- type: review
7
- template: review
8
- profile: standard
9
- feature_id: null
10
- parent_wbs: null
11
- priority: P2
12
- tags: ["review"]
13
- dependencies: []
14
- created_at: "{{ CREATED_AT }}"
15
- updated_at: "{{ CREATED_AT }}"
16
- ---
17
-
18
- ## {{ WBS }}. {{ NAME }}
19
-
20
- ### Background
21
-
22
- {{ BACKGROUND }}
23
-
24
- #### Review Findings
25
-
26
- The code-review findings this task must address — logged here as **input** (what was found
27
- in the reviewed PR/commit/diff). Fix in priority order (P1 → P2 → …); re-review after.
28
-
29
- | Severity | File | Finding | Recommendation |
30
- | -------- | ---- | ------- | -------------- |
31
- | P1 | | | |
32
- | P2 | | | |
33
-
34
- ### Plan
35
-
36
- - [ ] Fix P1 findings
37
- - [ ] Fix P2 findings
38
- - [ ] Fix all the remaining findings if any
39
- - [ ] Re-review the changed code
40
-
41
- ### Review
42
-
43
- Post-implementation reflection — filled **after** the first fix round: what went wrong, what
44
- remains to fix before closing, and any **back-issues** (new findings surfaced by the fix).
45
-
46
- | Severity | File | Finding | Recommendation |
47
- | -------- | ---- | ------- | -------------- |
48
- | P1 | | | |
49
- | P2 | | | |
50
-
51
- ### References
52
-
53
- ### History
@@ -1,48 +0,0 @@
1
- ---
2
- schema_version: 1
3
- name: "{{ NAME }}"
4
- description: ""
5
- status: backlog
6
- type: task
7
- profile: standard
8
- feature_id: null
9
- parent_wbs: null
10
- priority: P2
11
- tags: []
12
- dependencies: []
13
- created_at: "{{ CREATED_AT }}"
14
- updated_at: "{{ CREATED_AT }}"
15
- ---
16
-
17
- ## {{ WBS }}. {{ NAME }}
18
-
19
- ### Background
20
-
21
- {{ BACKGROUND }}
22
-
23
- ### Acceptance Criteria
24
-
25
- ```gherkin
26
- Feature: {{ NAME }}
27
-
28
- Scenario: Basic acceptance
29
- Given a precondition
30
- When an action is taken
31
- Then an expected result occurs
32
- ```
33
-
34
- - [ ] Acceptance checklist item
35
-
36
- ### Plan
37
-
38
- - [ ] Implementation step
39
-
40
- ### Solution
41
-
42
- ### Testing
43
-
44
- ### Review
45
-
46
- ### References
47
-
48
- ### History