@gobing-ai/spur 0.4.0 → 0.4.2

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 (172) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/config/config.global.yaml +4 -2
  3. package/config/decisions/gates.yaml +36 -0
  4. package/config/decisions/history-anatomy.yaml +35 -0
  5. package/config/decisions/idea-pipeline.yaml +38 -0
  6. package/config/plugin-scripts.json +15 -0
  7. package/config/rules/docs/sourced-api-claims.yaml +60 -0
  8. package/config/rules/recommended-pre-check.yaml +8 -3
  9. package/config/rules/structure/scratch-evidence-pointer.yaml +35 -0
  10. package/config/script-placement-baseline.json +2 -2
  11. package/config/templates/AGENTS.md +1 -1
  12. package/config/workflows/feature-lifecycle.yaml +9 -2
  13. package/config/workflows/history-anatomy.yaml +22 -0
  14. package/config/workflows/idea-pipeline.yaml +75 -0
  15. package/config/workflows/task-pipeline.yaml +216 -57
  16. package/config/workflows/wrapup-pipeline.yaml +107 -14
  17. package/package.json +10 -10
  18. package/plugins/sp/README.md +12 -3
  19. package/plugins/sp/agents/super-planner.md +16 -6
  20. package/plugins/sp/agents/super-reviewer.md +14 -0
  21. package/plugins/sp/bunfig.toml +8 -0
  22. package/plugins/sp/commands/dev-brainstorm.md +29 -16
  23. package/plugins/sp/commands/dev-find-way.md +80 -0
  24. package/plugins/sp/commands/dev-idea.md +6 -0
  25. package/plugins/sp/commands/dev-plan.md +6 -0
  26. package/plugins/sp/commands/dev-review-session.md +4 -1
  27. package/plugins/sp/commands/dev-run.md +12 -2
  28. package/plugins/sp/commands/dev-runall.md +32 -15
  29. package/plugins/sp/hooks/context-owner.ts +56 -0
  30. package/plugins/sp/hooks/context-post-tool.ts +23 -0
  31. package/plugins/sp/hooks/context-session-start.ts +49 -27
  32. package/plugins/sp/hooks/context-session-stop.ts +131 -30
  33. package/plugins/sp/hooks/pi/guard-extension.ts +21 -8
  34. package/plugins/sp/lib/idea-handoff.generated.mjs +43 -5
  35. package/plugins/sp/lib/inline-run-app.ts +28 -0
  36. package/plugins/sp/lib/inline-run.generated.d.mts +5 -0
  37. package/plugins/sp/lib/inline-run.generated.mjs +351 -46
  38. package/plugins/sp/lib/plan-projection.generated.d.mts +47 -0
  39. package/plugins/sp/lib/plan-projection.generated.mjs +368 -0
  40. package/plugins/sp/lib/quality-gate.generated.d.mts +35 -1
  41. package/plugins/sp/lib/quality-gate.generated.mjs +30296 -71
  42. package/plugins/sp/lib/residual-scan.generated.d.mts +19 -0
  43. package/plugins/sp/lib/residual-scan.generated.mjs +66 -0
  44. package/plugins/sp/lib/run-summary-core.ts +561 -0
  45. package/plugins/sp/lib/transcript.ts +150 -7
  46. package/plugins/sp/plugin.json +1 -1
  47. package/plugins/sp/references/roles.md +1 -1
  48. package/plugins/sp/scripts/batch-plan.mjs +488 -0
  49. package/plugins/sp/scripts/batch-plan.ts +145 -0
  50. package/plugins/sp/scripts/batch-preflight.mjs +51 -3
  51. package/plugins/sp/scripts/batch-preflight.ts +72 -1
  52. package/plugins/sp/scripts/commit-guard.mjs +216 -0
  53. package/plugins/sp/scripts/commit-guard.ts +249 -0
  54. package/plugins/sp/scripts/inline-run-dispatch.mjs +130 -0
  55. package/plugins/sp/scripts/inline-run-dispatch.ts +135 -0
  56. package/plugins/sp/scripts/inline-run-setup.mjs +562 -80
  57. package/plugins/sp/scripts/inline-run-setup.ts +52 -78
  58. package/plugins/sp/scripts/persist-out-check.mjs +90 -41
  59. package/plugins/sp/scripts/persist-out-check.ts +112 -57
  60. package/plugins/sp/scripts/pr-reviewing.mjs +19 -5
  61. package/plugins/sp/scripts/pr-reviewing.ts +34 -9
  62. package/plugins/sp/scripts/quality-gate.mjs +30213 -61
  63. package/plugins/sp/scripts/quality-gate.ts +21 -5
  64. package/plugins/sp/scripts/residual-scan.mjs +86 -31
  65. package/plugins/sp/scripts/residual-scan.ts +28 -30
  66. package/plugins/sp/scripts/run-summary.mjs +432 -14
  67. package/plugins/sp/scripts/run-summary.ts +41 -177
  68. package/plugins/sp/scripts/session-timeline.mjs +109 -11
  69. package/plugins/sp/scripts/session-timeline.ts +57 -19
  70. package/plugins/sp/scripts/task-diffstat.mjs +32 -29
  71. package/plugins/sp/scripts/task-diffstat.ts +34 -34
  72. package/plugins/sp/scripts/wrapup-steps.mjs +150 -2
  73. package/plugins/sp/scripts/wrapup-steps.ts +203 -1
  74. package/plugins/sp/skills/brainstorm/SKILL.md +9 -47
  75. package/plugins/sp/skills/branch-workflow/SKILL.md +1 -0
  76. package/plugins/sp/skills/branch-workflow/references/worktree-patterns.md +1 -0
  77. package/plugins/sp/skills/code-implementation/SKILL.md +48 -1
  78. package/plugins/sp/skills/code-verification/SKILL.md +3 -4
  79. package/plugins/sp/skills/code-verification/references/secu-review.md +3 -0
  80. package/plugins/sp/skills/code-verification/references/verdict-schema.md +68 -0
  81. package/plugins/sp/skills/fleet-join/SKILL.md +1 -1
  82. package/plugins/sp/skills/indexed-context/SKILL.md +14 -7
  83. package/plugins/sp/skills/parallel-execution/SKILL.md +2 -2
  84. package/plugins/sp/skills/parallel-execution/references/dispatch-surface.md +1 -1
  85. package/plugins/sp/skills/parallel-execution/references/fan-out-patterns.md +2 -2
  86. package/plugins/sp/skills/pr-reviewing/SKILL.md +7 -3
  87. package/plugins/sp/skills/session-review/SKILL.md +40 -7
  88. package/plugins/sp/skills/source-driven-development/SKILL.md +5 -0
  89. package/plugins/sp/skills/spec-decomposition/references/decomposition.md +5 -3
  90. package/plugins/sp/skills/spur-cli/SKILL.md +5 -2
  91. package/plugins/sp/skills/spur-cli/references/agent.md +20 -15
  92. package/plugins/sp/skills/spur-cli/references/decision.md +16 -8
  93. package/plugins/sp/skills/spur-cli/references/message.md +7 -7
  94. package/plugins/sp/skills/spur-cli/references/projects.md +7 -5
  95. package/plugins/sp/skills/spur-cli/references/tasks/verbs.md +19 -3
  96. package/plugins/sp/skills/spur-cli/references/tasks.md +10 -7
  97. package/plugins/sp/skills/spur-cli/references/workflows/authoring-workflows.md +7 -0
  98. package/plugins/sp/skills/spur-cli/references/workflows.md +7 -2
  99. package/plugins/sp/skills/spur-composer/SKILL.md +1 -1
  100. package/plugins/sp/skills/spur-dev/SKILL.md +20 -14
  101. package/plugins/sp/skills/spur-dev/references/ac-style-guide.md +33 -3
  102. package/plugins/sp/skills/spur-dev/references/cross-cutting.md +49 -14
  103. package/plugins/sp/skills/spur-dev/references/dev-operations.md +37 -17
  104. package/plugins/sp/skills/spur-dev/references/execution-batch-continuation.md +68 -0
  105. package/plugins/sp/skills/spur-dev/references/execution-batch-report.md +199 -0
  106. package/plugins/sp/skills/spur-dev/references/execution-batch.md +156 -1024
  107. package/plugins/sp/skills/spur-dev/references/execution-parallel-isolation.md +133 -0
  108. package/plugins/sp/skills/spur-dev/references/execution-workflow.md +6 -0
  109. package/plugins/sp/skills/spur-dev/references/execution-worktree-landing.md +471 -0
  110. package/plugins/sp/skills/spur-dev/references/execution-worktree-setup.md +379 -0
  111. package/plugins/sp/skills/spur-dev/references/flag-glossary.md +44 -12
  112. package/plugins/sp/skills/spur-dev/references/glossary.md +2 -2
  113. package/plugins/sp/skills/spur-dev/references/inline-pipeline-driver.md +340 -165
  114. package/plugins/sp/skills/spur-dev/references/structured-trace-emission.md +178 -0
  115. package/plugins/sp/skills/wayfinder/SKILL.md +10 -14
  116. package/schemas/state-machine-workflow.schema.json +23 -0
  117. package/spur.js +42402 -26889
  118. package/web/_astro/{BoardApp.VeoxxLBy.js → BoardApp.B3GQZvj8.js} +1 -1
  119. package/web/_astro/BoardApp.BT7L2kSb.js +324 -0
  120. package/web/_astro/TaskDetail.Cxov1_N_.js +1 -0
  121. package/web/_astro/{arc.f4e0OYLg.js → arc.vaT7tPOB.js} +1 -1
  122. package/web/_astro/{architectureDiagram-3BPJPVTR.SSIvxi-Z.js → architectureDiagram-3BPJPVTR.DRMmTjkN.js} +1 -1
  123. package/web/_astro/{blockDiagram-GPEHLZMM.DSXIHOFf.js → blockDiagram-GPEHLZMM.cla0LO_q.js} +1 -1
  124. package/web/_astro/{c4Diagram-AAUBKEIU.DG9Ubu85.js → c4Diagram-AAUBKEIU.D4YAIBKn.js} +1 -1
  125. package/web/_astro/channel.D4nG9YLf.js +1 -0
  126. package/web/_astro/{chunk-2J33WTMH.C7QU88Qh.js → chunk-2J33WTMH.BrSJbMRZ.js} +1 -1
  127. package/web/_astro/{chunk-4BX2VUAB.26Z94xBj.js → chunk-4BX2VUAB.CuLMgKF5.js} +1 -1
  128. package/web/_astro/{chunk-55IACEB6.DYRiBkqw.js → chunk-55IACEB6.zkvgu54F.js} +1 -1
  129. package/web/_astro/{chunk-727SXJPM.D5Vfv5pe.js → chunk-727SXJPM.DxWkACh8.js} +1 -1
  130. package/web/_astro/{chunk-AQP2D5EJ.DXHV47bN.js → chunk-AQP2D5EJ.42XrmIlH.js} +1 -1
  131. package/web/_astro/{chunk-FMBD7UC4.D1suAfOs.js → chunk-FMBD7UC4.Bao2wMK9.js} +1 -1
  132. package/web/_astro/{chunk-ND2GUHAM.D6BmdHEY.js → chunk-ND2GUHAM.BhydojD1.js} +1 -1
  133. package/web/_astro/{chunk-QZHKN3VN.rnPU-Ak-.js → chunk-QZHKN3VN.CmZ82g-H.js} +1 -1
  134. package/web/_astro/{classDiagram-4FO5ZUOK.CvEfXIo1.js → classDiagram-4FO5ZUOK.Blr83Gt_.js} +1 -1
  135. package/web/_astro/{classDiagram-v2-Q7XG4LA2.CvEfXIo1.js → classDiagram-v2-Q7XG4LA2.Blr83Gt_.js} +1 -1
  136. package/web/_astro/{cose-bilkent-S5V4N54A.DkMisLSm.js → cose-bilkent-S5V4N54A.qmfv0SPn.js} +1 -1
  137. package/web/_astro/{cynefin-OW5HDTMX.C77Q-pF6.js → cynefin-OW5HDTMX.BtELhXPR.js} +1 -1
  138. package/web/_astro/{dagre-BM42HDAG.YaV8ce27.js → dagre-BM42HDAG.AWmaAvXo.js} +1 -1
  139. package/web/_astro/{diagram-2AECGRRQ.CUyGEKge.js → diagram-2AECGRRQ.DdH34KDi.js} +1 -1
  140. package/web/_astro/{diagram-5GNKFQAL.CRgd8dgj.js → diagram-5GNKFQAL.BmNPix1k.js} +1 -1
  141. package/web/_astro/{diagram-KO2AKTUF.Bmzys9hq.js → diagram-KO2AKTUF.BNQd_Ppo.js} +1 -1
  142. package/web/_astro/{diagram-LMA3HP47.BvmJY4Lw.js → diagram-LMA3HP47.BAPgDi0e.js} +1 -1
  143. package/web/_astro/{diagram-OG6HWLK6.0hEhD30d.js → diagram-OG6HWLK6.DZ4zyLdC.js} +1 -1
  144. package/web/_astro/{erDiagram-TEJ5UH35.Bjt8lrnm.js → erDiagram-TEJ5UH35.Q1TySMmw.js} +1 -1
  145. package/web/_astro/{flowDiagram-I6XJVG4X.CJZTjRDV.js → flowDiagram-I6XJVG4X.BXhFBicG.js} +1 -1
  146. package/web/_astro/{ganttDiagram-6RSMTGT7._VqoVafc.js → ganttDiagram-6RSMTGT7.vHSgcuo2.js} +1 -1
  147. package/web/_astro/{gitGraphDiagram-PVQCEYII.CEDb7tVo.js → gitGraphDiagram-PVQCEYII.Bi1f73C7.js} +1 -1
  148. package/web/_astro/index.BhSwN8yY.css +1 -0
  149. package/web/_astro/{infoDiagram-5YYISTIA.CgwifPpK.js → infoDiagram-5YYISTIA.DPvXZEWm.js} +1 -1
  150. package/web/_astro/{ishikawaDiagram-YF4QCWOH.B99wx2J1.js → ishikawaDiagram-YF4QCWOH.Cz829fsV.js} +1 -1
  151. package/web/_astro/{journeyDiagram-JHISSGLW.2hRwJbEo.js → journeyDiagram-JHISSGLW.enRnB9Me.js} +1 -1
  152. package/web/_astro/{kanban-definition-UN3LZRKU.BahJkKNC.js → kanban-definition-UN3LZRKU.DnTNk6d2.js} +1 -1
  153. package/web/_astro/{linear.DguUUe6V.js → linear.BzGnBwNL.js} +1 -1
  154. package/web/_astro/{mermaid.core.CvgfmqVr.js → mermaid.core.CSbTpIVz.js} +4 -4
  155. package/web/_astro/{mindmap-definition-RKZ34NQL.B3qEA3x9.js → mindmap-definition-RKZ34NQL.DGJDy8zT.js} +1 -1
  156. package/web/_astro/{pieDiagram-4H26LBE5.FlWA-_ru.js → pieDiagram-4H26LBE5.HSPdi5_8.js} +1 -1
  157. package/web/_astro/{quadrantDiagram-W4KKPZXB.9bVn35X7.js → quadrantDiagram-W4KKPZXB.6Gv8mz2Y.js} +1 -1
  158. package/web/_astro/{requirementDiagram-4Y6WPE33.ChykIhct.js → requirementDiagram-4Y6WPE33.CnBCtFwa.js} +1 -1
  159. package/web/_astro/{sankeyDiagram-5OEKKPKP.Z2Y3hkYb.js → sankeyDiagram-5OEKKPKP.5M8VWAu8.js} +1 -1
  160. package/web/_astro/{sequenceDiagram-3UESZ5HK.J7JsHWDt.js → sequenceDiagram-3UESZ5HK.DN41XN-U.js} +1 -1
  161. package/web/_astro/{stateDiagram-AJRCARHV.DAPbiCSS.js → stateDiagram-AJRCARHV.ErOO3W8j.js} +1 -1
  162. package/web/_astro/{stateDiagram-v2-BHNVJYJU.D2jvSi0q.js → stateDiagram-v2-BHNVJYJU.B_ZHICM-.js} +1 -1
  163. package/web/_astro/{timeline-definition-PNZ67QCA.psW2ci29.js → timeline-definition-PNZ67QCA.CWdosr07.js} +1 -1
  164. package/web/_astro/{vennDiagram-CIIHVFJN.DlILYVmg.js → vennDiagram-CIIHVFJN.BHzYjNV5.js} +1 -1
  165. package/web/_astro/{wardleyDiagram-YWT4CUSO.xNWwLJnn.js → wardleyDiagram-YWT4CUSO.h8x4IwPX.js} +1 -1
  166. package/web/_astro/{xychartDiagram-2RQKCTM6.EU_BuH7V.js → xychartDiagram-2RQKCTM6.RTapW2X8.js} +1 -1
  167. package/web/index.html +2 -2
  168. package/plugins/sp/commands/dev-parallel.md +0 -31
  169. package/web/_astro/BoardApp.Bzr4OXrW.js +0 -324
  170. package/web/_astro/TaskDetail.LAyR_Pvn.js +0 -1
  171. package/web/_astro/channel.CUFdtWhm.js +0 -1
  172. package/web/_astro/index.Dg_3bOaJ.css +0 -1
@@ -0,0 +1,130 @@
1
+ #!/usr/bin/env node
2
+ // @bun
3
+
4
+ // plugins/sp/scripts/inline-run-dispatch.ts
5
+ import { spawnSync } from "child_process";
6
+ import { existsSync } from "fs";
7
+ import { dirname, join, resolve } from "path";
8
+ import { fileURLToPath } from "url";
9
+
10
+ // plugins/sp/lib/env.ts
11
+ function getEnvVar(name, fallback) {
12
+ const raw = process.env[name];
13
+ return raw === undefined ? fallback : raw;
14
+ }
15
+
16
+ // plugins/sp/lib/inline-run-app.ts
17
+ async function loadInlineApp(spurBin, resolveAppEntry, required) {
18
+ const { entry, portable } = resolveAppEntry(spurBin);
19
+ const app = await import(entry);
20
+ const missing = portable ? required.filter((name) => typeof app[name] !== "function") : [];
21
+ if (missing.length > 0) {
22
+ 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.`);
23
+ }
24
+ return { app, portable };
25
+ }
26
+
27
+ // plugins/sp/scripts/inline-run-dispatch.ts
28
+ function usage() {
29
+ console.error([
30
+ "Usage: bun plugins/sp/scripts/inline-run-dispatch.ts --attribution --run-id <id> --stage <state> [--executor <name>] [--agent <name>] [--model <m>] [--spur-bin <path>]",
31
+ " bun plugins/sp/scripts/inline-run-dispatch.ts --dispatch-failure --run-id <id> --stage <state> --decision <escalate|stop|host-inline> (--text <record> | --text-file <path>) [--executor <name>] [--agent <name>] [--model <m>] [--spur-bin <path>]",
32
+ " --attribution writes .spur/run/<run-id>-attribution.jsonl; an omitted/empty --executor records the explicit no-attribution marker.",
33
+ " --dispatch-failure classifies the record upstream, writes .spur/run/<run-id>-dispatch-fallback.json, and carries an attributed capacity exhaustion onto the durable availability path."
34
+ ].join(`
35
+ `));
36
+ process.exit(2);
37
+ }
38
+ var SAFE_RUN_ID_RE = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
39
+ function resolveAppEntry(spurBin) {
40
+ const fallback = fileURLToPath(new URL("../../../apps/cli/src/index.ts", import.meta.url));
41
+ const candidates = spurBin !== "" ? [spurBin] : [fallback];
42
+ for (const candidate of candidates) {
43
+ const tokens = candidate.split(/\s+/).filter(Boolean);
44
+ const mainModule = [...tokens].reverse().find((t) => t.endsWith(".ts"));
45
+ if (mainModule === undefined || !existsSync(mainModule))
46
+ continue;
47
+ const repoRoot = resolve(dirname(mainModule), "..", "..", "..");
48
+ const appEntry = join(repoRoot, "packages", "app", "src", "index.ts");
49
+ if (existsSync(appEntry))
50
+ return { entry: appEntry, portable: false };
51
+ }
52
+ const entry = fileURLToPath(new URL("../lib/inline-run.generated.mjs", import.meta.url));
53
+ if (!existsSync(entry)) {
54
+ throw new Error("inline application bundle is missing \u2014 rebuild/install the sp plugin before running inline");
55
+ }
56
+ return { entry, portable: true };
57
+ }
58
+ async function main() {
59
+ if (!process.versions.bun) {
60
+ const child = spawnSync("bun", [fileURLToPath(import.meta.url), ...process.argv.slice(2)], {
61
+ stdio: "inherit"
62
+ });
63
+ if (child.error)
64
+ console.error(`inline-run-dispatch requires Bun on PATH: ${child.error.message}`);
65
+ process.exit(child.status ?? 1);
66
+ }
67
+ const flags = new Map;
68
+ let attribution = false;
69
+ let dispatchFailure = false;
70
+ let spurBin = getEnvVar("SPUR_BIN") ?? "";
71
+ const argv = process.argv.slice(2);
72
+ for (let i = 0;i < argv.length; i++) {
73
+ const flag = argv[i];
74
+ if (flag === "--attribution")
75
+ attribution = true;
76
+ else if (flag === "--dispatch-failure")
77
+ dispatchFailure = true;
78
+ else if (flag === "--spur-bin")
79
+ spurBin = argv[++i] ?? spurBin;
80
+ else if (flag !== undefined)
81
+ flags.set(flag, argv[++i] ?? "");
82
+ }
83
+ const runId = flags.get("--run-id") ?? "";
84
+ const stage = flags.get("--stage") ?? "";
85
+ const decision = flags.get("--decision") ?? "";
86
+ const text = flags.get("--text") ?? "";
87
+ const textFile = flags.get("--text-file") ?? "";
88
+ const executor = flags.get("--executor") ?? "";
89
+ const agent = flags.get("--agent") ?? "";
90
+ const model = flags.get("--model") ?? "";
91
+ if (attribution === dispatchFailure)
92
+ usage();
93
+ if (runId.trim() === "" || stage.trim() === "" || !SAFE_RUN_ID_RE.test(runId))
94
+ usage();
95
+ const identity = {
96
+ executor: executor.trim() === "" ? null : executor,
97
+ agent: agent.trim() === "" ? null : agent,
98
+ ...model.trim() === "" ? {} : { model }
99
+ };
100
+ const { app, portable } = await loadInlineApp(spurBin, resolveAppEntry, [
101
+ "recordInlineRunAttribution",
102
+ "runInlineRunDispatchFailure"
103
+ ]);
104
+ if (attribution) {
105
+ app.recordInlineRunAttribution(process.cwd(), runId, {
106
+ stage,
107
+ ...identity,
108
+ observedAt: new Date().toISOString()
109
+ });
110
+ process.stdout.write(`${JSON.stringify({ ok: true, runId, stage })}
111
+ `);
112
+ process.exit(0);
113
+ }
114
+ if (decision.trim() === "" || text === "" === (textFile === ""))
115
+ usage();
116
+ const { app: lib } = await loadInlineApp(spurBin, () => ({ entry: fileURLToPath(new URL("../lib/inline-run.generated.mjs", import.meta.url)), portable }), ["loadSpurConfig"]);
117
+ const spurConfig = await lib.loadSpurConfig(process.cwd(), portable ? { embeddedSchemas: lib.EMBEDDED_SPUR_SCHEMAS } : undefined);
118
+ process.exit(await app.runInlineRunDispatchFailure({
119
+ runId,
120
+ stage,
121
+ decision,
122
+ ...text === "" ? { textFile } : { text },
123
+ ...identity,
124
+ spurConfig
125
+ }));
126
+ }
127
+ main().catch((e) => {
128
+ console.error(`inline-run-dispatch: FAIL \u2014 ${e instanceof Error ? e.message : String(e)}`);
129
+ process.exit(1);
130
+ });
@@ -0,0 +1,135 @@
1
+ #!/usr/bin/env bun
2
+ /** Inline dispatch fail-fast facade (task 1134 R1–R4): argv/env only. The classification, the
3
+ * decision artifact and the durable availability write all live in the app runners
4
+ * (`recordInlineRunAttribution`, `runInlineRunDispatchFailure`), reached through the same
5
+ * `loadInlineApp` seam the setup facade uses. It is a SEPARATE script from `inline-run-setup.ts`
6
+ * because it is a separate operation and the ADR-130 glue budget is per script. */
7
+ import { spawnSync } from 'node:child_process';
8
+ import { existsSync } from 'node:fs';
9
+ import { dirname, join, resolve } from 'node:path';
10
+ import { fileURLToPath } from 'node:url';
11
+ // Type-only namespace import (erased before bundling; the smoke scanner exempts `import type`).
12
+ import type * as spurApp from '@gobing-ai/spur-app';
13
+ import { getEnvVar } from '../lib/env';
14
+ import { loadInlineApp } from '../lib/inline-run-app';
15
+
16
+ // EMBEDDED_SPUR_SCHEMAS is declared by the bundle twin, never exported by the barrel.
17
+ type InlineApp = typeof spurApp & { EMBEDDED_SPUR_SCHEMAS?: ReadonlyMap<string, string> };
18
+
19
+ function usage(): never {
20
+ console.error(
21
+ [
22
+ 'Usage: bun plugins/sp/scripts/inline-run-dispatch.ts --attribution --run-id <id> --stage <state> [--executor <name>] [--agent <name>] [--model <m>] [--spur-bin <path>]',
23
+ ' bun plugins/sp/scripts/inline-run-dispatch.ts --dispatch-failure --run-id <id> --stage <state> --decision <escalate|stop|host-inline> (--text <record> | --text-file <path>) [--executor <name>] [--agent <name>] [--model <m>] [--spur-bin <path>]',
24
+ ' --attribution writes .spur/run/<run-id>-attribution.jsonl; an omitted/empty --executor records the explicit no-attribution marker.',
25
+ ' --dispatch-failure classifies the record upstream, writes .spur/run/<run-id>-dispatch-fallback.json, and carries an attributed capacity exhaustion onto the durable availability path.',
26
+ ].join('\n'),
27
+ );
28
+ process.exit(2);
29
+ }
30
+
31
+ /** Run ids become filenames under `.spur/run/` — single safe component only (0804 R8). */
32
+ const SAFE_RUN_ID_RE = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
33
+
34
+ /** Resolve the app entry: --spur-bin/SPUR_BIN main module if it proves a repo checkout, else the bundle. */
35
+ function resolveAppEntry(spurBin: string): { entry: string; portable: boolean } {
36
+ const fallback = fileURLToPath(new URL('../../../apps/cli/src/index.ts', import.meta.url));
37
+ const candidates = spurBin !== '' ? [spurBin] : [fallback];
38
+ for (const candidate of candidates) {
39
+ const tokens = candidate.split(/\s+/).filter(Boolean);
40
+ // Main module = last path-like token; only a .ts source entry proves a repo checkout.
41
+ const mainModule = [...tokens].reverse().find((t) => t.endsWith('.ts'));
42
+ if (mainModule === undefined || !existsSync(mainModule)) continue;
43
+ const repoRoot = resolve(dirname(mainModule), '..', '..', '..');
44
+ const appEntry = join(repoRoot, 'packages', 'app', 'src', 'index.ts');
45
+ if (existsSync(appEntry)) return { entry: appEntry, portable: false };
46
+ }
47
+ const entry = fileURLToPath(new URL('../lib/inline-run.generated.mjs', import.meta.url));
48
+ if (!existsSync(entry)) {
49
+ throw new Error('inline application bundle is missing — rebuild/install the sp plugin before running inline');
50
+ }
51
+ return { entry, portable: true };
52
+ }
53
+
54
+ async function main(): Promise<void> {
55
+ // The portable Node twin has no workspace imports; the app bundle still uses Spur's Bun runtime.
56
+ if (!process.versions.bun) {
57
+ const child = spawnSync('bun', [fileURLToPath(import.meta.url), ...process.argv.slice(2)], {
58
+ stdio: 'inherit',
59
+ });
60
+ if (child.error) console.error(`inline-run-dispatch requires Bun on PATH: ${child.error.message}`);
61
+ process.exit(child.status ?? 1);
62
+ }
63
+ const flags = new Map<string, string>();
64
+ let attribution = false;
65
+ let dispatchFailure = false;
66
+ let spurBin = getEnvVar('SPUR_BIN') ?? '';
67
+ const argv = process.argv.slice(2);
68
+ for (let i = 0; i < argv.length; i++) {
69
+ const flag = argv[i];
70
+ if (flag === '--attribution') attribution = true;
71
+ else if (flag === '--dispatch-failure') dispatchFailure = true;
72
+ else if (flag === '--spur-bin') spurBin = argv[++i] ?? spurBin;
73
+ else if (flag !== undefined) flags.set(flag, argv[++i] ?? '');
74
+ }
75
+ const runId = flags.get('--run-id') ?? '';
76
+ const stage = flags.get('--stage') ?? '';
77
+ const decision = flags.get('--decision') ?? '';
78
+ const text = flags.get('--text') ?? '';
79
+ const textFile = flags.get('--text-file') ?? '';
80
+ const executor = flags.get('--executor') ?? '';
81
+ const agent = flags.get('--agent') ?? '';
82
+ const model = flags.get('--model') ?? '';
83
+
84
+ if (attribution === dispatchFailure) usage();
85
+ if (runId.trim() === '' || stage.trim() === '' || !SAFE_RUN_ID_RE.test(runId)) usage();
86
+ // R3: the executor identity the inline stage actually used. An omitted/empty `--executor` is the
87
+ // explicit no-attribution marker — never a silent drop.
88
+ const identity = {
89
+ executor: executor.trim() === '' ? null : executor,
90
+ agent: agent.trim() === '' ? null : agent,
91
+ ...(model.trim() === '' ? {} : { model }),
92
+ };
93
+ const { app, portable } = await loadInlineApp<InlineApp>(spurBin, resolveAppEntry, [
94
+ 'recordInlineRunAttribution',
95
+ 'runInlineRunDispatchFailure',
96
+ ]);
97
+
98
+ if (attribution) {
99
+ app.recordInlineRunAttribution(process.cwd(), runId, {
100
+ stage,
101
+ ...identity,
102
+ observedAt: new Date().toISOString(),
103
+ });
104
+ process.stdout.write(`${JSON.stringify({ ok: true, runId, stage })}\n`);
105
+ process.exit(0);
106
+ }
107
+
108
+ if (decision.trim() === '' || (text === '') === (textFile === '')) usage();
109
+ // ADR-082: this facade is the composition root, so it loads the effective config once and
110
+ // threads the merged result into the app runner (never a per-slice load in packages/app).
111
+ const { app: lib } = await loadInlineApp<typeof import('../lib/inline-run.generated.mjs')>(
112
+ spurBin,
113
+ () => ({ entry: fileURLToPath(new URL('../lib/inline-run.generated.mjs', import.meta.url)), portable }),
114
+ ['loadSpurConfig'],
115
+ );
116
+ const spurConfig = await lib.loadSpurConfig(
117
+ process.cwd(),
118
+ portable ? { embeddedSchemas: lib.EMBEDDED_SPUR_SCHEMAS } : undefined,
119
+ );
120
+ process.exit(
121
+ await app.runInlineRunDispatchFailure({
122
+ runId,
123
+ stage,
124
+ decision: decision as 'escalate' | 'stop' | 'host-inline',
125
+ ...(text === '' ? { textFile } : { text }),
126
+ ...identity,
127
+ spurConfig,
128
+ }),
129
+ );
130
+ }
131
+
132
+ main().catch((e: unknown) => {
133
+ console.error(`inline-run-dispatch: FAIL — ${e instanceof Error ? e.message : String(e)}`);
134
+ process.exit(1);
135
+ });