@gobing-ai/spur 0.3.94 → 0.3.96

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.
Files changed (198) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/README.md +42 -0
  3. package/board/index.d.ts +38 -0
  4. package/config/plugin-scripts.json +3 -52
  5. package/config/rules/boundary/sp-script-placement.yaml +19 -0
  6. package/config/rules/strict/runtime-boundaries.yaml +5 -0
  7. package/config/rules/structure/test-location.yaml +2 -0
  8. package/config/rules/typescript/no-syscall-emulation-in-boundary-mock.yaml +1 -1
  9. package/config/rules/typescript/output-boundaries.yaml +15 -2
  10. package/config/script-placement-baseline.json +51 -0
  11. package/config/workflows/feature-verification.yaml +7 -4
  12. package/config/workflows/history-anatomy.yaml +23 -11
  13. package/config/workflows/idea-pipeline.yaml +87 -86
  14. package/config/workflows/pr-review.yaml +18 -9
  15. package/config/workflows/task-pipeline.yaml +84 -109
  16. package/config/workflows/wrapup-pipeline.yaml +173 -46
  17. package/package.json +15 -11
  18. package/plugins/sp/README.md +8 -10
  19. package/plugins/sp/agents/super-reviewer.md +43 -18
  20. package/plugins/sp/commands/dev-fixgha.md +83 -0
  21. package/plugins/sp/commands/dev-gitmsg.md +8 -6
  22. package/plugins/sp/commands/dev-gtd.md +2 -2
  23. package/plugins/sp/commands/dev-idea.md +22 -12
  24. package/plugins/sp/commands/dev-plan.md +8 -9
  25. package/plugins/sp/commands/dev-review.md +22 -13
  26. package/plugins/sp/commands/dev-verify.md +7 -4
  27. package/plugins/sp/commands/dev-verifyall.md +5 -4
  28. package/plugins/sp/commands/dev-wrap.md +1 -1
  29. package/plugins/sp/commands/spur-init.md +2 -2
  30. package/plugins/sp/hooks/context-post-tool.ts +2 -2
  31. package/plugins/sp/hooks/context-session-start.ts +2 -1
  32. package/plugins/sp/hooks/context-session-stop.ts +22 -14
  33. package/plugins/sp/hooks/pi/guard-extension.ts +109 -150
  34. package/plugins/sp/lib/history-anatomy.generated.d.mts +112 -0
  35. package/plugins/sp/lib/history-anatomy.generated.mjs +686 -0
  36. package/plugins/sp/lib/idea-handoff.generated.mjs +6 -5
  37. package/plugins/sp/lib/inline-run.generated.d.mts +13 -2
  38. package/plugins/sp/lib/inline-run.generated.mjs +33 -16
  39. package/plugins/sp/lib/quality-gate.generated.d.mts +104 -0
  40. package/plugins/sp/lib/quality-gate.generated.mjs +438 -0
  41. package/plugins/sp/lib/residual-scan.generated.d.mts +62 -0
  42. package/plugins/sp/lib/residual-scan.generated.mjs +210 -0
  43. package/plugins/sp/lib/spur-bin.ts +36 -0
  44. package/plugins/sp/lib/step-profile.generated.d.mts +71 -0
  45. package/plugins/sp/lib/step-profile.generated.mjs +174 -0
  46. package/plugins/sp/plugin.json +1 -1
  47. package/plugins/sp/references/roles.md +1 -1
  48. package/plugins/sp/scripts/daily-summary/daily-summary.mjs +3 -3
  49. package/plugins/sp/scripts/daily-summary/daily-summary.ts +3 -3
  50. package/plugins/sp/scripts/dogfood-testing/detect-pipeline-driving.mjs +2 -0
  51. package/plugins/sp/scripts/dogfood-testing/detect-pipeline-driving.ts +1 -0
  52. package/plugins/sp/scripts/dogfood-testing/validate-report.mjs +9 -7
  53. package/plugins/sp/scripts/dogfood-testing/validate-report.ts +8 -7
  54. package/plugins/sp/scripts/feature-verification-steps.ts +6 -6
  55. package/plugins/sp/scripts/history-anatomy-cache.mjs +23 -22
  56. package/plugins/sp/scripts/history-anatomy-cache.ts +23 -928
  57. package/plugins/sp/scripts/inline-run-setup.mjs +90 -315
  58. package/plugins/sp/scripts/inline-run-setup.ts +109 -639
  59. package/plugins/sp/scripts/pr-reviewing.mjs +2 -0
  60. package/plugins/sp/scripts/pr-reviewing.ts +1 -0
  61. package/plugins/sp/scripts/quality-gate.mjs +38 -21
  62. package/plugins/sp/scripts/quality-gate.ts +20 -660
  63. package/plugins/sp/scripts/residual-scan.mjs +138 -159
  64. package/plugins/sp/scripts/residual-scan.ts +102 -499
  65. package/plugins/sp/scripts/script-root.mjs +129 -0
  66. package/plugins/sp/scripts/script-root.ts +199 -0
  67. package/plugins/sp/scripts/workflow-step-profile.mjs +27 -16
  68. package/plugins/sp/scripts/workflow-step-profile.ts +22 -314
  69. package/plugins/sp/scripts/wrapup-drift-probe.mjs +12 -3
  70. package/plugins/sp/scripts/wrapup-drift-probe.ts +6 -3
  71. package/plugins/sp/scripts/wrapup-steps.mjs +42 -40
  72. package/plugins/sp/scripts/wrapup-steps.ts +64 -46
  73. package/plugins/sp/skills/code-improvement/SKILL.md +5 -4
  74. package/plugins/sp/skills/code-verification/SKILL.md +46 -16
  75. package/plugins/sp/skills/code-verification/references/verdict-schema.md +13 -2
  76. package/plugins/sp/skills/functional-review/SKILL.md +8 -5
  77. package/plugins/sp/skills/functional-review/references/verdict-schema.md +1 -1
  78. package/plugins/sp/skills/history-anatomy/references/modes.md +2 -1
  79. package/plugins/sp/skills/next-feature/references/handoff-routing.md +1 -1
  80. package/plugins/sp/skills/next-router/references/routing-table.md +2 -2
  81. package/plugins/sp/skills/spur-cli/SKILL.md +3 -3
  82. package/plugins/sp/skills/spur-cli/references/agent.md +10 -10
  83. package/plugins/sp/skills/spur-cli/references/features.md +17 -6
  84. package/plugins/sp/skills/spur-cli/references/init.md +17 -16
  85. package/plugins/sp/skills/spur-cli/references/self.md +3 -2
  86. package/plugins/sp/skills/spur-cli/references/serve.md +10 -10
  87. package/plugins/sp/skills/spur-cli/references/tasks/verbs.md +28 -5
  88. package/plugins/sp/skills/spur-cli/references/tasks.md +14 -8
  89. package/plugins/sp/skills/spur-dev/references/ac-style-guide.md +4 -3
  90. package/plugins/sp/skills/spur-dev/references/cross-cutting.md +11 -9
  91. package/plugins/sp/skills/spur-dev/references/decision-brief.md +1 -1
  92. package/plugins/sp/skills/spur-dev/references/dev-operations.md +86 -70
  93. package/plugins/sp/skills/spur-dev/references/done-housekeeping.md +3 -3
  94. package/plugins/sp/skills/spur-dev/references/execution-batch.md +107 -44
  95. package/plugins/sp/skills/spur-dev/references/execution-workflow.md +5 -9
  96. package/plugins/sp/skills/spur-dev/references/feature-link-helper.md +3 -3
  97. package/plugins/sp/skills/spur-dev/references/flag-glossary.md +31 -21
  98. package/plugins/sp/skills/spur-dev/references/gate-checklists.md +19 -19
  99. package/plugins/sp/skills/spur-dev/references/idea-evaluation.md +4 -3
  100. package/plugins/sp/skills/spur-dev/references/inline-pipeline-driver.md +35 -6
  101. package/plugins/sp/skills/spur-doctor/SKILL.md +1 -1
  102. package/plugins/sp/skills/sys-architecture/SKILL.md +3 -2
  103. package/plugins/sp/tsconfig.json +8 -0
  104. package/spur.js +6638 -4775
  105. package/web/_astro/BoardApp.BIjMatT1.js +1 -0
  106. package/web/_astro/{BoardApp.FTEs3-N8.js → BoardApp.GvjIe9Z6.js} +150 -153
  107. package/web/_astro/TaskDetail.BZM3EAFx.js +1 -0
  108. package/web/_astro/_commonjsHelpers.CqkleIqs.js +1 -0
  109. package/web/_astro/{arc.uuAf51IT.js → arc.HkRiZnoI.js} +1 -1
  110. package/web/_astro/architectureDiagram-3BPJPVTR.BDdK-tgZ.js +36 -0
  111. package/web/_astro/{blockDiagram-GPEHLZMM.D1mGCq3p.js → blockDiagram-GPEHLZMM.CZ9kOBxx.js} +1 -1
  112. package/web/_astro/board-facade-react-dom-client.B2U5wMvk.js +1 -0
  113. package/web/_astro/board-facade-react-dom.Cd2MuMhF.js +1 -0
  114. package/web/_astro/board-facade-react-jsx-dev-runtime.B_L3JzfC.js +1 -0
  115. package/web/_astro/board-facade-react-jsx-runtime.BY2yfHRi.js +1 -0
  116. package/web/_astro/board-facade-react-router-dom.Dy5pjzOw.js +1 -0
  117. package/web/_astro/board-facade-react-router.DjjqU27_.js +1 -0
  118. package/web/_astro/board-facade-react.Dgf4GRyj.js +1 -0
  119. package/web/_astro/{c4Diagram-AAUBKEIU.CMsolcde.js → c4Diagram-AAUBKEIU.DXK9qeRD.js} +1 -1
  120. package/web/_astro/channel.DlhX2MEt.js +1 -0
  121. package/web/_astro/{chunk-2J33WTMH.CrGA3fik.js → chunk-2J33WTMH.BTD2WeX5.js} +1 -1
  122. package/web/_astro/{chunk-4BX2VUAB.DsLVVla0.js → chunk-4BX2VUAB.Ci6Qbcvk.js} +1 -1
  123. package/web/_astro/{chunk-55IACEB6.Dxc59Tfi.js → chunk-55IACEB6.yl3zsj7p.js} +1 -1
  124. package/web/_astro/{chunk-727SXJPM.CaEVE2Wy.js → chunk-727SXJPM.ClXZsfyR.js} +1 -1
  125. package/web/_astro/{chunk-AQP2D5EJ.BiJ4HXeI.js → chunk-AQP2D5EJ.BmKWZBcP.js} +1 -1
  126. package/web/_astro/{chunk-FMBD7UC4.Mxf1fru5.js → chunk-FMBD7UC4.By30fcb7.js} +1 -1
  127. package/web/_astro/chunk-JMJ3UQ3L.BQJEJHu4.js +34 -0
  128. package/web/_astro/{chunk-ND2GUHAM.pyOWQixH.js → chunk-ND2GUHAM.24NmrD-k.js} +1 -1
  129. package/web/_astro/{chunk-QZHKN3VN.BmEsg4vr.js → chunk-QZHKN3VN.BN4sKdcS.js} +1 -1
  130. package/web/_astro/chunk-YNUBSHFH.CxeAygfi.js +7 -0
  131. package/web/_astro/{classDiagram-4FO5ZUOK.BHhhMFTO.js → classDiagram-4FO5ZUOK.CJpoMPb5.js} +1 -1
  132. package/web/_astro/{classDiagram-v2-Q7XG4LA2.BHhhMFTO.js → classDiagram-v2-Q7XG4LA2.CJpoMPb5.js} +1 -1
  133. package/web/_astro/client.BhluLQqe.js +1 -0
  134. package/web/_astro/client.DBpSRLnx.js +9 -0
  135. package/web/_astro/cose-bilkent-S5V4N54A.BraxQ2Nt.js +1 -0
  136. package/web/_astro/{cynefin-OW5HDTMX.C2j1_lKL.js → cynefin-OW5HDTMX.gzoU73oL.js} +1 -1
  137. package/web/_astro/{dagre-BM42HDAG.hZ2NCTdT.js → dagre-BM42HDAG.wyDfWySU.js} +1 -1
  138. package/web/_astro/{diagram-2AECGRRQ.Bfw5_EzK.js → diagram-2AECGRRQ.h2_EbMxV.js} +1 -1
  139. package/web/_astro/{diagram-5GNKFQAL.Bt1V_Tmk.js → diagram-5GNKFQAL.Db6dcs89.js} +1 -1
  140. package/web/_astro/{diagram-KO2AKTUF.DEk-YFwp.js → diagram-KO2AKTUF.NVwzxu_U.js} +1 -1
  141. package/web/_astro/{diagram-LMA3HP47.CDjndtQm.js → diagram-LMA3HP47.B-eNgDQk.js} +1 -1
  142. package/web/_astro/{diagram-OG6HWLK6.cFnUHScG.js → diagram-OG6HWLK6.Bsa8ZRbJ.js} +1 -1
  143. package/web/_astro/{erDiagram-TEJ5UH35.DlhYp7NV.js → erDiagram-TEJ5UH35.BospRE5Q.js} +1 -1
  144. package/web/_astro/{flowDiagram-I6XJVG4X.DNTpsxfx.js → flowDiagram-I6XJVG4X.DMzvHwAA.js} +1 -1
  145. package/web/_astro/ganttDiagram-6RSMTGT7.Dmdxgd-M.js +292 -0
  146. package/web/_astro/{gitGraphDiagram-PVQCEYII.DkEqNI0P.js → gitGraphDiagram-PVQCEYII.CaqdO0rL.js} +1 -1
  147. package/web/_astro/index.5dn-Zm-2.js +1 -0
  148. package/web/_astro/index.C-IqPpW2.js +1 -0
  149. package/web/_astro/index.Cyc_GA4f.js +1 -0
  150. package/web/_astro/index.EoZlzLK-.css +1 -0
  151. package/web/_astro/index.tZfqwSut.js +1 -0
  152. package/web/_astro/{infoDiagram-5YYISTIA.CrjioCTG.js → infoDiagram-5YYISTIA.DDVD9Wn7.js} +1 -1
  153. package/web/_astro/{ishikawaDiagram-YF4QCWOH.BcK0CN8n.js → ishikawaDiagram-YF4QCWOH.EKyCUFsL.js} +1 -1
  154. package/web/_astro/{journeyDiagram-JHISSGLW.p0CbBDX1.js → journeyDiagram-JHISSGLW.By2-RkYB.js} +1 -1
  155. package/web/_astro/jsx-runtime.DWOiEOII.js +1 -0
  156. package/web/_astro/{kanban-definition-UN3LZRKU.puHIFt6J.js → kanban-definition-UN3LZRKU.ar-WmlyR.js} +1 -1
  157. package/web/_astro/{linear.Bb-3a1d7.js → linear.BnzMBgo_.js} +1 -1
  158. package/web/_astro/{mermaid.core.CJDgXOJs.js → mermaid.core.DSqeFu2Y.js} +4 -4
  159. package/web/_astro/{mindmap-definition-RKZ34NQL.BoAZEI9t.js → mindmap-definition-RKZ34NQL.1XcF8Wh2.js} +1 -1
  160. package/web/_astro/{pieDiagram-4H26LBE5.CJNSdqY7.js → pieDiagram-4H26LBE5.DnItHOdK.js} +1 -1
  161. package/web/_astro/{quadrantDiagram-W4KKPZXB.DtTy7_0Z.js → quadrantDiagram-W4KKPZXB.BQQyrnBt.js} +1 -1
  162. package/web/_astro/{requirementDiagram-4Y6WPE33.BNYV98Xg.js → requirementDiagram-4Y6WPE33.Dmzxun82.js} +1 -1
  163. package/web/_astro/{sankeyDiagram-5OEKKPKP.DRq9DGHp.js → sankeyDiagram-5OEKKPKP.BBIeRKYB.js} +1 -1
  164. package/web/_astro/{sequenceDiagram-3UESZ5HK.B7KdBFCm.js → sequenceDiagram-3UESZ5HK.CUNVdBBf.js} +1 -1
  165. package/web/_astro/{stateDiagram-AJRCARHV.CD62ZJ2H.js → stateDiagram-AJRCARHV.DlFf2sJm.js} +1 -1
  166. package/web/_astro/{stateDiagram-v2-BHNVJYJU.CeVk1TAj.js → stateDiagram-v2-BHNVJYJU.DXR7I1H5.js} +1 -1
  167. package/web/_astro/{timeline-definition-PNZ67QCA.C_ZPA5tT.js → timeline-definition-PNZ67QCA.BcO-GyX2.js} +1 -1
  168. package/web/_astro/{vennDiagram-CIIHVFJN.CrThO_FQ.js → vennDiagram-CIIHVFJN.ChCECmmW.js} +1 -1
  169. package/web/_astro/{wardleyDiagram-YWT4CUSO.DSZCA5nl.js → wardleyDiagram-YWT4CUSO.CRaT12mD.js} +1 -1
  170. package/web/_astro/{xychartDiagram-2RQKCTM6.KI7baTuk.js → xychartDiagram-2RQKCTM6.B3lsHVpj.js} +1 -1
  171. package/web/board-runtime.json +69 -0
  172. package/web/index.html +2 -2
  173. package/plugins/sp/lib/artifact-digest.generated.d.mts +0 -7
  174. package/plugins/sp/lib/artifact-digest.generated.mjs +0 -48
  175. package/plugins/sp/scripts/feature-sync-bounded.mjs +0 -298
  176. package/plugins/sp/scripts/feature-sync-bounded.ts +0 -479
  177. package/plugins/sp/scripts/idea-coverage-check.ts +0 -168
  178. package/plugins/sp/scripts/inline-pipeline-parity-check.ts +0 -298
  179. package/plugins/sp/scripts/record-feature-sync.mjs +0 -63
  180. package/plugins/sp/scripts/record-feature-sync.ts +0 -84
  181. package/plugins/sp/scripts/script-contract-check.ts +0 -377
  182. package/plugins/sp/scripts/stage-registry-adapter.ts +0 -1533
  183. package/plugins/sp/scripts/surface-drift-inventory.ts +0 -989
  184. package/plugins/sp/scripts/task-evidence-precheck.ts +0 -187
  185. package/plugins/sp/scripts/task-size-precheck.ts +0 -210
  186. package/plugins/sp/scripts/transition-shim-check.ts +0 -238
  187. package/plugins/sp/scripts/validate-commands.ts +0 -689
  188. package/plugins/sp/scripts/validate-flag-contracts.ts +0 -878
  189. package/plugins/sp/scripts/verify-answer-lint.ts +0 -547
  190. package/web/_astro/BoardApp.C02hAHPO.js +0 -1
  191. package/web/_astro/TaskDetail.C-GdsS-t.js +0 -1
  192. package/web/_astro/architectureDiagram-3BPJPVTR.CGe629A1.js +0 -36
  193. package/web/_astro/channel.fsgl7o5j.js +0 -1
  194. package/web/_astro/client.yhYJvxCU.js +0 -9
  195. package/web/_astro/cose-bilkent-S5V4N54A.2fH4YOlp.js +0 -1
  196. package/web/_astro/ganttDiagram-6RSMTGT7.DC_p36PI.js +0 -292
  197. package/web/_astro/index.De90oHcH.js +0 -1
  198. package/web/_astro/index.Hjbr15fG.css +0 -1
@@ -3,18 +3,7 @@
3
3
 
4
4
  // plugins/sp/scripts/inline-run-setup.ts
5
5
  import { spawnSync } from "child_process";
6
- import {
7
- appendFileSync,
8
- closeSync,
9
- existsSync,
10
- mkdirSync,
11
- openSync,
12
- readFileSync,
13
- renameSync,
14
- unlinkSync,
15
- writeFileSync,
16
- writeSync
17
- } from "fs";
6
+ import { existsSync } from "fs";
18
7
  import { dirname, join, resolve } from "path";
19
8
  import { fileURLToPath } from "url";
20
9
 
@@ -26,35 +15,34 @@ function getEnvVar(name, fallback) {
26
15
 
27
16
  // plugins/sp/scripts/inline-run-setup.ts
28
17
  function usage() {
29
- console.error("Usage: bun plugins/sp/scripts/inline-run-setup.ts --run-id <id> --file <definition> [--spur-bin <path>]");
30
- console.error(" bun plugins/sp/scripts/inline-run-setup.ts --fingerprint --task-file <path> [--feature-file <path>] [--spur-bin <path>]");
31
- console.error(" bun plugins/sp/scripts/inline-run-setup.ts --action --run-id <id> --node <state> --kind <kind> " + "--status <done|failed> --ok <true|false> --duration-ms <n> [--spur-bin <path>]");
32
- console.error(" bun plugins/sp/scripts/inline-run-setup.ts --close --run-id <id> --status <done|failed|paused> [--reason <terminal-reason>] [--spur-bin <path>]");
33
- console.error(" bun plugins/sp/scripts/inline-run-setup.ts --persist-out --from <worktree-path> [--spur-bin <path>]");
34
- console.error(" terminal-reason is a closed enum (0937 R2): done, paused-operator, failed-check, failed-agent, " + "failed-timeout, failed-guard, cancelled, interrupted, retry-exhausted");
35
- console.error(" bun plugins/sp/scripts/inline-run-setup.ts --decide --run-id <id> --node <state> --options-json <file> [--spur-bin <path>]");
18
+ console.error([
19
+ "Usage: bun plugins/sp/scripts/inline-run-setup.ts --run-id <id> --file <definition> [--spur-bin <path>]",
20
+ " bun plugins/sp/scripts/inline-run-setup.ts --fingerprint --task-file <path> [--feature-file <path>] [--spur-bin <path>]",
21
+ " bun plugins/sp/scripts/inline-run-setup.ts --action --run-id <id> --node <state> --kind <kind> --status <done|failed> --ok <true|false> --duration-ms <n> [--spur-bin <path>]",
22
+ " bun plugins/sp/scripts/inline-run-setup.ts --actions-file <json-file> --run-id <id> [--spur-bin <path>] (1007 R5 batch trace emission)",
23
+ " bun plugins/sp/scripts/inline-run-setup.ts --close --run-id <id> --status <done|failed|paused> [--reason <terminal-reason>] [--spur-bin <path>]",
24
+ " bun plugins/sp/scripts/inline-run-setup.ts --persist-out --from <worktree-path> [--task-file <path>]... [--spur-bin <path>]",
25
+ " terminal-reason is a closed enum (0937 R2): done, paused-operator, failed-check, failed-agent, failed-timeout, failed-guard, cancelled, interrupted, retry-exhausted",
26
+ " bun plugins/sp/scripts/inline-run-setup.ts --decide --run-id <id> --node <state> --options-json <file> [--spur-bin <path>]"
27
+ ].join(`
28
+ `));
36
29
  process.exit(2);
37
30
  }
38
31
  var SAFE_RUN_ID_RE = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
39
32
  function refuseUnsafeRunId(runId) {
40
33
  console.error(`inline-run-setup: refusing unsafe run id: ${runId}`);
41
- console.error(" The run id must be a single safe filename component (alphanumeric/._-, no leading dot, " + "no path separators, interpolation or traversal; same class as the task-pipeline " + "route-reason guard, task 0804 R8). Allocate a fresh run id (uuid or timestamp slug) and retry.");
34
+ console.error(" The run id must be a single safe filename component (alphanumeric/._-, no leading dot, no path separators, interpolation or traversal; same class as the task-pipeline route-reason guard, task 0804 R8). Allocate a fresh run id (uuid or timestamp slug) and retry.");
42
35
  process.exit(1);
43
36
  }
44
37
  function resolveAppEntry(spurBin) {
45
- let candidates = [];
46
- if (spurBin !== "") {
47
- candidates = [spurBin];
48
- } else {
49
- candidates = [fileURLToPath(new URL("../../../apps/cli/src/index.ts", import.meta.url))];
50
- }
38
+ const fallback = fileURLToPath(new URL("../../../apps/cli/src/index.ts", import.meta.url));
39
+ const candidates = spurBin !== "" ? [spurBin] : [fallback];
51
40
  for (const candidate of candidates) {
52
41
  const tokens = candidate.split(/\s+/).filter(Boolean);
53
42
  const mainModule = [...tokens].reverse().find((t) => t.endsWith(".ts"));
54
43
  if (mainModule === undefined || !existsSync(mainModule))
55
44
  continue;
56
- const srcDir = dirname(mainModule);
57
- const repoRoot = resolve(srcDir, "..", "..", "..");
45
+ const repoRoot = resolve(dirname(mainModule), "..", "..", "..");
58
46
  const appEntry = join(repoRoot, "packages", "app", "src", "index.ts");
59
47
  if (existsSync(appEntry))
60
48
  return { entry: appEntry, portable: false };
@@ -77,83 +65,10 @@ async function readInstalledInventory(file, spurBin) {
77
65
  throw new Error(`could not resolve the workflow definition with the installed CLI: ${result.error?.message ?? result.stderr}`);
78
66
  }
79
67
  const value = JSON.parse(result.stdout);
80
- if (value && typeof value === "object" && "ok" in value && "data" in value && value.ok === true) {
68
+ if (value && typeof value === "object" && "ok" in value && "data" in value && value.ok === true)
81
69
  return value.data;
82
- }
83
70
  return value;
84
71
  }
85
- function writeOutcome(runId, outcome) {
86
- const runDir = join(process.cwd(), ".spur", "run");
87
- if (!existsSync(runDir))
88
- mkdirSync(runDir, { recursive: true });
89
- const statePath = join(runDir, `${runId}.state.json`);
90
- const markdownPath = join(runDir, `${runId}.md`);
91
- let prior = {};
92
- try {
93
- const parsed = JSON.parse(readFileSync(statePath, "utf8"));
94
- if (parsed !== null && typeof parsed === "object" && !Array.isArray(parsed)) {
95
- prior = parsed;
96
- }
97
- } catch {}
98
- const at = new Date().toISOString();
99
- const state = {
100
- schemaVersion: 1,
101
- runId,
102
- ...outcome.workflowName !== undefined ? { workflowName: outcome.workflowName } : {},
103
- ...outcome.status !== undefined ? { status: outcome.status } : {},
104
- startedAt: typeof prior.startedAt === "string" ? prior.startedAt : at,
105
- updatedAt: at,
106
- ...outcome.attached !== undefined ? { attached: outcome.attached } : {},
107
- ...outcome.definitionDigest !== undefined ? { definitionDigest: outcome.definitionDigest } : {},
108
- ...outcome.workflowVersion !== undefined ? { workflowVersion: outcome.workflowVersion } : {},
109
- ...outcome.resolvedPath !== undefined ? { resolvedPath: outcome.resolvedPath } : {},
110
- ...outcome.layer !== undefined ? { layer: outcome.layer } : {},
111
- ...outcome.workdir !== undefined ? { workdir: outcome.workdir } : {},
112
- ok: outcome.ok,
113
- ...outcome.ok === false && outcome.error !== undefined ? { error: outcome.error } : {}
114
- };
115
- const temp = `${statePath}.tmp`;
116
- try {
117
- writeFileSync(temp, `${JSON.stringify(state, null, 4)}
118
- `);
119
- renameSync(temp, statePath);
120
- } catch {
121
- try {
122
- unlinkSync(temp);
123
- } catch {}
124
- }
125
- let headerFd;
126
- try {
127
- headerFd = openSync(markdownPath, "wx");
128
- writeSync(headerFd, `# spur inline run ${runId} \u2014 ${outcome.workflowName ?? "unknown workflow"} \u2014 setup ${at}
129
- `);
130
- } catch {} finally {
131
- if (headerFd !== undefined)
132
- closeSync(headerFd);
133
- }
134
- }
135
- async function printFingerprint(taskFile, featureFile, spurBin) {
136
- const { entry } = resolveAppEntry(spurBin);
137
- const app = await import(entry);
138
- const workdir = process.cwd();
139
- const inputs = await app.readProofInputContents(undefined, workdir, {
140
- taskFile,
141
- ...featureFile.trim() !== "" ? { featureFile } : {}
142
- });
143
- if (!inputs.ok) {
144
- console.error(`inline-run-setup: FAIL \u2014 ${inputs.error}`);
145
- return 1;
146
- }
147
- const digest = await app.computeProofInputFingerprint({
148
- cwd: workdir,
149
- ...inputs.taskContent !== undefined ? { taskContent: inputs.taskContent } : {},
150
- ...inputs.featureContent !== undefined ? { featureContent: inputs.featureContent } : {}
151
- });
152
- process.stdout.write(`${digest}
153
- `);
154
- return 0;
155
- }
156
- var CLOSE_STATUSES = new Set(["done", "failed", "paused"]);
157
72
  var TERMINAL_REASONS = new Set([
158
73
  "done",
159
74
  "paused-operator",
@@ -165,128 +80,6 @@ var TERMINAL_REASONS = new Set([
165
80
  "interrupted",
166
81
  "retry-exhausted"
167
82
  ]);
168
- var ACTION_STATUSES = new Set(["done", "failed"]);
169
- function runRecordLogPath(runDir, runId) {
170
- const markdownPath = join(runDir, `${runId}.md`);
171
- const legacyLogPath = join(runDir, `${runId}.log`);
172
- if (existsSync(legacyLogPath) && !existsSync(markdownPath))
173
- return legacyLogPath;
174
- return markdownPath;
175
- }
176
- function appendRunLogLine(runId, detail) {
177
- try {
178
- const runDir = join(process.cwd(), ".spur", "run");
179
- if (!existsSync(runDir))
180
- mkdirSync(runDir, { recursive: true });
181
- const safeRunId = runId.replace(/[^A-Za-z0-9._-]/g, "_");
182
- const stamp = new Date().toISOString().replace(/\.\d{3}Z$/, "Z");
183
- appendFileSync(runRecordLogPath(runDir, safeRunId), `[${stamp}] ${detail}
184
- `);
185
- } catch {}
186
- }
187
- async function runTraceMode(input) {
188
- const operation = input.close ? "run.close" : "action.finish";
189
- const fail = (error) => {
190
- appendRunLogLine(input.runId, `trace-emission-failed operation=${operation} run=${input.runId}${input.node === "" ? "" : ` node=${input.node}`}${input.kind === "" ? "" : ` kind=${input.kind}`}: ${error}`);
191
- process.stdout.write(`${JSON.stringify({ ok: false, runId: input.runId, error })}
192
- `);
193
- return input.close ? 1 : 0;
194
- };
195
- let projectDb;
196
- try {
197
- const { entry } = resolveAppEntry(input.spurBin);
198
- const app = await import(entry);
199
- projectDb = await app.openInlineRunProjectDb(process.cwd());
200
- const writer = app.createWorkflowActionTraceWriter(projectDb.adapter, (failure) => {
201
- const detail = failure;
202
- appendRunLogLine(input.runId, `trace-emission-failed operation=${detail.operation ?? operation} run=${input.runId}: ${detail.error ?? "unknown error"}`);
203
- });
204
- const result = input.close ? await writer.closeRun(input.runId, input.status, undefined, input.reason) : await writer.recordAction({
205
- runId: input.runId,
206
- node: input.node,
207
- kind: input.kind,
208
- status: input.status,
209
- ok: input.ok,
210
- durationMs: input.durationMs
211
- });
212
- if (result.ok !== true && result.failure !== undefined) {
213
- const failure = result.failure;
214
- return fail(failure.error ?? "unknown trace emission failure");
215
- }
216
- if (input.close && input.status === "done" && result.actionRows === 0) {
217
- const error = `run ${input.runId} closed done with zero action_runs rows`;
218
- appendRunLogLine(input.runId, `trace-close-failed run=${input.runId}: ${error}`);
219
- process.stdout.write(`${JSON.stringify({ ok: false, runId: input.runId, error, code: "NO_ACTION_ROWS", actionRows: 0 })}
220
- `);
221
- return 1;
222
- }
223
- process.stdout.write(`${JSON.stringify({ ...result, runId: input.runId })}
224
- `);
225
- return 0;
226
- } catch (error) {
227
- if (input.close && error.name === "RunRowNotFoundError") {
228
- const message = error instanceof Error ? error.message : String(error);
229
- appendRunLogLine(input.runId, `trace-close-failed run=${input.runId}: ${message}`);
230
- process.stdout.write(`${JSON.stringify({ ok: false, runId: input.runId, error: message, code: "RUN_NOT_FOUND" })}
231
- `);
232
- return 1;
233
- }
234
- return fail(error instanceof Error ? error.message : String(error));
235
- } finally {
236
- projectDb?.close();
237
- }
238
- }
239
- async function runDecideMode(input) {
240
- const decideFailed = (error) => {
241
- process.stdout.write(`${JSON.stringify({ ok: false, runId: input.runId, error })}
242
- `);
243
- return 1;
244
- };
245
- let outcome;
246
- try {
247
- const { entry, portable } = resolveAppEntry(input.spurBin);
248
- const app = await import(entry);
249
- const lib = await import(fileURLToPath(new URL("../lib/inline-run.generated.mjs", import.meta.url)));
250
- const enabled = await lib.resolveDecideDecisionMakerEnabled(process.cwd(), portable ? { embeddedSchemas: lib.EMBEDDED_SPUR_SCHEMAS } : undefined);
251
- outcome = await app.runDecideForInlineRun({
252
- workdir: process.cwd(),
253
- optionsFile: input.optionsFile,
254
- enabled
255
- });
256
- } catch (error) {
257
- return decideFailed(error instanceof Error ? error.message : String(error));
258
- }
259
- if (!outcome.ok)
260
- return decideFailed(outcome.error ?? "decide failed without an error message");
261
- process.stdout.write(`${JSON.stringify({ ok: true, runId: input.runId, node: input.node, ...outcome })}
262
- `);
263
- appendRunLogLine(input.runId, `decide node=${input.node} value=${outcome.value ?? ""} source=${outcome.source ?? "default"} reason=${outcome.reason ?? ""}`);
264
- return await runTraceMode({
265
- runId: input.runId,
266
- close: false,
267
- node: input.node,
268
- kind: "decide",
269
- status: "done",
270
- ok: true,
271
- durationMs: outcome.durationMs ?? 0,
272
- spurBin: input.spurBin
273
- });
274
- }
275
- async function runPersistOutMode(input) {
276
- try {
277
- const { entry } = resolveAppEntry(input.spurBin);
278
- const app = await import(entry);
279
- const result = await app.persistWorktreeRuns({ fromWorkdir: input.from, toWorkdir: process.cwd() });
280
- process.stdout.write(`${JSON.stringify({ ok: true, persisted: result.persisted, skipped: result.skipped })}
281
- `);
282
- return 0;
283
- } catch (error) {
284
- const message = error instanceof Error ? error.message : String(error);
285
- process.stdout.write(`${JSON.stringify({ ok: false, error: message })}
286
- `);
287
- return 1;
288
- }
289
- }
290
83
  async function main() {
291
84
  if (!process.versions.bun) {
292
85
  const child = spawnSync("bun", [fileURLToPath(import.meta.url), ...process.argv.slice(2)], {
@@ -296,83 +89,88 @@ async function main() {
296
89
  console.error(`inline-run-setup requires Bun on PATH: ${child.error.message}`);
297
90
  process.exit(child.status ?? 1);
298
91
  }
299
- let runId = "";
300
- let file = "";
301
- let fingerprint = false;
302
- let taskFile = "";
303
- let featureFile = "";
304
- let action = false;
305
- let close = false;
306
- let decide = false;
307
- let persistOut = false;
308
- let from = "";
309
- let optionsJson = "";
310
- let node = "";
311
- let kind = "";
312
- let status = "";
313
- let reason = "";
314
- let okRaw = "";
315
- let durationRaw = "";
92
+ const flags = new Map;
93
+ let fingerprint = false, action = false, close = false, decide = false, persistOut = false;
94
+ const taskFiles = [];
316
95
  let spurBin = getEnvVar("SPUR_BIN") ?? "";
317
96
  const argv = process.argv.slice(2);
318
97
  for (let i = 0;i < argv.length; i++) {
319
- if (argv[i] === "--run-id")
320
- runId = argv[++i] ?? "";
321
- else if (argv[i] === "--file")
322
- file = argv[++i] ?? "";
323
- else if (argv[i] === "--fingerprint")
98
+ const flag = argv[i];
99
+ if (flag === "--fingerprint")
324
100
  fingerprint = true;
325
- else if (argv[i] === "--task-file")
326
- taskFile = argv[++i] ?? "";
327
- else if (argv[i] === "--feature-file")
328
- featureFile = argv[++i] ?? "";
329
- else if (argv[i] === "--action")
101
+ else if (flag === "--action")
330
102
  action = true;
331
- else if (argv[i] === "--close")
103
+ else if (flag === "--close")
332
104
  close = true;
333
- else if (argv[i] === "--decide")
105
+ else if (flag === "--decide")
334
106
  decide = true;
335
- else if (argv[i] === "--persist-out")
107
+ else if (flag === "--persist-out")
336
108
  persistOut = true;
337
- else if (argv[i] === "--from")
338
- from = argv[++i] ?? "";
339
- else if (argv[i] === "--options-json")
340
- optionsJson = argv[++i] ?? "";
341
- else if (argv[i] === "--node")
342
- node = argv[++i] ?? "";
343
- else if (argv[i] === "--kind")
344
- kind = argv[++i] ?? "";
345
- else if (argv[i] === "--status")
346
- status = argv[++i] ?? "";
347
- else if (argv[i] === "--reason")
348
- reason = argv[++i] ?? "";
349
- else if (argv[i] === "--ok")
350
- okRaw = argv[++i] ?? "";
351
- else if (argv[i] === "--duration-ms")
352
- durationRaw = argv[++i] ?? "";
353
- else if (argv[i] === "--spur-bin")
109
+ else if (flag === "--task-file")
110
+ taskFiles.push(argv[++i] ?? "");
111
+ else if (flag === "--spur-bin")
354
112
  spurBin = argv[++i] ?? spurBin;
355
- }
113
+ else if (flag !== undefined)
114
+ flags.set(flag, argv[++i] ?? "");
115
+ }
116
+ const runId = flags.get("--run-id") ?? "";
117
+ const file = flags.get("--file") ?? "";
118
+ const featureFile = flags.get("--feature-file") ?? "";
119
+ const from = flags.get("--from") ?? "";
120
+ const optionsJson = flags.get("--options-json") ?? "";
121
+ const node = flags.get("--node") ?? "";
122
+ const kind = flags.get("--kind") ?? "";
123
+ const status = flags.get("--status") ?? "";
124
+ const reason = flags.get("--reason") ?? "";
125
+ const okRaw = flags.get("--ok") ?? "";
126
+ const durationRaw = flags.get("--duration-ms") ?? "";
127
+ const actionsFile = flags.get("--actions-file") ?? "";
356
128
  if (fingerprint) {
357
- if (runId !== "" || file !== "" || taskFile.trim() === "")
129
+ if (runId !== "" || file !== "" || taskFiles.length !== 1 || (taskFiles[0] ?? "").trim() === "")
130
+ usage();
131
+ const app2 = await import(resolveAppEntry(spurBin).entry);
132
+ process.exit(await app2.runInlineRunFingerprint({ taskFile: taskFiles[0] ?? "", featureFile }));
133
+ }
134
+ if (actionsFile !== "") {
135
+ if (action || close || fingerprint || decide || persistOut || file !== "" || taskFiles.length > 0)
136
+ usage();
137
+ if (runId.trim() === "" || status !== "" || node !== "" || kind !== "")
358
138
  usage();
359
- process.exit(await printFingerprint(taskFile, featureFile, spurBin));
139
+ if (okRaw !== "" || durationRaw !== "")
140
+ usage();
141
+ if (!SAFE_RUN_ID_RE.test(runId))
142
+ refuseUnsafeRunId(runId);
143
+ const app2 = await import(resolveAppEntry(spurBin).entry);
144
+ process.exit(await app2.runInlineRunTraceBatch({ runId, actionsFile }));
360
145
  }
361
146
  if (decide) {
362
- if (action || close || fingerprint || file !== "" || taskFile !== "")
147
+ if (action || close || fingerprint || file !== "" || taskFiles.length > 0)
363
148
  usage();
364
149
  if (runId.trim() === "" || node.trim() === "" || optionsJson.trim() === "")
365
150
  usage();
366
151
  if (!SAFE_RUN_ID_RE.test(runId))
367
152
  refuseUnsafeRunId(runId);
368
- process.exit(await runDecideMode({ runId, node, optionsFile: optionsJson, spurBin }));
153
+ try {
154
+ const { entry: entry2, portable: portable2 } = resolveAppEntry(spurBin);
155
+ const app2 = await import(entry2);
156
+ const bundlePath = fileURLToPath(new URL("../lib/inline-run.generated.mjs", import.meta.url));
157
+ const lib = await import(bundlePath);
158
+ const enabled = await lib.resolveDecideDecisionMakerEnabled(process.cwd(), portable2 ? { embeddedSchemas: lib.EMBEDDED_SPUR_SCHEMAS } : undefined);
159
+ process.exit(await app2.runInlineRunDecide({ runId, node, optionsFile: optionsJson, enabled }));
160
+ } catch (error) {
161
+ const message = error instanceof Error ? error.message : String(error);
162
+ process.stdout.write(`${JSON.stringify({ ok: false, runId, error: message })}
163
+ `);
164
+ process.exit(1);
165
+ }
369
166
  }
370
167
  if (persistOut) {
371
- if (fingerprint || decide || action || close || runId !== "" || file !== "" || taskFile !== "")
168
+ if (fingerprint || decide || action || close || runId !== "" || file !== "")
372
169
  usage();
373
- if (from.trim() === "")
170
+ if (from.trim() === "" || taskFiles.some((taskFile) => taskFile.trim() === ""))
374
171
  usage();
375
- process.exit(await runPersistOutMode({ from, spurBin }));
172
+ const app2 = await import(resolveAppEntry(spurBin).entry);
173
+ process.exit(await app2.runInlineRunPersistOut({ from, taskFiles }));
376
174
  }
377
175
  if (action || close) {
378
176
  if (action && close)
@@ -381,16 +179,13 @@ async function main() {
381
179
  usage();
382
180
  if (!SAFE_RUN_ID_RE.test(runId))
383
181
  refuseUnsafeRunId(runId);
182
+ const app2 = await import(resolveAppEntry(spurBin).entry);
384
183
  if (close) {
385
- if (!CLOSE_STATUSES.has(status))
184
+ if (!app2.isInlineRunCloseStatus(status))
386
185
  usage();
387
- if (reason.trim() === "") {
388
- if (status === "failed")
389
- usage();
390
- } else if (!TERMINAL_REASONS.has(reason)) {
186
+ if (reason.trim() === "" ? status === "failed" : !TERMINAL_REASONS.has(reason))
391
187
  usage();
392
- }
393
- process.exit(await runTraceMode({
188
+ process.exit(await app2.runInlineRunTrace({
394
189
  runId,
395
190
  close: true,
396
191
  node: "",
@@ -398,21 +193,19 @@ async function main() {
398
193
  status,
399
194
  ok: true,
400
195
  durationMs: 0,
401
- spurBin,
402
196
  ...reason.trim() === "" ? {} : { reason }
403
197
  }));
404
198
  }
405
199
  if (node.trim() === "" || kind.trim() === "")
406
200
  usage();
407
- if (!ACTION_STATUSES.has(status))
201
+ if (!app2.isInlineRunActionStatus(status))
408
202
  usage();
409
203
  if (okRaw !== "true" && okRaw !== "false")
410
204
  usage();
411
- const ok = okRaw === "true";
412
205
  const durationMs = Number(durationRaw);
413
206
  if (durationRaw.trim() === "" || !Number.isFinite(durationMs) || durationMs < 0)
414
207
  usage();
415
- process.exit(await runTraceMode({ runId, close: false, node, kind, status, ok, durationMs, spurBin }));
208
+ process.exit(await app2.runInlineRunTrace({ runId, close: false, node, kind, status, ok: okRaw === "true", durationMs }));
416
209
  }
417
210
  if (runId.trim() === "" || file.trim() === "")
418
211
  usage();
@@ -420,38 +213,20 @@ async function main() {
420
213
  refuseUnsafeRunId(runId);
421
214
  const { entry, portable } = resolveAppEntry(spurBin);
422
215
  const app = await import(entry);
423
- const workdir = process.cwd();
424
216
  let inventory;
425
217
  try {
426
218
  inventory = await readInstalledInventory(file, spurBin);
427
219
  } catch (error) {
428
220
  const message = `could not resolve the workflow definition: ${error instanceof Error ? error.message : String(error)}`;
429
- writeOutcome(runId, { ok: false, runId, error: message });
221
+ app.writeInlineRunOutcome(runId, { ok: false, runId, error: message });
430
222
  throw new Error(message);
431
223
  }
432
- const projectDb = await app.openInlineRunProjectDb(workdir);
433
- let exitCode = 0;
434
- try {
435
- const result = await app.createOrAttachInlineRun({
436
- workdir,
437
- getDb: async () => projectDb.adapter,
438
- file,
439
- runId,
440
- inventory,
441
- ...portable ? { embeddedSchemas: app.EMBEDDED_SPUR_SCHEMAS } : {}
442
- });
443
- writeOutcome(runId, result);
444
- if (!result.ok) {
445
- console.error(`inline-run-setup: FAIL for run ${runId}`);
446
- console.error(` ${result.error}`);
447
- exitCode = 1;
448
- } else {
449
- console.error(`inline-run-setup: ${result.attached ? "attached" : "created"} run ${runId} (${result.workflowName}, layer ${result.layer}, digest ${result.definitionDigest}, status ${result.status})`);
450
- }
451
- } finally {
452
- projectDb.close();
453
- }
454
- process.exit(exitCode);
224
+ process.exit(await app.runInlineRunSetup({
225
+ runId,
226
+ file,
227
+ inventory,
228
+ ...portable ? { embeddedSchemas: app.EMBEDDED_SPUR_SCHEMAS } : {}
229
+ }));
455
230
  }
456
231
  main().catch((e) => {
457
232
  console.error(`inline-run-setup: FAIL \u2014 ${e instanceof Error ? e.message : String(e)}`);