@gobing-ai/spur 0.4.1 → 0.4.3

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 (159) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/config/config.global.yaml +4 -2
  3. package/config/decisions/maker-liveness.yaml +39 -0
  4. package/config/plugin-scripts.json +0 -4
  5. package/config/rules/README.md +1 -0
  6. package/config/rules/docs/sourced-api-claims.yaml +56 -12
  7. package/config/rules/strict/runtime-boundaries.yaml +7 -0
  8. package/config/script-placement-baseline.json +1 -19
  9. package/config/templates/AGENTS.md +1 -1
  10. package/config/workflows/feature-lifecycle.yaml +46 -1
  11. package/config/workflows/feature-verification.yaml +11 -0
  12. package/config/workflows/wrapup-pipeline.yaml +1 -1
  13. package/package.json +10 -10
  14. package/plugins/sp/README.md +23 -18
  15. package/plugins/sp/agents/super-planner.md +5 -6
  16. package/plugins/sp/agents/super-reviewer.md +43 -0
  17. package/plugins/sp/bunfig.toml +8 -5
  18. package/plugins/sp/commands/dev-review-session.md +2 -1
  19. package/plugins/sp/commands/dev-runall.md +18 -9
  20. package/plugins/sp/hooks/careful-guard.ts +1 -1
  21. package/plugins/sp/hooks/context-post-tool.ts +7 -404
  22. package/plugins/sp/hooks/context-session-start.ts +6 -192
  23. package/plugins/sp/hooks/context-session-stop.ts +6 -91
  24. package/plugins/sp/hooks/pi/guard-extension.ts +26 -12
  25. package/plugins/sp/hooks/task-write-guard.ts +1 -1
  26. package/plugins/sp/{hooks → lib}/agent-hint.ts +1 -1
  27. package/plugins/sp/lib/context-owner.ts +56 -0
  28. package/plugins/sp/lib/context-post-tool.ts +432 -0
  29. package/plugins/sp/lib/context-session-start.ts +214 -0
  30. package/plugins/sp/lib/context-session-stop.ts +196 -0
  31. package/plugins/sp/lib/dogfood-pipeline.ts +265 -0
  32. package/plugins/sp/lib/dogfood-report.ts +278 -0
  33. package/plugins/sp/lib/idea-handoff.generated.mjs +6 -4
  34. package/plugins/sp/lib/inline-run-app.ts +40 -0
  35. package/plugins/sp/lib/inline-run.generated.d.mts +3 -3
  36. package/plugins/sp/lib/inline-run.generated.mjs +1361 -87
  37. package/plugins/sp/lib/plan-projection.generated.d.mts +169 -0
  38. package/plugins/sp/lib/plan-projection.generated.mjs +704 -1
  39. package/plugins/sp/lib/quality-gate.generated.d.mts +0 -12
  40. package/plugins/sp/lib/quality-gate.generated.mjs +62 -231
  41. package/plugins/sp/lib/residual-scan.generated.d.mts +7 -1
  42. package/plugins/sp/lib/residual-scan.generated.mjs +71 -6
  43. package/plugins/sp/lib/run-summary-core.ts +34 -370
  44. package/plugins/sp/lib/transcript.ts +37 -200
  45. package/plugins/sp/plugin.json +1 -1
  46. package/plugins/sp/references/roles.md +1 -1
  47. package/plugins/sp/scripts/batch-plan.mjs +740 -1
  48. package/plugins/sp/scripts/batch-plan.ts +48 -3
  49. package/plugins/sp/scripts/batch-preflight.mjs +23 -1
  50. package/plugins/sp/scripts/batch-preflight.ts +35 -295
  51. package/plugins/sp/scripts/daily-summary/daily-summary.mjs +108 -87
  52. package/plugins/sp/scripts/daily-summary/daily-summary.ts +111 -104
  53. package/plugins/sp/scripts/dogfood-testing/detect-pipeline-driving.mjs +2 -1
  54. package/plugins/sp/scripts/dogfood-testing/detect-pipeline-driving.ts +7 -265
  55. package/plugins/sp/scripts/dogfood-testing/validate-report.mjs +3 -0
  56. package/plugins/sp/scripts/dogfood-testing/validate-report.ts +6 -272
  57. package/plugins/sp/scripts/feature-verification-steps.mjs +7 -123
  58. package/plugins/sp/scripts/feature-verification-steps.ts +29 -181
  59. package/plugins/sp/scripts/inline-run-dispatch.mjs +27 -26
  60. package/plugins/sp/scripts/inline-run-dispatch.ts +1 -21
  61. package/plugins/sp/scripts/inline-run-setup.mjs +158 -120
  62. package/plugins/sp/scripts/inline-run-setup.ts +1 -21
  63. package/plugins/sp/scripts/pr-reviewing.mjs +19 -5
  64. package/plugins/sp/scripts/pr-reviewing.ts +34 -9
  65. package/plugins/sp/scripts/quality-gate.mjs +38 -208
  66. package/plugins/sp/scripts/residual-scan.mjs +73 -6
  67. package/plugins/sp/scripts/residual-scan.ts +8 -5
  68. package/plugins/sp/scripts/run-summary.mjs +160 -95
  69. package/plugins/sp/scripts/run-summary.ts +7 -5
  70. package/plugins/sp/scripts/session-timeline.mjs +57 -29
  71. package/plugins/sp/scripts/session-timeline.ts +4 -18
  72. package/plugins/sp/scripts/task-diffstat.mjs +43 -30
  73. package/plugins/sp/scripts/task-diffstat.ts +40 -71
  74. package/plugins/sp/skills/code-implementation/SKILL.md +7 -1
  75. package/plugins/sp/skills/code-verification/references/secu-review.md +2 -1
  76. package/plugins/sp/skills/code-verification/references/verdict-schema.md +4 -0
  77. package/plugins/sp/skills/dogfood-testing/SKILL.md +2 -2
  78. package/plugins/sp/skills/indexed-context/SKILL.md +14 -7
  79. package/plugins/sp/skills/next-feature/SKILL.md +1 -1
  80. package/plugins/sp/skills/parallel-execution/SKILL.md +2 -2
  81. package/plugins/sp/skills/parallel-execution/references/dispatch-surface.md +2 -2
  82. package/plugins/sp/skills/parallel-execution/references/fan-out-patterns.md +2 -2
  83. package/plugins/sp/skills/pr-reviewing/SKILL.md +7 -3
  84. package/plugins/sp/skills/session-review/SKILL.md +5 -3
  85. package/plugins/sp/skills/source-driven-development/SKILL.md +7 -4
  86. package/plugins/sp/skills/spur-cli/references/builder.md +3 -2
  87. package/plugins/sp/skills/spur-cli/references/decision.md +40 -8
  88. package/plugins/sp/skills/spur-cli/references/features.md +46 -5
  89. package/plugins/sp/skills/spur-cli/references/projects.md +7 -5
  90. package/plugins/sp/skills/spur-dev/SKILL.md +2 -0
  91. package/plugins/sp/skills/spur-dev/references/ac-style-guide.md +27 -0
  92. package/plugins/sp/skills/spur-dev/references/dev-operations.md +2 -3
  93. package/plugins/sp/skills/spur-dev/references/execution-batch-report.md +6 -1
  94. package/plugins/sp/skills/spur-dev/references/execution-batch.md +56 -7
  95. package/plugins/sp/skills/spur-dev/references/execution-parallel-isolation.md +4 -1
  96. package/plugins/sp/skills/spur-dev/references/execution-worktree-landing.md +22 -0
  97. package/plugins/sp/skills/spur-dev/references/execution-worktree-setup.md +14 -0
  98. package/plugins/sp/skills/spur-dev/references/flag-glossary.md +13 -11
  99. package/plugins/sp/skills/spur-dev/references/inline-pipeline-driver.md +37 -25
  100. package/spur.js +89792 -76992
  101. package/web/_astro/{BoardApp.D66k_s_T.js → BoardApp.5IIl2O-z.js} +1 -1
  102. package/web/_astro/BoardApp.Drnx-KeM.js +324 -0
  103. package/web/_astro/TaskDetail.Ci4LNn9S.js +1 -0
  104. package/web/_astro/{arc._BUadrdH.js → arc.j2EVkYVd.js} +1 -1
  105. package/web/_astro/{architectureDiagram-3BPJPVTR.Dh7xS1Pp.js → architectureDiagram-3BPJPVTR.DZWSFqEG.js} +1 -1
  106. package/web/_astro/{blockDiagram-GPEHLZMM.DFDVpfXm.js → blockDiagram-GPEHLZMM.DE6GpXCN.js} +1 -1
  107. package/web/_astro/{c4Diagram-AAUBKEIU.V7_3_glh.js → c4Diagram-AAUBKEIU.DoVR1G6B.js} +1 -1
  108. package/web/_astro/channel.DmdDxGsq.js +1 -0
  109. package/web/_astro/{chunk-2J33WTMH.De1yx7Jz.js → chunk-2J33WTMH.Do6-Gbyf.js} +1 -1
  110. package/web/_astro/{chunk-4BX2VUAB.BDhHFuxx.js → chunk-4BX2VUAB.BYV1HGHx.js} +1 -1
  111. package/web/_astro/{chunk-55IACEB6.DDLkWnmC.js → chunk-55IACEB6.CvI1zkrc.js} +1 -1
  112. package/web/_astro/{chunk-727SXJPM.CwfiLnA7.js → chunk-727SXJPM.DNGSCRlX.js} +1 -1
  113. package/web/_astro/{chunk-AQP2D5EJ.CtGDmUnM.js → chunk-AQP2D5EJ.DXhhgT5z.js} +1 -1
  114. package/web/_astro/{chunk-FMBD7UC4.BOLjpw73.js → chunk-FMBD7UC4.dzkRKP2u.js} +1 -1
  115. package/web/_astro/{chunk-ND2GUHAM.BDhQls6O.js → chunk-ND2GUHAM.DujGLeEf.js} +1 -1
  116. package/web/_astro/{chunk-QZHKN3VN.76qBynDF.js → chunk-QZHKN3VN.BWtIT3ZU.js} +1 -1
  117. package/web/_astro/{classDiagram-4FO5ZUOK.N0mpPHT0.js → classDiagram-4FO5ZUOK.DyApg0Nn.js} +1 -1
  118. package/web/_astro/{classDiagram-v2-Q7XG4LA2.N0mpPHT0.js → classDiagram-v2-Q7XG4LA2.DyApg0Nn.js} +1 -1
  119. package/web/_astro/{cose-bilkent-S5V4N54A.CKknFfjB.js → cose-bilkent-S5V4N54A.DJQB-u_l.js} +1 -1
  120. package/web/_astro/{cynefin-OW5HDTMX.Cw3ZWBAh.js → cynefin-OW5HDTMX.bi3D_siV.js} +1 -1
  121. package/web/_astro/{dagre-BM42HDAG.D3yIKCRD.js → dagre-BM42HDAG.C-f7TrRg.js} +1 -1
  122. package/web/_astro/{diagram-2AECGRRQ.D51dO83Z.js → diagram-2AECGRRQ.DrPfjHQB.js} +1 -1
  123. package/web/_astro/{diagram-5GNKFQAL.Bb-FViDj.js → diagram-5GNKFQAL.C9S4WUHO.js} +1 -1
  124. package/web/_astro/{diagram-KO2AKTUF.D3mN8S6x.js → diagram-KO2AKTUF.Dzzye8QH.js} +1 -1
  125. package/web/_astro/{diagram-LMA3HP47.dMMyPcbJ.js → diagram-LMA3HP47.DX-LRiof.js} +1 -1
  126. package/web/_astro/{diagram-OG6HWLK6.AzbvzY4C.js → diagram-OG6HWLK6.Z-kAKwIz.js} +1 -1
  127. package/web/_astro/{erDiagram-TEJ5UH35.BR5I4OZb.js → erDiagram-TEJ5UH35.D6dyClSv.js} +1 -1
  128. package/web/_astro/{flowDiagram-I6XJVG4X.DFqvhGkn.js → flowDiagram-I6XJVG4X.Cx3q2cxD.js} +1 -1
  129. package/web/_astro/{ganttDiagram-6RSMTGT7.ByJbhH6J.js → ganttDiagram-6RSMTGT7.DU3gH6CT.js} +1 -1
  130. package/web/_astro/{gitGraphDiagram-PVQCEYII.CInHjQR3.js → gitGraphDiagram-PVQCEYII.ISmCnJ87.js} +1 -1
  131. package/web/_astro/index.Do2lLdgo.css +1 -0
  132. package/web/_astro/{infoDiagram-5YYISTIA.Cm0dGVjJ.js → infoDiagram-5YYISTIA.DR6_iNOK.js} +1 -1
  133. package/web/_astro/{ishikawaDiagram-YF4QCWOH.C3nOP6Sj.js → ishikawaDiagram-YF4QCWOH.D7wY-ZGA.js} +1 -1
  134. package/web/_astro/{journeyDiagram-JHISSGLW.4IvLa2MW.js → journeyDiagram-JHISSGLW.DkP9-WK8.js} +1 -1
  135. package/web/_astro/{kanban-definition-UN3LZRKU.DS87Cl-k.js → kanban-definition-UN3LZRKU.wtgY98M2.js} +1 -1
  136. package/web/_astro/{linear.MzmRrNER.js → linear.CI6djNi7.js} +1 -1
  137. package/web/_astro/{mermaid.core.fK-WNswS.js → mermaid.core.CuQPxS8b.js} +4 -4
  138. package/web/_astro/{mindmap-definition-RKZ34NQL.BqipOHhY.js → mindmap-definition-RKZ34NQL.5HNIEG7R.js} +1 -1
  139. package/web/_astro/{pieDiagram-4H26LBE5.0nBiPqFG.js → pieDiagram-4H26LBE5.CchhDFjE.js} +1 -1
  140. package/web/_astro/{quadrantDiagram-W4KKPZXB.Szj7Zpx5.js → quadrantDiagram-W4KKPZXB.PYXXJPSJ.js} +1 -1
  141. package/web/_astro/{requirementDiagram-4Y6WPE33.D4UR6TWo.js → requirementDiagram-4Y6WPE33.cUQuNAKU.js} +1 -1
  142. package/web/_astro/{sankeyDiagram-5OEKKPKP.DAE7BdOs.js → sankeyDiagram-5OEKKPKP.BO7MnRIA.js} +1 -1
  143. package/web/_astro/{sequenceDiagram-3UESZ5HK.BP9ZVq25.js → sequenceDiagram-3UESZ5HK.D9bElyD6.js} +1 -1
  144. package/web/_astro/{stateDiagram-AJRCARHV.BQW7mIxk.js → stateDiagram-AJRCARHV.DhGdRz7I.js} +1 -1
  145. package/web/_astro/{stateDiagram-v2-BHNVJYJU.PbK4nDih.js → stateDiagram-v2-BHNVJYJU.5GvLeAoL.js} +1 -1
  146. package/web/_astro/{timeline-definition-PNZ67QCA.Ci_GvUpo.js → timeline-definition-PNZ67QCA.LtAT9haM.js} +1 -1
  147. package/web/_astro/{vennDiagram-CIIHVFJN.DNw1Y5qx.js → vennDiagram-CIIHVFJN.DRth1yLA.js} +1 -1
  148. package/web/_astro/{wardleyDiagram-YWT4CUSO.CwlkaaNe.js → wardleyDiagram-YWT4CUSO.h9MlZfcr.js} +1 -1
  149. package/web/_astro/{xychartDiagram-2RQKCTM6.MI6ZuiH_.js → xychartDiagram-2RQKCTM6.DHqy9DYA.js} +1 -1
  150. package/web/index.html +2 -2
  151. package/config/proportional-route-table.ts +0 -155
  152. package/plugins/sp/commands/dev-parallel.md +0 -37
  153. package/plugins/sp/scripts/daily-summary/logger.ts +0 -28
  154. package/web/_astro/BoardApp.D6sjAJr_.js +0 -324
  155. package/web/_astro/TaskDetail.BynLnMNc.js +0 -1
  156. package/web/_astro/channel.DZVfto-r.js +0 -1
  157. package/web/_astro/index.COh_kBxN.css +0 -1
  158. /package/plugins/sp/{hooks → lib}/destructive-policy.ts +0 -0
  159. /package/plugins/sp/{hooks → lib}/task-file-policy.ts +0 -0
@@ -5,6 +5,572 @@
5
5
  import { readFileSync } from "fs";
6
6
 
7
7
  // plugins/sp/lib/plan-projection.generated.mjs
8
+ var NON_ABORTING_PREFLIGHT_CODES = new Set([
9
+ "L4.scenario-unverified",
10
+ "L4.verifying-incomplete-tasks"
11
+ ]);
12
+ function classifyFeaturePreflightFindings(findings) {
13
+ const aborting = [];
14
+ const nonAborting = [];
15
+ for (const f of findings) {
16
+ if (f.severity === "error") {
17
+ if (NON_ABORTING_PREFLIGHT_CODES.has(f.code)) {
18
+ nonAborting.push(f);
19
+ } else {
20
+ aborting.push(f);
21
+ }
22
+ } else {
23
+ nonAborting.push(f);
24
+ }
25
+ }
26
+ return {
27
+ aborting,
28
+ nonAborting,
29
+ shouldAbort: aborting.length > 0
30
+ };
31
+ }
32
+ function quickReadiness(input) {
33
+ const status = (input.status ?? "").toLowerCase();
34
+ const operation = input.operation;
35
+ if (operation !== "run" && operation !== "refine" && operation !== "verify") {
36
+ return {
37
+ action: "invalid",
38
+ code: "IV",
39
+ reason: `quick-readiness: unknown operation '${operation}' (${input.wbs})`
40
+ };
41
+ }
42
+ if (input.filteredCount !== undefined) {
43
+ if (input.filteredCount < 0) {
44
+ return {
45
+ action: "invalid",
46
+ code: "IV",
47
+ reason: `quick-readiness: invalid negative filtered count '${input.filteredCount}' (${input.wbs})`
48
+ };
49
+ }
50
+ if (input.filteredCount === 0) {
51
+ return {
52
+ action: "skipped",
53
+ code: "EMPTY",
54
+ reason: `quick-readiness: empty status-filtered set — nothing to ${operation} (${input.wbs})`
55
+ };
56
+ }
57
+ }
58
+ if (status === "cancelled" || status === "done") {
59
+ if (operation === "verify" && input.force === true) {
60
+ return {
61
+ action: "runnable",
62
+ code: "FORCE",
63
+ reason: `quick-readiness: verify --force re-verification of ${status} task ${input.wbs}`
64
+ };
65
+ }
66
+ return {
67
+ action: "skipped",
68
+ code: status === "done" ? "DONE" : "CANCELLED",
69
+ reason: `quick-readiness: already ${status} — no ${operation} hop (${input.wbs})`
70
+ };
71
+ }
72
+ if (status === "blocked") {
73
+ return {
74
+ action: "blocked",
75
+ code: "BLK",
76
+ reason: `quick-readiness: blocked — human/handover first (${input.wbs})`
77
+ };
78
+ }
79
+ if (operation === "run" && input.dependencies && input.dependencies.length > 0) {
80
+ const unmet = input.dependencies.filter((d) => (input.depStatuses?.[d] ?? "missing").toLowerCase() !== "done");
81
+ if (unmet.length > 0) {
82
+ return {
83
+ action: "blocked",
84
+ code: "DEP",
85
+ reason: `quick-readiness: unmet deps — ${unmet.join(", ")} (${input.wbs})`,
86
+ unmetDeps: unmet
87
+ };
88
+ }
89
+ }
90
+ const required = input.requiredSections ?? [];
91
+ const present = input.presentSections ?? [];
92
+ const gaps = required.filter((s) => {
93
+ const finding = input.sectionFindings?.[s];
94
+ return !present.includes(s) || finding !== undefined && finding !== "";
95
+ });
96
+ if (operation === "refine") {
97
+ if (status !== "backlog" && status !== "todo") {
98
+ return {
99
+ action: "skipped",
100
+ code: "NONPLAN",
101
+ reason: `quick-readiness: refine targets backlog/todo only, not '${status}' (${input.wbs})`
102
+ };
103
+ }
104
+ return {
105
+ action: "runnable",
106
+ code: "OK",
107
+ reason: `quick-readiness: refine ready for ${input.wbs} (${gaps.length} planning gap(s) to fill)`
108
+ };
109
+ }
110
+ if (operation === "verify") {
111
+ if (status !== "testing" && status !== "wip") {
112
+ return {
113
+ action: "invalid",
114
+ code: "NOVERIFY",
115
+ reason: `quick-readiness: verify needs testing/wip, not '${status}' (${input.wbs})`
116
+ };
117
+ }
118
+ return {
119
+ action: "runnable",
120
+ code: "OK",
121
+ reason: `quick-readiness: verify ready for ${input.wbs}`
122
+ };
123
+ }
124
+ if (status !== "todo" && status !== "wip" && status !== "testing") {
125
+ return {
126
+ action: "invalid",
127
+ code: "NORUN",
128
+ reason: `quick-readiness: run needs todo/wip/testing, not '${status}' (${input.wbs})`
129
+ };
130
+ }
131
+ if (gaps.length > 0) {
132
+ return {
133
+ action: "needs-refinement",
134
+ code: "REFINE",
135
+ reason: `quick-readiness: implementation sections incomplete (${input.wbs})`,
136
+ gaps
137
+ };
138
+ }
139
+ return {
140
+ action: "runnable",
141
+ code: "OK",
142
+ reason: `quick-readiness: run ready for ${input.wbs}`
143
+ };
144
+ }
145
+ function preflightTask(input) {
146
+ const status = (input.status ?? "").toLowerCase();
147
+ const deps = input.dependencies ?? [];
148
+ const depStatuses = input.depStatuses ?? {};
149
+ if (status === "cancelled") {
150
+ return {
151
+ action: "skip",
152
+ code: "A9",
153
+ reason: `dev-next: cancelled — nothing to advance (${input.wbs})`
154
+ };
155
+ }
156
+ if (status === "done") {
157
+ return {
158
+ action: "skip",
159
+ code: "A8",
160
+ reason: `dev-next: already done — batch does not auto-wrap (${input.wbs})`
161
+ };
162
+ }
163
+ if (status === "blocked") {
164
+ return {
165
+ action: "skip",
166
+ code: "A7",
167
+ reason: `dev-next: blocked — do not launch pipeline; human/handover first (${input.wbs})`
168
+ };
169
+ }
170
+ if (status === "todo" || status === "backlog") {
171
+ const unmet = deps.filter((d) => {
172
+ const st = (depStatuses[d] ?? "missing").toLowerCase();
173
+ return st !== "done";
174
+ });
175
+ if (unmet.length > 0) {
176
+ return {
177
+ action: "skip",
178
+ code: "A2",
179
+ reason: `dev-next: blocked by deps — unmet: ${unmet.join(", ")} (${input.wbs})`,
180
+ unmetDeps: unmet
181
+ };
182
+ }
183
+ }
184
+ return { action: "run", code: "OK", reason: `preflight clear — launch task-pipeline for ${input.wbs}` };
185
+ }
186
+ var zeroTokens = () => ({ input: 0, cacheCreate: 0, cacheRead: 0, output: 0 });
187
+ function formatDuration(ms) {
188
+ const s = Math.round(ms / 1000);
189
+ const h = Math.floor(s / 3600);
190
+ const m = Math.floor(s % 3600 / 60);
191
+ const ss = String(s % 60).padStart(2, "0");
192
+ return h > 0 ? `${h}:${String(m).padStart(2, "0")}:${ss}` : `${m}:${ss}`;
193
+ }
194
+ function formatTokens(n) {
195
+ if (n >= 999500)
196
+ return `${(n / 1e6).toFixed(1)}M`;
197
+ return n >= 1000 ? `${Math.round(n / 1000)}k` : String(n);
198
+ }
199
+ function formatTokenSplit(t) {
200
+ if (!t)
201
+ return "n/a";
202
+ const nonCached = t.input + t.cacheCreate + t.output;
203
+ return `${formatTokens(nonCached + t.cacheRead)} / ${formatTokens(nonCached)}`;
204
+ }
205
+ var OPERATOR_TOOLS = new Set(["AskUserQuestion", "ask_user_question"]);
206
+ function isCompaction(row) {
207
+ return row.type === "compaction" || row.type === "system" && row.subtype === "compact_boundary";
208
+ }
209
+ function firstTextContent(row) {
210
+ const content = row.message?.content;
211
+ if (typeof content === "string")
212
+ return content;
213
+ if (!Array.isArray(content))
214
+ return;
215
+ return content.find((b) => b.type === "text")?.text;
216
+ }
217
+ function skillInvocationPrompt(text) {
218
+ const name = /^\s*<skill name="([^"]+)"/.exec(text)?.[1];
219
+ if (name === undefined)
220
+ return;
221
+ const close = text.lastIndexOf("</skill>");
222
+ const args = close < 0 ? "" : text.slice(close + "</skill>".length).trim();
223
+ return args === "" ? `/${name}` : `/${name} ${args}`;
224
+ }
225
+ function promptText(row) {
226
+ if (row.type === "custom_message") {
227
+ if (row.display === false)
228
+ return;
229
+ const text = typeof row.content === "string" ? row.content.trim() : "";
230
+ return text === "" ? undefined : text;
231
+ }
232
+ if (row.type === "message") {
233
+ if (row.message?.role !== "user")
234
+ return;
235
+ const text = firstTextContent(row);
236
+ if (text === undefined)
237
+ return;
238
+ return skillInvocationPrompt(text) ?? text;
239
+ }
240
+ if (row.type !== "user" || row.isMeta || row.isCompactSummary)
241
+ return;
242
+ const content = row.message?.content;
243
+ if (typeof content === "string")
244
+ return content;
245
+ if (!Array.isArray(content) || content.some((b) => b.type === "tool_result"))
246
+ return;
247
+ return content.find((b) => b.type === "text")?.text;
248
+ }
249
+ function sumTokens(all) {
250
+ const total = zeroTokens();
251
+ for (const t of all)
252
+ for (const k of Object.keys(total))
253
+ total[k] += t[k];
254
+ return total;
255
+ }
256
+ function sumSpans(spans) {
257
+ return {
258
+ workMs: spans.reduce((n, s) => n + s.workMs, 0),
259
+ waitMs: spans.reduce((n, s) => n + s.waitMs, 0),
260
+ toolCalls: spans.reduce((n, s) => n + s.toolCalls, 0),
261
+ tokens: sumTokens(spans.map((s) => s.tokens))
262
+ };
263
+ }
264
+ var newAcc = (ts) => ({
265
+ last: ts,
266
+ askMs: 0,
267
+ asks: new Map,
268
+ toolIds: new Set,
269
+ messages: new Map,
270
+ compactions: 0
271
+ });
272
+ function accumulate(acc, row, ts) {
273
+ acc.last = Math.max(acc.last, ts);
274
+ if (row.type === "message") {
275
+ const m = row.message;
276
+ if (m?.role === "assistant") {
277
+ const blocks2 = Array.isArray(m.content) ? m.content : [];
278
+ for (const b of blocks2) {
279
+ if (b.type === "toolCall" && b.id) {
280
+ acc.toolIds.add(b.id);
281
+ if (b.name && OPERATOR_TOOLS.has(b.name))
282
+ acc.asks.set(b.id, ts);
283
+ }
284
+ }
285
+ const u2 = m.usage;
286
+ if (u2 && row.id)
287
+ acc.messages.set(row.id, {
288
+ input: u2.input ?? 0,
289
+ cacheCreate: u2.cacheWrite ?? 0,
290
+ cacheRead: u2.cacheRead ?? 0,
291
+ output: u2.output ?? 0
292
+ });
293
+ } else if (m?.role === "toolResult" && m.toolCallId && acc.asks.has(m.toolCallId)) {
294
+ acc.askMs += ts - (acc.asks.get(m.toolCallId) ?? ts);
295
+ }
296
+ return;
297
+ }
298
+ if (row.type === "compaction")
299
+ acc.compactions++;
300
+ const blocks = Array.isArray(row.message?.content) ? row.message.content : [];
301
+ for (const b of blocks) {
302
+ if (b.type === "tool_use" && b.id) {
303
+ acc.toolIds.add(b.id);
304
+ if (b.name && OPERATOR_TOOLS.has(b.name))
305
+ acc.asks.set(b.id, ts);
306
+ } else if (b.type === "tool_result" && b.tool_use_id && acc.asks.has(b.tool_use_id)) {
307
+ acc.askMs += ts - (acc.asks.get(b.tool_use_id) ?? ts);
308
+ }
309
+ }
310
+ const u = row.message?.usage;
311
+ if (row.type === "assistant" && u && row.message?.id) {
312
+ acc.messages.set(row.message.id, {
313
+ input: u.input_tokens ?? 0,
314
+ cacheCreate: u.cache_creation_input_tokens ?? 0,
315
+ cacheRead: u.cache_read_input_tokens ?? 0,
316
+ output: u.output_tokens ?? 0
317
+ });
318
+ }
319
+ }
320
+ var IDLE_GAP_MS = 300000;
321
+ var SHELL_TOOLS = new Set(["bash", "shell", "exec", "exec_command", "run_command", "command"]);
322
+ var SUBAGENT_TOOLS = new Set(["agent", "task", "invoke_subagent", "subagent", "dispatch_agent"]);
323
+ var LOWERCASE_OPERATOR_TOOLS = new Set(["askuserquestion", "ask_user_question"]);
324
+ var ACTOR_PRECEDENCE = ["operator", "subagent", "shell", "other"];
325
+ function actorOf(tool) {
326
+ const t = tool.toLowerCase().replace(/[^a-z_]/g, "");
327
+ if (LOWERCASE_OPERATOR_TOOLS.has(t))
328
+ return "operator";
329
+ if (SUBAGENT_TOOLS.has(t))
330
+ return "subagent";
331
+ if (SHELL_TOOLS.has(t))
332
+ return "shell";
333
+ return "other";
334
+ }
335
+ function unionMs(spans, start, end) {
336
+ const clipped = spans.map(([a, b]) => [Math.max(a, start), Math.min(b, end)]).filter(([a, b]) => b > a).sort((x, y) => x[0] - y[0]);
337
+ let total = 0;
338
+ let cursor = Number.NaN;
339
+ let openUntil = Number.NaN;
340
+ for (const [a, b] of clipped) {
341
+ if (!Number.isFinite(cursor)) {
342
+ cursor = a;
343
+ openUntil = b;
344
+ continue;
345
+ }
346
+ if (a > openUntil) {
347
+ total += openUntil - cursor;
348
+ cursor = a;
349
+ openUntil = b;
350
+ } else {
351
+ openUntil = Math.max(openUntil, b);
352
+ }
353
+ }
354
+ if (Number.isFinite(cursor))
355
+ total += openUntil - cursor;
356
+ return total;
357
+ }
358
+ function subtract(spans, claimed) {
359
+ let free = spans;
360
+ for (const [ca, cb] of claimed) {
361
+ const next = [];
362
+ for (const [a, b] of free) {
363
+ if (cb <= a || ca >= b) {
364
+ next.push([a, b]);
365
+ continue;
366
+ }
367
+ if (ca > a)
368
+ next.push([a, ca]);
369
+ if (cb < b)
370
+ next.push([cb, b]);
371
+ }
372
+ free = next;
373
+ }
374
+ return free;
375
+ }
376
+ function actorSplit(rows, start, end) {
377
+ const empty = {
378
+ operatorMs: 0,
379
+ longestOperatorWaitMs: 0,
380
+ modelMs: 0,
381
+ shellMs: 0,
382
+ subagentMs: 0,
383
+ otherToolMs: 0,
384
+ idleMs: 0,
385
+ compactions: 0,
386
+ subagentErrors: 0
387
+ };
388
+ const window = end - start;
389
+ if (window <= 0)
390
+ return empty;
391
+ const calls = new Map;
392
+ const results = new Map;
393
+ const eventTs = [];
394
+ let compactions = 0;
395
+ for (const [row, ts] of rows) {
396
+ if (ts < start || ts > end)
397
+ continue;
398
+ eventTs.push(ts);
399
+ if (isCompaction(row))
400
+ compactions++;
401
+ const blocks = Array.isArray(row.message?.content) ? row.message?.content : [];
402
+ if (row.message?.role === "toolResult" && row.message.toolCallId !== undefined) {
403
+ results.set(row.message.toolCallId, { ts, isError: row.message.isError === true });
404
+ }
405
+ for (const b of blocks) {
406
+ if ((b.type === "tool_use" || b.type === "toolCall") && b.id !== undefined && b.name !== undefined) {
407
+ calls.set(b.id, { id: b.id, actor: actorOf(b.name), ts });
408
+ } else if (b.type === "tool_result" && b.tool_use_id !== undefined) {
409
+ results.set(b.tool_use_id, { ts, isError: b.is_error === true });
410
+ }
411
+ }
412
+ }
413
+ let subagentErrors = 0;
414
+ const resolved = [];
415
+ for (const call of calls.values()) {
416
+ const result = results.get(call.id);
417
+ if (result?.isError === true && call.actor === "subagent")
418
+ subagentErrors++;
419
+ resolved.push({ ...call, end: Math.min(result?.ts ?? end, end) });
420
+ }
421
+ const claimed = [];
422
+ const byActor = new Map;
423
+ for (const actor of ACTOR_PRECEDENCE) {
424
+ const mine = resolved.filter((r) => r.actor === actor).map((r) => [r.ts, r.end]);
425
+ const free = subtract(mine, claimed);
426
+ for (const span of free)
427
+ claimed.push(span);
428
+ const ms = unionMs(free, start, end);
429
+ byActor.set(actor, ms);
430
+ if (actor === "operator") {
431
+ empty.longestOperatorWaitMs = Math.max(0, ...mine.map(([a, b]) => b - a));
432
+ }
433
+ }
434
+ const boundaries = [...new Set([start, end, ...eventTs, ...claimed.flat()])].sort((a, b) => a - b);
435
+ const claimedSorted = [...claimed].sort((a, b) => a[0] - b[0]);
436
+ let idleMs = 0;
437
+ for (let i = 0;i < boundaries.length - 1; i++) {
438
+ const a = boundaries[i];
439
+ const b = boundaries[i + 1];
440
+ if (b - a < IDLE_GAP_MS)
441
+ continue;
442
+ if (claimedSorted.some(([ca, cb]) => ca < b && cb > a))
443
+ continue;
444
+ idleMs += b - a;
445
+ }
446
+ const accounted = (byActor.get("operator") ?? 0) + (byActor.get("subagent") ?? 0) + (byActor.get("shell") ?? 0) + (byActor.get("other") ?? 0) + idleMs;
447
+ return {
448
+ ...empty,
449
+ operatorMs: byActor.get("operator") ?? 0,
450
+ subagentMs: byActor.get("subagent") ?? 0,
451
+ shellMs: byActor.get("shell") ?? 0,
452
+ otherToolMs: byActor.get("other") ?? 0,
453
+ idleMs,
454
+ modelMs: Math.max(0, window - accounted),
455
+ compactions,
456
+ subagentErrors
457
+ };
458
+ }
459
+ function attemptWindow(attempts) {
460
+ const starts = attempts.map((a) => Date.parse(a.startedAt ?? "")).filter((n) => !Number.isNaN(n));
461
+ const ends = attempts.map((a) => Date.parse(a.completedAt ?? "")).filter((n) => !Number.isNaN(n));
462
+ return starts.length && ends.length ? [Math.min(...starts), Math.max(...ends)] : undefined;
463
+ }
464
+ function summaryRow(stage, status, span, measured) {
465
+ return {
466
+ stage,
467
+ status,
468
+ workMs: span.workMs,
469
+ waitMs: span.waitMs,
470
+ toolCalls: measured ? span.toolCalls : null,
471
+ tokens: measured ? span.tokens : null,
472
+ time: formatDuration(span.workMs),
473
+ wait: formatDuration(span.waitMs),
474
+ token: formatTokenSplit(measured ? span.tokens : null)
475
+ };
476
+ }
477
+ function buildRunSummary(rows, inputs, opts = {}) {
478
+ const { since, until } = opts;
479
+ const windows = [];
480
+ for (const { label, progress } of inputs) {
481
+ const states = (progress.states ?? []).flatMap((s) => {
482
+ const w = attemptWindow((s.actions ?? []).flatMap((a) => a.attempts ?? []));
483
+ return w ? [{ s, w }] : [];
484
+ });
485
+ if (!states.length)
486
+ throw new Error(`progress for run ${progress.runId} has no timed attempts`);
487
+ if (inputs.length === 1) {
488
+ for (const { s, w } of states) {
489
+ const stage = (s.visit ?? 1) > 1 ? `${s.state} (visit ${s.visit})` : s.state;
490
+ windows.push({ stage, status: s.status ?? "", start: w[0], end: w[1] });
491
+ }
492
+ } else {
493
+ const stage = label ?? `${progress.workflow ?? "run"} ${progress.runId.slice(0, 8)}`;
494
+ const start = Math.min(...states.map(({ w }) => w[0]));
495
+ windows.push({
496
+ stage,
497
+ status: progress.status ?? "",
498
+ start,
499
+ end: Math.max(...states.map(({ w }) => w[1]))
500
+ });
501
+ }
502
+ }
503
+ windows.sort((a, b) => a.start - b.start);
504
+ const measure = (start, end) => {
505
+ const acc = newAcc(start);
506
+ for (const [row, ts] of rows)
507
+ if (ts >= start && ts <= end && promptText(row) === undefined)
508
+ accumulate(acc, row, ts);
509
+ const span = {
510
+ workMs: end - start - acc.askMs,
511
+ waitMs: acc.askMs,
512
+ toolCalls: acc.toolIds.size,
513
+ tokens: sumTokens([...acc.messages.values()])
514
+ };
515
+ return { span, measured: acc.toolIds.size > 0 || acc.messages.size > 0 };
516
+ };
517
+ const sinceMs = since ? Date.parse(since) : Number.NaN;
518
+ const totalStart = Number.isNaN(sinceMs) ? Math.min(...windows.map((w) => w.start)) : sinceMs;
519
+ let totalEnd = Math.max(...windows.map((w) => w.end));
520
+ if (!Number.isNaN(sinceMs)) {
521
+ for (const [, ts] of rows)
522
+ if (ts >= sinceMs)
523
+ totalEnd = Math.max(totalEnd, ts);
524
+ }
525
+ const untilMs = until ? Date.parse(until) : Number.NaN;
526
+ if (!Number.isNaN(untilMs) && untilMs > totalStart)
527
+ totalEnd = untilMs;
528
+ const stageRows = windows.map((w) => {
529
+ const { span, measured } = measure(w.start, w.end);
530
+ return summaryRow(w.stage, w.status, span, measured);
531
+ });
532
+ const totalMeasure = measure(totalStart, totalEnd);
533
+ const statuses = [...new Set(inputs.map((i) => i.progress.status ?? ""))].join("/");
534
+ const total = summaryRow("Total", statuses, totalMeasure.span, totalMeasure.measured);
535
+ const overlaps = windows.some((w, i) => i > 0 && w.start < (windows[i - 1]?.end ?? 0));
536
+ const sum = sumSpans(stageRows.map((r) => ({ ...r, toolCalls: r.toolCalls ?? 0, tokens: r.tokens ?? zeroTokens() })));
537
+ const overhead = {
538
+ workMs: total.workMs - sum.workMs,
539
+ waitMs: total.waitMs - sum.waitMs,
540
+ toolCalls: (total.toolCalls ?? 0) - sum.toolCalls,
541
+ tokens: Object.fromEntries(Object.keys(sum.tokens).map((k) => [k, (total.tokens?.[k] ?? 0) - sum.tokens[k]]))
542
+ };
543
+ if (!overlaps && overhead.workMs > 0)
544
+ stageRows.push(summaryRow("driver overhead", "", overhead, totalMeasure.measured));
545
+ return {
546
+ available: true,
547
+ rows: stageRows,
548
+ total,
549
+ actors: actorSplit(rows, totalStart, totalEnd),
550
+ gate: { ms: null, reason: "no matching check receipt" },
551
+ skippedLines: opts.skippedLines ?? 0
552
+ };
553
+ }
554
+ var SENSITIVE_PREFIXES = ["drizzle/", "packages/config/", ".github/", "plugins/sp/hooks/"];
555
+ var SENSITIVE_GLOBS = ["apps/server/src/**/auth*", "**/*secret*", "**/*.sql"];
556
+ function globToRegex(glob) {
557
+ const escaped = glob.replace(/[.+^${}()|[\]\\]/g, "\\$&").replaceAll("**/", "\x00").replaceAll("**", "\x01").replaceAll("*", "[^/]*").replaceAll("\x00", "(?:.*/)?").replaceAll("\x01", ".*");
558
+ return new RegExp(`^${escaped}$`, "s");
559
+ }
560
+ var SENSITIVE_MATCHERS = [
561
+ ...SENSITIVE_PREFIXES.map((prefix) => ({
562
+ label: prefix,
563
+ test: (path) => path.startsWith(prefix)
564
+ })),
565
+ ...SENSITIVE_GLOBS.map((glob) => {
566
+ const regex = globToRegex(glob);
567
+ return { label: glob, test: (path) => regex.test(path) };
568
+ })
569
+ ];
570
+ function sensitiveReasonForPath(path) {
571
+ const match = SENSITIVE_MATCHERS.find((entry) => entry.test(path));
572
+ return match ? `matches sensitive pattern ${match.label}` : null;
573
+ }
8
574
  function planLetter(index) {
9
575
  if (!Number.isInteger(index) || index < 0 || index > 25) {
10
576
  throw new Error(`plan letter out of range: ${index} (expected 0..25)`);
@@ -222,6 +788,118 @@ function buildBatchPlan(tasks) {
222
788
  }
223
789
  return waves;
224
790
  }
791
+ var byWbs = (a, b) => a.localeCompare(b, "en", { numeric: true });
792
+ function kahnOrder(nodes, preds) {
793
+ const indegree = new Map(nodes.map((n) => [n, preds.get(n)?.size ?? 0]));
794
+ const succ = new Map(nodes.map((n) => [n, []]));
795
+ for (const [n, ps] of preds)
796
+ for (const p of ps)
797
+ succ.get(p)?.push(n);
798
+ const ready = nodes.filter((n) => indegree.get(n) === 0).sort(byWbs);
799
+ const order = [];
800
+ for (let n = ready.shift();n !== undefined; n = ready.shift()) {
801
+ order.push(n);
802
+ for (const s of succ.get(n) ?? []) {
803
+ const d = (indegree.get(s) ?? 0) - 1;
804
+ indegree.set(s, d);
805
+ if (d === 0) {
806
+ ready.push(s);
807
+ ready.sort(byWbs);
808
+ }
809
+ }
810
+ }
811
+ return order;
812
+ }
813
+ function findCycle(remaining, preds) {
814
+ const state = new Map;
815
+ const stack = [];
816
+ const visit = (n) => {
817
+ state.set(n, "visiting");
818
+ stack.push(n);
819
+ for (const p of [...preds.get(n) ?? []].sort(byWbs)) {
820
+ if (!remaining.has(p))
821
+ continue;
822
+ if (state.get(p) === "visiting")
823
+ return [...stack.slice(stack.indexOf(p)), p];
824
+ if (!state.has(p)) {
825
+ const cycle = visit(p);
826
+ if (cycle)
827
+ return cycle;
828
+ }
829
+ }
830
+ stack.pop();
831
+ state.set(n, "done");
832
+ return null;
833
+ };
834
+ for (const n of [...remaining].sort(byWbs)) {
835
+ if (state.has(n))
836
+ continue;
837
+ const cycle = visit(n);
838
+ if (cycle)
839
+ return cycle;
840
+ }
841
+ return [];
842
+ }
843
+ function scheduleBatch(tasks, opts = {}) {
844
+ const byId = new Map;
845
+ for (const t of tasks)
846
+ if (!byId.has(t.wbs))
847
+ byId.set(t.wbs, t);
848
+ const nodes = [...byId.keys()].sort(byWbs);
849
+ const preds = new Map(nodes.map((n) => [n, new Set((byId.get(n)?.dependencies ?? []).filter((d) => byId.has(d)))]));
850
+ const depOrder = kahnOrder(nodes, preds);
851
+ if (depOrder.length < nodes.length) {
852
+ const cycle = findCycle(new Set(nodes.filter((n) => !depOrder.includes(n))), preds);
853
+ return {
854
+ order: [],
855
+ waves: [],
856
+ cycle,
857
+ overlaps: [],
858
+ mode: "sequential",
859
+ reason: `dependency cycle ${cycle.join(" → ")}`
860
+ };
861
+ }
862
+ const files = new Map(nodes.map((n) => [n, new Set((byId.get(n)?.files ?? []).map(normalizeTaskPath))]));
863
+ const overlaps = [];
864
+ for (let i = 0;i < depOrder.length; i++) {
865
+ const before = depOrder[i];
866
+ const beforeFiles = files.get(before) ?? new Set;
867
+ for (const after of depOrder.slice(i + 1)) {
868
+ const shared = [...beforeFiles].filter((f) => files.get(after)?.has(f)).sort();
869
+ if (shared.length === 0)
870
+ continue;
871
+ overlaps.push({ before, after, files: shared });
872
+ preds.get(after)?.add(before);
873
+ }
874
+ }
875
+ const order = kahnOrder(nodes, preds);
876
+ const level = new Map;
877
+ for (const n of order) {
878
+ let l = 0;
879
+ for (const p of preds.get(n) ?? [])
880
+ l = Math.max(l, (level.get(p) ?? 0) + 1);
881
+ level.set(n, l);
882
+ }
883
+ const waves = [];
884
+ for (const n of order) {
885
+ const l = level.get(n) ?? 0;
886
+ waves[l] = [...waves[l] ?? [], n].sort(byWbs);
887
+ }
888
+ const widest = Math.max(0, ...waves.map((w) => w.length));
889
+ const concurrency = opts.concurrency ?? 2;
890
+ let reason = `widest wave holds ${widest} independent tasks`;
891
+ if (nodes.length < 2)
892
+ reason = "fewer than two tasks";
893
+ else if (concurrency < 2)
894
+ reason = "concurrency is 1";
895
+ else if (widest < 2)
896
+ reason = "every wave holds one task (dependency or file-overlap chain)";
897
+ const mode = nodes.length >= 2 && concurrency >= 2 && widest >= 2 ? "parallel" : "sequential";
898
+ return { order, waves, cycle: null, overlaps, mode, reason };
899
+ }
900
+ function normalizeTaskPath(path) {
901
+ return path.trim().replace(/^\.\//, "");
902
+ }
225
903
  function taskPhaseChildren(taskLetter, plan) {
226
904
  if (!Array.isArray(plan))
227
905
  return [];
@@ -250,13 +928,22 @@ function hostText(item) {
250
928
  }
251
929
 
252
930
  // plugins/sp/scripts/batch-plan.ts
253
- var BATCH_PLAN_USAGE = "usage: batch-plan.ts waves --tasks <tasks.json> | task-children --letter <A-Z> --plan <plan.json>";
931
+ var BATCH_PLAN_USAGE = "usage: batch-plan.ts waves --tasks <tasks.json> | order --tasks <tasks.json> [--concurrency <n>] | task-children --letter <A-Z> --plan <plan.json>";
254
932
  function readJsonFile(path) {
255
933
  return JSON.parse(readFileSync(path, "utf8"));
256
934
  }
257
935
  function isBatchTask(value) {
258
936
  return typeof value === "object" && value !== null && typeof value.wbs === "string" && typeof value.name === "string";
259
937
  }
938
+ function isStringArray(value) {
939
+ return Array.isArray(value) && value.every((v) => typeof v === "string");
940
+ }
941
+ function isScheduleTask(value) {
942
+ if (typeof value !== "object" || value === null)
943
+ return false;
944
+ const t = value;
945
+ return typeof t.wbs === "string" && (t.dependencies === undefined || isStringArray(t.dependencies)) && (t.files === undefined || isStringArray(t.files));
946
+ }
260
947
  function main(argv) {
261
948
  const mode = argv[0];
262
949
  if (mode === "waves") {
@@ -279,6 +966,37 @@ function main(argv) {
279
966
  return 1;
280
967
  }
281
968
  process.stdout.write(`${JSON.stringify({ waves: buildBatchPlan(tasks) })}
969
+ `);
970
+ return 0;
971
+ }
972
+ if (mode === "order") {
973
+ let tasksPath = "";
974
+ let concurrency = 2;
975
+ for (let i = 1;i + 1 < argv.length; i += 2) {
976
+ if (argv[i] === "--tasks")
977
+ tasksPath = argv[i + 1] ?? "";
978
+ else if (argv[i] === "--concurrency")
979
+ concurrency = Number(argv[i + 1]);
980
+ }
981
+ if (tasksPath === "" || !Number.isInteger(concurrency) || concurrency < 1) {
982
+ process.stderr.write(`${BATCH_PLAN_USAGE}
983
+ `);
984
+ return 2;
985
+ }
986
+ let tasks;
987
+ try {
988
+ tasks = readJsonFile(tasksPath);
989
+ } catch (error) {
990
+ process.stderr.write(`batch-plan: cannot read ${tasksPath} \u2014 ${error instanceof Error ? error.message : String(error)}
991
+ `);
992
+ return 1;
993
+ }
994
+ if (!Array.isArray(tasks) || !tasks.every(isScheduleTask)) {
995
+ process.stderr.write(`batch-plan: --tasks file must be a JSON array of {wbs, dependencies?, files?}
996
+ `);
997
+ return 1;
998
+ }
999
+ process.stdout.write(`${JSON.stringify(scheduleBatch(tasks, { concurrency }))}
282
1000
  `);
283
1001
  return 0;
284
1002
  }
@@ -321,15 +1039,36 @@ function main(argv) {
321
1039
  process.exit(main(process.argv.slice(2)));
322
1040
  }
323
1041
  export {
1042
+ zeroTokens,
324
1043
  validatePhaseTable,
325
1044
  taskPhaseChildren,
1045
+ sumTokens,
1046
+ sumSpans,
1047
+ skillInvocationPrompt,
1048
+ sensitiveReasonForPath,
1049
+ scheduleBatch,
1050
+ quickReadiness,
1051
+ promptText,
1052
+ preflightTask,
326
1053
  planLetter,
327
1054
  planChild,
1055
+ newAcc,
328
1056
  main,
1057
+ isCompaction,
329
1058
  insertOnEntry,
330
1059
  hostText,
331
1060
  hostStatus,
1061
+ formatTokens,
1062
+ formatTokenSplit,
1063
+ formatDuration,
1064
+ classifyFeaturePreflightFindings,
1065
+ buildRunSummary,
332
1066
  buildPhasedPlan,
333
1067
  buildBatchPlan,
1068
+ actorSplit,
1069
+ accumulate,
1070
+ SENSITIVE_PREFIXES,
1071
+ SENSITIVE_GLOBS,
1072
+ NON_ABORTING_PREFLIGHT_CODES,
334
1073
  BATCH_PLAN_USAGE
335
1074
  };