@gobing-ai/spur 0.4.8 → 0.4.10

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 (211) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/config/pipeline-budgets.json +2 -2
  3. package/config/rules/README.md +1 -1
  4. package/config/rules/boundary/config-loading-ownership.yaml +1 -0
  5. package/config/rules/boundary/env-var-hygiene.yaml +0 -1
  6. package/config/rules/boundary/sp-plugin-standalone.yaml +1 -1
  7. package/config/rules/boundary/sp-script-placement.yaml +3 -3
  8. package/config/rules/docs/sourced-api-claims.yaml +29 -64
  9. package/config/rules/strict/runtime-boundaries.yaml +40 -1
  10. package/config/rules/structure/protected-files.yaml +1 -1
  11. package/config/rules/surface/check-cli-surface.yaml +33 -0
  12. package/config/rules/typescript/no-debugger.yaml +3 -0
  13. package/config/workflow-candidates.json +2 -2
  14. package/config/workflows/feature-verification.yaml +6 -19
  15. package/config/workflows/history-anatomy.yaml +14 -28
  16. package/config/workflows/idea-pipeline.yaml +6 -19
  17. package/config/workflows/pr-review.yaml +9 -16
  18. package/config/workflows/task-pipeline.yaml +18 -25
  19. package/config/workflows/wrapup-pipeline.yaml +21 -63
  20. package/package.json +3 -3
  21. package/plugins/sp/README.md +70 -37
  22. package/plugins/sp/agents/expert-spur.md +0 -1
  23. package/plugins/sp/agents/super-planner.md +5 -5
  24. package/plugins/sp/agents/super-reviewer.md +2 -2
  25. package/plugins/sp/commands/dev-changelog.md +1 -1
  26. package/plugins/sp/commands/dev-daily.md +1 -1
  27. package/plugins/sp/commands/dev-pr-review.md +1 -1
  28. package/plugins/sp/commands/dev-review-session.md +1 -1
  29. package/plugins/sp/commands/dev-runall.md +4 -4
  30. package/plugins/sp/commands/dev-verify.md +4 -6
  31. package/plugins/sp/commands/dev-verifyall.md +4 -5
  32. package/plugins/sp/hooks/hooks.json +11 -1
  33. package/plugins/sp/lib/context-post-tool.ts +1 -1
  34. package/plugins/sp/lib/context-session-start.ts +1 -1
  35. package/plugins/sp/lib/context-session-stop.ts +1 -1
  36. package/plugins/sp/lib/destructive-policy.ts +65 -1
  37. package/plugins/sp/plugin.json +2 -5
  38. package/plugins/sp/{hooks → scripts/hooks}/agent-lifecycle.ts +1 -1
  39. package/plugins/sp/scripts/hooks/careful-guard.ts +94 -0
  40. package/plugins/sp/{hooks → scripts/hooks}/context-post-tool.ts +2 -2
  41. package/plugins/sp/{hooks → scripts/hooks}/context-session-start.ts +1 -1
  42. package/plugins/sp/{hooks → scripts/hooks}/context-session-stop.ts +1 -1
  43. package/plugins/sp/{hooks → scripts/hooks}/fleet-guest-stop.ts +6 -4
  44. package/plugins/sp/{hooks → scripts/hooks}/task-write-guard.ts +38 -21
  45. package/plugins/sp/skills/brainstorm/SKILL.md +12 -11
  46. package/plugins/sp/skills/brainstorm/agents/openai.yaml +1 -1
  47. package/plugins/sp/skills/brainstorm/examples/ideation-example.md +0 -4
  48. package/plugins/sp/skills/brainstorm/metadata.openclaw +1 -1
  49. package/plugins/sp/skills/brainstorm/references/workflows.md +4 -14
  50. package/plugins/sp/skills/code-implementation/SKILL.md +1 -1
  51. package/plugins/sp/skills/code-verification/SKILL.md +8 -8
  52. package/plugins/sp/skills/code-verification/references/secu-review.md +4 -4
  53. package/plugins/sp/skills/code-verification/references/verdict-schema.md +9 -1
  54. package/plugins/sp/skills/daily-summary/SKILL.md +4 -5
  55. package/plugins/sp/skills/dogfood-testing/SKILL.md +9 -9
  56. package/plugins/sp/skills/dogfood-testing/references/report-template.md +1 -1
  57. package/plugins/sp/skills/history-anatomy/references/modes.md +2 -2
  58. package/plugins/sp/skills/history-anatomy/references/operations.md +3 -4
  59. package/plugins/sp/skills/history-anatomy/references/report-contract.md +2 -3
  60. package/plugins/sp/skills/issue-finding/SKILL.md +1 -1
  61. package/plugins/sp/skills/next-router/references/routing-table.md +1 -1
  62. package/plugins/sp/skills/parallel-execution/SKILL.md +2 -2
  63. package/plugins/sp/skills/parallel-execution/references/result-synthesis.md +9 -10
  64. package/plugins/sp/skills/pr-reviewing/SKILL.md +14 -15
  65. package/plugins/sp/skills/reverse-engineering/SKILL.md +4 -6
  66. package/plugins/sp/skills/session-review/SKILL.md +13 -29
  67. package/plugins/sp/skills/source-driven-development/SKILL.md +26 -27
  68. package/plugins/sp/skills/spur-check/SKILL.md +14 -13
  69. package/plugins/sp/skills/spur-cli/SKILL.md +1 -1
  70. package/plugins/sp/skills/spur-cli/references/agent.md +17 -2
  71. package/plugins/sp/skills/spur-cli/references/builder.md +27 -6
  72. package/plugins/sp/skills/spur-cli/references/features.md +6 -1
  73. package/plugins/sp/skills/spur-cli/references/history.md +3 -0
  74. package/plugins/sp/skills/spur-cli/references/self.md +5 -3
  75. package/plugins/sp/skills/spur-cli/references/tasks/verbs.md +3 -0
  76. package/plugins/sp/skills/spur-cli/references/tasks.md +13 -3
  77. package/plugins/sp/skills/spur-cli/references/workflows/authoring-workflows.md +1 -1
  78. package/plugins/sp/skills/spur-cli/references/workflows.md +36 -1
  79. package/plugins/sp/skills/spur-dev/SKILL.md +1 -1
  80. package/plugins/sp/skills/spur-dev/references/cross-cutting.md +1 -1
  81. package/plugins/sp/skills/spur-dev/references/dev-operations.md +12 -13
  82. package/plugins/sp/skills/spur-dev/references/execution-batch-report.md +10 -11
  83. package/plugins/sp/skills/spur-dev/references/execution-batch.md +16 -17
  84. package/plugins/sp/skills/spur-dev/references/execution-parallel-isolation.md +1 -1
  85. package/plugins/sp/skills/spur-dev/references/execution-worktree-landing.md +9 -12
  86. package/plugins/sp/skills/spur-dev/references/execution-worktree-setup.md +6 -8
  87. package/plugins/sp/skills/spur-dev/references/gate-checklists.md +1 -1
  88. package/plugins/sp/skills/spur-dev/references/inline-pipeline-driver.md +33 -40
  89. package/plugins/sp/skills/spur-dev/references/structured-trace-emission.md +24 -24
  90. package/plugins/sp/skills/spur-doctor/SKILL.md +5 -4
  91. package/spur.js +12726 -1456
  92. package/web/_astro/{BoardApp.CaNvQBD_.js → BoardApp.B5rTVNd_.js} +1 -1
  93. package/web/_astro/{BoardApp.C5jnkkKP.js → BoardApp.BMprmPRc.js} +141 -141
  94. package/web/_astro/{TaskDetail.DGKndhI8.js → TaskDetail.Ddsur8qd.js} +1 -1
  95. package/web/_astro/{arc.DHrwdg3G.js → arc.DLqVVBxP.js} +1 -1
  96. package/web/_astro/{architectureDiagram-3BPJPVTR.pvKpK1s5.js → architectureDiagram-3BPJPVTR.C-Pk3uAi.js} +1 -1
  97. package/web/_astro/{blockDiagram-GPEHLZMM.BQmRcZsS.js → blockDiagram-GPEHLZMM.BRO1i_Nm.js} +1 -1
  98. package/web/_astro/{c4Diagram-AAUBKEIU.CCLjHeH1.js → c4Diagram-AAUBKEIU.BU_cWu6e.js} +1 -1
  99. package/web/_astro/channel.Bs3Illhy.js +1 -0
  100. package/web/_astro/{chunk-2J33WTMH.DL6Yuoma.js → chunk-2J33WTMH.D2VXiQ2t.js} +1 -1
  101. package/web/_astro/{chunk-4BX2VUAB.CzlyEI7l.js → chunk-4BX2VUAB.BcHT772J.js} +1 -1
  102. package/web/_astro/{chunk-55IACEB6.B60EWxp1.js → chunk-55IACEB6.DhDtICmo.js} +1 -1
  103. package/web/_astro/{chunk-727SXJPM.c7v_fRLo.js → chunk-727SXJPM.BoKHp7gV.js} +1 -1
  104. package/web/_astro/{chunk-AQP2D5EJ.8BxLWdDc.js → chunk-AQP2D5EJ.Chu4JRpE.js} +1 -1
  105. package/web/_astro/{chunk-FMBD7UC4.DmKuxGFT.js → chunk-FMBD7UC4.wHdwKPlq.js} +1 -1
  106. package/web/_astro/{chunk-ND2GUHAM.1Zwd4mqV.js → chunk-ND2GUHAM.ffN-M1JB.js} +1 -1
  107. package/web/_astro/{chunk-QZHKN3VN.BvYLu-A1.js → chunk-QZHKN3VN.FRubwg4S.js} +1 -1
  108. package/web/_astro/{classDiagram-4FO5ZUOK.CEqRt0WU.js → classDiagram-4FO5ZUOK.c22tQsc_.js} +1 -1
  109. package/web/_astro/{classDiagram-v2-Q7XG4LA2.CEqRt0WU.js → classDiagram-v2-Q7XG4LA2.c22tQsc_.js} +1 -1
  110. package/web/_astro/{cose-bilkent-S5V4N54A.C7JiUdj2.js → cose-bilkent-S5V4N54A.B9c6fiSj.js} +1 -1
  111. package/web/_astro/{cynefin-OW5HDTMX.B6mZ-1be.js → cynefin-OW5HDTMX.DrZsrUHl.js} +1 -1
  112. package/web/_astro/{dagre-BM42HDAG.BuryfDy6.js → dagre-BM42HDAG.DG1U4b3y.js} +1 -1
  113. package/web/_astro/{diagram-2AECGRRQ.dao39YQM.js → diagram-2AECGRRQ.CH15b-K3.js} +1 -1
  114. package/web/_astro/{diagram-5GNKFQAL.lnGZe4At.js → diagram-5GNKFQAL.BH1j7twR.js} +1 -1
  115. package/web/_astro/{diagram-KO2AKTUF.uWfhzEfT.js → diagram-KO2AKTUF.q2zCkbAj.js} +1 -1
  116. package/web/_astro/{diagram-LMA3HP47.ppq8-198.js → diagram-LMA3HP47.BLaaZnHV.js} +1 -1
  117. package/web/_astro/{diagram-OG6HWLK6.CKSCqDt5.js → diagram-OG6HWLK6.BcpRdQwI.js} +1 -1
  118. package/web/_astro/{erDiagram-TEJ5UH35.sFSf2yBN.js → erDiagram-TEJ5UH35.D7KBlqfM.js} +1 -1
  119. package/web/_astro/{flowDiagram-I6XJVG4X.BJQjKLbz.js → flowDiagram-I6XJVG4X.D_gmuRqg.js} +1 -1
  120. package/web/_astro/{ganttDiagram-6RSMTGT7.DAHKNj2N.js → ganttDiagram-6RSMTGT7.DzgWYniD.js} +1 -1
  121. package/web/_astro/{gitGraphDiagram-PVQCEYII.BlmzcW_O.js → gitGraphDiagram-PVQCEYII.576i3hm3.js} +1 -1
  122. package/web/_astro/index.DiC1a6lh.css +1 -0
  123. package/web/_astro/{infoDiagram-5YYISTIA.DGhjaqsi.js → infoDiagram-5YYISTIA.DMCclBiz.js} +1 -1
  124. package/web/_astro/{ishikawaDiagram-YF4QCWOH.gKIpgt6F.js → ishikawaDiagram-YF4QCWOH.9Kyca4SH.js} +1 -1
  125. package/web/_astro/{journeyDiagram-JHISSGLW.DG3tmP0R.js → journeyDiagram-JHISSGLW.chbdG-8q.js} +1 -1
  126. package/web/_astro/{kanban-definition-UN3LZRKU.CgHDSxkR.js → kanban-definition-UN3LZRKU.Bds54-e0.js} +1 -1
  127. package/web/_astro/{linear.BuGEpcuM.js → linear.D5ND9GN_.js} +1 -1
  128. package/web/_astro/{mermaid.core.DBVqkWBp.js → mermaid.core.D9N_PGbj.js} +4 -4
  129. package/web/_astro/{mindmap-definition-RKZ34NQL.D-kOcji9.js → mindmap-definition-RKZ34NQL.t0xop5vp.js} +1 -1
  130. package/web/_astro/{pieDiagram-4H26LBE5.BvGPwtZt.js → pieDiagram-4H26LBE5.DqgKh6hM.js} +1 -1
  131. package/web/_astro/{quadrantDiagram-W4KKPZXB.BKb_9FV6.js → quadrantDiagram-W4KKPZXB.DeuHVi2t.js} +1 -1
  132. package/web/_astro/{requirementDiagram-4Y6WPE33.Cx0fvD8v.js → requirementDiagram-4Y6WPE33.BdYFSG4W.js} +1 -1
  133. package/web/_astro/{sankeyDiagram-5OEKKPKP.fatULi0W.js → sankeyDiagram-5OEKKPKP.k1LPPETJ.js} +1 -1
  134. package/web/_astro/{sequenceDiagram-3UESZ5HK.ChHyKqco.js → sequenceDiagram-3UESZ5HK.DdyfpzoG.js} +1 -1
  135. package/web/_astro/{stateDiagram-AJRCARHV.BdUpKWyj.js → stateDiagram-AJRCARHV.DSEI8il0.js} +1 -1
  136. package/web/_astro/{stateDiagram-v2-BHNVJYJU.fmJxqIm5.js → stateDiagram-v2-BHNVJYJU.CoMnQRKo.js} +1 -1
  137. package/web/_astro/{timeline-definition-PNZ67QCA.BayDn5-V.js → timeline-definition-PNZ67QCA.DM1AHNm-.js} +1 -1
  138. package/web/_astro/{vennDiagram-CIIHVFJN.Bm2Aggwa.js → vennDiagram-CIIHVFJN.C-cJmVtl.js} +1 -1
  139. package/web/_astro/{wardleyDiagram-YWT4CUSO.DI75O-uG.js → wardleyDiagram-YWT4CUSO.D0xMzKPb.js} +1 -1
  140. package/web/_astro/{xychartDiagram-2RQKCTM6.DL90E7Q7.js → xychartDiagram-2RQKCTM6.BH5xJcEq.js} +1 -1
  141. package/web/index.html +2 -2
  142. package/config/plugin-scripts.json +0 -116
  143. package/config/script-placement-baseline.json +0 -33
  144. package/plugins/sp/hooks/careful-guard.ts +0 -74
  145. package/plugins/sp/hooks/pi/guard-extension.ts +0 -304
  146. package/plugins/sp/lib/dogfood-pipeline.ts +0 -265
  147. package/plugins/sp/lib/dogfood-report.ts +0 -278
  148. package/plugins/sp/lib/history-anatomy.generated.d.mts +0 -112
  149. package/plugins/sp/lib/history-anatomy.generated.mjs +0 -691
  150. package/plugins/sp/lib/idea-handoff.generated.d.mts +0 -17
  151. package/plugins/sp/lib/idea-handoff.generated.mjs +0 -1446
  152. package/plugins/sp/lib/inline-run-app.ts +0 -68
  153. package/plugins/sp/lib/inline-run.generated.d.mts +0 -37
  154. package/plugins/sp/lib/inline-run.generated.mjs +0 -3103
  155. package/plugins/sp/lib/plan-projection.generated.d.mts +0 -197
  156. package/plugins/sp/lib/plan-projection.generated.mjs +0 -982
  157. package/plugins/sp/lib/quality-gate.generated.d.mts +0 -139
  158. package/plugins/sp/lib/quality-gate.generated.mjs +0 -30510
  159. package/plugins/sp/lib/residual-scan.generated.d.mts +0 -93
  160. package/plugins/sp/lib/residual-scan.generated.mjs +0 -383
  161. package/plugins/sp/lib/run-summary-core.ts +0 -225
  162. package/plugins/sp/lib/spur-bin.ts +0 -36
  163. package/plugins/sp/lib/step-profile.generated.d.mts +0 -71
  164. package/plugins/sp/lib/step-profile.generated.mjs +0 -174
  165. package/plugins/sp/lib/transcript.ts +0 -112
  166. package/plugins/sp/scripts/batch-plan.mjs +0 -1096
  167. package/plugins/sp/scripts/batch-plan.ts +0 -145
  168. package/plugins/sp/scripts/batch-preflight.mjs +0 -423
  169. package/plugins/sp/scripts/batch-preflight.ts +0 -270
  170. package/plugins/sp/scripts/commit-guard.mjs +0 -226
  171. package/plugins/sp/scripts/commit-guard.ts +0 -248
  172. package/plugins/sp/scripts/daily-summary/daily-summary.mjs +0 -626
  173. package/plugins/sp/scripts/daily-summary/daily-summary.ts +0 -855
  174. package/plugins/sp/scripts/dogfood-testing/detect-pipeline-driving.mjs +0 -211
  175. package/plugins/sp/scripts/dogfood-testing/detect-pipeline-driving.ts +0 -110
  176. package/plugins/sp/scripts/dogfood-testing/validate-report.mjs +0 -282
  177. package/plugins/sp/scripts/dogfood-testing/validate-report.ts +0 -98
  178. package/plugins/sp/scripts/feature-verification-steps.mjs +0 -82
  179. package/plugins/sp/scripts/feature-verification-steps.ts +0 -164
  180. package/plugins/sp/scripts/history-anatomy-cache.mjs +0 -931
  181. package/plugins/sp/scripts/history-anatomy-cache.ts +0 -247
  182. package/plugins/sp/scripts/idea-handoff.mjs +0 -32
  183. package/plugins/sp/scripts/idea-handoff.ts +0 -47
  184. package/plugins/sp/scripts/inline-run-dispatch.mjs +0 -131
  185. package/plugins/sp/scripts/inline-run-dispatch.ts +0 -115
  186. package/plugins/sp/scripts/inline-run-setup.mjs +0 -746
  187. package/plugins/sp/scripts/inline-run-setup.ts +0 -198
  188. package/plugins/sp/scripts/persist-out-check.mjs +0 -217
  189. package/plugins/sp/scripts/persist-out-check.ts +0 -249
  190. package/plugins/sp/scripts/pr-reviewing.mjs +0 -712
  191. package/plugins/sp/scripts/pr-reviewing.ts +0 -952
  192. package/plugins/sp/scripts/quality-gate.mjs +0 -30491
  193. package/plugins/sp/scripts/quality-gate.ts +0 -80
  194. package/plugins/sp/scripts/residual-scan.mjs +0 -634
  195. package/plugins/sp/scripts/residual-scan.ts +0 -248
  196. package/plugins/sp/scripts/run-summary.mjs +0 -758
  197. package/plugins/sp/scripts/run-summary.ts +0 -107
  198. package/plugins/sp/scripts/script-root.mjs +0 -129
  199. package/plugins/sp/scripts/script-root.ts +0 -199
  200. package/plugins/sp/scripts/session-timeline.mjs +0 -359
  201. package/plugins/sp/scripts/session-timeline.ts +0 -190
  202. package/plugins/sp/scripts/task-diffstat.mjs +0 -181
  203. package/plugins/sp/scripts/task-diffstat.ts +0 -219
  204. package/plugins/sp/scripts/workflow-step-profile.mjs +0 -338
  205. package/plugins/sp/scripts/workflow-step-profile.ts +0 -165
  206. package/plugins/sp/scripts/wrapup-drift-probe.mjs +0 -190
  207. package/plugins/sp/scripts/wrapup-drift-probe.ts +0 -261
  208. package/plugins/sp/scripts/wrapup-steps.mjs +0 -627
  209. package/plugins/sp/scripts/wrapup-steps.ts +0 -891
  210. package/web/_astro/channel.C7wG9RxA.js +0 -1
  211. package/web/_astro/index.BCY1JJHd.css +0 -1
@@ -1,270 +0,0 @@
1
- /**
2
- * batch-preflight — planner adapter over the pure admission policy (task 0279; task 1176 move).
3
- *
4
- * The decisions — TABLE A STOP evaluation, quick command-aware readiness and feature-finding
5
- * classification — are application policy in `packages/app/src/services/planning-admission.ts`,
6
- * reached through the generated pure bundle. This entry keeps what belongs to the planner: the CLI
7
- * surface, its argv/usage/error contracts, and the one-shot recovery hints the batch driver prints.
8
- *
9
- * Recovery hints (one-shot after FAIL) map stuck statuses to a single /sp:dev-* hop per TABLE A; the
10
- * batch driver prints or dispatches at most once per WBS.
11
- */
12
-
13
- import {
14
- classifyFeaturePreflightFindings,
15
- type FeaturePreflightClassification,
16
- type FeaturePreflightFinding,
17
- NON_ABORTING_PREFLIGHT_CODES,
18
- type PreflightInput,
19
- type PreflightResult,
20
- preflightTask,
21
- type QuickReadinessInput,
22
- type QuickReadinessResult,
23
- quickReadiness,
24
- type ReadinessOperation,
25
- type TaskStatus,
26
- } from '../lib/plan-projection.generated.mjs';
27
-
28
- export type {
29
- FeaturePreflightClassification,
30
- FeaturePreflightFinding,
31
- PreflightInput,
32
- PreflightResult,
33
- QuickReadinessInput,
34
- QuickReadinessResult,
35
- ReadinessOperation,
36
- TaskStatus,
37
- };
38
- // R1: existing plugin callers keep their import path — the moved names are re-exported, not relocated
39
- // out from under them.
40
- export { classifyFeaturePreflightFindings, NON_ABORTING_PREFLIGHT_CODES, preflightTask, quickReadiness };
41
-
42
- /**
43
- * One-shot recovery hint after a non-PASS pipeline or stuck status (TABLE A primary hop).
44
- * Caller substitutes the real WBS; never loop this — at most one hop per WBS per batch.
45
- */
46
- export function recoveryHint(status: TaskStatus, wbs: string): { command: string; code: string } | null {
47
- const st = (status ?? '').toLowerCase();
48
- switch (st) {
49
- case 'backlog':
50
- return { code: 'A1', command: `/sp:dev-refine ${wbs} --auto --next` };
51
- case 'todo':
52
- return { code: 'A3', command: `/sp:dev-run ${wbs} --auto --next` };
53
- case 'wip':
54
- return { code: 'A5', command: `/sp:dev-run ${wbs} --mode implement --auto --next` };
55
- case 'testing':
56
- return { code: 'A6', command: `/sp:dev-verify ${wbs} --auto --next` };
57
- case 'blocked':
58
- return {
59
- code: 'A7',
60
- command: `/sp:dev-handover "blocked task ${wbs} — see Notes/History"`,
61
- };
62
- default:
63
- return null;
64
- }
65
- }
66
-
67
- // ── CLI for agents / dogfood (optional) ──────────────────────────────────────
68
-
69
- export interface PreflightCliArgs {
70
- status: string | null;
71
- deps: string[];
72
- depStatuses: Record<string, string>;
73
- wbs: string;
74
- recovery: boolean;
75
- help: boolean;
76
- json: boolean;
77
- /** Quick-readiness operation (run|refine|verify); when set, run quickReadiness. */
78
- operation: ReadinessOperation | null;
79
- /** Verify-only re-verification (R2 AC). */
80
- force: boolean;
81
- /** Status-filtered candidate set size; 0 = empty set. */
82
- filteredCount: number | null;
83
- /** Required planning sections (matrix-selected). */
84
- requiredSections: string[];
85
- /** Sections actually present in the task. */
86
- presentSections: string[];
87
- /** Feature findings to classify (1132 R4). */
88
- classifyFeature: string | null;
89
- }
90
-
91
- export function parsePreflightCliArgs(argv: string[]): PreflightCliArgs {
92
- let status: string | null = null;
93
- let deps: string[] = [];
94
- const depStatuses: Record<string, string> = {};
95
- let wbs = '0000';
96
- let recovery = false;
97
- let help = false;
98
- let json = false;
99
- let operation: ReadinessOperation | null = null;
100
- let force = false;
101
- let filteredCount: number | null = null;
102
- let requiredSections: string[] = [];
103
- let presentSections: string[] = [];
104
- let classifyFeature: string | null = null;
105
-
106
- for (let i = 0; i < argv.length; i++) {
107
- const a = argv[i];
108
- if (a === '--help' || a === '-h') help = true;
109
- else if (a === '--json') json = true;
110
- else if (a === '--recovery') recovery = true;
111
- else if (a === '--force') force = true;
112
- else if (a === '--operation') {
113
- const v = argv[++i] ?? '';
114
- operation = v === 'run' || v === 'refine' || v === 'verify' ? v : null;
115
- } else if (a === '--filtered-count') {
116
- const v = Number(argv[++i]);
117
- filteredCount = Number.isFinite(v) ? v : null;
118
- } else if (a === '--required-sections') {
119
- const raw = argv[++i] ?? '';
120
- requiredSections =
121
- raw.length === 0
122
- ? []
123
- : raw
124
- .split(',')
125
- .map((s) => s.trim())
126
- .filter(Boolean);
127
- } else if (a === '--present-sections') {
128
- const raw = argv[++i] ?? '';
129
- presentSections =
130
- raw.length === 0
131
- ? []
132
- : raw
133
- .split(',')
134
- .map((s) => s.trim())
135
- .filter(Boolean);
136
- } else if (a === '--wbs') wbs = argv[++i] ?? wbs;
137
- else if (a === '--classify-feature') {
138
- classifyFeature = argv[++i] ?? '[]';
139
- } else if (a === '--status') status = argv[++i] ?? null;
140
- else if (a === '--deps') {
141
- const raw = argv[++i] ?? '';
142
- deps =
143
- raw.length === 0
144
- ? []
145
- : raw
146
- .split(',')
147
- .map((s) => s.trim())
148
- .filter(Boolean);
149
- } else if (a === '--dep-status') {
150
- // format: 0275:done,0276:todo
151
- const raw = argv[++i] ?? '';
152
- for (const part of raw.split(',')) {
153
- const [k, v] = part.split(':');
154
- if (k && v) depStatuses[k.trim()] = v.trim();
155
- }
156
- }
157
- }
158
- return {
159
- status,
160
- deps,
161
- depStatuses,
162
- wbs,
163
- recovery,
164
- help,
165
- json,
166
- operation,
167
- force,
168
- filteredCount,
169
- requiredSections,
170
- presentSections,
171
- classifyFeature,
172
- };
173
- }
174
-
175
- export const PREFLIGHT_CLI_USAGE = `Usage:
176
- bun plugins/sp/scripts/batch-preflight.ts --wbs <wbs> --status <status> \\
177
- [--deps 0275,0276] [--dep-status 0275:done,0276:todo] [--recovery] [--json]
178
- bun plugins/sp/scripts/batch-preflight.ts --operation <run|refine|verify> --wbs <wbs> --status <status> \
179
- [--filtered-count <n>] [--required-sections A,B] [--present-sections A,B] [--force] [--json]
180
-
181
- Exit: 0 = run (or recovery hint printed); 2 = skip; 1 = usage.`;
182
-
183
- export function runPreflightCli(argv: string[]): { exitCode: number; stdout: string; stderr: string } {
184
- const args = parsePreflightCliArgs(argv);
185
- if (args.help) return { exitCode: 0, stdout: '', stderr: PREFLIGHT_CLI_USAGE };
186
-
187
- // Feature findings classification (1132 R4)
188
- if (args.classifyFeature !== null) {
189
- try {
190
- const findings = JSON.parse(args.classifyFeature);
191
- const res = classifyFeaturePreflightFindings(Array.isArray(findings) ? findings : []);
192
- return {
193
- exitCode: res.shouldAbort ? 1 : 0,
194
- stdout: `${JSON.stringify(res, null, 2)}\n`,
195
- stderr: '',
196
- };
197
- } catch (e) {
198
- return {
199
- exitCode: 1,
200
- stdout: '',
201
- stderr: `Invalid JSON for --classify-feature: ${String(e)}`,
202
- };
203
- }
204
- }
205
-
206
- if (!args.status) return { exitCode: 1, stdout: '', stderr: PREFLIGHT_CLI_USAGE };
207
-
208
- if (args.recovery) {
209
- const hint = recoveryHint(args.status, args.wbs);
210
- const body = args.json
211
- ? `${JSON.stringify({ recovery: hint }, null, 2)}\n`
212
- : hint
213
- ? `${hint.command}\n`
214
- : 'no recovery hop\n';
215
- return { exitCode: 0, stdout: body, stderr: '' };
216
- }
217
-
218
- // Quick command-aware readiness (0814 R2) — read-only admission decision.
219
- if (args.operation !== null) {
220
- const result = quickReadiness({
221
- wbs: args.wbs,
222
- status: args.status,
223
- operation: args.operation,
224
- dependencies: args.deps,
225
- depStatuses: args.depStatuses,
226
- ...(args.filteredCount !== null ? { filteredCount: args.filteredCount } : {}),
227
- ...(args.requiredSections.length > 0 ? { requiredSections: args.requiredSections } : {}),
228
- ...(args.presentSections.length > 0 ? { presentSections: args.presentSections } : {}),
229
- ...(args.force ? { force: true } : {}),
230
- });
231
- const runnable = result.action === 'runnable' || result.action === 'needs-refinement';
232
- if (args.json) {
233
- return { exitCode: runnable ? 0 : 2, stdout: `${JSON.stringify(result, null, 2)}\n`, stderr: '' };
234
- }
235
- return {
236
- exitCode: runnable ? 0 : 2,
237
- stdout: `${result.action}${result.code ? ` ${result.code}` : ''}: ${result.reason}\n`,
238
- stderr: '',
239
- };
240
- }
241
-
242
- const result = preflightTask({
243
- wbs: args.wbs,
244
- status: args.status,
245
- dependencies: args.deps,
246
- depStatuses: args.depStatuses,
247
- });
248
- if (args.json) {
249
- return {
250
- exitCode: result.action === 'run' ? 0 : 2,
251
- stdout: `${JSON.stringify(result, null, 2)}\n`,
252
- stderr: '',
253
- };
254
- }
255
- if (result.action === 'run') {
256
- return { exitCode: 0, stdout: `run: ${result.reason ?? 'ok'}\n`, stderr: '' };
257
- }
258
- return {
259
- exitCode: 2,
260
- stdout: `skip ${result.code}: ${result.reason}\n`,
261
- stderr: '',
262
- };
263
- }
264
-
265
- if (import.meta.main) {
266
- const { exitCode, stdout, stderr } = runPreflightCli(process.argv.slice(2));
267
- if (stdout) process.stdout.write(stdout);
268
- if (stderr) process.stderr.write(`${stderr}\n`);
269
- process.exit(exitCode);
270
- }
@@ -1,226 +0,0 @@
1
- #!/usr/bin/env node
2
- // @bun
3
-
4
- // plugins/sp/scripts/commit-guard.ts
5
- import { spawnSync } from "child_process";
6
- import { mkdirSync, readFileSync, renameSync, writeFileSync } from "fs";
7
- import { join } from "path";
8
- var COMMIT_GUARD_USAGE = "usage: commit-guard start --run <id> | stage --run <id> [--base <sha>] -- <paths\u2026> | check --run <id>";
9
- var isBookkeeping = (path) => path === ".spur" || path.startsWith(".spur/");
10
- var at = (options, rel) => options.cwd ? join(options.cwd, rel) : rel;
11
- var artifactPath = (run) => join(".spur", "run", `${run}-tree-start.json`);
12
- function gitOf(options) {
13
- if (options.git)
14
- return options.git;
15
- return (args) => {
16
- const run = spawnSync("git", args, { cwd: options.cwd, encoding: "utf8" });
17
- return { status: run.status ?? 1, stdout: run.stdout ?? "" };
18
- };
19
- }
20
- function hashPath(path, git) {
21
- const run = git(["hash-object", "--", path]);
22
- return run.status === 0 ? run.stdout.trim() : null;
23
- }
24
- function parsePorcelainZ(stdout) {
25
- const tokens = stdout.split("\x00");
26
- const paths = [];
27
- for (let i = 0;i < tokens.length; i++) {
28
- const entry = tokens[i] ?? "";
29
- if (entry.length < 4)
30
- continue;
31
- const path = entry.slice(3);
32
- if (!isBookkeeping(path))
33
- paths.push(path);
34
- if (/[RC]/.test(entry.slice(0, 2)))
35
- i++;
36
- }
37
- return paths;
38
- }
39
- function conflictMarkerLine(content) {
40
- let opener = false;
41
- for (const line of content.split(`
42
- `)) {
43
- if (/^<{7}( |$)/.test(line))
44
- opener = true;
45
- else if (/^={7}(\s|$)/.test(line) && opener)
46
- return true;
47
- else if (/^>{7}( |$)/.test(line))
48
- return true;
49
- }
50
- return opener;
51
- }
52
- function changedSinceBase(base, git) {
53
- const tracked = git(["diff", "--name-only", base]);
54
- if (tracked.status !== 0)
55
- throw new Error(`git diff --name-only failed (status ${tracked.status})`);
56
- const others = git(["ls-files", "--others", "--exclude-standard", "-z"]);
57
- if (others.status !== 0)
58
- throw new Error(`git ls-files --others failed (status ${others.status})`);
59
- const clean = (out, sep) => out.split(sep).map((p) => p.trim()).filter((p) => p.length > 0 && !isBookkeeping(p));
60
- return new Set([...clean(tracked.stdout, `
61
- `), ...clean(others.stdout, "\x00")]);
62
- }
63
- function readTreeStart(run, options) {
64
- try {
65
- const parsed = JSON.parse(readFileSync(at(options, artifactPath(run)), "utf8"));
66
- if (typeof parsed.head === "string" && Array.isArray(parsed.dirty))
67
- return parsed;
68
- } catch {}
69
- return null;
70
- }
71
- function runContext(run, options) {
72
- const git = gitOf(options);
73
- const start = readTreeStart(run, options);
74
- if (!start)
75
- return null;
76
- let base = options.base ?? "";
77
- if (!base) {
78
- try {
79
- const anchored = readFileSync(at(options, join(".spur", "run", `${run}-base.sha`)), "utf8").trim();
80
- if (/^[0-9a-f]{7,40}$/i.test(anchored))
81
- base = anchored;
82
- } catch {}
83
- }
84
- if (!base)
85
- base = start.head.length > 0 ? start.head : "HEAD";
86
- const written = changedSinceBase(base, git);
87
- for (const entry of start.dirty)
88
- if (hashPath(entry.path, git) === entry.sha)
89
- written.delete(entry.path);
90
- return { git, base, start, written };
91
- }
92
- function missingStart(run) {
93
- process.stderr.write(`commit-guard: no ${artifactPath(run)} \u2014 run \`commit-guard start --run ${run}\` first
94
- `);
95
- return 1;
96
- }
97
- function runStart(run, options = {}) {
98
- const git = gitOf(options);
99
- const head = git(["rev-parse", "HEAD"]);
100
- const status = git(["status", "--porcelain=v1", "-z"]);
101
- if (head.status !== 0 || status.status !== 0) {
102
- process.stderr.write(`commit-guard: git probe failed (rev-parse HEAD / status) \u2014 is this a work tree?
103
- `);
104
- return 1;
105
- }
106
- const snapshot = {
107
- head: head.stdout.trim(),
108
- dirty: parsePorcelainZ(status.stdout).map((path) => ({ path, sha: hashPath(path, git) }))
109
- };
110
- const abs = at(options, artifactPath(run));
111
- mkdirSync(join(abs, ".."), { recursive: true });
112
- writeFileSync(`${abs}.tmp`, `${JSON.stringify(snapshot, null, 2)}
113
- `);
114
- renameSync(`${abs}.tmp`, abs);
115
- return 0;
116
- }
117
- function conflictingTarget(paths, options, git) {
118
- const probe = git(["diff", "--name-only", "--diff-filter=U"]);
119
- if (probe.status !== 0)
120
- throw new Error(`git diff --name-only --diff-filter=U failed (status ${probe.status})`);
121
- const unmerged = new Set(probe.stdout.split(`
122
- `).map((p) => p.trim()));
123
- for (const path of paths) {
124
- if (unmerged.has(path))
125
- return `${path} (unmerged)`;
126
- try {
127
- if (conflictMarkerLine(readFileSync(at(options, path), "utf8")))
128
- return `${path} (conflict markers)`;
129
- } catch {}
130
- }
131
- return null;
132
- }
133
- function usageError() {
134
- process.stderr.write(`${COMMIT_GUARD_USAGE}
135
- `);
136
- return 1;
137
- }
138
- function probeDiagnostic(err) {
139
- const detail = err instanceof Error ? err.message : String(err);
140
- return `commit-guard: git probe failed (${detail}) \u2014 tree state unproven, nothing staged or reported
141
- `;
142
- }
143
- function runStage(run, paths, options = {}) {
144
- if (paths.length === 0)
145
- return { staged: [], foreign: [], exitCode: usageError() };
146
- let context, conflict;
147
- try {
148
- context = runContext(run, options);
149
- if (!context)
150
- return { staged: [], foreign: [], exitCode: missingStart(run) };
151
- conflict = conflictingTarget(paths, options, context.git);
152
- } catch (err) {
153
- process.stderr.write(probeDiagnostic(err));
154
- return { staged: [], foreign: [], exitCode: 1 };
155
- }
156
- if (conflict) {
157
- process.stderr.write(`commit-guard: refusing to stage ${conflict}; nothing staged
158
- `);
159
- return { staged: [], foreign: [], exitCode: 3 };
160
- }
161
- const dirtyAtStart = new Set(context.start.dirty.map((entry) => entry.path));
162
- const staged = paths.filter((path) => context.written.has(path));
163
- const refused = paths.filter((path) => !context.written.has(path));
164
- for (const path of refused) {
165
- const why = dirtyAtStart.has(path) ? "foreign \u2014 dirty at start, untouched by this run" : "unchanged since base";
166
- process.stderr.write(`commit-guard: refused ${path} (${why})
167
- `);
168
- }
169
- if (staged.length > 0 && context.git(["add", "--", ...staged]).status !== 0) {
170
- process.stderr.write(`commit-guard: git add failed \u2014 nothing staged
171
- `);
172
- return { staged: [], foreign: [], exitCode: 1 };
173
- }
174
- const foreign = refused.filter((path) => dirtyAtStart.has(path));
175
- process.stdout.write(`${JSON.stringify({ staged, foreign })}
176
- `);
177
- return { staged, foreign, exitCode: refused.length > 0 ? 2 : 0 };
178
- }
179
- function runCheck(run, options = {}) {
180
- try {
181
- const context = runContext(run, options);
182
- if (!context)
183
- return missingStart(run);
184
- const { base, git, written } = context;
185
- const foreign = [...changedSinceBase(base, git)].filter((path) => !written.has(path)).sort();
186
- process.stdout.write(`${JSON.stringify({ foreign })}
187
- `);
188
- return 0;
189
- } catch (err) {
190
- process.stderr.write(probeDiagnostic(err));
191
- return 1;
192
- }
193
- }
194
- function takeFlag(args, name) {
195
- const index = args.indexOf(name);
196
- const value = index === -1 ? undefined : args[index + 1];
197
- const missing = value === undefined || value.startsWith("--");
198
- if (index !== -1)
199
- args.splice(index, missing ? 1 : 2);
200
- return missing ? undefined : value;
201
- }
202
- function main(argv, options = {}) {
203
- const args = [...argv];
204
- takeFlag(args, "--spur-bin");
205
- const sub = args.shift();
206
- const run = takeFlag(args, "--run"), base = takeFlag(args, "--base");
207
- if (!run)
208
- return usageError();
209
- if (sub === "start")
210
- return args.length === 0 ? runStart(run, options) : usageError();
211
- if (sub === "check")
212
- return args.length === 0 ? runCheck(run, { ...options, base }) : usageError();
213
- if (sub === "stage")
214
- return runStage(run, args.filter((arg) => arg !== "--"), { ...options, base }).exitCode;
215
- return usageError();
216
- }
217
- process.exit(main(process.argv.slice(2)));
218
- export {
219
- COMMIT_GUARD_USAGE,
220
- conflictMarkerLine,
221
- main,
222
- parsePorcelainZ,
223
- runCheck,
224
- runStage,
225
- runStart
226
- };