@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,758 +0,0 @@
1
- #!/usr/bin/env node
2
- // @bun
3
-
4
- // plugins/sp/scripts/run-summary.ts
5
- import { readFileSync as readFileSync2 } from "fs";
6
-
7
- // plugins/sp/lib/env.ts
8
- function getEnvVars() {
9
- return process.env;
10
- }
11
-
12
- // plugins/sp/lib/run-summary-core.ts
13
- import { existsSync as existsSync2, mkdirSync, readFileSync, writeFileSync } from "node:fs";
14
- import { join as join2 } from "node:path";
15
-
16
- // plugins/sp/lib/env.ts
17
- function getEnvVars2() {
18
- return process.env;
19
- }
20
-
21
- // plugins/sp/lib/plan-projection.generated.mjs
22
- var NON_ABORTING_PREFLIGHT_CODES = new Set([
23
- "L4.scenario-unverified",
24
- "L4.verifying-incomplete-tasks"
25
- ]);
26
- var zeroTokens = () => ({ input: 0, cacheCreate: 0, cacheRead: 0, output: 0 });
27
- function formatDuration(ms) {
28
- const s = Math.round(ms / 1000);
29
- const h = Math.floor(s / 3600);
30
- const m = Math.floor(s % 3600 / 60);
31
- const ss = String(s % 60).padStart(2, "0");
32
- return h > 0 ? `${h}:${String(m).padStart(2, "0")}:${ss}` : `${m}:${ss}`;
33
- }
34
- function formatTokens(n) {
35
- if (n >= 999500)
36
- return `${(n / 1e6).toFixed(1)}M`;
37
- return n >= 1000 ? `${Math.round(n / 1000)}k` : String(n);
38
- }
39
- function formatTokenSplit(t) {
40
- if (!t)
41
- return "n/a";
42
- const nonCached = t.input + t.cacheCreate + t.output;
43
- return `${formatTokens(nonCached + t.cacheRead)} / ${formatTokens(nonCached)}`;
44
- }
45
- var OPERATOR_TOOLS = new Set(["AskUserQuestion", "ask_user_question"]);
46
- function isCompaction(row) {
47
- return row.type === "compaction" || row.type === "system" && row.subtype === "compact_boundary";
48
- }
49
- function firstTextContent(row) {
50
- const content = row.message?.content;
51
- if (typeof content === "string")
52
- return content;
53
- if (!Array.isArray(content))
54
- return;
55
- return content.find((b) => b.type === "text")?.text;
56
- }
57
- function skillInvocationPrompt(text) {
58
- const name = /^\s*<skill name="([^"]+)"/.exec(text)?.[1];
59
- if (name === undefined)
60
- return;
61
- const close = text.lastIndexOf("</skill>");
62
- const args = close < 0 ? "" : text.slice(close + "</skill>".length).trim();
63
- return args === "" ? `/${name}` : `/${name} ${args}`;
64
- }
65
- function promptText(row) {
66
- if (row.type === "custom_message") {
67
- if (row.display === false)
68
- return;
69
- const text = typeof row.content === "string" ? row.content.trim() : "";
70
- return text === "" ? undefined : text;
71
- }
72
- if (row.type === "message") {
73
- if (row.message?.role !== "user")
74
- return;
75
- const text = firstTextContent(row);
76
- if (text === undefined)
77
- return;
78
- return skillInvocationPrompt(text) ?? text;
79
- }
80
- if (row.type !== "user" || row.isMeta || row.isCompactSummary)
81
- return;
82
- const content = row.message?.content;
83
- if (typeof content === "string")
84
- return content;
85
- if (!Array.isArray(content) || content.some((b) => b.type === "tool_result"))
86
- return;
87
- return content.find((b) => b.type === "text")?.text;
88
- }
89
- function sumTokens(all) {
90
- const total = zeroTokens();
91
- for (const t of all)
92
- for (const k of Object.keys(total))
93
- total[k] += t[k];
94
- return total;
95
- }
96
- function sumSpans(spans) {
97
- return {
98
- workMs: spans.reduce((n, s) => n + s.workMs, 0),
99
- waitMs: spans.reduce((n, s) => n + s.waitMs, 0),
100
- toolCalls: spans.reduce((n, s) => n + s.toolCalls, 0),
101
- tokens: sumTokens(spans.map((s) => s.tokens))
102
- };
103
- }
104
- var newAcc = (ts) => ({
105
- last: ts,
106
- askMs: 0,
107
- asks: new Map,
108
- toolIds: new Set,
109
- messages: new Map,
110
- compactions: 0
111
- });
112
- function accumulate(acc, row, ts) {
113
- acc.last = Math.max(acc.last, ts);
114
- if (row.type === "message") {
115
- const m = row.message;
116
- if (m?.role === "assistant") {
117
- const blocks = Array.isArray(m.content) ? m.content : [];
118
- for (const b of blocks) {
119
- if (b.type === "toolCall" && b.id) {
120
- acc.toolIds.add(b.id);
121
- if (b.name && OPERATOR_TOOLS.has(b.name))
122
- acc.asks.set(b.id, ts);
123
- }
124
- }
125
- const u = m.usage;
126
- if (u && row.id)
127
- acc.messages.set(row.id, {
128
- input: u.input ?? 0,
129
- cacheCreate: u.cacheWrite ?? 0,
130
- cacheRead: u.cacheRead ?? 0,
131
- output: u.output ?? 0
132
- });
133
- } else if (m?.role === "toolResult" && m.toolCallId && acc.asks.has(m.toolCallId)) {
134
- acc.askMs += ts - (acc.asks.get(m.toolCallId) ?? ts);
135
- }
136
- return;
137
- }
138
- if (row.type === "compaction")
139
- acc.compactions++;
140
- const blocks = Array.isArray(row.message?.content) ? row.message.content : [];
141
- for (const b of blocks) {
142
- if (b.type === "tool_use" && b.id) {
143
- acc.toolIds.add(b.id);
144
- if (b.name && OPERATOR_TOOLS.has(b.name))
145
- acc.asks.set(b.id, ts);
146
- } else if (b.type === "tool_result" && b.tool_use_id && acc.asks.has(b.tool_use_id)) {
147
- acc.askMs += ts - (acc.asks.get(b.tool_use_id) ?? ts);
148
- }
149
- }
150
- const u = row.message?.usage;
151
- if (row.type === "assistant" && u && row.message?.id) {
152
- acc.messages.set(row.message.id, {
153
- input: u.input_tokens ?? 0,
154
- cacheCreate: u.cache_creation_input_tokens ?? 0,
155
- cacheRead: u.cache_read_input_tokens ?? 0,
156
- output: u.output_tokens ?? 0
157
- });
158
- }
159
- }
160
- var IDLE_GAP_MS = 300000;
161
- var SHELL_TOOLS = new Set(["bash", "shell", "exec", "exec_command", "run_command", "command"]);
162
- var SUBAGENT_TOOLS = new Set(["agent", "task", "invoke_subagent", "subagent", "dispatch_agent"]);
163
- var LOWERCASE_OPERATOR_TOOLS = new Set(["askuserquestion", "ask_user_question"]);
164
- var ACTOR_PRECEDENCE = ["operator", "subagent", "shell", "other"];
165
- function actorOf(tool) {
166
- const t = tool.toLowerCase().replace(/[^a-z_]/g, "");
167
- if (LOWERCASE_OPERATOR_TOOLS.has(t))
168
- return "operator";
169
- if (SUBAGENT_TOOLS.has(t))
170
- return "subagent";
171
- if (SHELL_TOOLS.has(t))
172
- return "shell";
173
- return "other";
174
- }
175
- function unionMs(spans, start, end) {
176
- 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]);
177
- let total = 0;
178
- let cursor = Number.NaN;
179
- let openUntil = Number.NaN;
180
- for (const [a, b] of clipped) {
181
- if (!Number.isFinite(cursor)) {
182
- cursor = a;
183
- openUntil = b;
184
- continue;
185
- }
186
- if (a > openUntil) {
187
- total += openUntil - cursor;
188
- cursor = a;
189
- openUntil = b;
190
- } else {
191
- openUntil = Math.max(openUntil, b);
192
- }
193
- }
194
- if (Number.isFinite(cursor))
195
- total += openUntil - cursor;
196
- return total;
197
- }
198
- function subtract(spans, claimed) {
199
- let free = spans;
200
- for (const [ca, cb] of claimed) {
201
- const next = [];
202
- for (const [a, b] of free) {
203
- if (cb <= a || ca >= b) {
204
- next.push([a, b]);
205
- continue;
206
- }
207
- if (ca > a)
208
- next.push([a, ca]);
209
- if (cb < b)
210
- next.push([cb, b]);
211
- }
212
- free = next;
213
- }
214
- return free;
215
- }
216
- function actorSplit(rows, start, end) {
217
- const empty = {
218
- operatorMs: 0,
219
- longestOperatorWaitMs: 0,
220
- modelMs: 0,
221
- shellMs: 0,
222
- subagentMs: 0,
223
- otherToolMs: 0,
224
- idleMs: 0,
225
- compactions: 0,
226
- subagentErrors: 0
227
- };
228
- const window = end - start;
229
- if (window <= 0)
230
- return empty;
231
- const calls = new Map;
232
- const results = new Map;
233
- const eventTs = [];
234
- let compactions = 0;
235
- for (const [row, ts] of rows) {
236
- if (ts < start || ts > end)
237
- continue;
238
- eventTs.push(ts);
239
- if (isCompaction(row))
240
- compactions++;
241
- const blocks = Array.isArray(row.message?.content) ? row.message?.content : [];
242
- if (row.message?.role === "toolResult" && row.message.toolCallId !== undefined) {
243
- results.set(row.message.toolCallId, { ts, isError: row.message.isError === true });
244
- }
245
- for (const b of blocks) {
246
- if ((b.type === "tool_use" || b.type === "toolCall") && b.id !== undefined && b.name !== undefined) {
247
- calls.set(b.id, { id: b.id, actor: actorOf(b.name), ts });
248
- } else if (b.type === "tool_result" && b.tool_use_id !== undefined) {
249
- results.set(b.tool_use_id, { ts, isError: b.is_error === true });
250
- }
251
- }
252
- }
253
- let subagentErrors = 0;
254
- const resolved = [];
255
- for (const call of calls.values()) {
256
- const result = results.get(call.id);
257
- if (result?.isError === true && call.actor === "subagent")
258
- subagentErrors++;
259
- resolved.push({ ...call, end: Math.min(result?.ts ?? end, end) });
260
- }
261
- const claimed = [];
262
- const byActor = new Map;
263
- for (const actor of ACTOR_PRECEDENCE) {
264
- const mine = resolved.filter((r) => r.actor === actor).map((r) => [r.ts, r.end]);
265
- const free = subtract(mine, claimed);
266
- for (const span of free)
267
- claimed.push(span);
268
- const ms = unionMs(free, start, end);
269
- byActor.set(actor, ms);
270
- if (actor === "operator") {
271
- empty.longestOperatorWaitMs = Math.max(0, ...mine.map(([a, b]) => b - a));
272
- }
273
- }
274
- const boundaries = [...new Set([start, end, ...eventTs, ...claimed.flat()])].sort((a, b) => a - b);
275
- const claimedSorted = [...claimed].sort((a, b) => a[0] - b[0]);
276
- let idleMs = 0;
277
- for (let i = 0;i < boundaries.length - 1; i++) {
278
- const a = boundaries[i];
279
- const b = boundaries[i + 1];
280
- if (b - a < IDLE_GAP_MS)
281
- continue;
282
- if (claimedSorted.some(([ca, cb]) => ca < b && cb > a))
283
- continue;
284
- idleMs += b - a;
285
- }
286
- const accounted = (byActor.get("operator") ?? 0) + (byActor.get("subagent") ?? 0) + (byActor.get("shell") ?? 0) + (byActor.get("other") ?? 0) + idleMs;
287
- return {
288
- ...empty,
289
- operatorMs: byActor.get("operator") ?? 0,
290
- subagentMs: byActor.get("subagent") ?? 0,
291
- shellMs: byActor.get("shell") ?? 0,
292
- otherToolMs: byActor.get("other") ?? 0,
293
- idleMs,
294
- modelMs: Math.max(0, window - accounted),
295
- compactions,
296
- subagentErrors
297
- };
298
- }
299
- function attemptWindow(attempts) {
300
- const starts = attempts.map((a) => Date.parse(a.startedAt ?? "")).filter((n) => !Number.isNaN(n));
301
- const ends = attempts.map((a) => Date.parse(a.completedAt ?? "")).filter((n) => !Number.isNaN(n));
302
- return starts.length && ends.length ? [Math.min(...starts), Math.max(...ends)] : undefined;
303
- }
304
- function summaryRow(stage, status, span, measured) {
305
- return {
306
- stage,
307
- status,
308
- workMs: span.workMs,
309
- waitMs: span.waitMs,
310
- toolCalls: measured ? span.toolCalls : null,
311
- tokens: measured ? span.tokens : null,
312
- time: formatDuration(span.workMs),
313
- wait: formatDuration(span.waitMs),
314
- token: formatTokenSplit(measured ? span.tokens : null)
315
- };
316
- }
317
- function buildRunSummary(rows, inputs, opts = {}) {
318
- const { since, until } = opts;
319
- const windows = [];
320
- for (const { label, progress } of inputs) {
321
- const states = (progress.states ?? []).flatMap((s) => {
322
- const w = attemptWindow((s.actions ?? []).flatMap((a) => a.attempts ?? []));
323
- return w ? [{ s, w }] : [];
324
- });
325
- if (!states.length)
326
- throw new Error(`progress for run ${progress.runId} has no timed attempts`);
327
- if (inputs.length === 1) {
328
- for (const { s, w } of states) {
329
- const stage = (s.visit ?? 1) > 1 ? `${s.state} (visit ${s.visit})` : s.state;
330
- windows.push({ stage, status: s.status ?? "", start: w[0], end: w[1] });
331
- }
332
- } else {
333
- const stage = label ?? `${progress.workflow ?? "run"} ${progress.runId.slice(0, 8)}`;
334
- const start = Math.min(...states.map(({ w }) => w[0]));
335
- windows.push({
336
- stage,
337
- status: progress.status ?? "",
338
- start,
339
- end: Math.max(...states.map(({ w }) => w[1]))
340
- });
341
- }
342
- }
343
- windows.sort((a, b) => a.start - b.start);
344
- const measure = (start, end) => {
345
- const acc = newAcc(start);
346
- for (const [row, ts] of rows)
347
- if (ts >= start && ts <= end && promptText(row) === undefined)
348
- accumulate(acc, row, ts);
349
- const span = {
350
- workMs: end - start - acc.askMs,
351
- waitMs: acc.askMs,
352
- toolCalls: acc.toolIds.size,
353
- tokens: sumTokens([...acc.messages.values()])
354
- };
355
- return { span, measured: acc.toolIds.size > 0 || acc.messages.size > 0 };
356
- };
357
- const sinceMs = since ? Date.parse(since) : Number.NaN;
358
- const totalStart = Number.isNaN(sinceMs) ? Math.min(...windows.map((w) => w.start)) : sinceMs;
359
- let totalEnd = Math.max(...windows.map((w) => w.end));
360
- if (!Number.isNaN(sinceMs)) {
361
- for (const [, ts] of rows)
362
- if (ts >= sinceMs)
363
- totalEnd = Math.max(totalEnd, ts);
364
- }
365
- const untilMs = until ? Date.parse(until) : Number.NaN;
366
- if (!Number.isNaN(untilMs) && untilMs > totalStart)
367
- totalEnd = untilMs;
368
- const stageRows = windows.map((w) => {
369
- const { span, measured } = measure(w.start, w.end);
370
- return summaryRow(w.stage, w.status, span, measured);
371
- });
372
- const totalMeasure = measure(totalStart, totalEnd);
373
- const statuses = [...new Set(inputs.map((i) => i.progress.status ?? ""))].join("/");
374
- const total = summaryRow("Total", statuses, totalMeasure.span, totalMeasure.measured);
375
- const overlaps = windows.some((w, i) => i > 0 && w.start < (windows[i - 1]?.end ?? 0));
376
- const sum = sumSpans(stageRows.map((r) => ({ ...r, toolCalls: r.toolCalls ?? 0, tokens: r.tokens ?? zeroTokens() })));
377
- const overhead = {
378
- workMs: total.workMs - sum.workMs,
379
- waitMs: total.waitMs - sum.waitMs,
380
- toolCalls: (total.toolCalls ?? 0) - sum.toolCalls,
381
- tokens: Object.fromEntries(Object.keys(sum.tokens).map((k) => [k, (total.tokens?.[k] ?? 0) - sum.tokens[k]]))
382
- };
383
- if (!overlaps && overhead.workMs > 0)
384
- stageRows.push(summaryRow("driver overhead", "", overhead, totalMeasure.measured));
385
- return {
386
- available: true,
387
- rows: stageRows,
388
- total,
389
- actors: actorSplit(rows, totalStart, totalEnd),
390
- gate: { ms: null, reason: "no matching check receipt" },
391
- skippedLines: opts.skippedLines ?? 0
392
- };
393
- }
394
- var SENSITIVE_PREFIXES = ["drizzle/", "packages/config/", ".github/", "plugins/sp/hooks/"];
395
- var SENSITIVE_GLOBS = ["apps/server/src/**/auth*", "**/*secret*", "**/*.sql"];
396
- function globToRegex(glob) {
397
- const escaped = glob.replace(/[.+^${}()|[\]\\]/g, "\\$&").replaceAll("**/", "\x00").replaceAll("**", "\x01").replaceAll("*", "[^/]*").replaceAll("\x00", "(?:.*/)?").replaceAll("\x01", ".*");
398
- return new RegExp(`^${escaped}$`, "s");
399
- }
400
- var SENSITIVE_MATCHERS = [
401
- ...SENSITIVE_PREFIXES.map((prefix) => ({
402
- label: prefix,
403
- test: (path) => path.startsWith(prefix)
404
- })),
405
- ...SENSITIVE_GLOBS.map((glob) => {
406
- const regex = globToRegex(glob);
407
- return { label: glob, test: (path) => regex.test(path) };
408
- })
409
- ];
410
-
411
- // plugins/sp/lib/transcript.ts
412
- import { existsSync, readdirSync } from "node:fs";
413
- import { homedir } from "node:os";
414
- import { join } from "node:path";
415
- var SESSION_ID = /^[A-Za-z0-9_-]+$/;
416
- var PI_ROLES = new Set(["user", "assistant", "toolResult"]);
417
- function sniffFormat(rows) {
418
- for (const row of rows) {
419
- if (row.type === "message" && PI_ROLES.has(row.message?.role ?? ""))
420
- return "pi";
421
- if (row.type === "user" || row.type === "assistant")
422
- return "claude";
423
- }
424
- return "unknown";
425
- }
426
- function parseRows(lines) {
427
- const rows = [];
428
- let skippedLines = 0;
429
- for (const line of lines) {
430
- if (!line.trim())
431
- continue;
432
- let row;
433
- try {
434
- row = JSON.parse(line);
435
- } catch {
436
- skippedLines++;
437
- continue;
438
- }
439
- const ts = row.timestamp ? Date.parse(row.timestamp) : Number.NaN;
440
- if (!Number.isNaN(ts))
441
- rows.push([row, ts]);
442
- }
443
- return { rows, skippedLines, format: sniffFormat(rows.map(([r]) => r)) };
444
- }
445
- function resolveTranscript(env, projectsRoot = join(homedir(), ".claude", "projects"), override) {
446
- if (override)
447
- return existsSync(override) ? { ok: true, path: override } : { ok: false, reason: "no transcript" };
448
- const id = env.CLAUDE_CODE_SESSION_ID;
449
- if (!id) {
450
- const piFile = env.PI_SESSION_FILE;
451
- if (piFile !== undefined && piFile !== "") {
452
- return existsSync(piFile) ? { ok: true, path: piFile } : { ok: false, reason: `PI_SESSION_FILE ${piFile} does not exist` };
453
- }
454
- return {
455
- ok: false,
456
- reason: "no host session id; pass --transcript <path> (pi: ~/.pi/agent/sessions/<cwd-slug>/<file>.jsonl, " + "or set PI_SESSION_FILE)"
457
- };
458
- }
459
- if (!SESSION_ID.test(id))
460
- return { ok: false, reason: "refusing a session id with path characters" };
461
- if (!existsSync(projectsRoot))
462
- return { ok: false, reason: `no transcript root ${projectsRoot}` };
463
- for (const dir of readdirSync(projectsRoot)) {
464
- const path = join(projectsRoot, dir, `${id}.jsonl`);
465
- if (existsSync(path))
466
- return { ok: true, path };
467
- }
468
- return { ok: false, reason: `no transcript for session ${id}` };
469
- }
470
-
471
- // plugins/sp/lib/run-summary-core.ts
472
- function renderSummaryMarkdown(summary) {
473
- const line = (r, bold = false) => {
474
- const stage = bold ? `**${r.stage}**` : r.stage;
475
- return `| ${stage} | ${r.status} | ${r.time} | ${r.wait} | ${r.toolCalls ?? "n/a"} | ${r.token} |`;
476
- };
477
- const a = summary.actors;
478
- const window = summary.total.workMs + summary.total.waitMs;
479
- const share = (ms) => window > 0 ? `${Math.round(ms / window * 100)}%` : "n/a";
480
- const gate = summary.gate.ms === null ? `n/a (${summary.gate.reason})` : formatDuration(summary.gate.ms);
481
- return [
482
- "| Stage | Status | Time | Wait | Tool calls | Token (total / non-cached) |",
483
- "| --- | --- | ---: | ---: | ---: | ---: |",
484
- ...summary.rows.map((r) => line(r)),
485
- line(summary.total, true),
486
- "",
487
- "| Actor | Time | Share |",
488
- "| --- | ---: | ---: |",
489
- `| operator wait (longest ${formatDuration(a.longestOperatorWaitMs)}) | ${formatDuration(a.operatorMs)} | ${share(a.operatorMs)} |`,
490
- `| model | ${formatDuration(a.modelMs)} | ${share(a.modelMs)} |`,
491
- `| shell | ${formatDuration(a.shellMs)} | ${share(a.shellMs)} |`,
492
- `| of which gate (informational) | ${gate} | — |`,
493
- `| subagent | ${formatDuration(a.subagentMs)} | ${share(a.subagentMs)} |`,
494
- `| other tools | ${formatDuration(a.otherToolMs)} | ${share(a.otherToolMs)} |`,
495
- `| idle / unattributed | ${formatDuration(a.idleMs)} | ${share(a.idleMs)} |`,
496
- "",
497
- `Compactions: ${a.compactions} · Subagent errors: ${a.subagentErrors}`
498
- ].join(`
499
- `);
500
- }
501
- function renderRollupMarkdown(rows) {
502
- const line = (label, r, actors, bold = false) => {
503
- const name = bold ? `**${label}**` : label;
504
- return `| ${name} | ${r.status} | ${r.time} | ${r.wait} | ${r.toolCalls ?? "n/a"} | ${r.token} | ${formatDuration(actors.operatorMs)} | ${formatDuration(actors.subagentMs)} |`;
505
- };
506
- const total = rows.reduce((acc, { summary }) => ({
507
- workMs: acc.workMs + summary.total.workMs,
508
- waitMs: acc.waitMs + summary.total.waitMs,
509
- toolCalls: (acc.toolCalls ?? 0) + (summary.total.toolCalls ?? 0),
510
- tokens: sumTokens([acc.tokens, summary.total.tokens ?? zeroTokens()])
511
- }), { workMs: 0, waitMs: 0, toolCalls: 0, tokens: zeroTokens() });
512
- const totalRow = {
513
- stage: "Batch total",
514
- status: "",
515
- workMs: total.workMs,
516
- waitMs: total.waitMs,
517
- toolCalls: total.toolCalls,
518
- tokens: total.tokens,
519
- time: formatDuration(total.workMs),
520
- wait: formatDuration(total.waitMs),
521
- token: formatTokenSplit(total.tokens)
522
- };
523
- const totalActors = {
524
- operatorMs: 0,
525
- longestOperatorWaitMs: 0,
526
- modelMs: 0,
527
- shellMs: 0,
528
- subagentMs: 0,
529
- otherToolMs: 0,
530
- idleMs: 0,
531
- compactions: 0,
532
- subagentErrors: 0
533
- };
534
- for (const { summary } of rows) {
535
- totalActors.operatorMs += summary.actors.operatorMs;
536
- totalActors.subagentMs += summary.actors.subagentMs;
537
- totalActors.compactions += summary.actors.compactions;
538
- totalActors.subagentErrors += summary.actors.subagentErrors;
539
- }
540
- const out = [
541
- "| Run | Status | Time | Wait | Tool calls | Token (total / non-cached) | Operator wait | Subagent |",
542
- "| --- | --- | ---: | ---: | ---: | ---: | ---: | ---: |",
543
- ...rows.map(({ label, summary }) => line(label, summary.total, summary.actors)),
544
- line("Batch total", totalRow, totalActors, true)
545
- ];
546
- const starts = rows.map((r) => Date.parse(r.summary.startedAt ?? "")).filter((n) => !Number.isNaN(n));
547
- const ends = rows.map((r) => Date.parse(r.summary.completedAt ?? "")).filter((n) => !Number.isNaN(n));
548
- const overlaps = starts.length === rows.length && rows.some((r, i) => rows.some((o, j) => i !== j && Date.parse(r.summary.startedAt ?? "") < Date.parse(o.summary.completedAt ?? "") && Date.parse(o.summary.startedAt ?? "") < Date.parse(r.summary.completedAt ?? "")));
549
- if (overlaps && starts.length > 0 && ends.length > 0) {
550
- out.push("", `Wall span: ${new Date(Math.min(...starts)).toISOString()} → ${new Date(Math.max(...ends)).toISOString()}`);
551
- }
552
- return out.join(`
553
- `);
554
- }
555
- function writeCloseSummary(run, opts = {}) {
556
- const cwd = opts.cwd ?? process.cwd();
557
- const runDir = join2(cwd, ".spur", "run");
558
- const rel = join2(".spur", "run", `${run.runId}-summary.md`);
559
- const abs = join2(cwd, rel);
560
- const na = (reason) => {
561
- mkdirSync(runDir, { recursive: true });
562
- writeFileSync(abs, `Execution summary: n/a (${reason})
563
- `);
564
- return rel;
565
- };
566
- try {
567
- if (run.startedAt === undefined)
568
- return na("run row has no started_at");
569
- const resolved = resolveTranscript(opts.env ?? getEnvVars2(), opts.projectsRoot);
570
- if (!resolved.ok)
571
- return na(resolved.reason);
572
- const { rows, skippedLines } = parseRows(readFileSync(resolved.path, "utf8").split(`
573
- `));
574
- const summary = buildRunSummary(rows, [{ progress: run.progress }], {
575
- since: run.startedAt,
576
- until: run.completedAt,
577
- skippedLines
578
- });
579
- if (summary.rows.length === 0)
580
- return na("no timed attempts");
581
- mkdirSync(runDir, { recursive: true });
582
- writeFileSync(abs, `${renderSummaryMarkdown(summary)}
583
- `);
584
- writeFileSync(join2(runDir, `${run.runId}-summary.json`), JSON.stringify({ ...summary, runId: run.runId, startedAt: run.startedAt, completedAt: run.completedAt }, null, 2));
585
- return rel;
586
- } catch (error) {
587
- return na(error instanceof Error ? error.message : String(error));
588
- }
589
- }
590
- function gateFromReceipt(cwd, runId) {
591
- const path = join2(cwd, ".spur", "run", `${runId}-check-receipt.json`);
592
- if (!existsSync2(path))
593
- return { ms: null, reason: "no matching check receipt" };
594
- try {
595
- const parsed = JSON.parse(readFileSync(path, "utf8"));
596
- if (parsed.runId !== runId)
597
- return { ms: null, reason: "check receipt belongs to another run" };
598
- const ms = parsed.gateRuntimeMs;
599
- if (typeof ms !== "number" || !Number.isFinite(ms))
600
- return { ms: null, reason: "check receipt has no gate time" };
601
- return { ms };
602
- } catch {
603
- return { ms: null, reason: "check receipt is unreadable" };
604
- }
605
- }
606
-
607
- // plugins/sp/lib/transcript.ts
608
- import { existsSync as existsSync3, readdirSync as readdirSync2 } from "node:fs";
609
- import { homedir as homedir2 } from "node:os";
610
- import { join as join3 } from "node:path";
611
- var SESSION_ID2 = /^[A-Za-z0-9_-]+$/;
612
- var PI_ROLES2 = new Set(["user", "assistant", "toolResult"]);
613
- function sniffFormat2(rows) {
614
- for (const row of rows) {
615
- if (row.type === "message" && PI_ROLES2.has(row.message?.role ?? ""))
616
- return "pi";
617
- if (row.type === "user" || row.type === "assistant")
618
- return "claude";
619
- }
620
- return "unknown";
621
- }
622
- function parseRows2(lines) {
623
- const rows = [];
624
- let skippedLines = 0;
625
- for (const line of lines) {
626
- if (!line.trim())
627
- continue;
628
- let row;
629
- try {
630
- row = JSON.parse(line);
631
- } catch {
632
- skippedLines++;
633
- continue;
634
- }
635
- const ts = row.timestamp ? Date.parse(row.timestamp) : Number.NaN;
636
- if (!Number.isNaN(ts))
637
- rows.push([row, ts]);
638
- }
639
- return { rows, skippedLines, format: sniffFormat2(rows.map(([r]) => r)) };
640
- }
641
- function resolveTranscript2(env, projectsRoot = join3(homedir2(), ".claude", "projects"), override) {
642
- if (override)
643
- return existsSync3(override) ? { ok: true, path: override } : { ok: false, reason: "no transcript" };
644
- const id = env.CLAUDE_CODE_SESSION_ID;
645
- if (!id) {
646
- const piFile = env.PI_SESSION_FILE;
647
- if (piFile !== undefined && piFile !== "") {
648
- return existsSync3(piFile) ? { ok: true, path: piFile } : { ok: false, reason: `PI_SESSION_FILE ${piFile} does not exist` };
649
- }
650
- return {
651
- ok: false,
652
- reason: "no host session id; pass --transcript <path> (pi: ~/.pi/agent/sessions/<cwd-slug>/<file>.jsonl, " + "or set PI_SESSION_FILE)"
653
- };
654
- }
655
- if (!SESSION_ID2.test(id))
656
- return { ok: false, reason: "refusing a session id with path characters" };
657
- if (!existsSync3(projectsRoot))
658
- return { ok: false, reason: `no transcript root ${projectsRoot}` };
659
- for (const dir of readdirSync2(projectsRoot)) {
660
- const path = join3(projectsRoot, dir, `${id}.jsonl`);
661
- if (existsSync3(path))
662
- return { ok: true, path };
663
- }
664
- return { ok: false, reason: `no transcript for session ${id}` };
665
- }
666
-
667
- // plugins/sp/scripts/run-summary.ts
668
- var RUN_SUMMARY_USAGE = "usage: run-summary (--progress [<label>=]<file> [--progress ...] | --rollup [<label>=]<summary.json> [--rollup ...]) [--transcript <path>] [--since <iso>] [--markdown]";
669
- function main(argv, env = getEnvVars(), write = (s) => process.stdout.write(s), projectsRoot) {
670
- let transcript;
671
- let since;
672
- let markdown = false;
673
- const progressArgs = [];
674
- const rollupArgs = [];
675
- for (let i = 0;i < argv.length; i++) {
676
- const arg = argv[i];
677
- if (arg === "--transcript" && argv[i + 1])
678
- transcript = argv[++i];
679
- else if (arg === "--progress" && argv[i + 1])
680
- progressArgs.push(argv[++i]);
681
- else if (arg === "--rollup" && argv[i + 1])
682
- rollupArgs.push(argv[++i]);
683
- else if (arg === "--since" && argv[i + 1])
684
- since = argv[++i];
685
- else if (arg === "--markdown")
686
- markdown = true;
687
- else if (arg === "--spur-bin" && argv[i + 1])
688
- i++;
689
- else {
690
- process.stderr.write(`${RUN_SUMMARY_USAGE}
691
- `);
692
- return 2;
693
- }
694
- }
695
- if (rollupArgs.length > 0 && (progressArgs.length > 0 || since !== undefined || transcript !== undefined)) {
696
- process.stderr.write(`${RUN_SUMMARY_USAGE}
697
- `);
698
- return 2;
699
- }
700
- if (rollupArgs.length > 0) {
701
- try {
702
- const inputs = rollupArgs.map((arg) => {
703
- const eq = arg.indexOf("=");
704
- const [label, file] = eq > 0 ? [arg.slice(0, eq), arg.slice(eq + 1)] : [undefined, arg];
705
- const summary = JSON.parse(readFileSync2(file, "utf8"));
706
- return { label: label ?? summary.runId ?? file, summary };
707
- });
708
- write(`${renderRollupMarkdown(inputs)}
709
- `);
710
- return 0;
711
- } catch (error) {
712
- process.stderr.write(`run-summary: ${error.message}
713
- `);
714
- return 2;
715
- }
716
- }
717
- if (!progressArgs.length) {
718
- process.stderr.write(`${RUN_SUMMARY_USAGE}
719
- `);
720
- return 2;
721
- }
722
- const resolved = resolveTranscript2(env, projectsRoot, transcript);
723
- if (!resolved.ok) {
724
- const out = markdown ? `Execution summary: n/a (${resolved.reason})` : JSON.stringify({ available: false, reason: resolved.reason });
725
- write(`${out}
726
- `);
727
- return 0;
728
- }
729
- try {
730
- const inputs = progressArgs.map((arg) => {
731
- const eq = arg.indexOf("=");
732
- const [label, file] = eq > 0 ? [arg.slice(0, eq), arg.slice(eq + 1)] : [undefined, arg];
733
- return { label, progress: JSON.parse(readFileSync2(file, "utf8")) };
734
- });
735
- const { rows, skippedLines } = parseRows2(readFileSync2(resolved.path, "utf8").split(`
736
- `));
737
- const summary = buildRunSummary(rows, inputs, { since, skippedLines });
738
- summary.gate = gateFromReceipt(process.cwd(), inputs[0]?.progress.runId ?? "");
739
- write(`${markdown ? renderSummaryMarkdown(summary) : JSON.stringify({ ...summary, transcript: resolved.path })}
740
- `);
741
- return 0;
742
- } catch (error) {
743
- process.stderr.write(`run-summary: ${error.message}
744
- `);
745
- return 2;
746
- }
747
- }
748
- process.exit(main(process.argv.slice(2)));
749
- export {
750
- RUN_SUMMARY_USAGE,
751
- actorSplit,
752
- buildRunSummary,
753
- gateFromReceipt,
754
- main,
755
- renderRollupMarkdown,
756
- renderSummaryMarkdown,
757
- writeCloseSummary
758
- };