@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.
- package/.claude-plugin/marketplace.json +1 -1
- package/config/pipeline-budgets.json +2 -2
- package/config/rules/README.md +1 -1
- package/config/rules/boundary/config-loading-ownership.yaml +1 -0
- package/config/rules/boundary/env-var-hygiene.yaml +0 -1
- package/config/rules/boundary/sp-plugin-standalone.yaml +1 -1
- package/config/rules/boundary/sp-script-placement.yaml +3 -3
- package/config/rules/docs/sourced-api-claims.yaml +29 -64
- package/config/rules/strict/runtime-boundaries.yaml +40 -1
- package/config/rules/structure/protected-files.yaml +1 -1
- package/config/rules/surface/check-cli-surface.yaml +33 -0
- package/config/rules/typescript/no-debugger.yaml +3 -0
- package/config/workflow-candidates.json +2 -2
- package/config/workflows/feature-verification.yaml +6 -19
- package/config/workflows/history-anatomy.yaml +14 -28
- package/config/workflows/idea-pipeline.yaml +6 -19
- package/config/workflows/pr-review.yaml +9 -16
- package/config/workflows/task-pipeline.yaml +18 -25
- package/config/workflows/wrapup-pipeline.yaml +21 -63
- package/package.json +3 -3
- package/plugins/sp/README.md +70 -37
- package/plugins/sp/agents/expert-spur.md +0 -1
- package/plugins/sp/agents/super-planner.md +5 -5
- package/plugins/sp/agents/super-reviewer.md +2 -2
- package/plugins/sp/commands/dev-changelog.md +1 -1
- package/plugins/sp/commands/dev-daily.md +1 -1
- package/plugins/sp/commands/dev-pr-review.md +1 -1
- package/plugins/sp/commands/dev-review-session.md +1 -1
- package/plugins/sp/commands/dev-runall.md +4 -4
- package/plugins/sp/commands/dev-verify.md +4 -6
- package/plugins/sp/commands/dev-verifyall.md +4 -5
- package/plugins/sp/hooks/hooks.json +11 -1
- package/plugins/sp/lib/context-post-tool.ts +1 -1
- package/plugins/sp/lib/context-session-start.ts +1 -1
- package/plugins/sp/lib/context-session-stop.ts +1 -1
- package/plugins/sp/lib/destructive-policy.ts +65 -1
- package/plugins/sp/plugin.json +2 -5
- package/plugins/sp/{hooks → scripts/hooks}/agent-lifecycle.ts +1 -1
- package/plugins/sp/scripts/hooks/careful-guard.ts +94 -0
- package/plugins/sp/{hooks → scripts/hooks}/context-post-tool.ts +2 -2
- package/plugins/sp/{hooks → scripts/hooks}/context-session-start.ts +1 -1
- package/plugins/sp/{hooks → scripts/hooks}/context-session-stop.ts +1 -1
- package/plugins/sp/{hooks → scripts/hooks}/fleet-guest-stop.ts +6 -4
- package/plugins/sp/{hooks → scripts/hooks}/task-write-guard.ts +38 -21
- package/plugins/sp/skills/brainstorm/SKILL.md +12 -11
- package/plugins/sp/skills/brainstorm/agents/openai.yaml +1 -1
- package/plugins/sp/skills/brainstorm/examples/ideation-example.md +0 -4
- package/plugins/sp/skills/brainstorm/metadata.openclaw +1 -1
- package/plugins/sp/skills/brainstorm/references/workflows.md +4 -14
- package/plugins/sp/skills/code-implementation/SKILL.md +1 -1
- package/plugins/sp/skills/code-verification/SKILL.md +8 -8
- package/plugins/sp/skills/code-verification/references/secu-review.md +4 -4
- package/plugins/sp/skills/code-verification/references/verdict-schema.md +9 -1
- package/plugins/sp/skills/daily-summary/SKILL.md +4 -5
- package/plugins/sp/skills/dogfood-testing/SKILL.md +9 -9
- package/plugins/sp/skills/dogfood-testing/references/report-template.md +1 -1
- package/plugins/sp/skills/history-anatomy/references/modes.md +2 -2
- package/plugins/sp/skills/history-anatomy/references/operations.md +3 -4
- package/plugins/sp/skills/history-anatomy/references/report-contract.md +2 -3
- package/plugins/sp/skills/issue-finding/SKILL.md +1 -1
- package/plugins/sp/skills/next-router/references/routing-table.md +1 -1
- package/plugins/sp/skills/parallel-execution/SKILL.md +2 -2
- package/plugins/sp/skills/parallel-execution/references/result-synthesis.md +9 -10
- package/plugins/sp/skills/pr-reviewing/SKILL.md +14 -15
- package/plugins/sp/skills/reverse-engineering/SKILL.md +4 -6
- package/plugins/sp/skills/session-review/SKILL.md +13 -29
- package/plugins/sp/skills/source-driven-development/SKILL.md +26 -27
- package/plugins/sp/skills/spur-check/SKILL.md +14 -13
- package/plugins/sp/skills/spur-cli/SKILL.md +1 -1
- package/plugins/sp/skills/spur-cli/references/agent.md +17 -2
- package/plugins/sp/skills/spur-cli/references/builder.md +27 -6
- package/plugins/sp/skills/spur-cli/references/features.md +6 -1
- package/plugins/sp/skills/spur-cli/references/history.md +3 -0
- package/plugins/sp/skills/spur-cli/references/self.md +5 -3
- package/plugins/sp/skills/spur-cli/references/tasks/verbs.md +3 -0
- package/plugins/sp/skills/spur-cli/references/tasks.md +13 -3
- package/plugins/sp/skills/spur-cli/references/workflows/authoring-workflows.md +1 -1
- package/plugins/sp/skills/spur-cli/references/workflows.md +36 -1
- package/plugins/sp/skills/spur-dev/SKILL.md +1 -1
- package/plugins/sp/skills/spur-dev/references/cross-cutting.md +1 -1
- package/plugins/sp/skills/spur-dev/references/dev-operations.md +12 -13
- package/plugins/sp/skills/spur-dev/references/execution-batch-report.md +10 -11
- package/plugins/sp/skills/spur-dev/references/execution-batch.md +16 -17
- package/plugins/sp/skills/spur-dev/references/execution-parallel-isolation.md +1 -1
- package/plugins/sp/skills/spur-dev/references/execution-worktree-landing.md +9 -12
- package/plugins/sp/skills/spur-dev/references/execution-worktree-setup.md +6 -8
- package/plugins/sp/skills/spur-dev/references/gate-checklists.md +1 -1
- package/plugins/sp/skills/spur-dev/references/inline-pipeline-driver.md +33 -40
- package/plugins/sp/skills/spur-dev/references/structured-trace-emission.md +24 -24
- package/plugins/sp/skills/spur-doctor/SKILL.md +5 -4
- package/spur.js +12726 -1456
- package/web/_astro/{BoardApp.CaNvQBD_.js → BoardApp.B5rTVNd_.js} +1 -1
- package/web/_astro/{BoardApp.C5jnkkKP.js → BoardApp.BMprmPRc.js} +141 -141
- package/web/_astro/{TaskDetail.DGKndhI8.js → TaskDetail.Ddsur8qd.js} +1 -1
- package/web/_astro/{arc.DHrwdg3G.js → arc.DLqVVBxP.js} +1 -1
- package/web/_astro/{architectureDiagram-3BPJPVTR.pvKpK1s5.js → architectureDiagram-3BPJPVTR.C-Pk3uAi.js} +1 -1
- package/web/_astro/{blockDiagram-GPEHLZMM.BQmRcZsS.js → blockDiagram-GPEHLZMM.BRO1i_Nm.js} +1 -1
- package/web/_astro/{c4Diagram-AAUBKEIU.CCLjHeH1.js → c4Diagram-AAUBKEIU.BU_cWu6e.js} +1 -1
- package/web/_astro/channel.Bs3Illhy.js +1 -0
- package/web/_astro/{chunk-2J33WTMH.DL6Yuoma.js → chunk-2J33WTMH.D2VXiQ2t.js} +1 -1
- package/web/_astro/{chunk-4BX2VUAB.CzlyEI7l.js → chunk-4BX2VUAB.BcHT772J.js} +1 -1
- package/web/_astro/{chunk-55IACEB6.B60EWxp1.js → chunk-55IACEB6.DhDtICmo.js} +1 -1
- package/web/_astro/{chunk-727SXJPM.c7v_fRLo.js → chunk-727SXJPM.BoKHp7gV.js} +1 -1
- package/web/_astro/{chunk-AQP2D5EJ.8BxLWdDc.js → chunk-AQP2D5EJ.Chu4JRpE.js} +1 -1
- package/web/_astro/{chunk-FMBD7UC4.DmKuxGFT.js → chunk-FMBD7UC4.wHdwKPlq.js} +1 -1
- package/web/_astro/{chunk-ND2GUHAM.1Zwd4mqV.js → chunk-ND2GUHAM.ffN-M1JB.js} +1 -1
- package/web/_astro/{chunk-QZHKN3VN.BvYLu-A1.js → chunk-QZHKN3VN.FRubwg4S.js} +1 -1
- package/web/_astro/{classDiagram-4FO5ZUOK.CEqRt0WU.js → classDiagram-4FO5ZUOK.c22tQsc_.js} +1 -1
- package/web/_astro/{classDiagram-v2-Q7XG4LA2.CEqRt0WU.js → classDiagram-v2-Q7XG4LA2.c22tQsc_.js} +1 -1
- package/web/_astro/{cose-bilkent-S5V4N54A.C7JiUdj2.js → cose-bilkent-S5V4N54A.B9c6fiSj.js} +1 -1
- package/web/_astro/{cynefin-OW5HDTMX.B6mZ-1be.js → cynefin-OW5HDTMX.DrZsrUHl.js} +1 -1
- package/web/_astro/{dagre-BM42HDAG.BuryfDy6.js → dagre-BM42HDAG.DG1U4b3y.js} +1 -1
- package/web/_astro/{diagram-2AECGRRQ.dao39YQM.js → diagram-2AECGRRQ.CH15b-K3.js} +1 -1
- package/web/_astro/{diagram-5GNKFQAL.lnGZe4At.js → diagram-5GNKFQAL.BH1j7twR.js} +1 -1
- package/web/_astro/{diagram-KO2AKTUF.uWfhzEfT.js → diagram-KO2AKTUF.q2zCkbAj.js} +1 -1
- package/web/_astro/{diagram-LMA3HP47.ppq8-198.js → diagram-LMA3HP47.BLaaZnHV.js} +1 -1
- package/web/_astro/{diagram-OG6HWLK6.CKSCqDt5.js → diagram-OG6HWLK6.BcpRdQwI.js} +1 -1
- package/web/_astro/{erDiagram-TEJ5UH35.sFSf2yBN.js → erDiagram-TEJ5UH35.D7KBlqfM.js} +1 -1
- package/web/_astro/{flowDiagram-I6XJVG4X.BJQjKLbz.js → flowDiagram-I6XJVG4X.D_gmuRqg.js} +1 -1
- package/web/_astro/{ganttDiagram-6RSMTGT7.DAHKNj2N.js → ganttDiagram-6RSMTGT7.DzgWYniD.js} +1 -1
- package/web/_astro/{gitGraphDiagram-PVQCEYII.BlmzcW_O.js → gitGraphDiagram-PVQCEYII.576i3hm3.js} +1 -1
- package/web/_astro/index.DiC1a6lh.css +1 -0
- package/web/_astro/{infoDiagram-5YYISTIA.DGhjaqsi.js → infoDiagram-5YYISTIA.DMCclBiz.js} +1 -1
- package/web/_astro/{ishikawaDiagram-YF4QCWOH.gKIpgt6F.js → ishikawaDiagram-YF4QCWOH.9Kyca4SH.js} +1 -1
- package/web/_astro/{journeyDiagram-JHISSGLW.DG3tmP0R.js → journeyDiagram-JHISSGLW.chbdG-8q.js} +1 -1
- package/web/_astro/{kanban-definition-UN3LZRKU.CgHDSxkR.js → kanban-definition-UN3LZRKU.Bds54-e0.js} +1 -1
- package/web/_astro/{linear.BuGEpcuM.js → linear.D5ND9GN_.js} +1 -1
- package/web/_astro/{mermaid.core.DBVqkWBp.js → mermaid.core.D9N_PGbj.js} +4 -4
- package/web/_astro/{mindmap-definition-RKZ34NQL.D-kOcji9.js → mindmap-definition-RKZ34NQL.t0xop5vp.js} +1 -1
- package/web/_astro/{pieDiagram-4H26LBE5.BvGPwtZt.js → pieDiagram-4H26LBE5.DqgKh6hM.js} +1 -1
- package/web/_astro/{quadrantDiagram-W4KKPZXB.BKb_9FV6.js → quadrantDiagram-W4KKPZXB.DeuHVi2t.js} +1 -1
- package/web/_astro/{requirementDiagram-4Y6WPE33.Cx0fvD8v.js → requirementDiagram-4Y6WPE33.BdYFSG4W.js} +1 -1
- package/web/_astro/{sankeyDiagram-5OEKKPKP.fatULi0W.js → sankeyDiagram-5OEKKPKP.k1LPPETJ.js} +1 -1
- package/web/_astro/{sequenceDiagram-3UESZ5HK.ChHyKqco.js → sequenceDiagram-3UESZ5HK.DdyfpzoG.js} +1 -1
- package/web/_astro/{stateDiagram-AJRCARHV.BdUpKWyj.js → stateDiagram-AJRCARHV.DSEI8il0.js} +1 -1
- package/web/_astro/{stateDiagram-v2-BHNVJYJU.fmJxqIm5.js → stateDiagram-v2-BHNVJYJU.CoMnQRKo.js} +1 -1
- package/web/_astro/{timeline-definition-PNZ67QCA.BayDn5-V.js → timeline-definition-PNZ67QCA.DM1AHNm-.js} +1 -1
- package/web/_astro/{vennDiagram-CIIHVFJN.Bm2Aggwa.js → vennDiagram-CIIHVFJN.C-cJmVtl.js} +1 -1
- package/web/_astro/{wardleyDiagram-YWT4CUSO.DI75O-uG.js → wardleyDiagram-YWT4CUSO.D0xMzKPb.js} +1 -1
- package/web/_astro/{xychartDiagram-2RQKCTM6.DL90E7Q7.js → xychartDiagram-2RQKCTM6.BH5xJcEq.js} +1 -1
- package/web/index.html +2 -2
- package/config/plugin-scripts.json +0 -116
- package/config/script-placement-baseline.json +0 -33
- package/plugins/sp/hooks/careful-guard.ts +0 -74
- package/plugins/sp/hooks/pi/guard-extension.ts +0 -304
- package/plugins/sp/lib/dogfood-pipeline.ts +0 -265
- package/plugins/sp/lib/dogfood-report.ts +0 -278
- package/plugins/sp/lib/history-anatomy.generated.d.mts +0 -112
- package/plugins/sp/lib/history-anatomy.generated.mjs +0 -691
- package/plugins/sp/lib/idea-handoff.generated.d.mts +0 -17
- package/plugins/sp/lib/idea-handoff.generated.mjs +0 -1446
- package/plugins/sp/lib/inline-run-app.ts +0 -68
- package/plugins/sp/lib/inline-run.generated.d.mts +0 -37
- package/plugins/sp/lib/inline-run.generated.mjs +0 -3103
- package/plugins/sp/lib/plan-projection.generated.d.mts +0 -197
- package/plugins/sp/lib/plan-projection.generated.mjs +0 -982
- package/plugins/sp/lib/quality-gate.generated.d.mts +0 -139
- package/plugins/sp/lib/quality-gate.generated.mjs +0 -30510
- package/plugins/sp/lib/residual-scan.generated.d.mts +0 -93
- package/plugins/sp/lib/residual-scan.generated.mjs +0 -383
- package/plugins/sp/lib/run-summary-core.ts +0 -225
- package/plugins/sp/lib/spur-bin.ts +0 -36
- package/plugins/sp/lib/step-profile.generated.d.mts +0 -71
- package/plugins/sp/lib/step-profile.generated.mjs +0 -174
- package/plugins/sp/lib/transcript.ts +0 -112
- package/plugins/sp/scripts/batch-plan.mjs +0 -1096
- package/plugins/sp/scripts/batch-plan.ts +0 -145
- package/plugins/sp/scripts/batch-preflight.mjs +0 -423
- package/plugins/sp/scripts/batch-preflight.ts +0 -270
- package/plugins/sp/scripts/commit-guard.mjs +0 -226
- package/plugins/sp/scripts/commit-guard.ts +0 -248
- package/plugins/sp/scripts/daily-summary/daily-summary.mjs +0 -626
- package/plugins/sp/scripts/daily-summary/daily-summary.ts +0 -855
- package/plugins/sp/scripts/dogfood-testing/detect-pipeline-driving.mjs +0 -211
- package/plugins/sp/scripts/dogfood-testing/detect-pipeline-driving.ts +0 -110
- package/plugins/sp/scripts/dogfood-testing/validate-report.mjs +0 -282
- package/plugins/sp/scripts/dogfood-testing/validate-report.ts +0 -98
- package/plugins/sp/scripts/feature-verification-steps.mjs +0 -82
- package/plugins/sp/scripts/feature-verification-steps.ts +0 -164
- package/plugins/sp/scripts/history-anatomy-cache.mjs +0 -931
- package/plugins/sp/scripts/history-anatomy-cache.ts +0 -247
- package/plugins/sp/scripts/idea-handoff.mjs +0 -32
- package/plugins/sp/scripts/idea-handoff.ts +0 -47
- package/plugins/sp/scripts/inline-run-dispatch.mjs +0 -131
- package/plugins/sp/scripts/inline-run-dispatch.ts +0 -115
- package/plugins/sp/scripts/inline-run-setup.mjs +0 -746
- package/plugins/sp/scripts/inline-run-setup.ts +0 -198
- package/plugins/sp/scripts/persist-out-check.mjs +0 -217
- package/plugins/sp/scripts/persist-out-check.ts +0 -249
- package/plugins/sp/scripts/pr-reviewing.mjs +0 -712
- package/plugins/sp/scripts/pr-reviewing.ts +0 -952
- package/plugins/sp/scripts/quality-gate.mjs +0 -30491
- package/plugins/sp/scripts/quality-gate.ts +0 -80
- package/plugins/sp/scripts/residual-scan.mjs +0 -634
- package/plugins/sp/scripts/residual-scan.ts +0 -248
- package/plugins/sp/scripts/run-summary.mjs +0 -758
- package/plugins/sp/scripts/run-summary.ts +0 -107
- package/plugins/sp/scripts/script-root.mjs +0 -129
- package/plugins/sp/scripts/script-root.ts +0 -199
- package/plugins/sp/scripts/session-timeline.mjs +0 -359
- package/plugins/sp/scripts/session-timeline.ts +0 -190
- package/plugins/sp/scripts/task-diffstat.mjs +0 -181
- package/plugins/sp/scripts/task-diffstat.ts +0 -219
- package/plugins/sp/scripts/workflow-step-profile.mjs +0 -338
- package/plugins/sp/scripts/workflow-step-profile.ts +0 -165
- package/plugins/sp/scripts/wrapup-drift-probe.mjs +0 -190
- package/plugins/sp/scripts/wrapup-drift-probe.ts +0 -261
- package/plugins/sp/scripts/wrapup-steps.mjs +0 -627
- package/plugins/sp/scripts/wrapup-steps.ts +0 -891
- package/web/_astro/channel.C7wG9RxA.js +0 -1
- package/web/_astro/index.BCY1JJHd.css +0 -1
|
@@ -1,746 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// @bun
|
|
3
|
-
|
|
4
|
-
// plugins/sp/scripts/inline-run-setup.ts
|
|
5
|
-
import { spawnSync } from "child_process";
|
|
6
|
-
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
7
|
-
|
|
8
|
-
// plugins/sp/lib/env.ts
|
|
9
|
-
function getEnvVar(name, fallback) {
|
|
10
|
-
const raw = process.env[name];
|
|
11
|
-
return raw === undefined ? fallback : raw;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
// plugins/sp/lib/inline-run-app.ts
|
|
15
|
-
import { existsSync } from "node:fs";
|
|
16
|
-
import { dirname, join, resolve } from "node:path";
|
|
17
|
-
import { fileURLToPath } from "node:url";
|
|
18
|
-
function resolveAppEntry(spurBin) {
|
|
19
|
-
const fallback = fileURLToPath(new URL("../../../apps/cli/src/index.ts", import.meta.url));
|
|
20
|
-
const candidates = spurBin !== "" ? [spurBin] : [fallback];
|
|
21
|
-
for (const candidate of candidates) {
|
|
22
|
-
const tokens = candidate.split(/\s+/).filter(Boolean);
|
|
23
|
-
const mainModule = [...tokens].reverse().find((t) => t.endsWith(".ts"));
|
|
24
|
-
if (mainModule === undefined || !existsSync(mainModule))
|
|
25
|
-
continue;
|
|
26
|
-
const repoRoot = resolve(dirname(mainModule), "..", "..", "..");
|
|
27
|
-
const appEntry = join(repoRoot, "packages", "app", "src", "index.ts");
|
|
28
|
-
if (existsSync(appEntry))
|
|
29
|
-
return { entry: appEntry, portable: false };
|
|
30
|
-
}
|
|
31
|
-
const entry = fileURLToPath(new URL("../lib/inline-run.generated.mjs", import.meta.url));
|
|
32
|
-
if (!existsSync(entry)) {
|
|
33
|
-
throw new Error("inline application bundle is missing — rebuild/install the sp plugin before running inline");
|
|
34
|
-
}
|
|
35
|
-
return { entry, portable: true };
|
|
36
|
-
}
|
|
37
|
-
async function loadInlineApp(spurBin, resolveAppEntry, required) {
|
|
38
|
-
const { entry, portable } = resolveAppEntry(spurBin);
|
|
39
|
-
const app = await import(entry);
|
|
40
|
-
const missing = portable ? required.filter((name) => typeof app[name] !== "function") : [];
|
|
41
|
-
if (missing.length > 0) {
|
|
42
|
-
throw new Error(`inline application bundle ${entry} is older than its scripts — missing ${missing.join(", ")}. Reinstall sp with a superskill release that stages lib/ (commit b42961b), or pass --spur-bin <spur checkout>/apps/cli/src/index.ts.`);
|
|
43
|
-
}
|
|
44
|
-
return { app, portable };
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// plugins/sp/lib/env.ts
|
|
48
|
-
function getEnvVars() {
|
|
49
|
-
return process.env;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
// plugins/sp/lib/run-summary-core.ts
|
|
53
|
-
import { existsSync as existsSync3, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
54
|
-
import { join as join3 } from "node:path";
|
|
55
|
-
|
|
56
|
-
// plugins/sp/lib/plan-projection.generated.mjs
|
|
57
|
-
var NON_ABORTING_PREFLIGHT_CODES = new Set([
|
|
58
|
-
"L4.scenario-unverified",
|
|
59
|
-
"L4.verifying-incomplete-tasks"
|
|
60
|
-
]);
|
|
61
|
-
var zeroTokens = () => ({ input: 0, cacheCreate: 0, cacheRead: 0, output: 0 });
|
|
62
|
-
function formatDuration(ms) {
|
|
63
|
-
const s = Math.round(ms / 1000);
|
|
64
|
-
const h = Math.floor(s / 3600);
|
|
65
|
-
const m = Math.floor(s % 3600 / 60);
|
|
66
|
-
const ss = String(s % 60).padStart(2, "0");
|
|
67
|
-
return h > 0 ? `${h}:${String(m).padStart(2, "0")}:${ss}` : `${m}:${ss}`;
|
|
68
|
-
}
|
|
69
|
-
function formatTokens(n) {
|
|
70
|
-
if (n >= 999500)
|
|
71
|
-
return `${(n / 1e6).toFixed(1)}M`;
|
|
72
|
-
return n >= 1000 ? `${Math.round(n / 1000)}k` : String(n);
|
|
73
|
-
}
|
|
74
|
-
function formatTokenSplit(t) {
|
|
75
|
-
if (!t)
|
|
76
|
-
return "n/a";
|
|
77
|
-
const nonCached = t.input + t.cacheCreate + t.output;
|
|
78
|
-
return `${formatTokens(nonCached + t.cacheRead)} / ${formatTokens(nonCached)}`;
|
|
79
|
-
}
|
|
80
|
-
var OPERATOR_TOOLS = new Set(["AskUserQuestion", "ask_user_question"]);
|
|
81
|
-
function isCompaction(row) {
|
|
82
|
-
return row.type === "compaction" || row.type === "system" && row.subtype === "compact_boundary";
|
|
83
|
-
}
|
|
84
|
-
function firstTextContent(row) {
|
|
85
|
-
const content = row.message?.content;
|
|
86
|
-
if (typeof content === "string")
|
|
87
|
-
return content;
|
|
88
|
-
if (!Array.isArray(content))
|
|
89
|
-
return;
|
|
90
|
-
return content.find((b) => b.type === "text")?.text;
|
|
91
|
-
}
|
|
92
|
-
function skillInvocationPrompt(text) {
|
|
93
|
-
const name = /^\s*<skill name="([^"]+)"/.exec(text)?.[1];
|
|
94
|
-
if (name === undefined)
|
|
95
|
-
return;
|
|
96
|
-
const close = text.lastIndexOf("</skill>");
|
|
97
|
-
const args = close < 0 ? "" : text.slice(close + "</skill>".length).trim();
|
|
98
|
-
return args === "" ? `/${name}` : `/${name} ${args}`;
|
|
99
|
-
}
|
|
100
|
-
function promptText(row) {
|
|
101
|
-
if (row.type === "custom_message") {
|
|
102
|
-
if (row.display === false)
|
|
103
|
-
return;
|
|
104
|
-
const text = typeof row.content === "string" ? row.content.trim() : "";
|
|
105
|
-
return text === "" ? undefined : text;
|
|
106
|
-
}
|
|
107
|
-
if (row.type === "message") {
|
|
108
|
-
if (row.message?.role !== "user")
|
|
109
|
-
return;
|
|
110
|
-
const text = firstTextContent(row);
|
|
111
|
-
if (text === undefined)
|
|
112
|
-
return;
|
|
113
|
-
return skillInvocationPrompt(text) ?? text;
|
|
114
|
-
}
|
|
115
|
-
if (row.type !== "user" || row.isMeta || row.isCompactSummary)
|
|
116
|
-
return;
|
|
117
|
-
const content = row.message?.content;
|
|
118
|
-
if (typeof content === "string")
|
|
119
|
-
return content;
|
|
120
|
-
if (!Array.isArray(content) || content.some((b) => b.type === "tool_result"))
|
|
121
|
-
return;
|
|
122
|
-
return content.find((b) => b.type === "text")?.text;
|
|
123
|
-
}
|
|
124
|
-
function sumTokens(all) {
|
|
125
|
-
const total = zeroTokens();
|
|
126
|
-
for (const t of all)
|
|
127
|
-
for (const k of Object.keys(total))
|
|
128
|
-
total[k] += t[k];
|
|
129
|
-
return total;
|
|
130
|
-
}
|
|
131
|
-
function sumSpans(spans) {
|
|
132
|
-
return {
|
|
133
|
-
workMs: spans.reduce((n, s) => n + s.workMs, 0),
|
|
134
|
-
waitMs: spans.reduce((n, s) => n + s.waitMs, 0),
|
|
135
|
-
toolCalls: spans.reduce((n, s) => n + s.toolCalls, 0),
|
|
136
|
-
tokens: sumTokens(spans.map((s) => s.tokens))
|
|
137
|
-
};
|
|
138
|
-
}
|
|
139
|
-
var newAcc = (ts) => ({
|
|
140
|
-
last: ts,
|
|
141
|
-
askMs: 0,
|
|
142
|
-
asks: new Map,
|
|
143
|
-
toolIds: new Set,
|
|
144
|
-
messages: new Map,
|
|
145
|
-
compactions: 0
|
|
146
|
-
});
|
|
147
|
-
function accumulate(acc, row, ts) {
|
|
148
|
-
acc.last = Math.max(acc.last, ts);
|
|
149
|
-
if (row.type === "message") {
|
|
150
|
-
const m = row.message;
|
|
151
|
-
if (m?.role === "assistant") {
|
|
152
|
-
const blocks = Array.isArray(m.content) ? m.content : [];
|
|
153
|
-
for (const b of blocks) {
|
|
154
|
-
if (b.type === "toolCall" && b.id) {
|
|
155
|
-
acc.toolIds.add(b.id);
|
|
156
|
-
if (b.name && OPERATOR_TOOLS.has(b.name))
|
|
157
|
-
acc.asks.set(b.id, ts);
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
const u = m.usage;
|
|
161
|
-
if (u && row.id)
|
|
162
|
-
acc.messages.set(row.id, {
|
|
163
|
-
input: u.input ?? 0,
|
|
164
|
-
cacheCreate: u.cacheWrite ?? 0,
|
|
165
|
-
cacheRead: u.cacheRead ?? 0,
|
|
166
|
-
output: u.output ?? 0
|
|
167
|
-
});
|
|
168
|
-
} else if (m?.role === "toolResult" && m.toolCallId && acc.asks.has(m.toolCallId)) {
|
|
169
|
-
acc.askMs += ts - (acc.asks.get(m.toolCallId) ?? ts);
|
|
170
|
-
}
|
|
171
|
-
return;
|
|
172
|
-
}
|
|
173
|
-
if (row.type === "compaction")
|
|
174
|
-
acc.compactions++;
|
|
175
|
-
const blocks = Array.isArray(row.message?.content) ? row.message.content : [];
|
|
176
|
-
for (const b of blocks) {
|
|
177
|
-
if (b.type === "tool_use" && b.id) {
|
|
178
|
-
acc.toolIds.add(b.id);
|
|
179
|
-
if (b.name && OPERATOR_TOOLS.has(b.name))
|
|
180
|
-
acc.asks.set(b.id, ts);
|
|
181
|
-
} else if (b.type === "tool_result" && b.tool_use_id && acc.asks.has(b.tool_use_id)) {
|
|
182
|
-
acc.askMs += ts - (acc.asks.get(b.tool_use_id) ?? ts);
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
const u = row.message?.usage;
|
|
186
|
-
if (row.type === "assistant" && u && row.message?.id) {
|
|
187
|
-
acc.messages.set(row.message.id, {
|
|
188
|
-
input: u.input_tokens ?? 0,
|
|
189
|
-
cacheCreate: u.cache_creation_input_tokens ?? 0,
|
|
190
|
-
cacheRead: u.cache_read_input_tokens ?? 0,
|
|
191
|
-
output: u.output_tokens ?? 0
|
|
192
|
-
});
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
var IDLE_GAP_MS = 300000;
|
|
196
|
-
var SHELL_TOOLS = new Set(["bash", "shell", "exec", "exec_command", "run_command", "command"]);
|
|
197
|
-
var SUBAGENT_TOOLS = new Set(["agent", "task", "invoke_subagent", "subagent", "dispatch_agent"]);
|
|
198
|
-
var LOWERCASE_OPERATOR_TOOLS = new Set(["askuserquestion", "ask_user_question"]);
|
|
199
|
-
var ACTOR_PRECEDENCE = ["operator", "subagent", "shell", "other"];
|
|
200
|
-
function actorOf(tool) {
|
|
201
|
-
const t = tool.toLowerCase().replace(/[^a-z_]/g, "");
|
|
202
|
-
if (LOWERCASE_OPERATOR_TOOLS.has(t))
|
|
203
|
-
return "operator";
|
|
204
|
-
if (SUBAGENT_TOOLS.has(t))
|
|
205
|
-
return "subagent";
|
|
206
|
-
if (SHELL_TOOLS.has(t))
|
|
207
|
-
return "shell";
|
|
208
|
-
return "other";
|
|
209
|
-
}
|
|
210
|
-
function unionMs(spans, start, end) {
|
|
211
|
-
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]);
|
|
212
|
-
let total = 0;
|
|
213
|
-
let cursor = Number.NaN;
|
|
214
|
-
let openUntil = Number.NaN;
|
|
215
|
-
for (const [a, b] of clipped) {
|
|
216
|
-
if (!Number.isFinite(cursor)) {
|
|
217
|
-
cursor = a;
|
|
218
|
-
openUntil = b;
|
|
219
|
-
continue;
|
|
220
|
-
}
|
|
221
|
-
if (a > openUntil) {
|
|
222
|
-
total += openUntil - cursor;
|
|
223
|
-
cursor = a;
|
|
224
|
-
openUntil = b;
|
|
225
|
-
} else {
|
|
226
|
-
openUntil = Math.max(openUntil, b);
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
if (Number.isFinite(cursor))
|
|
230
|
-
total += openUntil - cursor;
|
|
231
|
-
return total;
|
|
232
|
-
}
|
|
233
|
-
function subtract(spans, claimed) {
|
|
234
|
-
let free = spans;
|
|
235
|
-
for (const [ca, cb] of claimed) {
|
|
236
|
-
const next = [];
|
|
237
|
-
for (const [a, b] of free) {
|
|
238
|
-
if (cb <= a || ca >= b) {
|
|
239
|
-
next.push([a, b]);
|
|
240
|
-
continue;
|
|
241
|
-
}
|
|
242
|
-
if (ca > a)
|
|
243
|
-
next.push([a, ca]);
|
|
244
|
-
if (cb < b)
|
|
245
|
-
next.push([cb, b]);
|
|
246
|
-
}
|
|
247
|
-
free = next;
|
|
248
|
-
}
|
|
249
|
-
return free;
|
|
250
|
-
}
|
|
251
|
-
function actorSplit(rows, start, end) {
|
|
252
|
-
const empty = {
|
|
253
|
-
operatorMs: 0,
|
|
254
|
-
longestOperatorWaitMs: 0,
|
|
255
|
-
modelMs: 0,
|
|
256
|
-
shellMs: 0,
|
|
257
|
-
subagentMs: 0,
|
|
258
|
-
otherToolMs: 0,
|
|
259
|
-
idleMs: 0,
|
|
260
|
-
compactions: 0,
|
|
261
|
-
subagentErrors: 0
|
|
262
|
-
};
|
|
263
|
-
const window = end - start;
|
|
264
|
-
if (window <= 0)
|
|
265
|
-
return empty;
|
|
266
|
-
const calls = new Map;
|
|
267
|
-
const results = new Map;
|
|
268
|
-
const eventTs = [];
|
|
269
|
-
let compactions = 0;
|
|
270
|
-
for (const [row, ts] of rows) {
|
|
271
|
-
if (ts < start || ts > end)
|
|
272
|
-
continue;
|
|
273
|
-
eventTs.push(ts);
|
|
274
|
-
if (isCompaction(row))
|
|
275
|
-
compactions++;
|
|
276
|
-
const blocks = Array.isArray(row.message?.content) ? row.message?.content : [];
|
|
277
|
-
if (row.message?.role === "toolResult" && row.message.toolCallId !== undefined) {
|
|
278
|
-
results.set(row.message.toolCallId, { ts, isError: row.message.isError === true });
|
|
279
|
-
}
|
|
280
|
-
for (const b of blocks) {
|
|
281
|
-
if ((b.type === "tool_use" || b.type === "toolCall") && b.id !== undefined && b.name !== undefined) {
|
|
282
|
-
calls.set(b.id, { id: b.id, actor: actorOf(b.name), ts });
|
|
283
|
-
} else if (b.type === "tool_result" && b.tool_use_id !== undefined) {
|
|
284
|
-
results.set(b.tool_use_id, { ts, isError: b.is_error === true });
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
let subagentErrors = 0;
|
|
289
|
-
const resolved = [];
|
|
290
|
-
for (const call of calls.values()) {
|
|
291
|
-
const result = results.get(call.id);
|
|
292
|
-
if (result?.isError === true && call.actor === "subagent")
|
|
293
|
-
subagentErrors++;
|
|
294
|
-
resolved.push({ ...call, end: Math.min(result?.ts ?? end, end) });
|
|
295
|
-
}
|
|
296
|
-
const claimed = [];
|
|
297
|
-
const byActor = new Map;
|
|
298
|
-
for (const actor of ACTOR_PRECEDENCE) {
|
|
299
|
-
const mine = resolved.filter((r) => r.actor === actor).map((r) => [r.ts, r.end]);
|
|
300
|
-
const free = subtract(mine, claimed);
|
|
301
|
-
for (const span of free)
|
|
302
|
-
claimed.push(span);
|
|
303
|
-
const ms = unionMs(free, start, end);
|
|
304
|
-
byActor.set(actor, ms);
|
|
305
|
-
if (actor === "operator") {
|
|
306
|
-
empty.longestOperatorWaitMs = Math.max(0, ...mine.map(([a, b]) => b - a));
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
const boundaries = [...new Set([start, end, ...eventTs, ...claimed.flat()])].sort((a, b) => a - b);
|
|
310
|
-
const claimedSorted = [...claimed].sort((a, b) => a[0] - b[0]);
|
|
311
|
-
let idleMs = 0;
|
|
312
|
-
for (let i = 0;i < boundaries.length - 1; i++) {
|
|
313
|
-
const a = boundaries[i];
|
|
314
|
-
const b = boundaries[i + 1];
|
|
315
|
-
if (b - a < IDLE_GAP_MS)
|
|
316
|
-
continue;
|
|
317
|
-
if (claimedSorted.some(([ca, cb]) => ca < b && cb > a))
|
|
318
|
-
continue;
|
|
319
|
-
idleMs += b - a;
|
|
320
|
-
}
|
|
321
|
-
const accounted = (byActor.get("operator") ?? 0) + (byActor.get("subagent") ?? 0) + (byActor.get("shell") ?? 0) + (byActor.get("other") ?? 0) + idleMs;
|
|
322
|
-
return {
|
|
323
|
-
...empty,
|
|
324
|
-
operatorMs: byActor.get("operator") ?? 0,
|
|
325
|
-
subagentMs: byActor.get("subagent") ?? 0,
|
|
326
|
-
shellMs: byActor.get("shell") ?? 0,
|
|
327
|
-
otherToolMs: byActor.get("other") ?? 0,
|
|
328
|
-
idleMs,
|
|
329
|
-
modelMs: Math.max(0, window - accounted),
|
|
330
|
-
compactions,
|
|
331
|
-
subagentErrors
|
|
332
|
-
};
|
|
333
|
-
}
|
|
334
|
-
function attemptWindow(attempts) {
|
|
335
|
-
const starts = attempts.map((a) => Date.parse(a.startedAt ?? "")).filter((n) => !Number.isNaN(n));
|
|
336
|
-
const ends = attempts.map((a) => Date.parse(a.completedAt ?? "")).filter((n) => !Number.isNaN(n));
|
|
337
|
-
return starts.length && ends.length ? [Math.min(...starts), Math.max(...ends)] : undefined;
|
|
338
|
-
}
|
|
339
|
-
function summaryRow(stage, status, span, measured) {
|
|
340
|
-
return {
|
|
341
|
-
stage,
|
|
342
|
-
status,
|
|
343
|
-
workMs: span.workMs,
|
|
344
|
-
waitMs: span.waitMs,
|
|
345
|
-
toolCalls: measured ? span.toolCalls : null,
|
|
346
|
-
tokens: measured ? span.tokens : null,
|
|
347
|
-
time: formatDuration(span.workMs),
|
|
348
|
-
wait: formatDuration(span.waitMs),
|
|
349
|
-
token: formatTokenSplit(measured ? span.tokens : null)
|
|
350
|
-
};
|
|
351
|
-
}
|
|
352
|
-
function buildRunSummary(rows, inputs, opts = {}) {
|
|
353
|
-
const { since, until } = opts;
|
|
354
|
-
const windows = [];
|
|
355
|
-
for (const { label, progress } of inputs) {
|
|
356
|
-
const states = (progress.states ?? []).flatMap((s) => {
|
|
357
|
-
const w = attemptWindow((s.actions ?? []).flatMap((a) => a.attempts ?? []));
|
|
358
|
-
return w ? [{ s, w }] : [];
|
|
359
|
-
});
|
|
360
|
-
if (!states.length)
|
|
361
|
-
throw new Error(`progress for run ${progress.runId} has no timed attempts`);
|
|
362
|
-
if (inputs.length === 1) {
|
|
363
|
-
for (const { s, w } of states) {
|
|
364
|
-
const stage = (s.visit ?? 1) > 1 ? `${s.state} (visit ${s.visit})` : s.state;
|
|
365
|
-
windows.push({ stage, status: s.status ?? "", start: w[0], end: w[1] });
|
|
366
|
-
}
|
|
367
|
-
} else {
|
|
368
|
-
const stage = label ?? `${progress.workflow ?? "run"} ${progress.runId.slice(0, 8)}`;
|
|
369
|
-
const start = Math.min(...states.map(({ w }) => w[0]));
|
|
370
|
-
windows.push({
|
|
371
|
-
stage,
|
|
372
|
-
status: progress.status ?? "",
|
|
373
|
-
start,
|
|
374
|
-
end: Math.max(...states.map(({ w }) => w[1]))
|
|
375
|
-
});
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
windows.sort((a, b) => a.start - b.start);
|
|
379
|
-
const measure = (start, end) => {
|
|
380
|
-
const acc = newAcc(start);
|
|
381
|
-
for (const [row, ts] of rows)
|
|
382
|
-
if (ts >= start && ts <= end && promptText(row) === undefined)
|
|
383
|
-
accumulate(acc, row, ts);
|
|
384
|
-
const span = {
|
|
385
|
-
workMs: end - start - acc.askMs,
|
|
386
|
-
waitMs: acc.askMs,
|
|
387
|
-
toolCalls: acc.toolIds.size,
|
|
388
|
-
tokens: sumTokens([...acc.messages.values()])
|
|
389
|
-
};
|
|
390
|
-
return { span, measured: acc.toolIds.size > 0 || acc.messages.size > 0 };
|
|
391
|
-
};
|
|
392
|
-
const sinceMs = since ? Date.parse(since) : Number.NaN;
|
|
393
|
-
const totalStart = Number.isNaN(sinceMs) ? Math.min(...windows.map((w) => w.start)) : sinceMs;
|
|
394
|
-
let totalEnd = Math.max(...windows.map((w) => w.end));
|
|
395
|
-
if (!Number.isNaN(sinceMs)) {
|
|
396
|
-
for (const [, ts] of rows)
|
|
397
|
-
if (ts >= sinceMs)
|
|
398
|
-
totalEnd = Math.max(totalEnd, ts);
|
|
399
|
-
}
|
|
400
|
-
const untilMs = until ? Date.parse(until) : Number.NaN;
|
|
401
|
-
if (!Number.isNaN(untilMs) && untilMs > totalStart)
|
|
402
|
-
totalEnd = untilMs;
|
|
403
|
-
const stageRows = windows.map((w) => {
|
|
404
|
-
const { span, measured } = measure(w.start, w.end);
|
|
405
|
-
return summaryRow(w.stage, w.status, span, measured);
|
|
406
|
-
});
|
|
407
|
-
const totalMeasure = measure(totalStart, totalEnd);
|
|
408
|
-
const statuses = [...new Set(inputs.map((i) => i.progress.status ?? ""))].join("/");
|
|
409
|
-
const total = summaryRow("Total", statuses, totalMeasure.span, totalMeasure.measured);
|
|
410
|
-
const overlaps = windows.some((w, i) => i > 0 && w.start < (windows[i - 1]?.end ?? 0));
|
|
411
|
-
const sum = sumSpans(stageRows.map((r) => ({ ...r, toolCalls: r.toolCalls ?? 0, tokens: r.tokens ?? zeroTokens() })));
|
|
412
|
-
const overhead = {
|
|
413
|
-
workMs: total.workMs - sum.workMs,
|
|
414
|
-
waitMs: total.waitMs - sum.waitMs,
|
|
415
|
-
toolCalls: (total.toolCalls ?? 0) - sum.toolCalls,
|
|
416
|
-
tokens: Object.fromEntries(Object.keys(sum.tokens).map((k) => [k, (total.tokens?.[k] ?? 0) - sum.tokens[k]]))
|
|
417
|
-
};
|
|
418
|
-
if (!overlaps && overhead.workMs > 0)
|
|
419
|
-
stageRows.push(summaryRow("driver overhead", "", overhead, totalMeasure.measured));
|
|
420
|
-
return {
|
|
421
|
-
available: true,
|
|
422
|
-
rows: stageRows,
|
|
423
|
-
total,
|
|
424
|
-
actors: actorSplit(rows, totalStart, totalEnd),
|
|
425
|
-
gate: { ms: null, reason: "no matching check receipt" },
|
|
426
|
-
skippedLines: opts.skippedLines ?? 0
|
|
427
|
-
};
|
|
428
|
-
}
|
|
429
|
-
var SENSITIVE_PREFIXES = ["drizzle/", "packages/config/", ".github/", "plugins/sp/hooks/"];
|
|
430
|
-
var SENSITIVE_GLOBS = ["apps/server/src/**/auth*", "**/*secret*", "**/*.sql"];
|
|
431
|
-
function globToRegex(glob) {
|
|
432
|
-
const escaped = glob.replace(/[.+^${}()|[\]\\]/g, "\\$&").replaceAll("**/", "\x00").replaceAll("**", "\x01").replaceAll("*", "[^/]*").replaceAll("\x00", "(?:.*/)?").replaceAll("\x01", ".*");
|
|
433
|
-
return new RegExp(`^${escaped}$`, "s");
|
|
434
|
-
}
|
|
435
|
-
var SENSITIVE_MATCHERS = [
|
|
436
|
-
...SENSITIVE_PREFIXES.map((prefix) => ({
|
|
437
|
-
label: prefix,
|
|
438
|
-
test: (path) => path.startsWith(prefix)
|
|
439
|
-
})),
|
|
440
|
-
...SENSITIVE_GLOBS.map((glob) => {
|
|
441
|
-
const regex = globToRegex(glob);
|
|
442
|
-
return { label: glob, test: (path) => regex.test(path) };
|
|
443
|
-
})
|
|
444
|
-
];
|
|
445
|
-
|
|
446
|
-
// plugins/sp/lib/transcript.ts
|
|
447
|
-
import { existsSync as existsSync2, readdirSync } from "node:fs";
|
|
448
|
-
import { homedir } from "node:os";
|
|
449
|
-
import { join as join2 } from "node:path";
|
|
450
|
-
var SESSION_ID = /^[A-Za-z0-9_-]+$/;
|
|
451
|
-
var PI_ROLES = new Set(["user", "assistant", "toolResult"]);
|
|
452
|
-
function sniffFormat(rows) {
|
|
453
|
-
for (const row of rows) {
|
|
454
|
-
if (row.type === "message" && PI_ROLES.has(row.message?.role ?? ""))
|
|
455
|
-
return "pi";
|
|
456
|
-
if (row.type === "user" || row.type === "assistant")
|
|
457
|
-
return "claude";
|
|
458
|
-
}
|
|
459
|
-
return "unknown";
|
|
460
|
-
}
|
|
461
|
-
function parseRows(lines) {
|
|
462
|
-
const rows = [];
|
|
463
|
-
let skippedLines = 0;
|
|
464
|
-
for (const line of lines) {
|
|
465
|
-
if (!line.trim())
|
|
466
|
-
continue;
|
|
467
|
-
let row;
|
|
468
|
-
try {
|
|
469
|
-
row = JSON.parse(line);
|
|
470
|
-
} catch {
|
|
471
|
-
skippedLines++;
|
|
472
|
-
continue;
|
|
473
|
-
}
|
|
474
|
-
const ts = row.timestamp ? Date.parse(row.timestamp) : Number.NaN;
|
|
475
|
-
if (!Number.isNaN(ts))
|
|
476
|
-
rows.push([row, ts]);
|
|
477
|
-
}
|
|
478
|
-
return { rows, skippedLines, format: sniffFormat(rows.map(([r]) => r)) };
|
|
479
|
-
}
|
|
480
|
-
function resolveTranscript(env, projectsRoot = join2(homedir(), ".claude", "projects"), override) {
|
|
481
|
-
if (override)
|
|
482
|
-
return existsSync2(override) ? { ok: true, path: override } : { ok: false, reason: "no transcript" };
|
|
483
|
-
const id = env.CLAUDE_CODE_SESSION_ID;
|
|
484
|
-
if (!id) {
|
|
485
|
-
const piFile = env.PI_SESSION_FILE;
|
|
486
|
-
if (piFile !== undefined && piFile !== "") {
|
|
487
|
-
return existsSync2(piFile) ? { ok: true, path: piFile } : { ok: false, reason: `PI_SESSION_FILE ${piFile} does not exist` };
|
|
488
|
-
}
|
|
489
|
-
return {
|
|
490
|
-
ok: false,
|
|
491
|
-
reason: "no host session id; pass --transcript <path> (pi: ~/.pi/agent/sessions/<cwd-slug>/<file>.jsonl, " + "or set PI_SESSION_FILE)"
|
|
492
|
-
};
|
|
493
|
-
}
|
|
494
|
-
if (!SESSION_ID.test(id))
|
|
495
|
-
return { ok: false, reason: "refusing a session id with path characters" };
|
|
496
|
-
if (!existsSync2(projectsRoot))
|
|
497
|
-
return { ok: false, reason: `no transcript root ${projectsRoot}` };
|
|
498
|
-
for (const dir of readdirSync(projectsRoot)) {
|
|
499
|
-
const path = join2(projectsRoot, dir, `${id}.jsonl`);
|
|
500
|
-
if (existsSync2(path))
|
|
501
|
-
return { ok: true, path };
|
|
502
|
-
}
|
|
503
|
-
return { ok: false, reason: `no transcript for session ${id}` };
|
|
504
|
-
}
|
|
505
|
-
|
|
506
|
-
// plugins/sp/lib/run-summary-core.ts
|
|
507
|
-
function renderSummaryMarkdown(summary) {
|
|
508
|
-
const line = (r, bold = false) => {
|
|
509
|
-
const stage = bold ? `**${r.stage}**` : r.stage;
|
|
510
|
-
return `| ${stage} | ${r.status} | ${r.time} | ${r.wait} | ${r.toolCalls ?? "n/a"} | ${r.token} |`;
|
|
511
|
-
};
|
|
512
|
-
const a = summary.actors;
|
|
513
|
-
const window = summary.total.workMs + summary.total.waitMs;
|
|
514
|
-
const share = (ms) => window > 0 ? `${Math.round(ms / window * 100)}%` : "n/a";
|
|
515
|
-
const gate = summary.gate.ms === null ? `n/a (${summary.gate.reason})` : formatDuration(summary.gate.ms);
|
|
516
|
-
return [
|
|
517
|
-
"| Stage | Status | Time | Wait | Tool calls | Token (total / non-cached) |",
|
|
518
|
-
"| --- | --- | ---: | ---: | ---: | ---: |",
|
|
519
|
-
...summary.rows.map((r) => line(r)),
|
|
520
|
-
line(summary.total, true),
|
|
521
|
-
"",
|
|
522
|
-
"| Actor | Time | Share |",
|
|
523
|
-
"| --- | ---: | ---: |",
|
|
524
|
-
`| operator wait (longest ${formatDuration(a.longestOperatorWaitMs)}) | ${formatDuration(a.operatorMs)} | ${share(a.operatorMs)} |`,
|
|
525
|
-
`| model | ${formatDuration(a.modelMs)} | ${share(a.modelMs)} |`,
|
|
526
|
-
`| shell | ${formatDuration(a.shellMs)} | ${share(a.shellMs)} |`,
|
|
527
|
-
`| of which gate (informational) | ${gate} | — |`,
|
|
528
|
-
`| subagent | ${formatDuration(a.subagentMs)} | ${share(a.subagentMs)} |`,
|
|
529
|
-
`| other tools | ${formatDuration(a.otherToolMs)} | ${share(a.otherToolMs)} |`,
|
|
530
|
-
`| idle / unattributed | ${formatDuration(a.idleMs)} | ${share(a.idleMs)} |`,
|
|
531
|
-
"",
|
|
532
|
-
`Compactions: ${a.compactions} · Subagent errors: ${a.subagentErrors}`
|
|
533
|
-
].join(`
|
|
534
|
-
`);
|
|
535
|
-
}
|
|
536
|
-
function writeCloseSummary(run, opts = {}) {
|
|
537
|
-
const cwd = opts.cwd ?? process.cwd();
|
|
538
|
-
const runDir = join3(cwd, ".spur", "run");
|
|
539
|
-
const rel = join3(".spur", "run", `${run.runId}-summary.md`);
|
|
540
|
-
const abs = join3(cwd, rel);
|
|
541
|
-
const na = (reason) => {
|
|
542
|
-
mkdirSync(runDir, { recursive: true });
|
|
543
|
-
writeFileSync(abs, `Execution summary: n/a (${reason})
|
|
544
|
-
`);
|
|
545
|
-
return rel;
|
|
546
|
-
};
|
|
547
|
-
try {
|
|
548
|
-
if (run.startedAt === undefined)
|
|
549
|
-
return na("run row has no started_at");
|
|
550
|
-
const resolved = resolveTranscript(opts.env ?? getEnvVars(), opts.projectsRoot);
|
|
551
|
-
if (!resolved.ok)
|
|
552
|
-
return na(resolved.reason);
|
|
553
|
-
const { rows, skippedLines } = parseRows(readFileSync(resolved.path, "utf8").split(`
|
|
554
|
-
`));
|
|
555
|
-
const summary = buildRunSummary(rows, [{ progress: run.progress }], {
|
|
556
|
-
since: run.startedAt,
|
|
557
|
-
until: run.completedAt,
|
|
558
|
-
skippedLines
|
|
559
|
-
});
|
|
560
|
-
if (summary.rows.length === 0)
|
|
561
|
-
return na("no timed attempts");
|
|
562
|
-
mkdirSync(runDir, { recursive: true });
|
|
563
|
-
writeFileSync(abs, `${renderSummaryMarkdown(summary)}
|
|
564
|
-
`);
|
|
565
|
-
writeFileSync(join3(runDir, `${run.runId}-summary.json`), JSON.stringify({ ...summary, runId: run.runId, startedAt: run.startedAt, completedAt: run.completedAt }, null, 2));
|
|
566
|
-
return rel;
|
|
567
|
-
} catch (error) {
|
|
568
|
-
return na(error instanceof Error ? error.message : String(error));
|
|
569
|
-
}
|
|
570
|
-
}
|
|
571
|
-
// plugins/sp/scripts/run-summary.ts
|
|
572
|
-
if (false)
|
|
573
|
-
;
|
|
574
|
-
|
|
575
|
-
// plugins/sp/scripts/inline-run-setup.ts
|
|
576
|
-
function usage() {
|
|
577
|
-
const text = [
|
|
578
|
-
"Usage: bun plugins/sp/scripts/inline-run-setup.ts --run-id <id> --file <definition> [--spur-bin <path>]",
|
|
579
|
-
" bun plugins/sp/scripts/inline-run-setup.ts --fingerprint --task-file <path> [--feature-file <path>] [--spur-bin <path>]",
|
|
580
|
-
" bun plugins/sp/scripts/inline-run-setup.ts --action --run-id <id> --node <state> --kind <kind> --status <done|failed> --ok <true|false> [--duration-ms <n>] [--estimated] [--project-root <path>] [--spur-bin <path>]",
|
|
581
|
-
" bun plugins/sp/scripts/inline-run-setup.ts --node-enter --run-id <id> --node <state> [--project-root <path>] [--spur-bin <path>] (1136 R4: stamps the node enter time)",
|
|
582
|
-
" bun plugins/sp/scripts/inline-run-setup.ts --actions-file <json-file> --run-id <id> [--project-root <path>] [--spur-bin <path>] (1007 R5 batch trace emission)",
|
|
583
|
-
" bun plugins/sp/scripts/inline-run-setup.ts --close --run-id <id> --status <done|failed|paused> [--reason <terminal-reason>] [--no-summary] [--project-root <path>] [--spur-bin <path>]",
|
|
584
|
-
" --project-root (1136 R1) names the tree that OWNS the run row; without it the run row is read from the cwd tree, and a miss exits 1 with RUN_NOT_FOUND",
|
|
585
|
-
" --duration-ms is optional on --action (1136 R4): omitted, the duration is measured from the calling --node-enter; supplied, it records provenance host-reported",
|
|
586
|
-
" bun plugins/sp/scripts/inline-run-setup.ts --persist-out --from <worktree-path> [--task-file <path>]... [--spur-bin <path>]",
|
|
587
|
-
" terminal-reason is a closed enum (0937 R2): done, paused-operator, failed-check, failed-agent, failed-timeout, failed-guard, cancelled, interrupted, retry-exhausted",
|
|
588
|
-
" close defaults (1051 AC1): --status done \u2192 reason done, --status paused \u2192 reason paused-operator; --status failed requires an explicit --reason",
|
|
589
|
-
" bun plugins/sp/scripts/inline-run-setup.ts --decide --run-id <id> --node <state> --options-json <file> [--spur-bin <path>]"
|
|
590
|
-
].join(`
|
|
591
|
-
`);
|
|
592
|
-
console.error(text);
|
|
593
|
-
process.exit(2);
|
|
594
|
-
}
|
|
595
|
-
var SAFE_RUN_ID_RE = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
|
|
596
|
-
function refuseUnsafeRunId(runId) {
|
|
597
|
-
console.error(`inline-run-setup: refusing unsafe run id: ${runId}`);
|
|
598
|
-
console.error(" The run id must be a single safe filename component (alphanumeric/._-, no leading dot, no path separators, interpolation or traversal; same class as the task-pipeline route-reason guard, task 0804 R8). Allocate a fresh run id (uuid or timestamp slug) and retry.");
|
|
599
|
-
process.exit(1);
|
|
600
|
-
}
|
|
601
|
-
var SETUP_EXPORTS = ["readInstalledInventory", "runInlineRunSetup", "writeInlineRunOutcome"];
|
|
602
|
-
async function main() {
|
|
603
|
-
if (!process.versions.bun) {
|
|
604
|
-
const child = spawnSync("bun", [fileURLToPath2(import.meta.url), ...process.argv.slice(2)], {
|
|
605
|
-
stdio: "inherit"
|
|
606
|
-
});
|
|
607
|
-
if (child.error)
|
|
608
|
-
console.error(`inline-run-setup requires Bun on PATH: ${child.error.message}`);
|
|
609
|
-
process.exit(child.status ?? 1);
|
|
610
|
-
}
|
|
611
|
-
const flags = new Map;
|
|
612
|
-
let fingerprint = false, action = false, close = false, decide = false, persistOut = false, nodeEnter = false, estimated = false, noSummary = false;
|
|
613
|
-
const taskFiles = [];
|
|
614
|
-
let spurBin = getEnvVar("SPUR_BIN") ?? "";
|
|
615
|
-
const argv = process.argv.slice(2);
|
|
616
|
-
for (let i = 0;i < argv.length; i++) {
|
|
617
|
-
const flag = argv[i];
|
|
618
|
-
if (flag === "--fingerprint")
|
|
619
|
-
fingerprint = true;
|
|
620
|
-
else if (flag === "--action")
|
|
621
|
-
action = true;
|
|
622
|
-
else if (flag === "--close")
|
|
623
|
-
close = true;
|
|
624
|
-
else if (flag === "--decide")
|
|
625
|
-
decide = true;
|
|
626
|
-
else if (flag === "--node-enter")
|
|
627
|
-
nodeEnter = true;
|
|
628
|
-
else if (flag === "--persist-out")
|
|
629
|
-
persistOut = true;
|
|
630
|
-
else if (flag === "--estimated")
|
|
631
|
-
estimated = true;
|
|
632
|
-
else if (flag === "--no-summary")
|
|
633
|
-
noSummary = true;
|
|
634
|
-
else if (flag === "--task-file")
|
|
635
|
-
taskFiles.push(argv[++i] ?? "");
|
|
636
|
-
else if (flag === "--spur-bin")
|
|
637
|
-
spurBin = argv[++i] ?? spurBin;
|
|
638
|
-
else if (flag !== undefined)
|
|
639
|
-
flags.set(flag, argv[++i] ?? "");
|
|
640
|
-
}
|
|
641
|
-
const runId = flags.get("--run-id") ?? "";
|
|
642
|
-
const file = flags.get("--file") ?? "";
|
|
643
|
-
const featureFile = flags.get("--feature-file") ?? "";
|
|
644
|
-
const from = flags.get("--from") ?? "";
|
|
645
|
-
const optionsJson = flags.get("--options-json") ?? "";
|
|
646
|
-
const node = flags.get("--node") ?? "";
|
|
647
|
-
const kind = flags.get("--kind") ?? "";
|
|
648
|
-
const projectRoot = flags.get("--project-root") ?? "";
|
|
649
|
-
const status = flags.get("--status") ?? "";
|
|
650
|
-
const reason = flags.get("--reason") ?? "";
|
|
651
|
-
const okRaw = flags.get("--ok") ?? "";
|
|
652
|
-
const durationRaw = flags.get("--duration-ms") ?? "";
|
|
653
|
-
const actionsFile = flags.get("--actions-file") ?? "";
|
|
654
|
-
if (estimated && !action)
|
|
655
|
-
usage();
|
|
656
|
-
if (noSummary && !close)
|
|
657
|
-
usage();
|
|
658
|
-
if (fingerprint) {
|
|
659
|
-
if (runId !== "" || file !== "" || taskFiles.length !== 1 || (taskFiles[0] ?? "").trim() === "")
|
|
660
|
-
usage();
|
|
661
|
-
const { app } = await loadInlineApp(spurBin, resolveAppEntry, ["runInlineRunFingerprint"]);
|
|
662
|
-
process.exit(await app.runInlineRunFingerprint({ taskFile: taskFiles[0] ?? "", featureFile }));
|
|
663
|
-
}
|
|
664
|
-
if (decide) {
|
|
665
|
-
if (action || close || fingerprint || file !== "" || taskFiles.length > 0)
|
|
666
|
-
usage();
|
|
667
|
-
if (runId.trim() === "" || node.trim() === "" || optionsJson.trim() === "")
|
|
668
|
-
usage();
|
|
669
|
-
if (!SAFE_RUN_ID_RE.test(runId))
|
|
670
|
-
refuseUnsafeRunId(runId);
|
|
671
|
-
try {
|
|
672
|
-
const { app, portable } = await loadInlineApp(spurBin, resolveAppEntry, ["runInlineRunDecide"]);
|
|
673
|
-
const { app: lib } = await loadInlineApp(spurBin, () => ({ entry: fileURLToPath2(new URL("../lib/inline-run.generated.mjs", import.meta.url)), portable }), ["resolveDecideDecisionMakerEnabled", "loadSpurConfig"]);
|
|
674
|
-
const loadOpts = portable ? { embeddedSchemas: lib.EMBEDDED_SPUR_SCHEMAS } : undefined;
|
|
675
|
-
const [enabled, spurConfig] = await Promise.all([
|
|
676
|
-
lib.resolveDecideDecisionMakerEnabled(process.cwd(), loadOpts),
|
|
677
|
-
lib.loadSpurConfig(process.cwd(), loadOpts)
|
|
678
|
-
]);
|
|
679
|
-
process.exit(await app.runInlineRunDecide({ runId, node, optionsFile: optionsJson, enabled, spurConfig }));
|
|
680
|
-
} catch (error) {
|
|
681
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
682
|
-
process.stdout.write(`${JSON.stringify({ ok: false, runId, error: message })}
|
|
683
|
-
`);
|
|
684
|
-
process.exit(1);
|
|
685
|
-
}
|
|
686
|
-
}
|
|
687
|
-
if (persistOut) {
|
|
688
|
-
if (fingerprint || decide || action || close || runId !== "" || file !== "")
|
|
689
|
-
usage();
|
|
690
|
-
if (from.trim() === "" || taskFiles.some((taskFile) => taskFile.trim() === ""))
|
|
691
|
-
usage();
|
|
692
|
-
const { app } = await loadInlineApp(spurBin, resolveAppEntry, ["runInlineRunPersistOut"]);
|
|
693
|
-
process.exit(await app.runInlineRunPersistOut({ from, taskFiles }));
|
|
694
|
-
}
|
|
695
|
-
if (nodeEnter || actionsFile !== "" || action || close) {
|
|
696
|
-
if (!SAFE_RUN_ID_RE.test(runId))
|
|
697
|
-
refuseUnsafeRunId(runId);
|
|
698
|
-
const mode = nodeEnter ? "node-enter" : actionsFile !== "" ? "actions-file" : close ? "close" : "action";
|
|
699
|
-
const { app } = await loadInlineApp(spurBin, resolveAppEntry, ["runInlineRunTraceMode"]);
|
|
700
|
-
const code = await app.runInlineRunTraceMode({
|
|
701
|
-
mode,
|
|
702
|
-
runId,
|
|
703
|
-
node,
|
|
704
|
-
kind,
|
|
705
|
-
status,
|
|
706
|
-
reason,
|
|
707
|
-
ok: okRaw,
|
|
708
|
-
durationMs: durationRaw,
|
|
709
|
-
actionsFile,
|
|
710
|
-
projectRoot,
|
|
711
|
-
estimated,
|
|
712
|
-
noSummary,
|
|
713
|
-
...noSummary ? {} : { summarize: async (run) => writeCloseSummary(run) }
|
|
714
|
-
});
|
|
715
|
-
if (code === 2)
|
|
716
|
-
usage();
|
|
717
|
-
process.exit(code);
|
|
718
|
-
}
|
|
719
|
-
if (runId.trim() === "" || file.trim() === "")
|
|
720
|
-
usage();
|
|
721
|
-
if (!SAFE_RUN_ID_RE.test(runId))
|
|
722
|
-
refuseUnsafeRunId(runId);
|
|
723
|
-
const { app, portable } = await loadInlineApp(spurBin, resolveAppEntry, SETUP_EXPORTS);
|
|
724
|
-
let inventory;
|
|
725
|
-
try {
|
|
726
|
-
inventory = await app.readInstalledInventory({
|
|
727
|
-
file,
|
|
728
|
-
spurBin,
|
|
729
|
-
localCli: fileURLToPath2(new URL("../../../apps/cli/src/index.ts", import.meta.url))
|
|
730
|
-
});
|
|
731
|
-
} catch (error) {
|
|
732
|
-
const message = `could not resolve the workflow definition: ${error instanceof Error ? error.message : String(error)}`;
|
|
733
|
-
app.writeInlineRunOutcome(runId, { ok: false, runId, error: message });
|
|
734
|
-
throw new Error(message);
|
|
735
|
-
}
|
|
736
|
-
process.exit(await app.runInlineRunSetup({
|
|
737
|
-
runId,
|
|
738
|
-
file,
|
|
739
|
-
inventory,
|
|
740
|
-
...portable ? { embeddedSchemas: app.EMBEDDED_SPUR_SCHEMAS } : {}
|
|
741
|
-
}));
|
|
742
|
-
}
|
|
743
|
-
main().catch((e) => {
|
|
744
|
-
console.error(`inline-run-setup: FAIL \u2014 ${e instanceof Error ? e.message : String(e)}`);
|
|
745
|
-
process.exit(1);
|
|
746
|
-
});
|