@gobing-ai/spur 0.3.91 → 0.3.93
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 -9
- package/config/plugin-scripts.json +27 -1
- package/config/templates/AGENTS.md +4 -0
- package/config/templates/docs/02_ROADMAP.md +2 -0
- package/config/templates/docs/03_ARCHITECTURE.md +3 -1
- package/config/templates/docs/04_DESIGN.md +2 -0
- package/config/templates/docs/99_PROJECT_CONSTITUTION.md +10 -2
- package/config/workflow-candidates.json +72 -1
- package/config/workflows/feature-lifecycle.yaml +14 -5
- package/config/workflows/feature-verification.yaml +36 -27
- package/config/workflows/history-anatomy.yaml +16 -25
- package/config/workflows/idea-pipeline.yaml +76 -23
- package/config/workflows/pr-review.yaml +8 -0
- package/config/workflows/task-pipeline.yaml +362 -40
- package/config/workflows/wayfinder-resolution.yaml +5 -0
- package/config/workflows/wrapup-pipeline.yaml +102 -19
- package/package.json +9 -9
- package/plugins/sp/README.md +7 -2
- package/plugins/sp/agents/super-planner.md +14 -5
- package/plugins/sp/commands/dev-dogfood.md +4 -4
- package/plugins/sp/commands/dev-fixall.md +8 -5
- package/plugins/sp/commands/dev-run.md +8 -2
- package/plugins/sp/commands/dev-runall.md +14 -8
- package/plugins/sp/commands/dev-verify.md +9 -0
- package/plugins/sp/commands/dev-verifyall.md +5 -0
- package/plugins/sp/lib/idea-handoff.generated.mjs +306 -301
- package/plugins/sp/lib/inline-run.generated.d.mts +18 -0
- package/plugins/sp/lib/inline-run.generated.mjs +1468 -0
- package/plugins/sp/plugin.json +1 -1
- package/plugins/sp/references/environment-lens.md +1 -1
- package/plugins/sp/scripts/dogfood-testing/validate-report.mjs +136 -0
- package/plugins/sp/scripts/dogfood-testing/validate-report.ts +196 -2
- package/plugins/sp/scripts/feature-verification-steps.mjs +174 -0
- package/plugins/sp/scripts/feature-verification-steps.ts +275 -0
- package/plugins/sp/scripts/history-anatomy-cache.mjs +104 -4
- package/plugins/sp/scripts/history-anatomy-cache.ts +137 -13
- package/plugins/sp/scripts/inline-pipeline-parity-check.ts +2 -1
- package/plugins/sp/scripts/inline-run-setup.mjs +421 -0
- package/plugins/sp/scripts/inline-run-setup.ts +325 -78
- package/plugins/sp/scripts/quality-gate.mjs +248 -6
- package/plugins/sp/scripts/quality-gate.ts +410 -9
- package/plugins/sp/scripts/record-feature-sync.mjs +63 -0
- package/plugins/sp/scripts/record-feature-sync.ts +84 -0
- package/plugins/sp/scripts/residual-scan.mjs +484 -0
- package/plugins/sp/scripts/residual-scan.ts +640 -0
- package/plugins/sp/scripts/task-diffstat.mjs +156 -0
- package/plugins/sp/scripts/task-diffstat.ts +229 -0
- package/plugins/sp/scripts/task-evidence-precheck.ts +8 -3
- package/plugins/sp/scripts/task-size-precheck.ts +8 -3
- package/plugins/sp/scripts/wrapup-drift-probe.mjs +181 -0
- package/plugins/sp/scripts/wrapup-drift-probe.ts +258 -0
- package/plugins/sp/scripts/wrapup-steps.mjs +60 -1
- package/plugins/sp/scripts/wrapup-steps.ts +89 -4
- package/plugins/sp/skills/brainstorm/SKILL.md +2 -0
- package/plugins/sp/skills/brainstorm/references/workflows.md +17 -2
- package/plugins/sp/skills/branch-workflow/SKILL.md +1 -0
- package/plugins/sp/skills/branch-workflow/references/worktree-patterns.md +2 -0
- package/plugins/sp/skills/code-implementation/SKILL.md +17 -0
- package/plugins/sp/skills/code-verification/SKILL.md +21 -0
- package/plugins/sp/skills/code-verification/references/secu-review.md +3 -2
- package/plugins/sp/skills/code-verification/references/verdict-schema.md +1 -0
- package/plugins/sp/skills/dogfood-testing/SKILL.md +5 -3
- package/plugins/sp/skills/dogfood-testing/references/monitor-ledger.md +63 -26
- package/plugins/sp/skills/dogfood-testing/references/report-template.md +33 -10
- package/plugins/sp/skills/history-anatomy/references/modes.md +5 -3
- package/plugins/sp/skills/next-feature/references/ranking-rubric.md +1 -1
- package/plugins/sp/skills/next-router/references/routing-table.md +7 -0
- package/plugins/sp/skills/parallel-execution/references/dispatch-surface.md +1 -1
- package/plugins/sp/skills/session-review/SKILL.md +12 -2
- package/plugins/sp/skills/spur-check/SKILL.md +112 -0
- package/plugins/sp/skills/spur-cli/references/features.md +1 -1
- package/plugins/sp/skills/spur-cli/references/projects.md +3 -1
- package/plugins/sp/skills/spur-cli/references/workflows.md +39 -19
- package/plugins/sp/skills/spur-dev/SKILL.md +13 -5
- package/plugins/sp/skills/spur-dev/references/cross-cutting.md +18 -3
- package/plugins/sp/skills/spur-dev/references/dev-operations.md +2 -2
- package/plugins/sp/skills/spur-dev/references/document-authoring.md +85 -0
- package/plugins/sp/skills/spur-dev/references/execution-batch.md +286 -58
- package/plugins/sp/skills/spur-dev/references/execution-workflow.md +1 -1
- package/plugins/sp/skills/spur-dev/references/flag-glossary.md +27 -4
- package/plugins/sp/skills/spur-dev/references/gate-checklists.md +3 -2
- package/plugins/sp/skills/spur-dev/references/inline-pipeline-driver.md +61 -16
- package/plugins/sp/skills/spur-dev/references/planning-workflow.md +10 -5
- package/plugins/sp/skills/spur-dev/templates/design.md +31 -0
- package/plugins/sp/skills/spur-dev/templates/plan.md +32 -0
- package/plugins/sp/skills/spur-doctor/SKILL.md +60 -14
- package/schemas/state-machine-workflow.schema.json +4 -0
- package/spur.js +22501 -19644
- package/web/_astro/BoardApp.CerSBgis.js +192 -0
- package/web/_astro/BoardApp.eoTz0pZs.js +1 -0
- package/web/_astro/{TaskDetail.CXGltuT_.js → TaskDetail.DCqiC-OZ.js} +1 -1
- package/web/_astro/arc.CPwg6Rw0.js +1 -0
- package/web/_astro/{architectureDiagram-3BPJPVTR.DJ8DHkWE.js → architectureDiagram-3BPJPVTR.DM_vp_hO.js} +1 -1
- package/web/_astro/{blockDiagram-GPEHLZMM.D8DHK3Jl.js → blockDiagram-GPEHLZMM.DXVIiv0p.js} +1 -1
- package/web/_astro/{c4Diagram-AAUBKEIU.BugQbX9u.js → c4Diagram-AAUBKEIU.BbF_zCxW.js} +1 -1
- package/web/_astro/channel.MYZLKNwy.js +1 -0
- package/web/_astro/{chunk-2J33WTMH.Mv26KlVn.js → chunk-2J33WTMH.CAgQHpPC.js} +1 -1
- package/web/_astro/{chunk-4BX2VUAB.CD51JoT_.js → chunk-4BX2VUAB.BN-5tpw4.js} +1 -1
- package/web/_astro/{chunk-55IACEB6.D_PFaIEe.js → chunk-55IACEB6.CnPkEEr0.js} +1 -1
- package/web/_astro/{chunk-727SXJPM.DemMW1ao.js → chunk-727SXJPM.BQzQeMVm.js} +4 -4
- package/web/_astro/{chunk-AQP2D5EJ.Iu2V5-ex.js → chunk-AQP2D5EJ.B6xNyDnL.js} +1 -1
- package/web/_astro/{chunk-FMBD7UC4.MsNgSP-E.js → chunk-FMBD7UC4.C7f9Ih78.js} +1 -1
- package/web/_astro/{chunk-ND2GUHAM.DfbRaAlm.js → chunk-ND2GUHAM.CNV1dFXT.js} +1 -1
- package/web/_astro/{chunk-QZHKN3VN.BbJAQ4h-.js → chunk-QZHKN3VN.Cudn2TkJ.js} +1 -1
- package/web/_astro/{classDiagram-4FO5ZUOK.CPurtiC2.js → classDiagram-4FO5ZUOK.D1NwP50q.js} +1 -1
- package/web/_astro/{classDiagram-v2-Q7XG4LA2.CPurtiC2.js → classDiagram-v2-Q7XG4LA2.D1NwP50q.js} +1 -1
- package/web/_astro/{cose-bilkent-S5V4N54A.CKKdx1bM.js → cose-bilkent-S5V4N54A.B1wSL-Xb.js} +1 -1
- package/web/_astro/{cynefin-OW5HDTMX.CoKMTg-R.js → cynefin-OW5HDTMX.BmK52w8G.js} +1 -1
- package/web/_astro/{dagre-BM42HDAG.D4h4_k56.js → dagre-BM42HDAG.Bfy5CTDT.js} +2 -2
- package/web/_astro/diagram-2AECGRRQ.DhNnvUvX.js +43 -0
- package/web/_astro/diagram-5GNKFQAL.lTX5KwnS.js +10 -0
- package/web/_astro/{diagram-KO2AKTUF.BFoCkiCr.js → diagram-KO2AKTUF.CW_vMJ4z.js} +3 -3
- package/web/_astro/{diagram-LMA3HP47.exHn9OVx.js → diagram-LMA3HP47.B_8ZGF67.js} +1 -1
- package/web/_astro/{diagram-OG6HWLK6.CeqO34nN.js → diagram-OG6HWLK6.BppnHsdS.js} +1 -1
- package/web/_astro/{erDiagram-TEJ5UH35.D_v7HqxR.js → erDiagram-TEJ5UH35.BEuHXcjJ.js} +5 -5
- package/web/_astro/{flowDiagram-I6XJVG4X.EUmrpbwh.js → flowDiagram-I6XJVG4X.CH-UlnGr.js} +4 -4
- package/web/_astro/{ganttDiagram-6RSMTGT7.BOCF5lII.js → ganttDiagram-6RSMTGT7.BO81S85v.js} +1 -1
- package/web/_astro/{gitGraphDiagram-PVQCEYII.Di7otYZD.js → gitGraphDiagram-PVQCEYII.XnPxPPZN.js} +1 -1
- package/web/_astro/index.Hjbr15fG.css +1 -0
- package/web/_astro/{infoDiagram-5YYISTIA.TcBkCAJk.js → infoDiagram-5YYISTIA.JyjYRu_T.js} +1 -1
- package/web/_astro/{ishikawaDiagram-YF4QCWOH.D-2y4M0c.js → ishikawaDiagram-YF4QCWOH.BBRBF-Fo.js} +5 -5
- package/web/_astro/{journeyDiagram-JHISSGLW.DPbJI_n2.js → journeyDiagram-JHISSGLW.C_iymSyp.js} +1 -1
- package/web/_astro/{kanban-definition-UN3LZRKU.EFxhQ9Fj.js → kanban-definition-UN3LZRKU.DdfW-Oqt.js} +7 -7
- package/web/_astro/{linear.DSAsQLzs.js → linear.C2_IkbZT.js} +1 -1
- package/web/_astro/mermaid.core.GAOYeSR0.js +303 -0
- package/web/_astro/{mindmap-definition-RKZ34NQL.CJY1N_7V.js → mindmap-definition-RKZ34NQL.DAZIxQSK.js} +2 -2
- package/web/_astro/{pieDiagram-4H26LBE5.567ZNoL2.js → pieDiagram-4H26LBE5.CN8sIhKM.js} +3 -3
- package/web/_astro/{quadrantDiagram-W4KKPZXB.mqfz9-MY.js → quadrantDiagram-W4KKPZXB.3dGcX5GP.js} +1 -1
- package/web/_astro/{requirementDiagram-4Y6WPE33.Bv1Gv9In.js → requirementDiagram-4Y6WPE33.BV2y4dd6.js} +3 -3
- package/web/_astro/{sankeyDiagram-5OEKKPKP.B6Gs4X4r.js → sankeyDiagram-5OEKKPKP.Cqo15Tvo.js} +4 -4
- package/web/_astro/{sequenceDiagram-3UESZ5HK.BhYj4v-m.js → sequenceDiagram-3UESZ5HK.CROCPMJB.js} +1 -1
- package/web/_astro/{stateDiagram-AJRCARHV.BPbBnkpw.js → stateDiagram-AJRCARHV.RfXZrkFE.js} +1 -1
- package/web/_astro/{stateDiagram-v2-BHNVJYJU.C4squMNK.js → stateDiagram-v2-BHNVJYJU.CPXmbBs9.js} +1 -1
- package/web/_astro/{timeline-definition-PNZ67QCA.C_SwIHgl.js → timeline-definition-PNZ67QCA.DdgKTiO8.js} +3 -3
- package/web/_astro/{vennDiagram-CIIHVFJN.Bz4NZGpQ.js → vennDiagram-CIIHVFJN.CPNVSHF1.js} +5 -5
- package/web/_astro/{wardleyDiagram-YWT4CUSO.CozMVZ3i.js → wardleyDiagram-YWT4CUSO.CQhA0Jyr.js} +3 -3
- package/web/_astro/{xychartDiagram-2RQKCTM6.BwMGBwjB.js → xychartDiagram-2RQKCTM6.n61BWyy4.js} +1 -1
- package/web/index.html +2 -2
- package/config/workflows/decision-routing-example.yaml +0 -134
- package/web/_astro/BoardApp.BEDWpzsr.js +0 -188
- package/web/_astro/BoardApp.DQG2xfEz.js +0 -1
- package/web/_astro/arc.C0rrflm_.js +0 -1
- package/web/_astro/channel.SRrg1P-w.js +0 -1
- package/web/_astro/diagram-2AECGRRQ.DJ0h9zgw.js +0 -43
- package/web/_astro/diagram-5GNKFQAL.DvPk1jYd.js +0 -10
- package/web/_astro/index.Bx6GY4RH.css +0 -1
- package/web/_astro/mermaid.core.kAZjgJHG.js +0 -301
|
@@ -0,0 +1,421 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// @bun
|
|
3
|
+
|
|
4
|
+
// plugins/sp/scripts/inline-run-setup.ts
|
|
5
|
+
import { spawnSync } from "child_process";
|
|
6
|
+
import {
|
|
7
|
+
appendFileSync,
|
|
8
|
+
closeSync,
|
|
9
|
+
existsSync,
|
|
10
|
+
mkdirSync,
|
|
11
|
+
openSync,
|
|
12
|
+
readFileSync,
|
|
13
|
+
renameSync,
|
|
14
|
+
unlinkSync,
|
|
15
|
+
writeFileSync,
|
|
16
|
+
writeSync
|
|
17
|
+
} from "fs";
|
|
18
|
+
import { dirname, join, resolve } from "path";
|
|
19
|
+
import { fileURLToPath } from "url";
|
|
20
|
+
|
|
21
|
+
// plugins/sp/lib/env.ts
|
|
22
|
+
function getEnvVar(name, fallback) {
|
|
23
|
+
const raw = process.env[name];
|
|
24
|
+
return raw === undefined ? fallback : raw;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// plugins/sp/scripts/inline-run-setup.ts
|
|
28
|
+
function usage() {
|
|
29
|
+
console.error("Usage: bun plugins/sp/scripts/inline-run-setup.ts --run-id <id> --file <definition> [--spur-bin <path>]");
|
|
30
|
+
console.error(" bun plugins/sp/scripts/inline-run-setup.ts --fingerprint --task-file <path> [--feature-file <path>] [--spur-bin <path>]");
|
|
31
|
+
console.error(" 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> [--spur-bin <path>]");
|
|
32
|
+
console.error(" bun plugins/sp/scripts/inline-run-setup.ts --close --run-id <id> --status <done|failed|paused> [--reason <terminal-reason>] [--spur-bin <path>]");
|
|
33
|
+
console.error(" bun plugins/sp/scripts/inline-run-setup.ts --decide --run-id <id> --node <state> --options-json <file> [--spur-bin <path>]");
|
|
34
|
+
process.exit(2);
|
|
35
|
+
}
|
|
36
|
+
var SAFE_RUN_ID_RE = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
|
|
37
|
+
function refuseUnsafeRunId(runId) {
|
|
38
|
+
console.error(`inline-run-setup: refusing unsafe run id: ${runId}`);
|
|
39
|
+
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.");
|
|
40
|
+
process.exit(1);
|
|
41
|
+
}
|
|
42
|
+
function resolveAppEntry(spurBin) {
|
|
43
|
+
let candidates = [];
|
|
44
|
+
if (spurBin !== "") {
|
|
45
|
+
candidates = [spurBin];
|
|
46
|
+
} else {
|
|
47
|
+
candidates = [fileURLToPath(new URL("../../../apps/cli/src/index.ts", import.meta.url))];
|
|
48
|
+
}
|
|
49
|
+
for (const candidate of candidates) {
|
|
50
|
+
const tokens = candidate.split(/\s+/).filter(Boolean);
|
|
51
|
+
const mainModule = [...tokens].reverse().find((t) => t.endsWith(".ts"));
|
|
52
|
+
if (mainModule === undefined || !existsSync(mainModule))
|
|
53
|
+
continue;
|
|
54
|
+
const srcDir = dirname(mainModule);
|
|
55
|
+
const repoRoot = resolve(srcDir, "..", "..", "..");
|
|
56
|
+
const appEntry = join(repoRoot, "packages", "app", "src", "index.ts");
|
|
57
|
+
if (existsSync(appEntry))
|
|
58
|
+
return { entry: appEntry, portable: false };
|
|
59
|
+
}
|
|
60
|
+
const entry = fileURLToPath(new URL("../lib/inline-run.generated.mjs", import.meta.url));
|
|
61
|
+
if (!existsSync(entry)) {
|
|
62
|
+
throw new Error("inline application bundle is missing \u2014 rebuild/install the sp plugin before running inline");
|
|
63
|
+
}
|
|
64
|
+
return { entry, portable: true };
|
|
65
|
+
}
|
|
66
|
+
async function readInstalledInventory(file, spurBin) {
|
|
67
|
+
const localCli = fileURLToPath(new URL("../../../apps/cli/src/index.ts", import.meta.url));
|
|
68
|
+
const bundle = fileURLToPath(new URL("../lib/inline-run.generated.mjs", import.meta.url));
|
|
69
|
+
const { splitLaunchCommand } = await import(bundle);
|
|
70
|
+
const launch = spurBin ? splitLaunchCommand(spurBin, 'inline-run-setup "spurBin"') : existsSync(localCli) ? { command: "bun", leadingArgs: [localCli] } : { command: "spur", leadingArgs: [] };
|
|
71
|
+
if ("error" in launch)
|
|
72
|
+
throw new Error(launch.error);
|
|
73
|
+
const result = spawnSync(launch.command, [...launch.leadingArgs, "workflow", "show", file, "--format", "todo", "--json"], { cwd: process.cwd(), encoding: "utf8", timeout: 30000, maxBuffer: 4194304 });
|
|
74
|
+
if (result.status !== 0) {
|
|
75
|
+
throw new Error(`could not resolve the workflow definition with the installed CLI: ${result.error?.message ?? result.stderr}`);
|
|
76
|
+
}
|
|
77
|
+
const value = JSON.parse(result.stdout);
|
|
78
|
+
if (value && typeof value === "object" && "ok" in value && "data" in value && value.ok === true) {
|
|
79
|
+
return value.data;
|
|
80
|
+
}
|
|
81
|
+
return value;
|
|
82
|
+
}
|
|
83
|
+
function writeOutcome(runId, outcome) {
|
|
84
|
+
const runDir = join(process.cwd(), ".spur", "run");
|
|
85
|
+
if (!existsSync(runDir))
|
|
86
|
+
mkdirSync(runDir, { recursive: true });
|
|
87
|
+
const statePath = join(runDir, `${runId}.state.json`);
|
|
88
|
+
const markdownPath = join(runDir, `${runId}.md`);
|
|
89
|
+
let prior = {};
|
|
90
|
+
try {
|
|
91
|
+
const parsed = JSON.parse(readFileSync(statePath, "utf8"));
|
|
92
|
+
if (parsed !== null && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
93
|
+
prior = parsed;
|
|
94
|
+
}
|
|
95
|
+
} catch {}
|
|
96
|
+
const at = new Date().toISOString();
|
|
97
|
+
const state = {
|
|
98
|
+
schemaVersion: 1,
|
|
99
|
+
runId,
|
|
100
|
+
...outcome.workflowName !== undefined ? { workflowName: outcome.workflowName } : {},
|
|
101
|
+
...outcome.status !== undefined ? { status: outcome.status } : {},
|
|
102
|
+
startedAt: typeof prior.startedAt === "string" ? prior.startedAt : at,
|
|
103
|
+
updatedAt: at,
|
|
104
|
+
...outcome.attached !== undefined ? { attached: outcome.attached } : {},
|
|
105
|
+
...outcome.definitionDigest !== undefined ? { definitionDigest: outcome.definitionDigest } : {},
|
|
106
|
+
...outcome.workflowVersion !== undefined ? { workflowVersion: outcome.workflowVersion } : {},
|
|
107
|
+
...outcome.resolvedPath !== undefined ? { resolvedPath: outcome.resolvedPath } : {},
|
|
108
|
+
...outcome.layer !== undefined ? { layer: outcome.layer } : {},
|
|
109
|
+
...outcome.workdir !== undefined ? { workdir: outcome.workdir } : {},
|
|
110
|
+
ok: outcome.ok,
|
|
111
|
+
...outcome.ok === false && outcome.error !== undefined ? { error: outcome.error } : {}
|
|
112
|
+
};
|
|
113
|
+
const temp = `${statePath}.tmp`;
|
|
114
|
+
try {
|
|
115
|
+
writeFileSync(temp, `${JSON.stringify(state, null, 4)}
|
|
116
|
+
`);
|
|
117
|
+
renameSync(temp, statePath);
|
|
118
|
+
} catch {
|
|
119
|
+
try {
|
|
120
|
+
unlinkSync(temp);
|
|
121
|
+
} catch {}
|
|
122
|
+
}
|
|
123
|
+
let headerFd;
|
|
124
|
+
try {
|
|
125
|
+
headerFd = openSync(markdownPath, "wx");
|
|
126
|
+
writeSync(headerFd, `# spur inline run ${runId} \u2014 ${outcome.workflowName ?? "unknown workflow"} \u2014 setup ${at}
|
|
127
|
+
`);
|
|
128
|
+
} catch {} finally {
|
|
129
|
+
if (headerFd !== undefined)
|
|
130
|
+
closeSync(headerFd);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
async function printFingerprint(taskFile, featureFile, spurBin) {
|
|
134
|
+
const { entry } = resolveAppEntry(spurBin);
|
|
135
|
+
const app = await import(entry);
|
|
136
|
+
const workdir = process.cwd();
|
|
137
|
+
const inputs = await app.readProofInputContents(undefined, workdir, {
|
|
138
|
+
taskFile,
|
|
139
|
+
...featureFile.trim() !== "" ? { featureFile } : {}
|
|
140
|
+
});
|
|
141
|
+
if (!inputs.ok) {
|
|
142
|
+
console.error(`inline-run-setup: FAIL \u2014 ${inputs.error}`);
|
|
143
|
+
return 1;
|
|
144
|
+
}
|
|
145
|
+
const digest = await app.computeProofInputFingerprint({
|
|
146
|
+
cwd: workdir,
|
|
147
|
+
...inputs.taskContent !== undefined ? { taskContent: inputs.taskContent } : {},
|
|
148
|
+
...inputs.featureContent !== undefined ? { featureContent: inputs.featureContent } : {}
|
|
149
|
+
});
|
|
150
|
+
process.stdout.write(`${digest}
|
|
151
|
+
`);
|
|
152
|
+
return 0;
|
|
153
|
+
}
|
|
154
|
+
var CLOSE_STATUSES = new Set(["done", "failed", "paused"]);
|
|
155
|
+
var TERMINAL_REASONS = new Set([
|
|
156
|
+
"done",
|
|
157
|
+
"paused-operator",
|
|
158
|
+
"failed-check",
|
|
159
|
+
"failed-agent",
|
|
160
|
+
"failed-timeout",
|
|
161
|
+
"failed-guard",
|
|
162
|
+
"cancelled",
|
|
163
|
+
"interrupted",
|
|
164
|
+
"retry-exhausted"
|
|
165
|
+
]);
|
|
166
|
+
var ACTION_STATUSES = new Set(["done", "failed"]);
|
|
167
|
+
function runRecordLogPath(runDir, runId) {
|
|
168
|
+
const markdownPath = join(runDir, `${runId}.md`);
|
|
169
|
+
const legacyLogPath = join(runDir, `${runId}.log`);
|
|
170
|
+
if (existsSync(legacyLogPath) && !existsSync(markdownPath))
|
|
171
|
+
return legacyLogPath;
|
|
172
|
+
return markdownPath;
|
|
173
|
+
}
|
|
174
|
+
function appendTraceFailureLine(runId, detail) {
|
|
175
|
+
try {
|
|
176
|
+
const runDir = join(process.cwd(), ".spur", "run");
|
|
177
|
+
if (!existsSync(runDir))
|
|
178
|
+
mkdirSync(runDir, { recursive: true });
|
|
179
|
+
const safeRunId = runId.replace(/[^A-Za-z0-9._-]/g, "_");
|
|
180
|
+
const stamp = new Date().toISOString().replace(/\.\d{3}Z$/, "Z");
|
|
181
|
+
appendFileSync(runRecordLogPath(runDir, safeRunId), `[${stamp}] ${detail}
|
|
182
|
+
`);
|
|
183
|
+
} catch {}
|
|
184
|
+
}
|
|
185
|
+
async function runTraceMode(input) {
|
|
186
|
+
const operation = input.close ? "run.close" : "action.finish";
|
|
187
|
+
const fail = (error) => {
|
|
188
|
+
appendTraceFailureLine(input.runId, `trace-emission-failed operation=${operation} run=${input.runId}${input.node === "" ? "" : ` node=${input.node}`}${input.kind === "" ? "" : ` kind=${input.kind}`}: ${error}`);
|
|
189
|
+
process.stdout.write(`${JSON.stringify({ ok: false, runId: input.runId, error })}
|
|
190
|
+
`);
|
|
191
|
+
return input.close ? 1 : 0;
|
|
192
|
+
};
|
|
193
|
+
let projectDb;
|
|
194
|
+
try {
|
|
195
|
+
const { entry } = resolveAppEntry(input.spurBin);
|
|
196
|
+
const app = await import(entry);
|
|
197
|
+
projectDb = await app.openInlineRunProjectDb(process.cwd());
|
|
198
|
+
const writer = app.createWorkflowActionTraceWriter(projectDb.adapter, (failure) => {
|
|
199
|
+
const detail = failure;
|
|
200
|
+
appendTraceFailureLine(input.runId, `trace-emission-failed operation=${detail.operation ?? operation} run=${input.runId}: ${detail.error ?? "unknown error"}`);
|
|
201
|
+
});
|
|
202
|
+
const result = input.close ? await writer.closeRun(input.runId, input.status, undefined, input.reason) : await writer.recordAction({
|
|
203
|
+
runId: input.runId,
|
|
204
|
+
node: input.node,
|
|
205
|
+
kind: input.kind,
|
|
206
|
+
status: input.status,
|
|
207
|
+
ok: input.ok,
|
|
208
|
+
durationMs: input.durationMs
|
|
209
|
+
});
|
|
210
|
+
if (result.ok !== true && result.failure !== undefined) {
|
|
211
|
+
const failure = result.failure;
|
|
212
|
+
return fail(failure.error ?? "unknown trace emission failure");
|
|
213
|
+
}
|
|
214
|
+
process.stdout.write(`${JSON.stringify({ ...result, runId: input.runId })}
|
|
215
|
+
`);
|
|
216
|
+
return 0;
|
|
217
|
+
} catch (error) {
|
|
218
|
+
if (input.close && error.name === "RunRowNotFoundError") {
|
|
219
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
220
|
+
appendTraceFailureLine(input.runId, `trace-close-failed run=${input.runId}: ${message}`);
|
|
221
|
+
process.stdout.write(`${JSON.stringify({ ok: false, runId: input.runId, error: message, code: "RUN_NOT_FOUND" })}
|
|
222
|
+
`);
|
|
223
|
+
return 1;
|
|
224
|
+
}
|
|
225
|
+
return fail(error instanceof Error ? error.message : String(error));
|
|
226
|
+
} finally {
|
|
227
|
+
projectDb?.close();
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
async function runDecideMode(input) {
|
|
231
|
+
const decideFailed = (error) => {
|
|
232
|
+
process.stdout.write(`${JSON.stringify({ ok: false, runId: input.runId, error })}
|
|
233
|
+
`);
|
|
234
|
+
return 1;
|
|
235
|
+
};
|
|
236
|
+
let outcome;
|
|
237
|
+
try {
|
|
238
|
+
const { entry, portable } = resolveAppEntry(input.spurBin);
|
|
239
|
+
const app = await import(entry);
|
|
240
|
+
const lib = await import(fileURLToPath(new URL("../lib/inline-run.generated.mjs", import.meta.url)));
|
|
241
|
+
const enabled = await lib.resolveDecideDecisionMakerEnabled(process.cwd(), portable ? { embeddedSchemas: lib.EMBEDDED_SPUR_SCHEMAS } : undefined);
|
|
242
|
+
outcome = await app.runDecideForInlineRun({
|
|
243
|
+
workdir: process.cwd(),
|
|
244
|
+
optionsFile: input.optionsFile,
|
|
245
|
+
enabled
|
|
246
|
+
});
|
|
247
|
+
} catch (error) {
|
|
248
|
+
return decideFailed(error instanceof Error ? error.message : String(error));
|
|
249
|
+
}
|
|
250
|
+
if (!outcome.ok)
|
|
251
|
+
return decideFailed(outcome.error ?? "decide failed without an error message");
|
|
252
|
+
process.stdout.write(`${JSON.stringify({ ok: true, runId: input.runId, node: input.node, ...outcome })}
|
|
253
|
+
`);
|
|
254
|
+
return await runTraceMode({
|
|
255
|
+
runId: input.runId,
|
|
256
|
+
close: false,
|
|
257
|
+
node: input.node,
|
|
258
|
+
kind: "decide",
|
|
259
|
+
status: "done",
|
|
260
|
+
ok: true,
|
|
261
|
+
durationMs: outcome.durationMs ?? 0,
|
|
262
|
+
spurBin: input.spurBin
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
async function main() {
|
|
266
|
+
if (!process.versions.bun) {
|
|
267
|
+
const child = spawnSync("bun", [fileURLToPath(import.meta.url), ...process.argv.slice(2)], {
|
|
268
|
+
stdio: "inherit"
|
|
269
|
+
});
|
|
270
|
+
if (child.error)
|
|
271
|
+
console.error(`inline-run-setup requires Bun on PATH: ${child.error.message}`);
|
|
272
|
+
process.exit(child.status ?? 1);
|
|
273
|
+
}
|
|
274
|
+
let runId = "";
|
|
275
|
+
let file = "";
|
|
276
|
+
let fingerprint = false;
|
|
277
|
+
let taskFile = "";
|
|
278
|
+
let featureFile = "";
|
|
279
|
+
let action = false;
|
|
280
|
+
let close = false;
|
|
281
|
+
let decide = false;
|
|
282
|
+
let optionsJson = "";
|
|
283
|
+
let node = "";
|
|
284
|
+
let kind = "";
|
|
285
|
+
let status = "";
|
|
286
|
+
let reason = "";
|
|
287
|
+
let okRaw = "";
|
|
288
|
+
let durationRaw = "";
|
|
289
|
+
let spurBin = getEnvVar("SPUR_BIN") ?? "";
|
|
290
|
+
const argv = process.argv.slice(2);
|
|
291
|
+
for (let i = 0;i < argv.length; i++) {
|
|
292
|
+
if (argv[i] === "--run-id")
|
|
293
|
+
runId = argv[++i] ?? "";
|
|
294
|
+
else if (argv[i] === "--file")
|
|
295
|
+
file = argv[++i] ?? "";
|
|
296
|
+
else if (argv[i] === "--fingerprint")
|
|
297
|
+
fingerprint = true;
|
|
298
|
+
else if (argv[i] === "--task-file")
|
|
299
|
+
taskFile = argv[++i] ?? "";
|
|
300
|
+
else if (argv[i] === "--feature-file")
|
|
301
|
+
featureFile = argv[++i] ?? "";
|
|
302
|
+
else if (argv[i] === "--action")
|
|
303
|
+
action = true;
|
|
304
|
+
else if (argv[i] === "--close")
|
|
305
|
+
close = true;
|
|
306
|
+
else if (argv[i] === "--decide")
|
|
307
|
+
decide = true;
|
|
308
|
+
else if (argv[i] === "--options-json")
|
|
309
|
+
optionsJson = argv[++i] ?? "";
|
|
310
|
+
else if (argv[i] === "--node")
|
|
311
|
+
node = argv[++i] ?? "";
|
|
312
|
+
else if (argv[i] === "--kind")
|
|
313
|
+
kind = argv[++i] ?? "";
|
|
314
|
+
else if (argv[i] === "--status")
|
|
315
|
+
status = argv[++i] ?? "";
|
|
316
|
+
else if (argv[i] === "--reason")
|
|
317
|
+
reason = argv[++i] ?? "";
|
|
318
|
+
else if (argv[i] === "--ok")
|
|
319
|
+
okRaw = argv[++i] ?? "";
|
|
320
|
+
else if (argv[i] === "--duration-ms")
|
|
321
|
+
durationRaw = argv[++i] ?? "";
|
|
322
|
+
else if (argv[i] === "--spur-bin")
|
|
323
|
+
spurBin = argv[++i] ?? spurBin;
|
|
324
|
+
}
|
|
325
|
+
if (fingerprint) {
|
|
326
|
+
if (runId !== "" || file !== "" || taskFile.trim() === "")
|
|
327
|
+
usage();
|
|
328
|
+
process.exit(await printFingerprint(taskFile, featureFile, spurBin));
|
|
329
|
+
}
|
|
330
|
+
if (decide) {
|
|
331
|
+
if (action || close || fingerprint || file !== "" || taskFile !== "")
|
|
332
|
+
usage();
|
|
333
|
+
if (runId.trim() === "" || node.trim() === "" || optionsJson.trim() === "")
|
|
334
|
+
usage();
|
|
335
|
+
if (!SAFE_RUN_ID_RE.test(runId))
|
|
336
|
+
refuseUnsafeRunId(runId);
|
|
337
|
+
process.exit(await runDecideMode({ runId, node, optionsFile: optionsJson, spurBin }));
|
|
338
|
+
}
|
|
339
|
+
if (action || close) {
|
|
340
|
+
if (action && close)
|
|
341
|
+
usage();
|
|
342
|
+
if (runId.trim() === "" || status.trim() === "")
|
|
343
|
+
usage();
|
|
344
|
+
if (!SAFE_RUN_ID_RE.test(runId))
|
|
345
|
+
refuseUnsafeRunId(runId);
|
|
346
|
+
if (close) {
|
|
347
|
+
if (!CLOSE_STATUSES.has(status))
|
|
348
|
+
usage();
|
|
349
|
+
if (reason.trim() === "") {
|
|
350
|
+
if (status === "failed")
|
|
351
|
+
usage();
|
|
352
|
+
} else if (!TERMINAL_REASONS.has(reason)) {
|
|
353
|
+
usage();
|
|
354
|
+
}
|
|
355
|
+
process.exit(await runTraceMode({
|
|
356
|
+
runId,
|
|
357
|
+
close: true,
|
|
358
|
+
node: "",
|
|
359
|
+
kind: "",
|
|
360
|
+
status,
|
|
361
|
+
ok: true,
|
|
362
|
+
durationMs: 0,
|
|
363
|
+
spurBin,
|
|
364
|
+
...reason.trim() === "" ? {} : { reason }
|
|
365
|
+
}));
|
|
366
|
+
}
|
|
367
|
+
if (node.trim() === "" || kind.trim() === "")
|
|
368
|
+
usage();
|
|
369
|
+
if (!ACTION_STATUSES.has(status))
|
|
370
|
+
usage();
|
|
371
|
+
if (okRaw !== "true" && okRaw !== "false")
|
|
372
|
+
usage();
|
|
373
|
+
const ok = okRaw === "true";
|
|
374
|
+
const durationMs = Number(durationRaw);
|
|
375
|
+
if (durationRaw.trim() === "" || !Number.isFinite(durationMs) || durationMs < 0)
|
|
376
|
+
usage();
|
|
377
|
+
process.exit(await runTraceMode({ runId, close: false, node, kind, status, ok, durationMs, spurBin }));
|
|
378
|
+
}
|
|
379
|
+
if (runId.trim() === "" || file.trim() === "")
|
|
380
|
+
usage();
|
|
381
|
+
if (!SAFE_RUN_ID_RE.test(runId))
|
|
382
|
+
refuseUnsafeRunId(runId);
|
|
383
|
+
const { entry, portable } = resolveAppEntry(spurBin);
|
|
384
|
+
const app = await import(entry);
|
|
385
|
+
const workdir = process.cwd();
|
|
386
|
+
let inventory;
|
|
387
|
+
try {
|
|
388
|
+
inventory = await readInstalledInventory(file, spurBin);
|
|
389
|
+
} catch (error) {
|
|
390
|
+
const message = `could not resolve the workflow definition: ${error instanceof Error ? error.message : String(error)}`;
|
|
391
|
+
writeOutcome(runId, { ok: false, runId, error: message });
|
|
392
|
+
throw new Error(message);
|
|
393
|
+
}
|
|
394
|
+
const projectDb = await app.openInlineRunProjectDb(workdir);
|
|
395
|
+
let exitCode = 0;
|
|
396
|
+
try {
|
|
397
|
+
const result = await app.createOrAttachInlineRun({
|
|
398
|
+
workdir,
|
|
399
|
+
getDb: async () => projectDb.adapter,
|
|
400
|
+
file,
|
|
401
|
+
runId,
|
|
402
|
+
inventory,
|
|
403
|
+
...portable ? { embeddedSchemas: app.EMBEDDED_SPUR_SCHEMAS } : {}
|
|
404
|
+
});
|
|
405
|
+
writeOutcome(runId, result);
|
|
406
|
+
if (!result.ok) {
|
|
407
|
+
console.error(`inline-run-setup: FAIL for run ${runId}`);
|
|
408
|
+
console.error(` ${result.error}`);
|
|
409
|
+
exitCode = 1;
|
|
410
|
+
} else {
|
|
411
|
+
console.error(`inline-run-setup: ${result.attached ? "attached" : "created"} run ${runId} (${result.workflowName}, layer ${result.layer}, digest ${result.definitionDigest}, status ${result.status})`);
|
|
412
|
+
}
|
|
413
|
+
} finally {
|
|
414
|
+
projectDb.close();
|
|
415
|
+
}
|
|
416
|
+
process.exit(exitCode);
|
|
417
|
+
}
|
|
418
|
+
main().catch((e) => {
|
|
419
|
+
console.error(`inline-run-setup: FAIL \u2014 ${e instanceof Error ? e.message : String(e)}`);
|
|
420
|
+
process.exit(1);
|
|
421
|
+
});
|