@gobing-ai/spur 0.3.43 → 0.3.44
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/templates/AGENTS.md +5 -0
- package/config/workflows/idea-pipeline.yaml +205 -33
- package/config/workflows/task-pipeline.yaml +1 -1
- package/package.json +1 -1
- package/plugins/sp/commands/dev-runall.md +3 -1
- package/plugins/sp/plugin.json +1 -1
- package/plugins/sp/skills/spur-cli/SKILL.md +3 -0
- package/plugins/sp/skills/spur-cli/references/features/hierarchy-mece.md +8 -3
- package/plugins/sp/skills/spur-cli/references/tasks.md +22 -0
- package/plugins/sp/skills/spur-dev/SKILL.md +12 -4
- package/plugins/sp/skills/spur-dev/references/dev-operations.md +1 -1
- package/plugins/sp/skills/spur-dev/references/execution-batch.md +12 -3
- package/plugins/sp/skills/spur-dev/references/flag-glossary.md +1 -1
- package/plugins/sp/skills/spur-dev/references/planning-workflow.md +53 -0
- package/spur.js +147 -67
- package/web/_astro/{BoardApp.8sc6Rb1t.js → BoardApp.BnhkWEOG.js} +99 -99
- package/web/_astro/BoardApp.CxOS4HSo.js +1 -0
- package/web/_astro/{TaskDetail.DOKJP-Kp.js → TaskDetail.B6yiT-U7.js} +1 -1
- package/web/_astro/{arc.CTqmqspU.js → arc.PbmgYm3_.js} +1 -1
- package/web/_astro/{architectureDiagram-3BPJPVTR.B0iBdehs.js → architectureDiagram-3BPJPVTR.BkOMSdmD.js} +1 -1
- package/web/_astro/{blockDiagram-GPEHLZMM.BHE5UFj5.js → blockDiagram-GPEHLZMM.BtomoUdy.js} +1 -1
- package/web/_astro/{c4Diagram-AAUBKEIU.DQApkgGM.js → c4Diagram-AAUBKEIU.UzrYwJnF.js} +1 -1
- package/web/_astro/channel.DRRsElzO.js +1 -0
- package/web/_astro/{chunk-2J33WTMH.fPrKLM8v.js → chunk-2J33WTMH.nNChLHkw.js} +1 -1
- package/web/_astro/{chunk-4BX2VUAB.DExvwBhn.js → chunk-4BX2VUAB.DLYAecPo.js} +1 -1
- package/web/_astro/{chunk-55IACEB6.EjNkjuD6.js → chunk-55IACEB6.t5bzuj1J.js} +1 -1
- package/web/_astro/{chunk-727SXJPM.Dl8xy5rT.js → chunk-727SXJPM.DBV62bIy.js} +1 -1
- package/web/_astro/{chunk-AQP2D5EJ.BSzMBgBs.js → chunk-AQP2D5EJ.BdaPhTPs.js} +1 -1
- package/web/_astro/{chunk-FMBD7UC4.CAeOHpbb.js → chunk-FMBD7UC4.eoZ88KMf.js} +1 -1
- package/web/_astro/{chunk-ND2GUHAM.Bbeu_ZOH.js → chunk-ND2GUHAM.QdNDyeSh.js} +1 -1
- package/web/_astro/{chunk-QZHKN3VN.Da_GaIAZ.js → chunk-QZHKN3VN.DPaToLfx.js} +1 -1
- package/web/_astro/{classDiagram-4FO5ZUOK.sMfFYSQp.js → classDiagram-4FO5ZUOK.By9BMe7b.js} +1 -1
- package/web/_astro/{classDiagram-v2-Q7XG4LA2.sMfFYSQp.js → classDiagram-v2-Q7XG4LA2.By9BMe7b.js} +1 -1
- package/web/_astro/{cose-bilkent-S5V4N54A.PS3IwOH4.js → cose-bilkent-S5V4N54A.DMypl6q_.js} +1 -1
- package/web/_astro/{dagre-BM42HDAG.EQCx8a6D.js → dagre-BM42HDAG.p26m9grg.js} +1 -1
- package/web/_astro/{diagram-2AECGRRQ.aEFQl8v8.js → diagram-2AECGRRQ.C6m3oTIu.js} +1 -1
- package/web/_astro/{diagram-5GNKFQAL.jyN4tqFX.js → diagram-5GNKFQAL.JCxivn2z.js} +1 -1
- package/web/_astro/{diagram-KO2AKTUF.DaTVHnme.js → diagram-KO2AKTUF.DifbEd3P.js} +1 -1
- package/web/_astro/{diagram-LMA3HP47.DWsGRLAN.js → diagram-LMA3HP47.Bffga-cf.js} +1 -1
- package/web/_astro/{diagram-OG6HWLK6.DYxmOViu.js → diagram-OG6HWLK6.CUKx5_Cx.js} +1 -1
- package/web/_astro/{erDiagram-TEJ5UH35.BKM75coa.js → erDiagram-TEJ5UH35.DaJ9KZR0.js} +1 -1
- package/web/_astro/{flowDiagram-I6XJVG4X.B7qpqDW5.js → flowDiagram-I6XJVG4X.Do6l5pzg.js} +1 -1
- package/web/_astro/{ganttDiagram-6RSMTGT7.CYBL-hwy.js → ganttDiagram-6RSMTGT7.DqBKgB-3.js} +1 -1
- package/web/_astro/{gitGraphDiagram-PVQCEYII.Cz4ly3Kq.js → gitGraphDiagram-PVQCEYII.B3VLSQ22.js} +1 -1
- package/web/_astro/{index.yAse9IaO.css → index.QfZ9SC3X.css} +1 -1
- package/web/_astro/{infoDiagram-5YYISTIA.CIS7cXrJ.js → infoDiagram-5YYISTIA.hK8ZC1oa.js} +1 -1
- package/web/_astro/{ishikawaDiagram-YF4QCWOH.DQ-pmaOG.js → ishikawaDiagram-YF4QCWOH.C8DXwh9Z.js} +1 -1
- package/web/_astro/{journeyDiagram-JHISSGLW.DVS6F7UM.js → journeyDiagram-JHISSGLW.mj5aHQDb.js} +1 -1
- package/web/_astro/{kanban-definition-UN3LZRKU.D8t-G-U6.js → kanban-definition-UN3LZRKU.Dwlhi49r.js} +1 -1
- package/web/_astro/{linear.IO8rTd_n.js → linear.7zBHdlwl.js} +1 -1
- package/web/_astro/{mermaid.core.CjVnvFNf.js → mermaid.core.AYdA0EJr.js} +4 -4
- package/web/_astro/{mindmap-definition-RKZ34NQL.DZoEMTnU.js → mindmap-definition-RKZ34NQL.BSnniHEq.js} +1 -1
- package/web/_astro/{pieDiagram-4H26LBE5.86r1QWnX.js → pieDiagram-4H26LBE5.B7EVpA3J.js} +1 -1
- package/web/_astro/{quadrantDiagram-W4KKPZXB.DzFA9uPk.js → quadrantDiagram-W4KKPZXB.rg4i8YOY.js} +1 -1
- package/web/_astro/{requirementDiagram-4Y6WPE33.dQvdvfMr.js → requirementDiagram-4Y6WPE33.C4pr792x.js} +1 -1
- package/web/_astro/{sankeyDiagram-5OEKKPKP.BvC-VNI8.js → sankeyDiagram-5OEKKPKP.DTXU996O.js} +1 -1
- package/web/_astro/{sequenceDiagram-3UESZ5HK.DDQNwHs8.js → sequenceDiagram-3UESZ5HK.D73DOjzi.js} +1 -1
- package/web/_astro/{stateDiagram-AJRCARHV.BKwYy-tQ.js → stateDiagram-AJRCARHV.CVkeSaEH.js} +1 -1
- package/web/_astro/{stateDiagram-v2-BHNVJYJU.KxCiRYYf.js → stateDiagram-v2-BHNVJYJU.DkBGwoN5.js} +1 -1
- package/web/_astro/{timeline-definition-PNZ67QCA.Bh9NDjOx.js → timeline-definition-PNZ67QCA.DnJ9Yh7G.js} +1 -1
- package/web/_astro/{vennDiagram-CIIHVFJN.DIM-n6us.js → vennDiagram-CIIHVFJN.DN1tvR4Y.js} +1 -1
- package/web/_astro/{wardley-L42UT6IY.DW15qOFQ.js → wardley-L42UT6IY.D54jJ-7-.js} +1 -1
- package/web/_astro/{wardleyDiagram-YWT4CUSO.UTIvjsG9.js → wardleyDiagram-YWT4CUSO.C2grUBbE.js} +1 -1
- package/web/_astro/{xychartDiagram-2RQKCTM6.CQYhkVDS.js → xychartDiagram-2RQKCTM6.D05okBnV.js} +1 -1
- package/web/index.html +2 -2
- package/web/_astro/BoardApp.ChKnY1JD.js +0 -1
- package/web/_astro/channel.B0q9WGrH.js +0 -1
|
@@ -76,7 +76,13 @@ bun run apps/cli/src/index.ts feature check <id> --strict --json
|
|
|
76
76
|
|
|
77
77
|
- **Abort shape.** A non-zero check aborts the batch immediately: verdict `aborted`, zero attempted
|
|
78
78
|
tasks, and the structured feature findings (the `--json` finding list) reported verbatim. This is
|
|
79
|
-
the same abort vocabulary as cycle / unknown selector (Step 4/Step 5).
|
|
79
|
+
the same abort vocabulary as cycle / unknown selector (Step 4/Step 5). **Scoped to structural
|
|
80
|
+
findings:** `L4.scenario-unverified` is the expected state of any not-yet-run feature (its
|
|
81
|
+
covering tasks have no PASS verdicts yet) and `--strict` elevates it to error, so it is reported
|
|
82
|
+
verbatim but does **not** abort the batch; every other error finding (L1–L3 structural,
|
|
83
|
+
`L4.malformed-verdict-artifact`, `L4.uncovered-feature-scenario`) still aborts. The terminal
|
|
84
|
+
feature-transition gate enforces scenario verification after the batch runs, so pre-run unverified
|
|
85
|
+
scenarios are transient, not defects (dogfood 2026-08-11, feature I2).
|
|
80
86
|
- **Exactly once.** The check runs once per batch, before `task list`; it is not re-run per task.
|
|
81
87
|
- **Non-feature exclusion.** Explicit WBS lists, status pseudo-lists, and `ready` selectors add no
|
|
82
88
|
feature check — only an effective `feature:<id>` selector is feature-derived. When explicit
|
|
@@ -84,7 +90,10 @@ bun run apps/cli/src/index.ts feature check <id> --strict --json
|
|
|
84
90
|
- **Why.** `FeatureCheckService` emits `L3.scope-delineation` (Scope lacking an In/Out split) as a
|
|
85
91
|
**warning**; feature-scoped batches never ran a strict check before freezing, so the late feature
|
|
86
92
|
transition became the first blocking check. A selector-local strict preflight catches a known
|
|
87
|
-
strict finding before any task pipeline action without changing corpus-wide severity.
|
|
93
|
+
strict finding before any task pipeline action without changing corpus-wide severity. A not-yet-run
|
|
94
|
+
feature always fails `L4.scenario-unverified` under `--strict`, so that class is
|
|
95
|
+
reported-not-aborting per the Abort shape above — the preflight targets defects, not the
|
|
96
|
+
expected pre-run state.
|
|
88
97
|
- **Scope.** This preflight is advisory to severity policy: it does not alter `FeatureCheckService`,
|
|
89
98
|
`L3.scope-delineation` severity, `feature sync`, or batch-create.
|
|
90
99
|
|
|
@@ -672,7 +681,7 @@ Resume, merge, or discard:
|
|
|
672
681
|
discard: git worktree remove <worktree-path> && git branch -D <branch>
|
|
673
682
|
```
|
|
674
683
|
|
|
675
|
-
The report reuses the [`--next` chain contract](flag-glossary.md
|
|
684
|
+
The report reuses the [`--next` chain contract](flag-glossary.md#--next-chain-contract) halt-report
|
|
676
685
|
shape (halt cause + where + why), not new vocabulary. Retention is the right default: these batches
|
|
677
686
|
are long and already resumable via `--continue`; auto-deleting is data loss, auto-merging is a
|
|
678
687
|
partial result presented as a whole. The answer to "what happens if it fails" is "nothing happens,
|
|
@@ -366,7 +366,7 @@ swallowed as the name. `--worktree=<name>` is the unambiguous spelling. `/sp:dev
|
|
|
366
366
|
the flag (single step; not worth the worktree cost), and `--worktree --mode parallel` is rejected
|
|
367
367
|
(per-task parallel isolation stays task 0142). The full lifecycle — name resolution, dirty-tree
|
|
368
368
|
precheck, creation or adoption, crash-safe marker, merge-or-retain, and `--continue` re-entry — is
|
|
369
|
-
specified in [execution-batch.md § Worktree isolation](execution-batch.md#worktree-isolation---worktree).
|
|
369
|
+
specified in [execution-batch.md § Worktree isolation](execution-batch.md#worktree-isolation---worktree-name).
|
|
370
370
|
Portable `git worktree` commands only; the git mechanics are reused from
|
|
371
371
|
[worktree-patterns.md](../../branch-workflow/references/worktree-patterns.md).
|
|
372
372
|
|
|
@@ -217,6 +217,59 @@ fires, author the doc and **report** the chosen slug and a one-line rationale ("
|
|
|
217
217
|
`docs/design/<slug>.md` — new `spur <noun>` command + config key"); when it does not fire, report the
|
|
218
218
|
skip and why. Do not pause to ask; the operator reviews the satellite afterward.
|
|
219
219
|
|
|
220
|
+
## Step 5.6: Idea pipeline (sp:dev-idea) — planning handoff contracts
|
|
221
|
+
|
|
222
|
+
`/sp:dev-idea` runs the same planning half through the idea-pipeline workflow definition
|
|
223
|
+
(`.spur/workflows/idea-pipeline.yaml` — symlinked to the tracked SSOT). Two
|
|
224
|
+
artifacts are the contract between the workflow states and the operator:
|
|
225
|
+
|
|
226
|
+
**Goal/Scope intent (feature-create).** The agent writes body-only intent files
|
|
227
|
+
`.spur/run/<runId>-idea-goal.md` and `.spur/run/<runId>-idea-scope.md`, then persists both through
|
|
228
|
+
the corpus CLI (a missing/empty artifact stops the state before decomposition can begin):
|
|
229
|
+
|
|
230
|
+
```bash
|
|
231
|
+
spur feature update <id> --section Goal --from-file .spur/run/<runId>-idea-goal.md
|
|
232
|
+
spur feature update <id> --section Scope --from-file .spur/run/<runId>-idea-scope.md
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
- **Goal is intent only** — a short statement of what the feature achieves. Task breakdowns,
|
|
236
|
+
checklists, and how-to steps **never** enter Goal.
|
|
237
|
+
- **Scope carries explicit boundaries** — in-scope and out-of-scope bullets.
|
|
238
|
+
|
|
239
|
+
**Design review artifact (system-design / design-approval).** One run-scoped file,
|
|
240
|
+
`.spur/run/<runId>-idea-design-review.md`, with fixed headings `## Proposed design`,
|
|
241
|
+
`## Operator feedback`, and `## Reconciliation`:
|
|
242
|
+
|
|
243
|
+
- **First pass:** system-design writes the proposed design under `## Proposed design`.
|
|
244
|
+
- **Rejection:** before answering `no` at the design-approval gate, the operator edits
|
|
245
|
+
`## Operator feedback` with the concrete issue(s).
|
|
246
|
+
- **Retry:** system-design reads that feedback, revises the design/ADR artifacts, records the
|
|
247
|
+
changes under `## Reconciliation`, and — when the feedback invalidates an Acceptance Criteria
|
|
248
|
+
scenario — updates the feature AC through `spur feature update <id> --section
|
|
249
|
+
"Acceptance Criteria" --from-file <file>` (never direct feature-file edits).
|
|
250
|
+
- **Exit gate:** every design exit into decomposition (auto-approved and interactive-approved)
|
|
251
|
+
re-runs `spur feature check <id>`, so stale or invalidated AC cannot proceed.
|
|
252
|
+
|
|
253
|
+
**Task ordering (decompose / handoff-finalize).** Decomposition also emits the private
|
|
254
|
+
run-scoped order sidecar `.spur/run/<runId>-idea-task-order.json` — a JSON array of
|
|
255
|
+
`{ name, depends_on_names[] }` (one entry per batch item, names matching batch item `name`s
|
|
256
|
+
exactly; `[]` when no ordering exists). It is private workflow data, not part of
|
|
257
|
+
`task-batch.schema.json`. A post-decompose validation fails the run on any ambiguous or
|
|
258
|
+
missing title match. After batch creation, `handoff-finalize`:
|
|
259
|
+
|
|
260
|
+
1. Zips batch item names to the created WBS values from the captured
|
|
261
|
+
`.spur/run/<runId>-idea-batch-create-result.json` (`task batch-create --json` output;
|
|
262
|
+
`wbs[]` is in input order).
|
|
263
|
+
2. Applies every non-empty `depends_on_names` list through
|
|
264
|
+
`spur task deps <wbs> set <dep-wbs...> --json` — a mapping or CLI error fails the run
|
|
265
|
+
before handoff.
|
|
266
|
+
3. Refreshes the feature roster with `spur feature refresh --feature <id> --json`.
|
|
267
|
+
4. Checks each created task (`spur task check <wbs> --json`) and writes
|
|
268
|
+
`.spur/run/<runId>-idea-handoff.md` with exactly **one** next command:
|
|
269
|
+
`/sp:dev-refineall --feature <id> --auto --depth ready` when any task is unready
|
|
270
|
+
(runall is then omitted), otherwise `/sp:dev-runall --feature <id> --auto`. The terminal
|
|
271
|
+
handoff note points at this report.
|
|
272
|
+
|
|
220
273
|
## Step 6: Refine before execute (the spec-completion gate)
|
|
221
274
|
|
|
222
275
|
`batch-create` accepts optional `design` / `plan` / `acceptance_criteria` fields (plus
|
package/spur.js
CHANGED
|
@@ -51037,7 +51037,7 @@ function stripScenarioPrefixes(title) {
|
|
|
51037
51037
|
let previous;
|
|
51038
51038
|
do {
|
|
51039
51039
|
previous = out;
|
|
51040
|
-
out = out.replace(/^\[[^\]]*\]\s*/, "").replace(/^Scenario:\s*/i, "").replace(/^R\d+\s*[:\-\u2014]?\s*/, "").trim();
|
|
51040
|
+
out = out.replace(/^\[[^\]]*\]\s*/, "").replace(/\s*\[[^\]]*\]\s*$/, "").replace(/^Scenario:\s*/i, "").replace(/^R\d+\s*[:\-\u2014]?\s*/, "").trim();
|
|
51041
51041
|
} while (out !== previous);
|
|
51042
51042
|
return out;
|
|
51043
51043
|
}
|
|
@@ -51132,6 +51132,19 @@ function stripAcFence(body) {
|
|
|
51132
51132
|
return out.join(`
|
|
51133
51133
|
`);
|
|
51134
51134
|
}
|
|
51135
|
+
function looksLikeGherkinAc(body) {
|
|
51136
|
+
return /^\s*(?:Feature:|Scenario:|Scenario Outline:)/m.test(body);
|
|
51137
|
+
}
|
|
51138
|
+
function normalizeAcFence(body) {
|
|
51139
|
+
const trimmed = body.trim();
|
|
51140
|
+
if (trimmed === "" || /^\s*```/m.test(trimmed))
|
|
51141
|
+
return body;
|
|
51142
|
+
if (!looksLikeGherkinAc(trimmed))
|
|
51143
|
+
return body;
|
|
51144
|
+
return `\`\`\`gherkin
|
|
51145
|
+
${trimmed}
|
|
51146
|
+
\`\`\``;
|
|
51147
|
+
}
|
|
51135
51148
|
|
|
51136
51149
|
// ../../packages/domain/src/bdd/scaffold.ts
|
|
51137
51150
|
function renderScenarioStub(scenario) {
|
|
@@ -60303,6 +60316,11 @@ async function applyCliMigrations(adapter, migrations = CLI_MIGRATIONS) {
|
|
|
60303
60316
|
const existing = await adapter.queryFirst('SELECT id FROM "__spur_cli_migrations" WHERE id = ?', migration.id);
|
|
60304
60317
|
if (existing != null)
|
|
60305
60318
|
continue;
|
|
60319
|
+
if (migration.id === "0009_spur_cli_history_message_run_idx" && !await tableExists(adapter, "history_message")) {
|
|
60320
|
+
for (const statement of splitSqlStatements(HISTORY_IMPORT_SCHEMA_SQL)) {
|
|
60321
|
+
await adapter.exec(statement);
|
|
60322
|
+
}
|
|
60323
|
+
}
|
|
60306
60324
|
const addColumnGuard = migration.addColumnIfMissing;
|
|
60307
60325
|
const shouldApplySql = addColumnGuard === undefined || !await columnExists(adapter, addColumnGuard.table, addColumnGuard.column);
|
|
60308
60326
|
if (shouldApplySql) {
|
|
@@ -60332,6 +60350,10 @@ async function columnExists(adapter, table3, column2) {
|
|
|
60332
60350
|
const rows = await adapter.queryAll(`PRAGMA table_info("${table3}")`);
|
|
60333
60351
|
return rows.some((row) => row.name === column2);
|
|
60334
60352
|
}
|
|
60353
|
+
async function tableExists(adapter, table3) {
|
|
60354
|
+
const row = await adapter.queryFirst("SELECT name FROM sqlite_master WHERE type = 'table' AND name = ?", table3);
|
|
60355
|
+
return row != null;
|
|
60356
|
+
}
|
|
60335
60357
|
function splitSqlStatements(sql4) {
|
|
60336
60358
|
return sql4.split(";").map((statement) => statement.trim()).filter(Boolean);
|
|
60337
60359
|
}
|
|
@@ -62829,12 +62851,14 @@ __export(exports_src, {
|
|
|
62829
62851
|
normalizeTaskStatus: () => normalizeTaskStatus,
|
|
62830
62852
|
normalizeFeatureStatus: () => normalizeFeatureStatus,
|
|
62831
62853
|
normalizeCapabilityTier: () => normalizeCapabilityTier,
|
|
62854
|
+
normalizeAcFence: () => normalizeAcFence,
|
|
62832
62855
|
messageRollup: () => messageRollup,
|
|
62833
62856
|
mergeStubs: () => mergeStubs,
|
|
62834
62857
|
matrixEntrySchema: () => matrixEntrySchema,
|
|
62835
62858
|
matchEtlPayloads: () => matchEtlPayloads,
|
|
62836
62859
|
matchEtlForAction: () => matchEtlForAction,
|
|
62837
62860
|
loops: () => loops,
|
|
62861
|
+
looksLikeGherkinAc: () => looksLikeGherkinAc,
|
|
62838
62862
|
loadSqlMigrations: () => loadSqlMigrations,
|
|
62839
62863
|
loadAllEtlPayloads: () => loadAllEtlPayloads,
|
|
62840
62864
|
isTierEligible: () => isTierEligible,
|
|
@@ -66898,7 +66922,8 @@ class FeatureService {
|
|
|
66898
66922
|
const slug = basename2(f.filePath).replace(/^[A-Z][1-9]*_/, "").replace(/\.md$/, "");
|
|
66899
66923
|
const newPath = `${this.ctx.featuresDir}/${newId}_${slug}.md`;
|
|
66900
66924
|
removed.push({ path: f.filePath, content: raw });
|
|
66901
|
-
|
|
66925
|
+
const movedContent = doc2.serialize().replace(/^#\s+[A-Z][1-9]*:/m, `# ${newId}:`);
|
|
66926
|
+
await atomicWriteAsync(newPath, movedContent, newId, this.ctx.fs, this.ctx.projectName ?? "spur");
|
|
66902
66927
|
created.push(newPath);
|
|
66903
66928
|
if (newPath !== f.filePath)
|
|
66904
66929
|
await this.ctx.fs.deleteFile(f.filePath);
|
|
@@ -67913,7 +67938,8 @@ function applyMutation(doc2, mutation) {
|
|
|
67913
67938
|
break;
|
|
67914
67939
|
case "updateSection":
|
|
67915
67940
|
if (mutation.sectionName !== undefined && mutation.sectionBody !== undefined) {
|
|
67916
|
-
|
|
67941
|
+
const body = mutation.sectionName.toLowerCase() === "acceptance criteria" ? normalizeAcFence(mutation.sectionBody) : mutation.sectionBody;
|
|
67942
|
+
doc2.replaceSection(mutation.sectionName, body);
|
|
67917
67943
|
}
|
|
67918
67944
|
break;
|
|
67919
67945
|
case "updateBody":
|
|
@@ -70087,7 +70113,7 @@ function renderCreatedTaskContent(params) {
|
|
|
70087
70113
|
sectionPatches.Plan = (params.plan ?? "").trim();
|
|
70088
70114
|
}
|
|
70089
70115
|
if ((params.acceptanceCriteria ?? "").trim() !== "") {
|
|
70090
|
-
sectionPatches["Acceptance Criteria"] = (params.acceptanceCriteria ?? "").trim();
|
|
70116
|
+
sectionPatches["Acceptance Criteria"] = normalizeAcFence((params.acceptanceCriteria ?? "").trim());
|
|
70091
70117
|
}
|
|
70092
70118
|
if (Object.keys(sectionPatches).length > 0) {
|
|
70093
70119
|
const doc2 = MarkdownDocument.parse(content, "task");
|
|
@@ -70758,7 +70784,7 @@ ${issues}`);
|
|
|
70758
70784
|
taskBodies.Plan = (item.plan ?? "").trim();
|
|
70759
70785
|
}
|
|
70760
70786
|
if ((item.acceptance_criteria ?? "").trim() !== "") {
|
|
70761
|
-
taskBodies["Acceptance Criteria"] = (item.acceptance_criteria ?? "").trim();
|
|
70787
|
+
taskBodies["Acceptance Criteria"] = normalizeAcFence((item.acceptance_criteria ?? "").trim());
|
|
70762
70788
|
}
|
|
70763
70789
|
const content = buildTaskSkeleton({
|
|
70764
70790
|
wbs,
|
|
@@ -71279,7 +71305,7 @@ var init_task_verdict = __esm(() => {
|
|
|
71279
71305
|
});
|
|
71280
71306
|
|
|
71281
71307
|
// ../../packages/app/src/services/team-service.ts
|
|
71282
|
-
import { join as join16 } from "path";
|
|
71308
|
+
import { join as join16, resolve as resolve6 } from "path";
|
|
71283
71309
|
|
|
71284
71310
|
class TeamService {
|
|
71285
71311
|
ctx;
|
|
@@ -71487,13 +71513,18 @@ class TeamService {
|
|
|
71487
71513
|
const specs = await loadAgentSpecs(this.configDir);
|
|
71488
71514
|
const config4 = await this.loadTeamConfig();
|
|
71489
71515
|
const teams = new Map;
|
|
71516
|
+
const configTeamIds = new Set;
|
|
71490
71517
|
if (config4?.agent?.team) {
|
|
71491
71518
|
for (const [teamId, teamConfig] of Object.entries(config4.agent.team)) {
|
|
71519
|
+
configTeamIds.add(teamId);
|
|
71520
|
+
const workDir = this.resolveWorkspaceDir(teamConfig.work_dir);
|
|
71492
71521
|
teams.set(teamId, {
|
|
71493
71522
|
teamId,
|
|
71494
71523
|
name: teamConfig.name,
|
|
71495
71524
|
members: [],
|
|
71496
|
-
specs: []
|
|
71525
|
+
specs: [],
|
|
71526
|
+
workDir,
|
|
71527
|
+
isCurrentProject: this.isCurrentProject(workDir)
|
|
71497
71528
|
});
|
|
71498
71529
|
}
|
|
71499
71530
|
}
|
|
@@ -71510,7 +71541,9 @@ class TeamService {
|
|
|
71510
71541
|
teamId,
|
|
71511
71542
|
name: teamId,
|
|
71512
71543
|
members: [],
|
|
71513
|
-
specs: [spec]
|
|
71544
|
+
specs: [spec],
|
|
71545
|
+
workDir: null,
|
|
71546
|
+
isCurrentProject: false
|
|
71514
71547
|
});
|
|
71515
71548
|
}
|
|
71516
71549
|
} else {
|
|
@@ -71522,9 +71555,18 @@ class TeamService {
|
|
|
71522
71555
|
teamId: "__untethered__",
|
|
71523
71556
|
name: "Untethered",
|
|
71524
71557
|
members: [],
|
|
71525
|
-
specs: untethered
|
|
71558
|
+
specs: untethered,
|
|
71559
|
+
workDir: null,
|
|
71560
|
+
isCurrentProject: false
|
|
71526
71561
|
});
|
|
71527
71562
|
}
|
|
71563
|
+
for (const team of Array.from(teams.values())) {
|
|
71564
|
+
if (configTeamIds.has(team.teamId))
|
|
71565
|
+
continue;
|
|
71566
|
+
const common4 = this.commonWorkspace(team.specs);
|
|
71567
|
+
team.workDir = common4.workDir;
|
|
71568
|
+
team.isCurrentProject = common4.isCurrentProject;
|
|
71569
|
+
}
|
|
71528
71570
|
return Array.from(teams.values());
|
|
71529
71571
|
}
|
|
71530
71572
|
async materializeTeam(teamId, opts) {
|
|
@@ -71613,6 +71655,23 @@ class TeamService {
|
|
|
71613
71655
|
});
|
|
71614
71656
|
return result;
|
|
71615
71657
|
}
|
|
71658
|
+
resolveWorkspaceDir(p) {
|
|
71659
|
+
return resolve6(this.ctx.cwd, p);
|
|
71660
|
+
}
|
|
71661
|
+
isCurrentProject(workDir) {
|
|
71662
|
+
return resolve6(this.ctx.cwd, workDir) === resolve6(this.ctx.cwd, this.ctx.cwd);
|
|
71663
|
+
}
|
|
71664
|
+
commonWorkspace(specs) {
|
|
71665
|
+
if (specs.length === 0)
|
|
71666
|
+
return { workDir: null, isCurrentProject: false };
|
|
71667
|
+
const first = specs[0]?.workspace;
|
|
71668
|
+
if (typeof first !== "string" || first.length === 0)
|
|
71669
|
+
return { workDir: null, isCurrentProject: false };
|
|
71670
|
+
if (!specs.every((s2) => s2.workspace === first))
|
|
71671
|
+
return { workDir: null, isCurrentProject: false };
|
|
71672
|
+
const workDir = this.resolveWorkspaceDir(first);
|
|
71673
|
+
return { workDir, isCurrentProject: this.isCurrentProject(workDir) };
|
|
71674
|
+
}
|
|
71616
71675
|
async loadTeamConfig() {
|
|
71617
71676
|
try {
|
|
71618
71677
|
return await loadSpurConfig(this.ctx.cwd);
|
|
@@ -72050,16 +72109,16 @@ class WorkflowSteeringController {
|
|
|
72050
72109
|
active.state = "boundary";
|
|
72051
72110
|
active.version += 1;
|
|
72052
72111
|
this.publishState(active);
|
|
72053
|
-
return await new Promise((
|
|
72112
|
+
return await new Promise((resolve7) => {
|
|
72054
72113
|
const settle = (decision) => {
|
|
72055
72114
|
clearTimeout(timer);
|
|
72056
|
-
|
|
72115
|
+
resolve7(decision);
|
|
72057
72116
|
};
|
|
72058
72117
|
const timer = setTimeout(() => {
|
|
72059
72118
|
if (active.resolve !== settle)
|
|
72060
72119
|
return;
|
|
72061
72120
|
active.resolve = undefined;
|
|
72062
|
-
this.acceptDefault(active,
|
|
72121
|
+
this.acceptDefault(active, resolve7);
|
|
72063
72122
|
}, active.policy.timeoutMs);
|
|
72064
72123
|
timer.unref?.();
|
|
72065
72124
|
active.resolve = settle;
|
|
@@ -72157,7 +72216,7 @@ class WorkflowSteeringController {
|
|
|
72157
72216
|
deadlineAt: new Date(Date.now() + deadlineMs).toISOString()
|
|
72158
72217
|
});
|
|
72159
72218
|
}
|
|
72160
|
-
acceptDefault(active,
|
|
72219
|
+
acceptDefault(active, resolve7) {
|
|
72161
72220
|
const command = {
|
|
72162
72221
|
commandId: crypto.randomUUID(),
|
|
72163
72222
|
runId: active.runId,
|
|
@@ -72182,7 +72241,7 @@ class WorkflowSteeringController {
|
|
|
72182
72241
|
at: new Date().toISOString()
|
|
72183
72242
|
};
|
|
72184
72243
|
this.onAck?.(ack);
|
|
72185
|
-
|
|
72244
|
+
resolve7({ operation: "continue" });
|
|
72186
72245
|
}
|
|
72187
72246
|
accept(command, active, note) {
|
|
72188
72247
|
return this.publish({
|
|
@@ -73555,7 +73614,7 @@ var init_builtins2 = __esm(() => {
|
|
|
73555
73614
|
|
|
73556
73615
|
// ../../packages/app/src/services/workflow-service.ts
|
|
73557
73616
|
import { homedir as homedir5 } from "os";
|
|
73558
|
-
import { join as join19, resolve as
|
|
73617
|
+
import { join as join19, resolve as resolve7 } from "path";
|
|
73559
73618
|
function signalSubprocess(pid) {
|
|
73560
73619
|
if (!Number.isInteger(pid) || pid <= 1)
|
|
73561
73620
|
return false;
|
|
@@ -73625,7 +73684,7 @@ class WorkflowAppService {
|
|
|
73625
73684
|
};
|
|
73626
73685
|
}
|
|
73627
73686
|
async validate(file2, opts = {}) {
|
|
73628
|
-
const absolute =
|
|
73687
|
+
const absolute = resolve7(this.ctx.cwd, file2);
|
|
73629
73688
|
if (!await fileExists(absolute)) {
|
|
73630
73689
|
return { ok: false, valid: false, file: file2, errors: [`File not found: ${absolute}`] };
|
|
73631
73690
|
}
|
|
@@ -73684,7 +73743,7 @@ class WorkflowAppService {
|
|
|
73684
73743
|
...opts.vars ?? {},
|
|
73685
73744
|
__runId: runId
|
|
73686
73745
|
};
|
|
73687
|
-
const absolute =
|
|
73746
|
+
const absolute = resolve7(this.ctx.cwd, file2);
|
|
73688
73747
|
const embedded = this.embeddedSchemaOptions();
|
|
73689
73748
|
const workflow = await loadWorkflowDef(absolute, {
|
|
73690
73749
|
validateSchema: true,
|
|
@@ -73720,7 +73779,7 @@ class WorkflowAppService {
|
|
|
73720
73779
|
return;
|
|
73721
73780
|
let workflowName;
|
|
73722
73781
|
try {
|
|
73723
|
-
const def = await loadWorkflowDef(
|
|
73782
|
+
const def = await loadWorkflowDef(resolve7(this.ctx.cwd, file2), { validateSchema: false });
|
|
73724
73783
|
workflowName = def.name;
|
|
73725
73784
|
} catch {
|
|
73726
73785
|
return;
|
|
@@ -73845,7 +73904,7 @@ class WorkflowAppService {
|
|
|
73845
73904
|
if (entry === undefined)
|
|
73846
73905
|
return null;
|
|
73847
73906
|
for (const layer of listing.layers) {
|
|
73848
|
-
const abs =
|
|
73907
|
+
const abs = resolve7(layer.path, entry.path);
|
|
73849
73908
|
if (await fileExists(abs)) {
|
|
73850
73909
|
try {
|
|
73851
73910
|
return await loadWorkflowDef(abs, { validateSchema: false });
|
|
@@ -73861,7 +73920,7 @@ class WorkflowAppService {
|
|
|
73861
73920
|
const entries = [];
|
|
73862
73921
|
const scannedPaths = new Set;
|
|
73863
73922
|
for (const relPath of workflowPaths) {
|
|
73864
|
-
const absPath =
|
|
73923
|
+
const absPath = resolve7(projectRoot, relPath);
|
|
73865
73924
|
layers.push({ id: "project", path: absPath });
|
|
73866
73925
|
scannedPaths.add(absPath);
|
|
73867
73926
|
try {
|
|
@@ -73870,7 +73929,7 @@ class WorkflowAppService {
|
|
|
73870
73929
|
} catch {}
|
|
73871
73930
|
}
|
|
73872
73931
|
for (const relPath of workflowPaths) {
|
|
73873
|
-
const absPath =
|
|
73932
|
+
const absPath = resolve7(globalRoot, relPath);
|
|
73874
73933
|
if (scannedPaths.has(absPath))
|
|
73875
73934
|
continue;
|
|
73876
73935
|
layers.push({ id: "global", path: absPath });
|
|
@@ -74126,7 +74185,7 @@ async function scanWorkflowFiles(rootPath, source) {
|
|
|
74126
74185
|
async function walk3(dirPath) {
|
|
74127
74186
|
const names = await fs3.readDir(dirPath);
|
|
74128
74187
|
for (const name of names) {
|
|
74129
|
-
const fullPath =
|
|
74188
|
+
const fullPath = resolve7(dirPath, name);
|
|
74130
74189
|
const st = await fs3.stat(fullPath);
|
|
74131
74190
|
if (!st)
|
|
74132
74191
|
continue;
|
|
@@ -82132,15 +82191,15 @@ function parseLoopPoll(raw) {
|
|
|
82132
82191
|
return Number.isFinite(n2) && n2 > 0 ? n2 : DEFAULT_LOOP_POLL_MS;
|
|
82133
82192
|
}
|
|
82134
82193
|
function loopSleep(ms, signal) {
|
|
82135
|
-
return new Promise((
|
|
82194
|
+
return new Promise((resolve8) => {
|
|
82136
82195
|
if (signal?.aborted) {
|
|
82137
|
-
|
|
82196
|
+
resolve8();
|
|
82138
82197
|
return;
|
|
82139
82198
|
}
|
|
82140
|
-
const timer = setTimeout(
|
|
82199
|
+
const timer = setTimeout(resolve8, ms);
|
|
82141
82200
|
signal?.addEventListener("abort", () => {
|
|
82142
82201
|
clearTimeout(timer);
|
|
82143
|
-
|
|
82202
|
+
resolve8();
|
|
82144
82203
|
}, { once: true });
|
|
82145
82204
|
});
|
|
82146
82205
|
}
|
|
@@ -82200,7 +82259,7 @@ init_loader();
|
|
|
82200
82259
|
init_src2();
|
|
82201
82260
|
import { existsSync as existsSync10 } from "fs";
|
|
82202
82261
|
import { homedir as homedir6 } from "os";
|
|
82203
|
-
import { join as join21, resolve as
|
|
82262
|
+
import { join as join21, resolve as resolve8 } from "path";
|
|
82204
82263
|
|
|
82205
82264
|
// src/workflow/resolve-spur-bin.ts
|
|
82206
82265
|
import { basename as basename4 } from "path";
|
|
@@ -82221,7 +82280,7 @@ function resolveSpurBin() {
|
|
|
82221
82280
|
var GLOBAL_CONFIG_DIR = join21(".config", "spur");
|
|
82222
82281
|
function globalConfigRoot(context4) {
|
|
82223
82282
|
const override = context4.env.SPUR_GLOBAL_RULES_DIR;
|
|
82224
|
-
return override !== undefined && override.length > 0 ?
|
|
82283
|
+
return override !== undefined && override.length > 0 ? resolve8(context4.cwd, override) : join21(homedir6(), GLOBAL_CONFIG_DIR);
|
|
82225
82284
|
}
|
|
82226
82285
|
function resolveWorkflowPath(context4, profile) {
|
|
82227
82286
|
const bundledRoot = bundledConfigRoot();
|
|
@@ -82626,7 +82685,7 @@ import { createRequire } from "module";
|
|
|
82626
82685
|
var CLI_CONFIG = {
|
|
82627
82686
|
binaryName: "spur",
|
|
82628
82687
|
binaryLabel: "spur",
|
|
82629
|
-
binaryVersion: "0.3.
|
|
82688
|
+
binaryVersion: "0.3.44",
|
|
82630
82689
|
configDir: ".spur",
|
|
82631
82690
|
configFile: ".spur/config.yaml",
|
|
82632
82691
|
databaseFile: ".spur/spur.db"
|
|
@@ -82878,7 +82937,7 @@ init_loader();
|
|
|
82878
82937
|
init_src2();
|
|
82879
82938
|
init_dist10();
|
|
82880
82939
|
import { homedir as homedir7 } from "os";
|
|
82881
|
-
import { join as join22, resolve as
|
|
82940
|
+
import { join as join22, resolve as resolve9 } from "path";
|
|
82882
82941
|
|
|
82883
82942
|
// src/config/scaffold-manifest.ts
|
|
82884
82943
|
var SCAFFOLD_MANIFEST = [
|
|
@@ -82957,7 +83016,7 @@ If \`.spur/context/\` is absent, proceed normally. Never block work on its absen
|
|
|
82957
83016
|
`;
|
|
82958
83017
|
function globalRulesRoot(context4) {
|
|
82959
83018
|
const override = context4.env.SPUR_GLOBAL_RULES_DIR;
|
|
82960
|
-
return override !== undefined && override.length > 0 ?
|
|
83019
|
+
return override !== undefined && override.length > 0 ? resolve9(context4.cwd, override) : join22(homedir7(), GLOBAL_RULES_DIR2);
|
|
82961
83020
|
}
|
|
82962
83021
|
async function writeIfNew(context4, path9, content, force, result) {
|
|
82963
83022
|
if (!force && await context4.fs.exists(path9)) {
|
|
@@ -82988,7 +83047,7 @@ async function seedGlobalConfig(context4) {
|
|
|
82988
83047
|
if (source === null)
|
|
82989
83048
|
return 0;
|
|
82990
83049
|
const globalOverride = context4.env.SPUR_GLOBAL_RULES_DIR;
|
|
82991
|
-
const target = globalOverride !== undefined && globalOverride.length > 0 ?
|
|
83050
|
+
const target = globalOverride !== undefined && globalOverride.length > 0 ? resolve9(context4.cwd, globalOverride) : join22(homedir7(), GLOBAL_CONFIG_DIR2);
|
|
82992
83051
|
let written = 0;
|
|
82993
83052
|
for (const relPath of listBundledConfigFiles()) {
|
|
82994
83053
|
if (relPath === GLOBAL_CONFIG_EXAMPLE)
|
|
@@ -83261,15 +83320,15 @@ async function runMessageWatch(svc, output2, options, runtime = {}) {
|
|
|
83261
83320
|
}
|
|
83262
83321
|
}
|
|
83263
83322
|
function defaultSleep(ms, signal) {
|
|
83264
|
-
return new Promise((
|
|
83323
|
+
return new Promise((resolve10) => {
|
|
83265
83324
|
if (signal?.aborted) {
|
|
83266
|
-
|
|
83325
|
+
resolve10();
|
|
83267
83326
|
return;
|
|
83268
83327
|
}
|
|
83269
|
-
const timer = setTimeout(
|
|
83328
|
+
const timer = setTimeout(resolve10, ms);
|
|
83270
83329
|
signal?.addEventListener("abort", () => {
|
|
83271
83330
|
clearTimeout(timer);
|
|
83272
|
-
|
|
83331
|
+
resolve10();
|
|
83273
83332
|
}, { once: true });
|
|
83274
83333
|
});
|
|
83275
83334
|
}
|
|
@@ -83297,12 +83356,12 @@ function registerMigrateCommand(program2, context4) {
|
|
|
83297
83356
|
// src/commands/projects.ts
|
|
83298
83357
|
init_src3();
|
|
83299
83358
|
init_dist5();
|
|
83300
|
-
import { basename as basename5, resolve as
|
|
83359
|
+
import { basename as basename5, resolve as resolve10 } from "path";
|
|
83301
83360
|
function registerProjectsCommand(program2, context4) {
|
|
83302
83361
|
const projectsCmd = program2.command("projects").summary("manage the Spur multi-project registry");
|
|
83303
83362
|
projectsCmd.command("add").argument("<path>", "Project root directory path").option("--name <name>", "Display name for the project").option("--json", "Output JSON response").action(async (pathArg, options) => {
|
|
83304
83363
|
try {
|
|
83305
|
-
const absolutePath =
|
|
83364
|
+
const absolutePath = resolve10(context4.cwd, pathArg);
|
|
83306
83365
|
if (!await context4.fs.exists(absolutePath)) {
|
|
83307
83366
|
throw new Error(`Directory does not exist: ${absolutePath}`);
|
|
83308
83367
|
}
|
|
@@ -83988,8 +84047,8 @@ class EventPublisher {
|
|
|
83988
84047
|
if (bufferedEvents.length > 0) {
|
|
83989
84048
|
return { done: false, value: bufferedEvents.shift() };
|
|
83990
84049
|
}
|
|
83991
|
-
return new Promise((
|
|
83992
|
-
pullResolvers.push([
|
|
84050
|
+
return new Promise((resolve11, reject) => {
|
|
84051
|
+
pullResolvers.push([resolve11, reject]);
|
|
83993
84052
|
});
|
|
83994
84053
|
}, async () => {
|
|
83995
84054
|
unsubscribe();
|
|
@@ -90514,6 +90573,8 @@ var teamModule = {
|
|
|
90514
90573
|
const enriched = teams.map((team) => ({
|
|
90515
90574
|
teamId: team.teamId,
|
|
90516
90575
|
name: team.name,
|
|
90576
|
+
workDir: team.workDir,
|
|
90577
|
+
isCurrentProject: team.isCurrentProject,
|
|
90517
90578
|
members: team.specs.map((spec) => {
|
|
90518
90579
|
const proc = processes.find((p2) => p2.agentId === spec.id);
|
|
90519
90580
|
const configModel = spec.config?.model;
|
|
@@ -94549,32 +94610,51 @@ ${result.content}`);
|
|
|
94549
94610
|
const svc = await makeCheckService(context4);
|
|
94550
94611
|
const planningFolders = await resolvePlanningFolders(context4.fs);
|
|
94551
94612
|
const activeFolder = planningFolders.foldersConfig.active_folder;
|
|
94552
|
-
const tasksDir = options.folder ??
|
|
94553
|
-
const
|
|
94554
|
-
|
|
94613
|
+
const tasksDir = context4.fs.resolve(options.folder ?? activeFolder);
|
|
94614
|
+
const printResult = (result) => {
|
|
94615
|
+
if (json3)
|
|
94616
|
+
return;
|
|
94617
|
+
context4.output.write(`
|
|
94618
|
+
${result.wbs} (${result.status}): ${result.pass ? "PASS" : "FAIL"}`);
|
|
94619
|
+
for (const f of result.findings) {
|
|
94620
|
+
const tag = f.severity === "error" ? "ERR" : "WARN";
|
|
94621
|
+
context4.output.write(` [${tag}] ${f.layer} ${f.section}: ${f.message}`);
|
|
94622
|
+
}
|
|
94623
|
+
if (result.missingSections.length > 0) {
|
|
94624
|
+
context4.output.write(` Missing: ${result.missingSections.join(", ")}`);
|
|
94625
|
+
}
|
|
94626
|
+
};
|
|
94555
94627
|
const results = [];
|
|
94556
|
-
|
|
94557
|
-
const
|
|
94558
|
-
|
|
94559
|
-
|
|
94628
|
+
if (wbs) {
|
|
94629
|
+
const locator = options.folder !== undefined ? TaskLocator.forSingleDir(context4.fs, tasksDir) : await makeTaskLocator(context4);
|
|
94630
|
+
const hit = await locator.findByWbs(wbs);
|
|
94631
|
+
if (hit === null) {
|
|
94632
|
+
context4.output.error(`Task ${wbs} not found`);
|
|
94560
94633
|
context4.setExitCode(1);
|
|
94561
|
-
|
|
94634
|
+
} else {
|
|
94635
|
+
const result = await svc.check(hit.filePath, wbs, {
|
|
94636
|
+
strict,
|
|
94637
|
+
severityOverrides: planningFolders.severityOverrides
|
|
94638
|
+
});
|
|
94639
|
+
results.push(result);
|
|
94640
|
+
printResult(result);
|
|
94562
94641
|
}
|
|
94563
|
-
|
|
94564
|
-
|
|
94565
|
-
|
|
94566
|
-
|
|
94567
|
-
|
|
94568
|
-
|
|
94569
|
-
|
|
94570
|
-
|
|
94571
|
-
|
|
94572
|
-
const tag = f.severity === "error" ? "ERR" : "WARN";
|
|
94573
|
-
context4.output.write(` [${tag}] ${f.layer} ${f.section}: ${f.message}`);
|
|
94574
|
-
}
|
|
94575
|
-
if (result.missingSections.length > 0) {
|
|
94576
|
-
context4.output.write(` Missing: ${result.missingSections.join(", ")}`);
|
|
94642
|
+
} else {
|
|
94643
|
+
const entries = await context4.fs.readDir(tasksDir);
|
|
94644
|
+
const wbsPattern = entries.filter((n2) => /^\d{4}_.+\.md$/.test(n2)).map((n2) => n2.match(/^(\d{4})_/)?.[1]).filter((n2) => n2 !== undefined);
|
|
94645
|
+
for (const w of wbsPattern) {
|
|
94646
|
+
const fileName = entries.find((n2) => n2.startsWith(`${w}_`) && n2.endsWith(".md"));
|
|
94647
|
+
if (!fileName) {
|
|
94648
|
+
context4.output.error(`Task ${w} not found`);
|
|
94649
|
+
context4.setExitCode(1);
|
|
94650
|
+
continue;
|
|
94577
94651
|
}
|
|
94652
|
+
const result = await svc.check(`${tasksDir}/${fileName}`, w, {
|
|
94653
|
+
strict,
|
|
94654
|
+
severityOverrides: planningFolders.severityOverrides
|
|
94655
|
+
});
|
|
94656
|
+
results.push(result);
|
|
94657
|
+
printResult(result);
|
|
94578
94658
|
}
|
|
94579
94659
|
}
|
|
94580
94660
|
if (!wbs) {
|
|
@@ -94697,7 +94777,7 @@ ${result.wbs} (${result.status}): ${result.pass ? "PASS" : "FAIL"}`);
|
|
|
94697
94777
|
}
|
|
94698
94778
|
async function makeService2(context4, folderOverride, noLifecycle = false) {
|
|
94699
94779
|
const foldersConfig = (await resolvePlanningFolders(context4.fs)).foldersConfig;
|
|
94700
|
-
const tasksDir =
|
|
94780
|
+
const tasksDir = context4.fs.resolve(folderOverride ?? foldersConfig.active_folder);
|
|
94701
94781
|
const lifecycle = noLifecycle ? undefined : makeLifecycleAdapter(context4, TASK_LIFECYCLE_PROFILE);
|
|
94702
94782
|
const writeService = new PlanningWriteService({
|
|
94703
94783
|
fs: context4.fs,
|
|
@@ -95090,7 +95170,7 @@ init_dist7();
|
|
|
95090
95170
|
init_dist6();
|
|
95091
95171
|
init_dist5();
|
|
95092
95172
|
import { closeSync as closeSync5, fstatSync as fstatSync3, openSync as openSync5, readSync as readSync3 } from "fs";
|
|
95093
|
-
import { join as join30, resolve as
|
|
95173
|
+
import { join as join30, resolve as resolve11 } from "path";
|
|
95094
95174
|
import { createInterface } from "readline";
|
|
95095
95175
|
import { setTimeout as sleep3 } from "timers/promises";
|
|
95096
95176
|
function shQuote2(value2) {
|
|
@@ -95289,7 +95369,7 @@ Monitor with: spur workflow trace ${runId2} --follow`);
|
|
|
95289
95369
|
let planPreview;
|
|
95290
95370
|
if (options.plan !== false) {
|
|
95291
95371
|
try {
|
|
95292
|
-
const def = await loadWorkflowDef(
|
|
95372
|
+
const def = await loadWorkflowDef(resolve11(context4.cwd, file2), { validateSchema: false });
|
|
95293
95373
|
planPreview = renderRunPlan(def);
|
|
95294
95374
|
} catch {}
|
|
95295
95375
|
}
|
|
@@ -95753,7 +95833,7 @@ init_src3();
|
|
|
95753
95833
|
init_src();
|
|
95754
95834
|
init_src2();
|
|
95755
95835
|
init_dist5();
|
|
95756
|
-
import { dirname as dirname19, join as join31, resolve as
|
|
95836
|
+
import { dirname as dirname19, join as join31, resolve as resolve12 } from "path";
|
|
95757
95837
|
import { isatty as isatty4 } from "tty";
|
|
95758
95838
|
|
|
95759
95839
|
// ../../node_modules/.bun/@clack+core@1.4.1/node_modules/@clack/core/dist/index.mjs
|
|
@@ -97136,7 +97216,7 @@ class DefaultHitlResponder {
|
|
|
97136
97216
|
|
|
97137
97217
|
// src/context.ts
|
|
97138
97218
|
function createCliContext(options) {
|
|
97139
|
-
const cwd =
|
|
97219
|
+
const cwd = resolve12(options.cwd ?? process.cwd());
|
|
97140
97220
|
const env = options.env ?? process.env;
|
|
97141
97221
|
const fs3 = createNodeFileSystem3(cwd);
|
|
97142
97222
|
let dbPromise;
|