@gobing-ai/spur 0.3.95 → 0.3.97
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/plugin-scripts.json +0 -54
- package/config/rules/boundary/sp-script-placement.yaml +19 -0
- package/config/rules/strict/runtime-boundaries.yaml +6 -0
- package/config/rules/structure/test-location.yaml +2 -0
- package/config/rules/typescript/no-syscall-emulation-in-boundary-mock.yaml +1 -1
- package/config/rules/typescript/output-boundaries.yaml +15 -2
- package/config/script-placement-baseline.json +51 -0
- package/config/templates/AGENTS.md +3 -3
- package/config/workflows/feature-verification.yaml +1 -1
- package/config/workflows/history-anatomy.yaml +23 -11
- package/config/workflows/idea-pipeline.yaml +80 -85
- package/config/workflows/pr-review.yaml +18 -9
- package/config/workflows/task-pipeline.yaml +42 -95
- package/config/workflows/wrapup-pipeline.yaml +91 -38
- package/package.json +2 -2
- package/plugins/sp/README.md +14 -10
- package/plugins/sp/agents/super-reviewer.md +43 -18
- package/plugins/sp/commands/dev-fixgha.md +83 -0
- package/plugins/sp/commands/dev-gitmsg.md +8 -6
- package/plugins/sp/commands/dev-gtd.md +2 -2
- package/plugins/sp/commands/dev-idea.md +22 -12
- package/plugins/sp/commands/dev-job-dump.md +29 -0
- package/plugins/sp/commands/dev-job-resume.md +29 -0
- package/plugins/sp/commands/dev-plan.md +8 -9
- package/plugins/sp/commands/dev-review.md +22 -13
- package/plugins/sp/commands/dev-verifyall.md +1 -1
- package/plugins/sp/commands/spur-init.md +2 -2
- package/plugins/sp/lib/history-anatomy.generated.d.mts +112 -0
- package/plugins/sp/lib/history-anatomy.generated.mjs +686 -0
- package/plugins/sp/lib/idea-handoff.generated.mjs +5 -4
- package/plugins/sp/lib/inline-run.generated.d.mts +11 -0
- package/plugins/sp/lib/inline-run.generated.mjs +39 -13
- package/plugins/sp/lib/quality-gate.generated.d.mts +104 -0
- package/plugins/sp/lib/quality-gate.generated.mjs +445 -0
- package/plugins/sp/lib/residual-scan.generated.d.mts +63 -0
- package/plugins/sp/lib/residual-scan.generated.mjs +226 -0
- package/plugins/sp/lib/spur-bin.ts +36 -0
- package/plugins/sp/lib/step-profile.generated.d.mts +71 -0
- package/plugins/sp/lib/step-profile.generated.mjs +174 -0
- package/plugins/sp/plugin.json +1 -1
- package/plugins/sp/references/roles.md +3 -3
- package/plugins/sp/scripts/feature-verification-steps.mjs +14 -1
- package/plugins/sp/scripts/feature-verification-steps.ts +14 -1
- package/plugins/sp/scripts/history-anatomy-cache.mjs +20 -19
- package/plugins/sp/scripts/history-anatomy-cache.ts +23 -928
- package/plugins/sp/scripts/inline-run-setup.mjs +85 -320
- package/plugins/sp/scripts/inline-run-setup.ts +104 -667
- package/plugins/sp/scripts/quality-gate.mjs +46 -24
- package/plugins/sp/scripts/quality-gate.ts +13 -659
- package/plugins/sp/scripts/residual-scan.mjs +179 -174
- package/plugins/sp/scripts/residual-scan.ts +125 -517
- package/plugins/sp/scripts/script-root.mjs +5 -1
- package/plugins/sp/scripts/script-root.ts +5 -1
- package/plugins/sp/scripts/workflow-step-profile.mjs +25 -17
- package/plugins/sp/scripts/workflow-step-profile.ts +21 -315
- package/plugins/sp/scripts/wrapup-drift-probe.mjs +8 -2
- package/plugins/sp/scripts/wrapup-drift-probe.ts +3 -2
- package/plugins/sp/scripts/wrapup-steps.mjs +72 -33
- package/plugins/sp/scripts/wrapup-steps.ts +128 -37
- package/plugins/sp/skills/code-improvement/SKILL.md +5 -4
- package/plugins/sp/skills/code-verification/SKILL.md +34 -9
- package/plugins/sp/skills/code-verification/references/verdict-schema.md +3 -3
- package/plugins/sp/skills/functional-review/SKILL.md +7 -4
- package/plugins/sp/skills/functional-review/references/verdict-schema.md +1 -1
- package/plugins/sp/skills/history-anatomy/references/modes.md +2 -1
- package/plugins/sp/skills/next-feature/references/handoff-routing.md +1 -1
- package/plugins/sp/skills/next-router/references/routing-table.md +1 -1
- package/plugins/sp/skills/source-driven-development/SKILL.md +11 -0
- package/plugins/sp/skills/spur-cli/SKILL.md +3 -3
- package/plugins/sp/skills/spur-cli/references/agent.md +10 -10
- package/plugins/sp/skills/spur-cli/references/features.md +17 -6
- package/plugins/sp/skills/spur-cli/references/init.md +17 -16
- package/plugins/sp/skills/spur-cli/references/self.md +3 -2
- package/plugins/sp/skills/spur-cli/references/serve.md +10 -10
- package/plugins/sp/skills/spur-cli/references/tasks/section-editing.md +9 -5
- package/plugins/sp/skills/spur-cli/references/tasks/verbs.md +21 -6
- package/plugins/sp/skills/spur-cli/references/tasks.md +14 -8
- package/plugins/sp/skills/spur-cli/references/workflows.md +17 -14
- package/plugins/sp/skills/spur-dev/SKILL.md +2 -0
- package/plugins/sp/skills/spur-dev/references/ac-style-guide.md +4 -3
- package/plugins/sp/skills/spur-dev/references/cross-cutting.md +14 -10
- package/plugins/sp/skills/spur-dev/references/decision-brief.md +1 -1
- package/plugins/sp/skills/spur-dev/references/dev-operations.md +165 -75
- package/plugins/sp/skills/spur-dev/references/done-housekeeping.md +7 -5
- package/plugins/sp/skills/spur-dev/references/execution-batch.md +84 -39
- package/plugins/sp/skills/spur-dev/references/execution-workflow.md +8 -12
- package/plugins/sp/skills/spur-dev/references/feature-link-helper.md +3 -3
- package/plugins/sp/skills/spur-dev/references/flag-glossary.md +40 -21
- package/plugins/sp/skills/spur-dev/references/gate-checklists.md +19 -19
- package/plugins/sp/skills/spur-dev/references/idea-evaluation.md +4 -3
- package/plugins/sp/skills/spur-dev/references/inline-pipeline-driver.md +72 -15
- package/plugins/sp/skills/spur-doctor/SKILL.md +1 -1
- package/plugins/sp/skills/sys-architecture/SKILL.md +3 -2
- package/spur.js +4461 -2085
- package/web/_astro/{BoardApp.Cpxntzad.js → BoardApp.Bmv5WkJ9.js} +1 -1
- package/web/_astro/BoardApp.vveLOdDq.js +322 -0
- package/web/_astro/{TaskDetail.C5bW4WGV.js → TaskDetail.IKc3uDYV.js} +1 -1
- package/web/_astro/{arc.CyjRvNMY.js → arc.C63Ufg35.js} +1 -1
- package/web/_astro/{architectureDiagram-3BPJPVTR.B4lWRzJA.js → architectureDiagram-3BPJPVTR.CP8Hyldk.js} +1 -1
- package/web/_astro/{blockDiagram-GPEHLZMM.Be38USQ2.js → blockDiagram-GPEHLZMM.Bo_48MRA.js} +1 -1
- package/web/_astro/{c4Diagram-AAUBKEIU.DT9Fj5Qx.js → c4Diagram-AAUBKEIU.Ch61sw-O.js} +1 -1
- package/web/_astro/channel.BKCmJpWM.js +1 -0
- package/web/_astro/{chunk-2J33WTMH.SeSBWLg5.js → chunk-2J33WTMH.CHpzS7xf.js} +1 -1
- package/web/_astro/{chunk-4BX2VUAB.DoO4VE14.js → chunk-4BX2VUAB.DzMNNWIw.js} +1 -1
- package/web/_astro/{chunk-55IACEB6.DrmTFA53.js → chunk-55IACEB6.BqPrDw_Q.js} +1 -1
- package/web/_astro/{chunk-727SXJPM.Bi-TUb_V.js → chunk-727SXJPM.ZzBHhNw-.js} +1 -1
- package/web/_astro/{chunk-AQP2D5EJ.B-xp8Uhi.js → chunk-AQP2D5EJ.1_O_LLkx.js} +1 -1
- package/web/_astro/{chunk-FMBD7UC4.DGWrDnUU.js → chunk-FMBD7UC4.GS4UHg7_.js} +1 -1
- package/web/_astro/{chunk-ND2GUHAM._BagvPDy.js → chunk-ND2GUHAM.BnqotN4g.js} +1 -1
- package/web/_astro/{chunk-QZHKN3VN.VOboYWQ0.js → chunk-QZHKN3VN.Ghuh8zoJ.js} +1 -1
- package/web/_astro/{classDiagram-4FO5ZUOK.D_apfHS7.js → classDiagram-4FO5ZUOK.BW9vC3zg.js} +1 -1
- package/web/_astro/{classDiagram-v2-Q7XG4LA2.D_apfHS7.js → classDiagram-v2-Q7XG4LA2.BW9vC3zg.js} +1 -1
- package/web/_astro/{cose-bilkent-S5V4N54A.DNLU_L-x.js → cose-bilkent-S5V4N54A.D6jNC0ti.js} +1 -1
- package/web/_astro/{cynefin-OW5HDTMX.D8borhV-.js → cynefin-OW5HDTMX.BOGoCs4L.js} +1 -1
- package/web/_astro/{dagre-BM42HDAG.Uc0lvjcV.js → dagre-BM42HDAG.8iU5VcWi.js} +1 -1
- package/web/_astro/{diagram-2AECGRRQ.DDIxtDBo.js → diagram-2AECGRRQ.CVt88HwH.js} +1 -1
- package/web/_astro/{diagram-5GNKFQAL.DVoDnD3H.js → diagram-5GNKFQAL.C9HxUUMl.js} +1 -1
- package/web/_astro/{diagram-KO2AKTUF.CRubTJ-0.js → diagram-KO2AKTUF.4ywkQN3t.js} +1 -1
- package/web/_astro/{diagram-LMA3HP47.m9SeYcXQ.js → diagram-LMA3HP47.C48A6gpz.js} +1 -1
- package/web/_astro/{diagram-OG6HWLK6.C6s2ZjV5.js → diagram-OG6HWLK6.DxFieQW4.js} +1 -1
- package/web/_astro/{erDiagram-TEJ5UH35.nSzr5KTj.js → erDiagram-TEJ5UH35.CtjtQqYs.js} +1 -1
- package/web/_astro/{flowDiagram-I6XJVG4X.DrZcys16.js → flowDiagram-I6XJVG4X.C5zAiahU.js} +1 -1
- package/web/_astro/{ganttDiagram-6RSMTGT7.Do4M1b-K.js → ganttDiagram-6RSMTGT7.D_L6zsny.js} +1 -1
- package/web/_astro/{gitGraphDiagram-PVQCEYII.BWc9oJ4l.js → gitGraphDiagram-PVQCEYII.Ignnrk3V.js} +1 -1
- package/web/_astro/index.CbNz17Sx.css +1 -0
- package/web/_astro/{infoDiagram-5YYISTIA.BIdOctFk.js → infoDiagram-5YYISTIA.DLv8-P5u.js} +1 -1
- package/web/_astro/{ishikawaDiagram-YF4QCWOH.CV5SuG2t.js → ishikawaDiagram-YF4QCWOH.p0OTpwFO.js} +1 -1
- package/web/_astro/{journeyDiagram-JHISSGLW.CxE-rEJ-.js → journeyDiagram-JHISSGLW.CIaBt-DD.js} +1 -1
- package/web/_astro/{kanban-definition-UN3LZRKU.BPRdKWs_.js → kanban-definition-UN3LZRKU.DGySIntq.js} +1 -1
- package/web/_astro/{linear.zRsuuDTE.js → linear.BATV4RXu.js} +1 -1
- package/web/_astro/{mermaid.core.jAJTcMKc.js → mermaid.core.DzkwM3VX.js} +4 -4
- package/web/_astro/{mindmap-definition-RKZ34NQL.BffJxfCr.js → mindmap-definition-RKZ34NQL.3piBRRzA.js} +1 -1
- package/web/_astro/{pieDiagram-4H26LBE5.CacwWaE3.js → pieDiagram-4H26LBE5.DUNueLub.js} +1 -1
- package/web/_astro/{quadrantDiagram-W4KKPZXB.hXELD06-.js → quadrantDiagram-W4KKPZXB.9bihVbrt.js} +1 -1
- package/web/_astro/{requirementDiagram-4Y6WPE33.DdEbPcqT.js → requirementDiagram-4Y6WPE33.CkjsC-YT.js} +1 -1
- package/web/_astro/{sankeyDiagram-5OEKKPKP.DgaDocKv.js → sankeyDiagram-5OEKKPKP.Dbq4vaoQ.js} +1 -1
- package/web/_astro/{sequenceDiagram-3UESZ5HK.B0KScnAu.js → sequenceDiagram-3UESZ5HK.BivrH99P.js} +1 -1
- package/web/_astro/{stateDiagram-AJRCARHV.CV9M_WNc.js → stateDiagram-AJRCARHV.CqLQ1Vzz.js} +1 -1
- package/web/_astro/{stateDiagram-v2-BHNVJYJU.BOAo74Et.js → stateDiagram-v2-BHNVJYJU.DvvoLD3b.js} +1 -1
- package/web/_astro/{timeline-definition-PNZ67QCA.CqqepUMe.js → timeline-definition-PNZ67QCA.B-KR78gv.js} +1 -1
- package/web/_astro/{vennDiagram-CIIHVFJN.DDqe4dOG.js → vennDiagram-CIIHVFJN.Ck0Eieb5.js} +1 -1
- package/web/_astro/{wardleyDiagram-YWT4CUSO.B3R9OPdO.js → wardleyDiagram-YWT4CUSO.CRfdd_do.js} +1 -1
- package/web/_astro/{xychartDiagram-2RQKCTM6.B3SvgXpQ.js → xychartDiagram-2RQKCTM6.Ck_Jxk9C.js} +1 -1
- package/web/index.html +2 -2
- package/plugins/sp/lib/artifact-digest.generated.d.mts +0 -7
- package/plugins/sp/lib/artifact-digest.generated.mjs +0 -48
- package/plugins/sp/scripts/feature-sync-bounded.mjs +0 -301
- package/plugins/sp/scripts/feature-sync-bounded.ts +0 -481
- package/plugins/sp/scripts/idea-coverage-check.ts +0 -168
- package/plugins/sp/scripts/inline-pipeline-parity-check.ts +0 -298
- package/plugins/sp/scripts/record-feature-sync.mjs +0 -63
- package/plugins/sp/scripts/record-feature-sync.ts +0 -84
- package/plugins/sp/scripts/script-contract-check.ts +0 -506
- package/plugins/sp/scripts/stage-registry-adapter.ts +0 -1533
- package/plugins/sp/scripts/surface-drift-inventory.ts +0 -989
- package/plugins/sp/scripts/task-evidence-precheck.ts +0 -189
- package/plugins/sp/scripts/task-size-precheck.ts +0 -212
- package/plugins/sp/scripts/transition-shim-check.ts +0 -238
- package/plugins/sp/scripts/validate-commands.ts +0 -689
- package/plugins/sp/scripts/validate-flag-contracts.ts +0 -890
- package/plugins/sp/scripts/verify-answer-lint.ts +0 -549
- package/web/_astro/BoardApp.BxJuwD7I.js +0 -191
- package/web/_astro/channel.CI6N_tCg.js +0 -1
- package/web/_astro/index.CENnIEqT.css +0 -1
|
@@ -3,18 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
// plugins/sp/scripts/inline-run-setup.ts
|
|
5
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";
|
|
6
|
+
import { existsSync } from "fs";
|
|
18
7
|
import { dirname, join, resolve } from "path";
|
|
19
8
|
import { fileURLToPath } from "url";
|
|
20
9
|
|
|
@@ -26,35 +15,34 @@ function getEnvVar(name, fallback) {
|
|
|
26
15
|
|
|
27
16
|
// plugins/sp/scripts/inline-run-setup.ts
|
|
28
17
|
function usage() {
|
|
29
|
-
console.error(
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
18
|
+
console.error([
|
|
19
|
+
"Usage: bun plugins/sp/scripts/inline-run-setup.ts --run-id <id> --file <definition> [--spur-bin <path>]",
|
|
20
|
+
" bun plugins/sp/scripts/inline-run-setup.ts --fingerprint --task-file <path> [--feature-file <path>] [--spur-bin <path>]",
|
|
21
|
+
" 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>]",
|
|
22
|
+
" bun plugins/sp/scripts/inline-run-setup.ts --actions-file <json-file> --run-id <id> [--spur-bin <path>] (1007 R5 batch trace emission)",
|
|
23
|
+
" bun plugins/sp/scripts/inline-run-setup.ts --close --run-id <id> --status <done|failed|paused> [--reason <terminal-reason>] [--spur-bin <path>]",
|
|
24
|
+
" bun plugins/sp/scripts/inline-run-setup.ts --persist-out --from <worktree-path> [--task-file <path>]... [--spur-bin <path>]",
|
|
25
|
+
" terminal-reason is a closed enum (0937 R2): done, paused-operator, failed-check, failed-agent, failed-timeout, failed-guard, cancelled, interrupted, retry-exhausted",
|
|
26
|
+
" bun plugins/sp/scripts/inline-run-setup.ts --decide --run-id <id> --node <state> --options-json <file> [--spur-bin <path>]"
|
|
27
|
+
].join(`
|
|
28
|
+
`));
|
|
36
29
|
process.exit(2);
|
|
37
30
|
}
|
|
38
31
|
var SAFE_RUN_ID_RE = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
|
|
39
32
|
function refuseUnsafeRunId(runId) {
|
|
40
33
|
console.error(`inline-run-setup: refusing unsafe run id: ${runId}`);
|
|
41
|
-
console.error(" The run id must be a single safe filename component (alphanumeric/._-, no leading dot,
|
|
34
|
+
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.");
|
|
42
35
|
process.exit(1);
|
|
43
36
|
}
|
|
44
37
|
function resolveAppEntry(spurBin) {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
candidates = [spurBin];
|
|
48
|
-
} else {
|
|
49
|
-
candidates = [fileURLToPath(new URL("../../../apps/cli/src/index.ts", import.meta.url))];
|
|
50
|
-
}
|
|
38
|
+
const fallback = fileURLToPath(new URL("../../../apps/cli/src/index.ts", import.meta.url));
|
|
39
|
+
const candidates = spurBin !== "" ? [spurBin] : [fallback];
|
|
51
40
|
for (const candidate of candidates) {
|
|
52
41
|
const tokens = candidate.split(/\s+/).filter(Boolean);
|
|
53
42
|
const mainModule = [...tokens].reverse().find((t) => t.endsWith(".ts"));
|
|
54
43
|
if (mainModule === undefined || !existsSync(mainModule))
|
|
55
44
|
continue;
|
|
56
|
-
const
|
|
57
|
-
const repoRoot = resolve(srcDir, "..", "..", "..");
|
|
45
|
+
const repoRoot = resolve(dirname(mainModule), "..", "..", "..");
|
|
58
46
|
const appEntry = join(repoRoot, "packages", "app", "src", "index.ts");
|
|
59
47
|
if (existsSync(appEntry))
|
|
60
48
|
return { entry: appEntry, portable: false };
|
|
@@ -77,86 +65,10 @@ async function readInstalledInventory(file, spurBin) {
|
|
|
77
65
|
throw new Error(`could not resolve the workflow definition with the installed CLI: ${result.error?.message ?? result.stderr}`);
|
|
78
66
|
}
|
|
79
67
|
const value = JSON.parse(result.stdout);
|
|
80
|
-
if (value && typeof value === "object" && "ok" in value && "data" in value && value.ok === true)
|
|
68
|
+
if (value && typeof value === "object" && "ok" in value && "data" in value && value.ok === true)
|
|
81
69
|
return value.data;
|
|
82
|
-
}
|
|
83
70
|
return value;
|
|
84
71
|
}
|
|
85
|
-
function writeOutcome(runId, outcome) {
|
|
86
|
-
const runDir = join(process.cwd(), ".spur", "run");
|
|
87
|
-
if (!existsSync(runDir))
|
|
88
|
-
mkdirSync(runDir, { recursive: true });
|
|
89
|
-
const statePath = join(runDir, `${runId}.state.json`);
|
|
90
|
-
const markdownPath = join(runDir, `${runId}.md`);
|
|
91
|
-
let prior = {};
|
|
92
|
-
try {
|
|
93
|
-
const parsed = JSON.parse(readFileSync(statePath, "utf8"));
|
|
94
|
-
if (parsed !== null && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
95
|
-
prior = parsed;
|
|
96
|
-
}
|
|
97
|
-
} catch {}
|
|
98
|
-
const at = new Date().toISOString();
|
|
99
|
-
const state = {
|
|
100
|
-
schemaVersion: 1,
|
|
101
|
-
runId,
|
|
102
|
-
...outcome.workflowName !== undefined ? { workflowName: outcome.workflowName } : {},
|
|
103
|
-
...outcome.status !== undefined ? { status: outcome.status } : {},
|
|
104
|
-
startedAt: typeof prior.startedAt === "string" ? prior.startedAt : at,
|
|
105
|
-
updatedAt: at,
|
|
106
|
-
...outcome.attached !== undefined ? { attached: outcome.attached } : {},
|
|
107
|
-
...outcome.definitionDigest !== undefined ? { definitionDigest: outcome.definitionDigest } : {},
|
|
108
|
-
...outcome.workflowVersion !== undefined ? { workflowVersion: outcome.workflowVersion } : {},
|
|
109
|
-
...outcome.resolvedPath !== undefined ? { resolvedPath: outcome.resolvedPath } : {},
|
|
110
|
-
...outcome.layer !== undefined ? { layer: outcome.layer } : {},
|
|
111
|
-
...outcome.workdir !== undefined ? { workdir: outcome.workdir } : {},
|
|
112
|
-
ok: outcome.ok,
|
|
113
|
-
...outcome.ok === false && outcome.error !== undefined ? { error: outcome.error } : {}
|
|
114
|
-
};
|
|
115
|
-
const temp = `${statePath}.tmp`;
|
|
116
|
-
try {
|
|
117
|
-
writeFileSync(temp, `${JSON.stringify(state, null, 4)}
|
|
118
|
-
`);
|
|
119
|
-
renameSync(temp, statePath);
|
|
120
|
-
} catch {
|
|
121
|
-
try {
|
|
122
|
-
unlinkSync(temp);
|
|
123
|
-
} catch {}
|
|
124
|
-
}
|
|
125
|
-
let headerFd;
|
|
126
|
-
try {
|
|
127
|
-
headerFd = openSync(markdownPath, "wx");
|
|
128
|
-
writeSync(headerFd, `# spur inline run ${runId} \u2014 ${outcome.workflowName ?? "unknown workflow"} \u2014 setup ${at}
|
|
129
|
-
`);
|
|
130
|
-
} catch {} finally {
|
|
131
|
-
if (headerFd !== undefined)
|
|
132
|
-
closeSync(headerFd);
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
async function printFingerprint(taskFile, featureFile, spurBin) {
|
|
136
|
-
const { entry } = resolveAppEntry(spurBin);
|
|
137
|
-
const app = await import(entry);
|
|
138
|
-
const workdir = process.cwd();
|
|
139
|
-
const inputs = await app.readProofInputContents(undefined, workdir, {
|
|
140
|
-
taskFile,
|
|
141
|
-
...featureFile.trim() !== "" ? { featureFile } : {}
|
|
142
|
-
});
|
|
143
|
-
if (!inputs.ok) {
|
|
144
|
-
console.error(`inline-run-setup: FAIL \u2014 ${inputs.error}`);
|
|
145
|
-
return 1;
|
|
146
|
-
}
|
|
147
|
-
const digest = await app.computeProofInputFingerprint({
|
|
148
|
-
cwd: workdir,
|
|
149
|
-
...inputs.taskContent !== undefined ? { taskContent: inputs.taskContent } : {},
|
|
150
|
-
...inputs.featureContent !== undefined ? { featureContent: inputs.featureContent } : {}
|
|
151
|
-
});
|
|
152
|
-
process.stdout.write(`${digest}
|
|
153
|
-
`);
|
|
154
|
-
return 0;
|
|
155
|
-
}
|
|
156
|
-
var CLOSE_STATUSES = new Set(["done", "failed", "paused"]);
|
|
157
|
-
function isCloseStatus(status) {
|
|
158
|
-
return CLOSE_STATUSES.has(status);
|
|
159
|
-
}
|
|
160
72
|
var TERMINAL_REASONS = new Set([
|
|
161
73
|
"done",
|
|
162
74
|
"paused-operator",
|
|
@@ -168,135 +80,6 @@ var TERMINAL_REASONS = new Set([
|
|
|
168
80
|
"interrupted",
|
|
169
81
|
"retry-exhausted"
|
|
170
82
|
]);
|
|
171
|
-
var ACTION_STATUSES = new Set(["done", "failed"]);
|
|
172
|
-
function isActionStatus(status) {
|
|
173
|
-
return ACTION_STATUSES.has(status);
|
|
174
|
-
}
|
|
175
|
-
function runRecordLogPath(runDir, runId) {
|
|
176
|
-
const markdownPath = join(runDir, `${runId}.md`);
|
|
177
|
-
const legacyLogPath = join(runDir, `${runId}.log`);
|
|
178
|
-
if (existsSync(legacyLogPath) && !existsSync(markdownPath))
|
|
179
|
-
return legacyLogPath;
|
|
180
|
-
return markdownPath;
|
|
181
|
-
}
|
|
182
|
-
function appendRunLogLine(runId, detail) {
|
|
183
|
-
try {
|
|
184
|
-
const runDir = join(process.cwd(), ".spur", "run");
|
|
185
|
-
if (!existsSync(runDir))
|
|
186
|
-
mkdirSync(runDir, { recursive: true });
|
|
187
|
-
const safeRunId = runId.replace(/[^A-Za-z0-9._-]/g, "_");
|
|
188
|
-
const stamp = new Date().toISOString().replace(/\.\d{3}Z$/, "Z");
|
|
189
|
-
appendFileSync(runRecordLogPath(runDir, safeRunId), `[${stamp}] ${detail}
|
|
190
|
-
`);
|
|
191
|
-
} catch {}
|
|
192
|
-
}
|
|
193
|
-
async function runTraceMode(input) {
|
|
194
|
-
const operation = input.close ? "run.close" : "action.finish";
|
|
195
|
-
const fail = (error) => {
|
|
196
|
-
appendRunLogLine(input.runId, `trace-emission-failed operation=${operation} run=${input.runId}${input.node === "" ? "" : ` node=${input.node}`}${input.kind === "" ? "" : ` kind=${input.kind}`}: ${error}`);
|
|
197
|
-
process.stdout.write(`${JSON.stringify({ ok: false, runId: input.runId, error })}
|
|
198
|
-
`);
|
|
199
|
-
return input.close ? 1 : 0;
|
|
200
|
-
};
|
|
201
|
-
let projectDb;
|
|
202
|
-
try {
|
|
203
|
-
const { entry } = resolveAppEntry(input.spurBin);
|
|
204
|
-
const app = await import(entry);
|
|
205
|
-
projectDb = await app.openInlineRunProjectDb(process.cwd());
|
|
206
|
-
const writer = app.createWorkflowActionTraceWriter(projectDb.adapter, (failure) => {
|
|
207
|
-
const detail = failure;
|
|
208
|
-
appendRunLogLine(input.runId, `trace-emission-failed operation=${detail.operation ?? operation} run=${input.runId}: ${detail.error ?? "unknown error"}`);
|
|
209
|
-
});
|
|
210
|
-
const result = input.close ? await writer.closeRun(input.runId, input.status, undefined, input.reason) : await writer.recordAction({
|
|
211
|
-
runId: input.runId,
|
|
212
|
-
node: input.node,
|
|
213
|
-
kind: input.kind,
|
|
214
|
-
status: input.status,
|
|
215
|
-
ok: input.ok,
|
|
216
|
-
durationMs: input.durationMs
|
|
217
|
-
});
|
|
218
|
-
if (result.ok !== true && result.failure !== undefined) {
|
|
219
|
-
const failure = result.failure;
|
|
220
|
-
return fail(failure.error ?? "unknown trace emission failure");
|
|
221
|
-
}
|
|
222
|
-
if (input.close && input.status === "done" && result.actionRows === 0) {
|
|
223
|
-
const error = `run ${input.runId} closed done with zero action_runs rows`;
|
|
224
|
-
appendRunLogLine(input.runId, `trace-close-failed run=${input.runId}: ${error}`);
|
|
225
|
-
process.stdout.write(`${JSON.stringify({ ok: false, runId: input.runId, error, code: "NO_ACTION_ROWS", actionRows: 0 })}
|
|
226
|
-
`);
|
|
227
|
-
return 1;
|
|
228
|
-
}
|
|
229
|
-
process.stdout.write(`${JSON.stringify({ ...result, runId: input.runId })}
|
|
230
|
-
`);
|
|
231
|
-
return 0;
|
|
232
|
-
} catch (error) {
|
|
233
|
-
if (input.close && error.name === "RunRowNotFoundError") {
|
|
234
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
235
|
-
appendRunLogLine(input.runId, `trace-close-failed run=${input.runId}: ${message}`);
|
|
236
|
-
process.stdout.write(`${JSON.stringify({ ok: false, runId: input.runId, error: message, code: "RUN_NOT_FOUND" })}
|
|
237
|
-
`);
|
|
238
|
-
return 1;
|
|
239
|
-
}
|
|
240
|
-
return fail(error instanceof Error ? error.message : String(error));
|
|
241
|
-
} finally {
|
|
242
|
-
projectDb?.close();
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
async function runDecideMode(input) {
|
|
246
|
-
const decideFailed = (error) => {
|
|
247
|
-
process.stdout.write(`${JSON.stringify({ ok: false, runId: input.runId, error })}
|
|
248
|
-
`);
|
|
249
|
-
return 1;
|
|
250
|
-
};
|
|
251
|
-
let outcome;
|
|
252
|
-
try {
|
|
253
|
-
const { entry, portable } = resolveAppEntry(input.spurBin);
|
|
254
|
-
const app = await import(entry);
|
|
255
|
-
const lib = await import(fileURLToPath(new URL("../lib/inline-run.generated.mjs", import.meta.url)));
|
|
256
|
-
const enabled = await lib.resolveDecideDecisionMakerEnabled(process.cwd(), portable ? { embeddedSchemas: lib.EMBEDDED_SPUR_SCHEMAS } : undefined);
|
|
257
|
-
outcome = await app.runDecideForInlineRun({
|
|
258
|
-
workdir: process.cwd(),
|
|
259
|
-
optionsFile: input.optionsFile,
|
|
260
|
-
enabled
|
|
261
|
-
});
|
|
262
|
-
} catch (error) {
|
|
263
|
-
return decideFailed(error instanceof Error ? error.message : String(error));
|
|
264
|
-
}
|
|
265
|
-
if (!outcome.ok)
|
|
266
|
-
return decideFailed(outcome.error ?? "decide failed without an error message");
|
|
267
|
-
process.stdout.write(`${JSON.stringify({ runId: input.runId, node: input.node, ...outcome, ok: true })}
|
|
268
|
-
`);
|
|
269
|
-
appendRunLogLine(input.runId, `decide node=${input.node} value=${outcome.value ?? ""} source=${outcome.source ?? "default"} reason=${outcome.reason ?? ""}`);
|
|
270
|
-
return await runTraceMode({
|
|
271
|
-
runId: input.runId,
|
|
272
|
-
close: false,
|
|
273
|
-
node: input.node,
|
|
274
|
-
kind: "decide",
|
|
275
|
-
status: "done",
|
|
276
|
-
ok: true,
|
|
277
|
-
durationMs: outcome.durationMs ?? 0,
|
|
278
|
-
spurBin: input.spurBin
|
|
279
|
-
});
|
|
280
|
-
}
|
|
281
|
-
async function runPersistOutMode(input) {
|
|
282
|
-
try {
|
|
283
|
-
const { entry } = resolveAppEntry(input.spurBin);
|
|
284
|
-
const app = await import(entry);
|
|
285
|
-
const result = await app.persistWorktreeRuns({
|
|
286
|
-
fromWorkdir: input.from,
|
|
287
|
-
toWorkdir: process.cwd(),
|
|
288
|
-
...input.taskFiles.length > 0 ? { taskFiles: input.taskFiles } : {}
|
|
289
|
-
});
|
|
290
|
-
process.stdout.write(`${JSON.stringify({ ok: true, persisted: result.persisted, skipped: result.skipped })}
|
|
291
|
-
`);
|
|
292
|
-
return 0;
|
|
293
|
-
} catch (error) {
|
|
294
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
295
|
-
process.stdout.write(`${JSON.stringify({ ok: false, error: message })}
|
|
296
|
-
`);
|
|
297
|
-
return 1;
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
83
|
async function main() {
|
|
301
84
|
if (!process.versions.bun) {
|
|
302
85
|
const child = spawnSync("bun", [fileURLToPath(import.meta.url), ...process.argv.slice(2)], {
|
|
@@ -306,67 +89,59 @@ async function main() {
|
|
|
306
89
|
console.error(`inline-run-setup requires Bun on PATH: ${child.error.message}`);
|
|
307
90
|
process.exit(child.status ?? 1);
|
|
308
91
|
}
|
|
309
|
-
|
|
310
|
-
let
|
|
311
|
-
let fingerprint = false;
|
|
92
|
+
const flags = new Map;
|
|
93
|
+
let fingerprint = false, action = false, close = false, decide = false, persistOut = false;
|
|
312
94
|
const taskFiles = [];
|
|
313
|
-
let featureFile = "";
|
|
314
|
-
let action = false;
|
|
315
|
-
let close = false;
|
|
316
|
-
let decide = false;
|
|
317
|
-
let persistOut = false;
|
|
318
|
-
let from = "";
|
|
319
|
-
let optionsJson = "";
|
|
320
|
-
let node = "";
|
|
321
|
-
let kind = "";
|
|
322
|
-
let status = "";
|
|
323
|
-
let reason = "";
|
|
324
|
-
let okRaw = "";
|
|
325
|
-
let durationRaw = "";
|
|
326
95
|
let spurBin = getEnvVar("SPUR_BIN") ?? "";
|
|
327
96
|
const argv = process.argv.slice(2);
|
|
328
97
|
for (let i = 0;i < argv.length; i++) {
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
else if (argv[i] === "--file")
|
|
332
|
-
file = argv[++i] ?? "";
|
|
333
|
-
else if (argv[i] === "--fingerprint")
|
|
98
|
+
const flag = argv[i];
|
|
99
|
+
if (flag === "--fingerprint")
|
|
334
100
|
fingerprint = true;
|
|
335
|
-
else if (
|
|
336
|
-
taskFiles.push(argv[++i] ?? "");
|
|
337
|
-
else if (argv[i] === "--feature-file")
|
|
338
|
-
featureFile = argv[++i] ?? "";
|
|
339
|
-
else if (argv[i] === "--action")
|
|
101
|
+
else if (flag === "--action")
|
|
340
102
|
action = true;
|
|
341
|
-
else if (
|
|
103
|
+
else if (flag === "--close")
|
|
342
104
|
close = true;
|
|
343
|
-
else if (
|
|
105
|
+
else if (flag === "--decide")
|
|
344
106
|
decide = true;
|
|
345
|
-
else if (
|
|
107
|
+
else if (flag === "--persist-out")
|
|
346
108
|
persistOut = true;
|
|
347
|
-
else if (
|
|
348
|
-
|
|
349
|
-
else if (
|
|
350
|
-
optionsJson = argv[++i] ?? "";
|
|
351
|
-
else if (argv[i] === "--node")
|
|
352
|
-
node = argv[++i] ?? "";
|
|
353
|
-
else if (argv[i] === "--kind")
|
|
354
|
-
kind = argv[++i] ?? "";
|
|
355
|
-
else if (argv[i] === "--status")
|
|
356
|
-
status = argv[++i] ?? "";
|
|
357
|
-
else if (argv[i] === "--reason")
|
|
358
|
-
reason = argv[++i] ?? "";
|
|
359
|
-
else if (argv[i] === "--ok")
|
|
360
|
-
okRaw = argv[++i] ?? "";
|
|
361
|
-
else if (argv[i] === "--duration-ms")
|
|
362
|
-
durationRaw = argv[++i] ?? "";
|
|
363
|
-
else if (argv[i] === "--spur-bin")
|
|
109
|
+
else if (flag === "--task-file")
|
|
110
|
+
taskFiles.push(argv[++i] ?? "");
|
|
111
|
+
else if (flag === "--spur-bin")
|
|
364
112
|
spurBin = argv[++i] ?? spurBin;
|
|
365
|
-
|
|
113
|
+
else if (flag !== undefined)
|
|
114
|
+
flags.set(flag, argv[++i] ?? "");
|
|
115
|
+
}
|
|
116
|
+
const runId = flags.get("--run-id") ?? "";
|
|
117
|
+
const file = flags.get("--file") ?? "";
|
|
118
|
+
const featureFile = flags.get("--feature-file") ?? "";
|
|
119
|
+
const from = flags.get("--from") ?? "";
|
|
120
|
+
const optionsJson = flags.get("--options-json") ?? "";
|
|
121
|
+
const node = flags.get("--node") ?? "";
|
|
122
|
+
const kind = flags.get("--kind") ?? "";
|
|
123
|
+
const status = flags.get("--status") ?? "";
|
|
124
|
+
const reason = flags.get("--reason") ?? "";
|
|
125
|
+
const okRaw = flags.get("--ok") ?? "";
|
|
126
|
+
const durationRaw = flags.get("--duration-ms") ?? "";
|
|
127
|
+
const actionsFile = flags.get("--actions-file") ?? "";
|
|
366
128
|
if (fingerprint) {
|
|
367
129
|
if (runId !== "" || file !== "" || taskFiles.length !== 1 || (taskFiles[0] ?? "").trim() === "")
|
|
368
130
|
usage();
|
|
369
|
-
|
|
131
|
+
const app2 = await import(resolveAppEntry(spurBin).entry);
|
|
132
|
+
process.exit(await app2.runInlineRunFingerprint({ taskFile: taskFiles[0] ?? "", featureFile }));
|
|
133
|
+
}
|
|
134
|
+
if (actionsFile !== "") {
|
|
135
|
+
if (action || close || fingerprint || decide || persistOut || file !== "" || taskFiles.length > 0)
|
|
136
|
+
usage();
|
|
137
|
+
if (runId.trim() === "" || status !== "" || node !== "" || kind !== "")
|
|
138
|
+
usage();
|
|
139
|
+
if (okRaw !== "" || durationRaw !== "")
|
|
140
|
+
usage();
|
|
141
|
+
if (!SAFE_RUN_ID_RE.test(runId))
|
|
142
|
+
refuseUnsafeRunId(runId);
|
|
143
|
+
const app2 = await import(resolveAppEntry(spurBin).entry);
|
|
144
|
+
process.exit(await app2.runInlineRunTraceBatch({ runId, actionsFile }));
|
|
370
145
|
}
|
|
371
146
|
if (decide) {
|
|
372
147
|
if (action || close || fingerprint || file !== "" || taskFiles.length > 0)
|
|
@@ -375,14 +150,27 @@ async function main() {
|
|
|
375
150
|
usage();
|
|
376
151
|
if (!SAFE_RUN_ID_RE.test(runId))
|
|
377
152
|
refuseUnsafeRunId(runId);
|
|
378
|
-
|
|
153
|
+
try {
|
|
154
|
+
const { entry: entry2, portable: portable2 } = resolveAppEntry(spurBin);
|
|
155
|
+
const app2 = await import(entry2);
|
|
156
|
+
const bundlePath = fileURLToPath(new URL("../lib/inline-run.generated.mjs", import.meta.url));
|
|
157
|
+
const lib = await import(bundlePath);
|
|
158
|
+
const enabled = await lib.resolveDecideDecisionMakerEnabled(process.cwd(), portable2 ? { embeddedSchemas: lib.EMBEDDED_SPUR_SCHEMAS } : undefined);
|
|
159
|
+
process.exit(await app2.runInlineRunDecide({ runId, node, optionsFile: optionsJson, enabled }));
|
|
160
|
+
} catch (error) {
|
|
161
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
162
|
+
process.stdout.write(`${JSON.stringify({ ok: false, runId, error: message })}
|
|
163
|
+
`);
|
|
164
|
+
process.exit(1);
|
|
165
|
+
}
|
|
379
166
|
}
|
|
380
167
|
if (persistOut) {
|
|
381
168
|
if (fingerprint || decide || action || close || runId !== "" || file !== "")
|
|
382
169
|
usage();
|
|
383
170
|
if (from.trim() === "" || taskFiles.some((taskFile) => taskFile.trim() === ""))
|
|
384
171
|
usage();
|
|
385
|
-
|
|
172
|
+
const app2 = await import(resolveAppEntry(spurBin).entry);
|
|
173
|
+
process.exit(await app2.runInlineRunPersistOut({ from, taskFiles }));
|
|
386
174
|
}
|
|
387
175
|
if (action || close) {
|
|
388
176
|
if (action && close)
|
|
@@ -391,16 +179,13 @@ async function main() {
|
|
|
391
179
|
usage();
|
|
392
180
|
if (!SAFE_RUN_ID_RE.test(runId))
|
|
393
181
|
refuseUnsafeRunId(runId);
|
|
182
|
+
const app2 = await import(resolveAppEntry(spurBin).entry);
|
|
394
183
|
if (close) {
|
|
395
|
-
if (!
|
|
184
|
+
if (!app2.isInlineRunCloseStatus(status))
|
|
396
185
|
usage();
|
|
397
|
-
if (reason.trim() === "")
|
|
398
|
-
if (status === "failed")
|
|
399
|
-
usage();
|
|
400
|
-
} else if (!TERMINAL_REASONS.has(reason)) {
|
|
186
|
+
if (reason.trim() === "" ? status === "failed" : !TERMINAL_REASONS.has(reason))
|
|
401
187
|
usage();
|
|
402
|
-
|
|
403
|
-
process.exit(await runTraceMode({
|
|
188
|
+
process.exit(await app2.runInlineRunTrace({
|
|
404
189
|
runId,
|
|
405
190
|
close: true,
|
|
406
191
|
node: "",
|
|
@@ -408,21 +193,19 @@ async function main() {
|
|
|
408
193
|
status,
|
|
409
194
|
ok: true,
|
|
410
195
|
durationMs: 0,
|
|
411
|
-
spurBin,
|
|
412
196
|
...reason.trim() === "" ? {} : { reason }
|
|
413
197
|
}));
|
|
414
198
|
}
|
|
415
199
|
if (node.trim() === "" || kind.trim() === "")
|
|
416
200
|
usage();
|
|
417
|
-
if (!
|
|
201
|
+
if (!app2.isInlineRunActionStatus(status))
|
|
418
202
|
usage();
|
|
419
203
|
if (okRaw !== "true" && okRaw !== "false")
|
|
420
204
|
usage();
|
|
421
|
-
const ok = okRaw === "true";
|
|
422
205
|
const durationMs = Number(durationRaw);
|
|
423
206
|
if (durationRaw.trim() === "" || !Number.isFinite(durationMs) || durationMs < 0)
|
|
424
207
|
usage();
|
|
425
|
-
process.exit(await
|
|
208
|
+
process.exit(await app2.runInlineRunTrace({ runId, close: false, node, kind, status, ok: okRaw === "true", durationMs }));
|
|
426
209
|
}
|
|
427
210
|
if (runId.trim() === "" || file.trim() === "")
|
|
428
211
|
usage();
|
|
@@ -430,38 +213,20 @@ async function main() {
|
|
|
430
213
|
refuseUnsafeRunId(runId);
|
|
431
214
|
const { entry, portable } = resolveAppEntry(spurBin);
|
|
432
215
|
const app = await import(entry);
|
|
433
|
-
const workdir = process.cwd();
|
|
434
216
|
let inventory;
|
|
435
217
|
try {
|
|
436
218
|
inventory = await readInstalledInventory(file, spurBin);
|
|
437
219
|
} catch (error) {
|
|
438
220
|
const message = `could not resolve the workflow definition: ${error instanceof Error ? error.message : String(error)}`;
|
|
439
|
-
|
|
221
|
+
app.writeInlineRunOutcome(runId, { ok: false, runId, error: message });
|
|
440
222
|
throw new Error(message);
|
|
441
223
|
}
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
file,
|
|
449
|
-
runId,
|
|
450
|
-
inventory,
|
|
451
|
-
...portable ? { embeddedSchemas: app.EMBEDDED_SPUR_SCHEMAS } : {}
|
|
452
|
-
});
|
|
453
|
-
writeOutcome(runId, result);
|
|
454
|
-
if (!result.ok) {
|
|
455
|
-
console.error(`inline-run-setup: FAIL for run ${runId}`);
|
|
456
|
-
console.error(` ${result.error}`);
|
|
457
|
-
exitCode = 1;
|
|
458
|
-
} else {
|
|
459
|
-
console.error(`inline-run-setup: ${result.attached ? "attached" : "created"} run ${runId} (${result.workflowName}, layer ${result.layer}, digest ${result.definitionDigest}, status ${result.status})`);
|
|
460
|
-
}
|
|
461
|
-
} finally {
|
|
462
|
-
projectDb.close();
|
|
463
|
-
}
|
|
464
|
-
process.exit(exitCode);
|
|
224
|
+
process.exit(await app.runInlineRunSetup({
|
|
225
|
+
runId,
|
|
226
|
+
file,
|
|
227
|
+
inventory,
|
|
228
|
+
...portable ? { embeddedSchemas: app.EMBEDDED_SPUR_SCHEMAS } : {}
|
|
229
|
+
}));
|
|
465
230
|
}
|
|
466
231
|
main().catch((e) => {
|
|
467
232
|
console.error(`inline-run-setup: FAIL \u2014 ${e instanceof Error ? e.message : String(e)}`);
|