@gobing-ai/spur 0.4.9 → 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/rules/README.md +1 -1
- package/config/rules/boundary/env-var-hygiene.yaml +0 -1
- package/config/rules/boundary/sp-script-placement.yaml +3 -3
- package/config/rules/docs/sourced-api-claims.yaml +23 -96
- package/config/workflows/feature-verification.yaml +1 -9
- package/config/workflows/task-pipeline.yaml +2 -2
- package/package.json +1 -1
- package/plugins/sp/agents/expert-spur.md +0 -1
- package/plugins/sp/commands/dev-changelog.md +1 -1
- package/plugins/sp/plugin.json +1 -1
- 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-verification/references/secu-review.md +3 -4
- 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/parallel-execution/SKILL.md +1 -1
- package/plugins/sp/skills/parallel-execution/references/result-synthesis.md +9 -10
- package/plugins/sp/skills/reverse-engineering/SKILL.md +4 -6
- package/plugins/sp/skills/session-review/SKILL.md +10 -26
- package/plugins/sp/skills/source-driven-development/SKILL.md +25 -29
- package/plugins/sp/skills/spur-cli/references/agent.md +5 -2
- package/plugins/sp/skills/spur-cli/references/features.md +1 -1
- package/plugins/sp/skills/spur-cli/references/workflows/authoring-workflows.md +1 -1
- package/plugins/sp/skills/spur-dev/references/dev-operations.md +5 -6
- package/plugins/sp/skills/spur-dev/references/execution-worktree-landing.md +1 -1
- package/spur.js +378 -411
- 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/web/_astro/channel.C7wG9RxA.js +0 -1
- package/web/_astro/index.BCY1JJHd.css +0 -1
package/spur.js
CHANGED
|
@@ -14967,7 +14967,6 @@ var init_finding_codes = __esm(() => {
|
|
|
14967
14967
|
"L4.feature-receipt-run",
|
|
14968
14968
|
"L4.feature-receipt-stale",
|
|
14969
14969
|
"L4.feature-receipt-contract",
|
|
14970
|
-
"L4.feature-receipt-confidence",
|
|
14971
14970
|
"precheck-size",
|
|
14972
14971
|
"precheck-evidence",
|
|
14973
14972
|
"inventory-coverage",
|
|
@@ -15040,7 +15039,6 @@ var init_finding_codes = __esm(() => {
|
|
|
15040
15039
|
L4_FEATURE_RECEIPT_RUN: "L4.feature-receipt-run",
|
|
15041
15040
|
L4_FEATURE_RECEIPT_STALE: "L4.feature-receipt-stale",
|
|
15042
15041
|
L4_FEATURE_RECEIPT_CONTRACT: "L4.feature-receipt-contract",
|
|
15043
|
-
L4_FEATURE_RECEIPT_CONFIDENCE: "L4.feature-receipt-confidence",
|
|
15044
15042
|
PRECHECK_SIZE: "precheck-size",
|
|
15045
15043
|
PRECHECK_EVIDENCE: "precheck-evidence",
|
|
15046
15044
|
INVENTORY_COVERAGE: "inventory-coverage",
|
|
@@ -15065,6 +15063,7 @@ var init_projects = __esm(() => {
|
|
|
15065
15063
|
projectEntrySchema = object2({
|
|
15066
15064
|
name: string2().min(1),
|
|
15067
15065
|
path: string2().min(1),
|
|
15066
|
+
host: string2().optional(),
|
|
15068
15067
|
port: number2().int().nonnegative().default(0)
|
|
15069
15068
|
});
|
|
15070
15069
|
projectsFileSchema = object2({
|
|
@@ -85638,6 +85637,12 @@ function normalizeProjectPath(pathInput) {
|
|
|
85638
85637
|
function setPortProbeForTests(probe) {
|
|
85639
85638
|
testPortProbe = probe;
|
|
85640
85639
|
}
|
|
85640
|
+
function isLocalHost(host) {
|
|
85641
|
+
if (!host)
|
|
85642
|
+
return true;
|
|
85643
|
+
const trimmed = host.trim().toLowerCase();
|
|
85644
|
+
return trimmed === "localhost" || trimmed === "127.0.0.1" || trimmed === "::1" || trimmed === "";
|
|
85645
|
+
}
|
|
85641
85646
|
function classifyPortBindError(err) {
|
|
85642
85647
|
const code = err && typeof err === "object" && "code" in err ? String(err.code) : "";
|
|
85643
85648
|
if (code === "EADDRINUSE" || code === "EADDRNOTAVAIL") {
|
|
@@ -85648,31 +85653,34 @@ function classifyPortBindError(err) {
|
|
|
85648
85653
|
async function isPortLiveOnHost(port, host, timeoutMs) {
|
|
85649
85654
|
return new Promise((resolve) => {
|
|
85650
85655
|
const socket = connect({ port, host, timeout: timeoutMs });
|
|
85651
|
-
socket.
|
|
85652
|
-
|
|
85653
|
-
|
|
85654
|
-
|
|
85655
|
-
|
|
85656
|
-
|
|
85657
|
-
resolve(false);
|
|
85658
|
-
});
|
|
85659
|
-
socket.on("error", () => {
|
|
85656
|
+
socket.unref();
|
|
85657
|
+
let settled = false;
|
|
85658
|
+
const cleanup = (res) => {
|
|
85659
|
+
if (settled)
|
|
85660
|
+
return;
|
|
85661
|
+
settled = true;
|
|
85660
85662
|
socket.destroy();
|
|
85661
|
-
resolve(
|
|
85662
|
-
}
|
|
85663
|
+
resolve(res);
|
|
85664
|
+
};
|
|
85665
|
+
socket.on("connect", () => cleanup(true));
|
|
85666
|
+
socket.on("timeout", () => cleanup(false));
|
|
85667
|
+
socket.on("error", () => cleanup(false));
|
|
85663
85668
|
});
|
|
85664
85669
|
}
|
|
85665
|
-
async function isPortLive(port, timeoutMs = 200) {
|
|
85670
|
+
async function isPortLive(port, timeoutMs = 200, host = "localhost") {
|
|
85666
85671
|
if (testPortProbe) {
|
|
85667
|
-
return await testPortProbe(port) === "in-use";
|
|
85672
|
+
return await testPortProbe(port, host) === "in-use";
|
|
85668
85673
|
}
|
|
85669
|
-
if (port <= 0)
|
|
85674
|
+
if (port <= 0 || port > 65535)
|
|
85670
85675
|
return false;
|
|
85671
|
-
if (
|
|
85672
|
-
|
|
85673
|
-
|
|
85674
|
-
|
|
85675
|
-
|
|
85676
|
+
if (isLocalHost(host)) {
|
|
85677
|
+
if (await isPortLiveOnHost(port, "127.0.0.1", timeoutMs))
|
|
85678
|
+
return true;
|
|
85679
|
+
if (await isPortLiveOnHost(port, "::1", timeoutMs))
|
|
85680
|
+
return true;
|
|
85681
|
+
return false;
|
|
85682
|
+
}
|
|
85683
|
+
return isPortLiveOnHost(port, host, timeoutMs);
|
|
85676
85684
|
}
|
|
85677
85685
|
async function probePort(port) {
|
|
85678
85686
|
if (testPortProbe) {
|
|
@@ -85943,10 +85951,12 @@ class ProjectRegistry {
|
|
|
85943
85951
|
const data = this.readRaw();
|
|
85944
85952
|
let changed = false;
|
|
85945
85953
|
for (const project of data.projects) {
|
|
85946
|
-
|
|
85947
|
-
|
|
85948
|
-
project.path
|
|
85949
|
-
|
|
85954
|
+
if (isLocalHost(project.host)) {
|
|
85955
|
+
const normalized = normalizeProjectPath(project.path);
|
|
85956
|
+
if (normalized !== project.path) {
|
|
85957
|
+
project.path = normalized;
|
|
85958
|
+
changed = true;
|
|
85959
|
+
}
|
|
85950
85960
|
}
|
|
85951
85961
|
}
|
|
85952
85962
|
if (changed) {
|
|
@@ -85957,13 +85967,17 @@ class ProjectRegistry {
|
|
|
85957
85967
|
async list() {
|
|
85958
85968
|
await this.healTildePaths();
|
|
85959
85969
|
await this.refreshProjects();
|
|
85960
|
-
await this.healStale();
|
|
85961
85970
|
return this.readRaw().projects;
|
|
85962
85971
|
}
|
|
85963
85972
|
async getByPath(pathInput) {
|
|
85964
85973
|
const normalized = normalizeProjectPath(pathInput);
|
|
85965
85974
|
const projects = await this.list();
|
|
85966
|
-
return projects.find((p) =>
|
|
85975
|
+
return projects.find((p) => {
|
|
85976
|
+
if (isLocalHost(p.host)) {
|
|
85977
|
+
return normalizeProjectPath(p.path) === normalized;
|
|
85978
|
+
}
|
|
85979
|
+
return p.path === pathInput || p.path === normalized;
|
|
85980
|
+
});
|
|
85967
85981
|
}
|
|
85968
85982
|
async getByName(nameInput) {
|
|
85969
85983
|
const trimmed = nameInput.trim();
|
|
@@ -85973,11 +85987,20 @@ class ProjectRegistry {
|
|
|
85973
85987
|
async upsert(entry) {
|
|
85974
85988
|
return this.withLock(async () => {
|
|
85975
85989
|
const currentData = this.readRaw();
|
|
85976
|
-
const
|
|
85977
|
-
const
|
|
85990
|
+
const host = entry.host && entry.host.trim() !== "" ? entry.host.trim() : "localhost";
|
|
85991
|
+
const isLocal = isLocalHost(host);
|
|
85992
|
+
const normalizedPath = isLocal ? normalizeProjectPath(entry.path) : entry.path;
|
|
85993
|
+
const index = currentData.projects.findIndex((p) => {
|
|
85994
|
+
const pHost = p.host && p.host.trim() !== "" ? p.host.trim() : "localhost";
|
|
85995
|
+
if (isLocal) {
|
|
85996
|
+
return isLocalHost(pHost) && normalizeProjectPath(p.path) === normalizedPath;
|
|
85997
|
+
}
|
|
85998
|
+
return pHost.toLowerCase() === host.toLowerCase() && p.path === normalizedPath;
|
|
85999
|
+
});
|
|
85978
86000
|
const newEntry = {
|
|
85979
86001
|
name: entry.name,
|
|
85980
86002
|
path: normalizedPath,
|
|
86003
|
+
host,
|
|
85981
86004
|
port: entry.port ?? 0
|
|
85982
86005
|
};
|
|
85983
86006
|
if (index >= 0 && currentData.projects[index]) {
|
|
@@ -85985,13 +86008,20 @@ class ProjectRegistry {
|
|
|
85985
86008
|
currentData.projects[index] = {
|
|
85986
86009
|
...existing,
|
|
85987
86010
|
name: entry.name,
|
|
86011
|
+
host: entry.host ?? existing.host ?? "localhost",
|
|
85988
86012
|
port: entry.port ?? existing.port
|
|
85989
86013
|
};
|
|
85990
86014
|
} else {
|
|
85991
86015
|
currentData.projects.push(newEntry);
|
|
85992
86016
|
}
|
|
85993
86017
|
this.writeRaw(currentData);
|
|
85994
|
-
const res = currentData.projects.find((p) =>
|
|
86018
|
+
const res = currentData.projects.find((p) => {
|
|
86019
|
+
const pHost = p.host && p.host.trim() !== "" ? p.host.trim() : "localhost";
|
|
86020
|
+
if (isLocal) {
|
|
86021
|
+
return isLocalHost(pHost) && normalizeProjectPath(p.path) === normalizedPath;
|
|
86022
|
+
}
|
|
86023
|
+
return pHost.toLowerCase() === host.toLowerCase() && p.path === normalizedPath;
|
|
86024
|
+
});
|
|
85995
86025
|
if (!res) {
|
|
85996
86026
|
throw new Error(`Failed to upsert project: ${normalizedPath}`);
|
|
85997
86027
|
}
|
|
@@ -86004,9 +86034,10 @@ class ProjectRegistry {
|
|
|
86004
86034
|
const normalized = normalizeProjectPath(nameOrPath);
|
|
86005
86035
|
const initialLen = currentData.projects.length;
|
|
86006
86036
|
currentData.projects = currentData.projects.filter((p) => {
|
|
86007
|
-
const pNorm = normalizeProjectPath(p.path);
|
|
86037
|
+
const pNorm = isLocalHost(p.host) ? normalizeProjectPath(p.path) : p.path;
|
|
86038
|
+
const pathMatch = pNorm === normalized || p.path === nameOrPath;
|
|
86008
86039
|
const nameMatch = p.name.toLowerCase() === nameOrPath.trim().toLowerCase();
|
|
86009
|
-
return
|
|
86040
|
+
return !pathMatch && !nameMatch;
|
|
86010
86041
|
});
|
|
86011
86042
|
if (currentData.projects.length !== initialLen) {
|
|
86012
86043
|
this.writeRaw(currentData);
|
|
@@ -86019,7 +86050,10 @@ class ProjectRegistry {
|
|
|
86019
86050
|
return this.withLock(async () => {
|
|
86020
86051
|
const currentData = this.readRaw();
|
|
86021
86052
|
const normalized = normalizeProjectPath(pathInput);
|
|
86022
|
-
const project = currentData.projects.find((p) =>
|
|
86053
|
+
const project = currentData.projects.find((p) => {
|
|
86054
|
+
const pNorm = isLocalHost(p.host) ? normalizeProjectPath(p.path) : p.path;
|
|
86055
|
+
return pNorm === normalized || p.path === pathInput;
|
|
86056
|
+
});
|
|
86023
86057
|
if (project) {
|
|
86024
86058
|
project.port = port;
|
|
86025
86059
|
this.writeRaw(currentData);
|
|
@@ -86057,10 +86091,13 @@ class ProjectRegistry {
|
|
|
86057
86091
|
return this.withLock(async () => {
|
|
86058
86092
|
const data = this.readRaw();
|
|
86059
86093
|
let changed = false;
|
|
86094
|
+
const resetProjects = [];
|
|
86060
86095
|
for (const project of data.projects) {
|
|
86061
86096
|
if (project.port > 0) {
|
|
86062
|
-
const
|
|
86097
|
+
const host = project.host ?? "localhost";
|
|
86098
|
+
const live = await isPortLive(project.port, 200, host);
|
|
86063
86099
|
if (!live) {
|
|
86100
|
+
resetProjects.push({ ...project });
|
|
86064
86101
|
project.port = 0;
|
|
86065
86102
|
changed = true;
|
|
86066
86103
|
}
|
|
@@ -86069,6 +86106,7 @@ class ProjectRegistry {
|
|
|
86069
86106
|
if (changed) {
|
|
86070
86107
|
this.writeRaw(data);
|
|
86071
86108
|
}
|
|
86109
|
+
return resetProjects;
|
|
86072
86110
|
});
|
|
86073
86111
|
}
|
|
86074
86112
|
async refreshProjects(options) {
|
|
@@ -86078,9 +86116,15 @@ class ProjectRegistry {
|
|
|
86078
86116
|
await Promise.resolve().then(() => init_project_stop());
|
|
86079
86117
|
const removed = await this.withLock(async () => {
|
|
86080
86118
|
const data = this.readRaw();
|
|
86081
|
-
const purged = data.projects.filter((p) =>
|
|
86119
|
+
const purged = data.projects.filter((p) => {
|
|
86120
|
+
if (isLocalHost(p.host)) {
|
|
86121
|
+
return !existsSync8(normalizeProjectPath(p.path));
|
|
86122
|
+
}
|
|
86123
|
+
return false;
|
|
86124
|
+
});
|
|
86082
86125
|
if (purged.length > 0) {
|
|
86083
|
-
|
|
86126
|
+
const purgedPaths = new Set(purged.map((p) => `${p.host ?? "localhost"}:${normalizeProjectPath(p.path)}`));
|
|
86127
|
+
data.projects = data.projects.filter((p) => !purgedPaths.has(`${p.host ?? "localhost"}:${normalizeProjectPath(p.path)}`));
|
|
86084
86128
|
this.writeRaw(data);
|
|
86085
86129
|
}
|
|
86086
86130
|
return purged;
|
|
@@ -86088,7 +86132,7 @@ class ProjectRegistry {
|
|
|
86088
86132
|
const terminated = [];
|
|
86089
86133
|
if (terminate) {
|
|
86090
86134
|
for (const project of removed) {
|
|
86091
|
-
if (project.port > 0) {
|
|
86135
|
+
if (project.port > 0 && isLocalHost(project.host)) {
|
|
86092
86136
|
const stop = await stopRegisteredProject(project, {
|
|
86093
86137
|
processExecutor: executor,
|
|
86094
86138
|
killTimeoutMs,
|
|
@@ -86101,11 +86145,13 @@ class ProjectRegistry {
|
|
|
86101
86145
|
}
|
|
86102
86146
|
}
|
|
86103
86147
|
}
|
|
86148
|
+
const resetPorts = await this.healStale();
|
|
86104
86149
|
return {
|
|
86105
86150
|
removed,
|
|
86106
86151
|
terminated,
|
|
86107
86152
|
purgedProjects: removed,
|
|
86108
|
-
terminatedProcesses: terminated
|
|
86153
|
+
terminatedProcesses: terminated,
|
|
86154
|
+
resetPorts
|
|
86109
86155
|
};
|
|
86110
86156
|
}
|
|
86111
86157
|
}
|
|
@@ -89363,22 +89409,11 @@ async function completeFeatureVerificationReceipt(fs, runDir, running, options)
|
|
|
89363
89409
|
if (running.status !== "RUNNING") {
|
|
89364
89410
|
throw new Error(`receipt for run ${running.runId} is already ${running.status}`);
|
|
89365
89411
|
}
|
|
89366
|
-
if (options.confidence !== undefined) {
|
|
89367
|
-
if (!FEATURE_RECEIPT_CONFIDENCE_LEVELS.includes(options.confidence)) {
|
|
89368
|
-
throw new Error(`confidence must be HIGH, MEDIUM or LOW (got ${JSON.stringify(options.confidence)})`);
|
|
89369
|
-
}
|
|
89370
|
-
if (options.confidenceBasis === undefined || options.confidenceBasis.length === 0) {
|
|
89371
|
-
throw new Error(`confidence ${options.confidence} requires a non-empty confidenceBasis`);
|
|
89372
|
-
}
|
|
89373
|
-
}
|
|
89374
89412
|
const receipt = {
|
|
89375
89413
|
...running,
|
|
89376
89414
|
status: options.status,
|
|
89377
89415
|
inputDigest: options.inputDigest ?? running.inputDigest,
|
|
89378
|
-
completedAt: options.completedAt ?? new Date().toISOString().replace(/\.\d{3}Z$/, "Z")
|
|
89379
|
-
...options.confidence === undefined ? {} : { confidence: options.confidence },
|
|
89380
|
-
...options.confidenceBasis === undefined ? {} : { confidenceBasis: [...options.confidenceBasis] },
|
|
89381
|
-
...options.confidenceAcknowledged === true ? { confidenceAcknowledged: true } : {}
|
|
89416
|
+
completedAt: options.completedAt ?? new Date().toISOString().replace(/\.\d{3}Z$/, "Z")
|
|
89382
89417
|
};
|
|
89383
89418
|
const paths = featureReceiptPaths(runDir, receipt.featureId, receipt.runId);
|
|
89384
89419
|
await fs.ensureDir(runDir);
|
|
@@ -89406,23 +89441,6 @@ function parseFeatureVerificationReceipt(raw) {
|
|
|
89406
89441
|
if (typeof r.featureId !== "string" || typeof r.runId !== "string" || typeof r.workdir !== "string" || typeof v?.name !== "string" || typeof v?.sourcePath !== "string" || typeof v?.layer !== "string" || typeof v?.definitionDigest !== "string" || typeof r.verificationCmd !== "string" || typeof r.inputDigest !== "string" || r.status !== "RUNNING" && r.status !== "PASS" && r.status !== "FAIL" || typeof r.startedAt !== "string" || r.completedAt !== null && typeof r.completedAt !== "string") {
|
|
89407
89442
|
throw new Error("required fields missing or mistyped");
|
|
89408
89443
|
}
|
|
89409
|
-
if (r.confidence !== undefined) {
|
|
89410
|
-
if (!FEATURE_RECEIPT_CONFIDENCE_LEVELS.includes(r.confidence)) {
|
|
89411
|
-
throw new Error(`confidence must be HIGH, MEDIUM or LOW (got ${JSON.stringify(r.confidence)})`);
|
|
89412
|
-
}
|
|
89413
|
-
if (!Array.isArray(r.confidenceBasis) || r.confidenceBasis.length === 0) {
|
|
89414
|
-
throw new Error("confidence requires a non-empty confidenceBasis");
|
|
89415
|
-
}
|
|
89416
|
-
if (!r.confidenceBasis.every((entry) => typeof entry === "string" && entry.length > 0)) {
|
|
89417
|
-
throw new Error("confidenceBasis entries must be non-empty strings");
|
|
89418
|
-
}
|
|
89419
|
-
}
|
|
89420
|
-
if (r.confidenceBasis !== undefined && !Array.isArray(r.confidenceBasis)) {
|
|
89421
|
-
throw new Error("confidenceBasis must be an array");
|
|
89422
|
-
}
|
|
89423
|
-
if (r.confidenceAcknowledged !== undefined && typeof r.confidenceAcknowledged !== "boolean") {
|
|
89424
|
-
throw new Error("confidenceAcknowledged must be a boolean");
|
|
89425
|
-
}
|
|
89426
89444
|
return parsed;
|
|
89427
89445
|
}
|
|
89428
89446
|
async function readReceiptCopy(fs, path, label) {
|
|
@@ -89560,15 +89578,6 @@ async function validateFeatureVerificationReceipt(fs, options) {
|
|
|
89560
89578
|
detail: `receipt digest ${latest.inputDigest} \u2260 current inputs ${options.currentDigest} \u2014 tree, spec, learnings or check contract changed after the pass`
|
|
89561
89579
|
};
|
|
89562
89580
|
}
|
|
89563
|
-
const confidence = latest.confidence;
|
|
89564
|
-
const certified = confidence === "HIGH" || confidence === "MEDIUM";
|
|
89565
|
-
if (!certified && latest.confidenceAcknowledged !== true) {
|
|
89566
|
-
return {
|
|
89567
|
-
ok: false,
|
|
89568
|
-
reason: "unacknowledged-low-confidence",
|
|
89569
|
-
detail: confidence === undefined ? "receipt records a PASS with no confidence level (written before the field existed) \u2014 re-run the feature-scoped verification pass, which derives HIGH or MEDIUM from its own evidence" : `receipt records a ${confidence} confidence${(latest.confidenceBasis ?? []).length > 0 ? ` (basis: ${(latest.confidenceBasis ?? []).join("; ")})` : ""} \u2014 re-run the pass where the level can be HIGH or MEDIUM, or re-run it with the acknowledgement (ackLowConfidence) when LOW is the honest level`
|
|
89570
|
-
};
|
|
89571
|
-
}
|
|
89572
89581
|
return { ok: true, receipt: latest };
|
|
89573
89582
|
}
|
|
89574
89583
|
async function captureFeatureReceiptDigest(repoRoot, featureContent, learningsContent) {
|
|
@@ -89577,12 +89586,11 @@ async function captureFeatureReceiptDigest(repoRoot, featureContent, learningsCo
|
|
|
89577
89586
|
function isProofCaptureError(err) {
|
|
89578
89587
|
return err instanceof ProofCaptureError;
|
|
89579
89588
|
}
|
|
89580
|
-
var FEATURE_RECEIPT_SCHEMA_VERSION = 1, DEFAULT_FEATURE_VERIFICATION_CMD = "bun run spur-check-feature",
|
|
89589
|
+
var FEATURE_RECEIPT_SCHEMA_VERSION = 1, DEFAULT_FEATURE_VERIFICATION_CMD = "bun run spur-check-feature", RECEIPT_ID_PATTERN;
|
|
89581
89590
|
var init_feature_verification_receipt = __esm(() => {
|
|
89582
89591
|
init_durable_plane_ignore();
|
|
89583
89592
|
init_run_path();
|
|
89584
89593
|
init_proof_input_fingerprint();
|
|
89585
|
-
FEATURE_RECEIPT_CONFIDENCE_LEVELS = ["HIGH", "MEDIUM", "LOW"];
|
|
89586
89594
|
RECEIPT_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
|
|
89587
89595
|
});
|
|
89588
89596
|
|
|
@@ -92018,7 +92026,6 @@ var init_planning_check_base = __esm(() => {
|
|
|
92018
92026
|
FINDING_CODES.L4_FEATURE_RECEIPT_RUN,
|
|
92019
92027
|
FINDING_CODES.L4_FEATURE_RECEIPT_STALE,
|
|
92020
92028
|
FINDING_CODES.L4_FEATURE_RECEIPT_CONTRACT,
|
|
92021
|
-
FINDING_CODES.L4_FEATURE_RECEIPT_CONFIDENCE,
|
|
92022
92029
|
FINDING_CODES.L3_REVIEW_TESTING_CONTRADICTION,
|
|
92023
92030
|
FINDING_CODES.L4_ROLLUP_SUBTASKS_OPEN,
|
|
92024
92031
|
FINDING_CODES.L4_ROLLUP_MISSING_ROSTER,
|
|
@@ -93604,8 +93611,7 @@ var init_feature_check = __esm(() => {
|
|
|
93604
93611
|
failed: FINDING_CODES.L4_FEATURE_RECEIPT_FAILED,
|
|
93605
93612
|
run: FINDING_CODES.L4_FEATURE_RECEIPT_RUN,
|
|
93606
93613
|
stale: FINDING_CODES.L4_FEATURE_RECEIPT_STALE,
|
|
93607
|
-
"contract-mismatch": FINDING_CODES.L4_FEATURE_RECEIPT_CONTRACT
|
|
93608
|
-
"unacknowledged-low-confidence": FINDING_CODES.L4_FEATURE_RECEIPT_CONFIDENCE
|
|
93614
|
+
"contract-mismatch": FINDING_CODES.L4_FEATURE_RECEIPT_CONTRACT
|
|
93609
93615
|
};
|
|
93610
93616
|
findings.push({
|
|
93611
93617
|
layer: "L4",
|
|
@@ -101835,39 +101841,6 @@ var init_proof_fingerprint = __esm(() => {
|
|
|
101835
101841
|
ACCEPTED_OPTION_KEYS = ["expect", "featureFile", "taskFile", "var"];
|
|
101836
101842
|
});
|
|
101837
101843
|
|
|
101838
|
-
// ../../packages/app/src/workflow/actions/response-validate.ts
|
|
101839
|
-
class ResponseValidateActionRunner {
|
|
101840
|
-
kind = KIND14;
|
|
101841
|
-
engine;
|
|
101842
|
-
constructor(engine) {
|
|
101843
|
-
this.engine = engine;
|
|
101844
|
-
}
|
|
101845
|
-
async execute(options, _context) {
|
|
101846
|
-
const text = asString5(options.text);
|
|
101847
|
-
if (text === undefined) {
|
|
101848
|
-
return {
|
|
101849
|
-
ok: false,
|
|
101850
|
-
error: "response.validate: text is required (use a template like {{ steps.generate.answer }})"
|
|
101851
|
-
};
|
|
101852
|
-
}
|
|
101853
|
-
const result = this.engine.validate(text);
|
|
101854
|
-
return {
|
|
101855
|
-
ok: result.ok,
|
|
101856
|
-
data: {
|
|
101857
|
-
reason: result.reason,
|
|
101858
|
-
...result.issues !== undefined ? { issues: result.issues } : {}
|
|
101859
|
-
},
|
|
101860
|
-
error: result.ok ? undefined : result.reason
|
|
101861
|
-
};
|
|
101862
|
-
}
|
|
101863
|
-
}
|
|
101864
|
-
function asString5(value) {
|
|
101865
|
-
if (value === undefined || value === null)
|
|
101866
|
-
return;
|
|
101867
|
-
return String(value);
|
|
101868
|
-
}
|
|
101869
|
-
var KIND14 = "response.validate";
|
|
101870
|
-
|
|
101871
101844
|
// ../../packages/app/src/workflow/utils.ts
|
|
101872
101845
|
var identity5 = (s) => s, NO_COLOR;
|
|
101873
101846
|
var init_utils3 = __esm(() => {
|
|
@@ -101883,15 +101856,15 @@ var init_utils3 = __esm(() => {
|
|
|
101883
101856
|
|
|
101884
101857
|
// ../../packages/app/src/workflow/actions/rule-check.ts
|
|
101885
101858
|
class RuleCheckActionRunner {
|
|
101886
|
-
kind =
|
|
101859
|
+
kind = KIND14;
|
|
101887
101860
|
ruleService;
|
|
101888
101861
|
constructor(ruleService) {
|
|
101889
101862
|
this.ruleService = ruleService;
|
|
101890
101863
|
}
|
|
101891
101864
|
async execute(options, _context) {
|
|
101892
|
-
const preset =
|
|
101893
|
-
const rule =
|
|
101894
|
-
const failOn =
|
|
101865
|
+
const preset = asString5(options.preset) ?? "recommended-pre-check";
|
|
101866
|
+
const rule = asString5(options.rule);
|
|
101867
|
+
const failOn = asString5(options.failOn) ?? "error";
|
|
101895
101868
|
const result = await this.ruleService.evaluate({
|
|
101896
101869
|
preset,
|
|
101897
101870
|
failOn,
|
|
@@ -101913,12 +101886,12 @@ class RuleCheckActionRunner {
|
|
|
101913
101886
|
};
|
|
101914
101887
|
}
|
|
101915
101888
|
}
|
|
101916
|
-
function
|
|
101889
|
+
function asString5(value) {
|
|
101917
101890
|
if (value === undefined || value === null)
|
|
101918
101891
|
return;
|
|
101919
101892
|
return String(value);
|
|
101920
101893
|
}
|
|
101921
|
-
var
|
|
101894
|
+
var KIND14 = "rule.check";
|
|
101922
101895
|
var init_rule_check = __esm(() => {
|
|
101923
101896
|
init_utils3();
|
|
101924
101897
|
});
|
|
@@ -101986,7 +101959,7 @@ class RunArtifactActionRunner {
|
|
|
101986
101959
|
fileSystem;
|
|
101987
101960
|
artifactDao;
|
|
101988
101961
|
processExecutor;
|
|
101989
|
-
kind =
|
|
101962
|
+
kind = KIND15;
|
|
101990
101963
|
constructor(getDb, fileSystem = createNodeFileSystem2(), artifactDao, processExecutor) {
|
|
101991
101964
|
this.getDb = getDb;
|
|
101992
101965
|
this.fileSystem = fileSystem;
|
|
@@ -102012,7 +101985,7 @@ class RunArtifactActionRunner {
|
|
|
102012
101985
|
if (proofBinding !== undefined && proofBinding !== "current") {
|
|
102013
101986
|
return {
|
|
102014
101987
|
ok: false,
|
|
102015
|
-
error: `${
|
|
101988
|
+
error: `${KIND15}: unsupported proofBinding "${String(proofBinding)}" - only "current" is defined (ADR-071)`
|
|
102016
101989
|
};
|
|
102017
101990
|
}
|
|
102018
101991
|
const workdir = context.workdir ?? process.cwd();
|
|
@@ -102046,7 +102019,7 @@ class RunArtifactActionRunner {
|
|
|
102046
102019
|
try {
|
|
102047
102020
|
registeredPath = await persistDurableArtifact(normalized, workdir, context.runId, this.fileSystem, !requireExisting);
|
|
102048
102021
|
} catch (error) {
|
|
102049
|
-
return { ok: false, error: `${
|
|
102022
|
+
return { ok: false, error: `${KIND15}: durable persist failed: ${error.message}` };
|
|
102050
102023
|
}
|
|
102051
102024
|
}
|
|
102052
102025
|
let dao = this.artifactDao;
|
|
@@ -102077,24 +102050,24 @@ class RunArtifactActionRunner {
|
|
|
102077
102050
|
if (artifactKind !== "verify-verdict") {
|
|
102078
102051
|
return {
|
|
102079
102052
|
ok: false,
|
|
102080
|
-
error: `${
|
|
102053
|
+
error: `${KIND15}: proofBinding "current" is only supported for the verify-verdict artifact kind (got "${artifactKind}")`
|
|
102081
102054
|
};
|
|
102082
102055
|
}
|
|
102083
102056
|
if (!this.getDb) {
|
|
102084
102057
|
return {
|
|
102085
102058
|
ok: false,
|
|
102086
|
-
error: `${
|
|
102059
|
+
error: `${KIND15}: proofBinding "current" requires the composed database context (RunDao run identity) \u2014 refusing an unverifiable binding`
|
|
102087
102060
|
};
|
|
102088
102061
|
}
|
|
102089
102062
|
if (typeof options.taskFile !== "string" || options.taskFile.trim() === "") {
|
|
102090
102063
|
return {
|
|
102091
102064
|
ok: false,
|
|
102092
|
-
error: `${
|
|
102065
|
+
error: `${KIND15}: proofBinding "current" requires a non-empty "taskFile" option \u2014 the fresh digest must cover the canonical task spec`
|
|
102093
102066
|
};
|
|
102094
102067
|
}
|
|
102095
102068
|
const inputs = await readProofInputContents(this.fileSystem, workdir, options);
|
|
102096
102069
|
if (!inputs.ok) {
|
|
102097
|
-
return { ok: false, error: `${
|
|
102070
|
+
return { ok: false, error: `${KIND15}: ${inputs.error}` };
|
|
102098
102071
|
}
|
|
102099
102072
|
let digest;
|
|
102100
102073
|
try {
|
|
@@ -102106,19 +102079,19 @@ class RunArtifactActionRunner {
|
|
|
102106
102079
|
fileSystem: this.fileSystem
|
|
102107
102080
|
});
|
|
102108
102081
|
} catch (error) {
|
|
102109
|
-
return { ok: false, error: `${
|
|
102082
|
+
return { ok: false, error: `${KIND15}: fresh proof capture failed: ${error.message}` };
|
|
102110
102083
|
}
|
|
102111
102084
|
const varDigest = context.vars.proofDigestNow ?? context.vars.proofDigest;
|
|
102112
102085
|
if (typeof varDigest !== "string" || !PROOF_DIGEST_RE.test(varDigest)) {
|
|
102113
102086
|
return {
|
|
102114
102087
|
ok: false,
|
|
102115
|
-
error: `${
|
|
102088
|
+
error: `${KIND15}: proofBinding "current" does not hold - the run has no current proof input ` + `digest (vars.proofDigestNow/proofDigest missing or malformed), so the artifact cannot be ` + `bound to a proof that was never captured; capture one with proof.fingerprint first`
|
|
102116
102089
|
};
|
|
102117
102090
|
}
|
|
102118
102091
|
if (varDigest !== digest) {
|
|
102119
102092
|
return {
|
|
102120
102093
|
ok: false,
|
|
102121
|
-
error: `${
|
|
102094
|
+
error: `${KIND15}: the run's declared proof digest (${varDigest}) does not match the freshly ` + `captured digest (${digest}) \u2014 a stale or forged var cannot bless artifact content (0785 R3)`
|
|
102122
102095
|
};
|
|
102123
102096
|
}
|
|
102124
102097
|
let normalized;
|
|
@@ -102148,7 +102121,7 @@ class RunArtifactActionRunner {
|
|
|
102148
102121
|
try {
|
|
102149
102122
|
raw = await this.fileSystem.readFile(normalized);
|
|
102150
102123
|
} catch (error) {
|
|
102151
|
-
return { ok: false, error: `${
|
|
102124
|
+
return { ok: false, error: `${KIND15}: verdict artifact unreadable: ${error.message}` };
|
|
102152
102125
|
}
|
|
102153
102126
|
const taskWbs = String(context.vars.wbs ?? "");
|
|
102154
102127
|
const outcome = parseVerifyVerdict(raw, taskWbs);
|
|
@@ -102156,33 +102129,33 @@ class RunArtifactActionRunner {
|
|
|
102156
102129
|
const detail = outcome.kind === "invalid" && outcome.reason !== undefined ? `: ${outcome.reason}` : "";
|
|
102157
102130
|
return {
|
|
102158
102131
|
ok: false,
|
|
102159
|
-
error: `${
|
|
102132
|
+
error: `${KIND15}: verdict artifact is ${outcome.kind}${detail} \u2014 refusing to bind (0785 R3)`
|
|
102160
102133
|
};
|
|
102161
102134
|
}
|
|
102162
102135
|
const verdict = outcome.verdict;
|
|
102163
102136
|
if (verdict.wbs !== taskWbs) {
|
|
102164
102137
|
return {
|
|
102165
102138
|
ok: false,
|
|
102166
|
-
error: `${
|
|
102139
|
+
error: `${KIND15}: verdict artifact names wbs "${verdict.wbs}" but the run carries task "${taskWbs}"`
|
|
102167
102140
|
};
|
|
102168
102141
|
}
|
|
102169
102142
|
if (verdict.verdict !== "PASS") {
|
|
102170
102143
|
return {
|
|
102171
102144
|
ok: false,
|
|
102172
|
-
error: `${
|
|
102145
|
+
error: `${KIND15}: verdict aggregate is ${verdict.verdict}, not PASS \u2014 refusing to bind`
|
|
102173
102146
|
};
|
|
102174
102147
|
}
|
|
102175
102148
|
let root;
|
|
102176
102149
|
try {
|
|
102177
102150
|
root = JSON.parse(raw);
|
|
102178
102151
|
} catch {
|
|
102179
|
-
return { ok: false, error: `${
|
|
102152
|
+
return { ok: false, error: `${KIND15}: verdict artifact is malformed JSON` };
|
|
102180
102153
|
}
|
|
102181
102154
|
const proof = root.proof;
|
|
102182
102155
|
if (proof === null || typeof proof !== "object" || Array.isArray(proof)) {
|
|
102183
102156
|
return {
|
|
102184
102157
|
ok: false,
|
|
102185
|
-
error: `${
|
|
102158
|
+
error: `${KIND15}: verdict artifact carries no raw proof block \u2014 binding refused (0785 R3)`
|
|
102186
102159
|
};
|
|
102187
102160
|
}
|
|
102188
102161
|
const p = proof;
|
|
@@ -102196,13 +102169,13 @@ class RunArtifactActionRunner {
|
|
|
102196
102169
|
if (p.digest !== digest) {
|
|
102197
102170
|
return {
|
|
102198
102171
|
ok: false,
|
|
102199
|
-
error: `${
|
|
102172
|
+
error: `${KIND15}: proof.digest ${String(p.digest)} does not equal the freshly captured digest (${digest})`
|
|
102200
102173
|
};
|
|
102201
102174
|
}
|
|
102202
102175
|
if (p.runId !== context.runId) {
|
|
102203
102176
|
return {
|
|
102204
102177
|
ok: false,
|
|
102205
|
-
error: `${
|
|
102178
|
+
error: `${KIND15}: proof.runId ${String(p.runId)} does not match the certifying run ${context.runId}`
|
|
102206
102179
|
};
|
|
102207
102180
|
}
|
|
102208
102181
|
const db = await this.getDb();
|
|
@@ -102210,21 +102183,21 @@ class RunArtifactActionRunner {
|
|
|
102210
102183
|
if (runRow === undefined) {
|
|
102211
102184
|
return {
|
|
102212
102185
|
ok: false,
|
|
102213
|
-
error: `${
|
|
102186
|
+
error: `${KIND15}: run ${context.runId} has no authoritative row \u2014 refusing binding (0785 R3)`
|
|
102214
102187
|
};
|
|
102215
102188
|
}
|
|
102216
102189
|
let metadata = {};
|
|
102217
102190
|
try {
|
|
102218
102191
|
metadata = JSON.parse(runRow.metadata_json || "{}");
|
|
102219
102192
|
} catch {
|
|
102220
|
-
return { ok: false, error: `${
|
|
102193
|
+
return { ok: false, error: `${KIND15}: run ${context.runId} metadata_json is malformed \u2014 refusing binding` };
|
|
102221
102194
|
}
|
|
102222
102195
|
const resumeDigest = metadata.resumeDefinitionDigest;
|
|
102223
102196
|
const expectedDefinition = typeof resumeDigest === "string" && resumeDigest !== "" ? resumeDigest : metadata.definitionDigest;
|
|
102224
102197
|
if (typeof expectedDefinition !== "string" || p.definitionDigest !== expectedDefinition) {
|
|
102225
102198
|
return {
|
|
102226
102199
|
ok: false,
|
|
102227
|
-
error: `${
|
|
102200
|
+
error: `${KIND15}: proof.definitionDigest ${String(p.definitionDigest)} does not match the run's ` + `${typeof resumeDigest === "string" && resumeDigest !== "" ? "resume " : ""}definition digest \u2014 ` + `a stale-definition artifact cannot certify this run (0785 R3)`
|
|
102228
102201
|
};
|
|
102229
102202
|
}
|
|
102230
102203
|
const qualityGate = stageOf("qualityGate");
|
|
@@ -102233,7 +102206,7 @@ class RunArtifactActionRunner {
|
|
|
102233
102206
|
if (qualityGate === null || review === null || verification === null) {
|
|
102234
102207
|
return {
|
|
102235
102208
|
ok: false,
|
|
102236
|
-
error: `${
|
|
102209
|
+
error: `${KIND15}: proof.stages is missing qualityGate/review/verification evidence \u2014 binding refused`
|
|
102237
102210
|
};
|
|
102238
102211
|
}
|
|
102239
102212
|
for (const [name, stage] of [
|
|
@@ -102244,26 +102217,26 @@ class RunArtifactActionRunner {
|
|
|
102244
102217
|
if (stage.digest !== digest) {
|
|
102245
102218
|
return {
|
|
102246
102219
|
ok: false,
|
|
102247
|
-
error: `${
|
|
102220
|
+
error: `${KIND15}: proof.stages.${name}.digest does not equal the freshly captured digest`
|
|
102248
102221
|
};
|
|
102249
102222
|
}
|
|
102250
102223
|
}
|
|
102251
102224
|
if (qualityGate.status !== "PASS") {
|
|
102252
102225
|
return {
|
|
102253
102226
|
ok: false,
|
|
102254
|
-
error: `${
|
|
102227
|
+
error: `${KIND15}: proof.stages.qualityGate.status is ${String(qualityGate.status)}, not PASS`
|
|
102255
102228
|
};
|
|
102256
102229
|
}
|
|
102257
102230
|
if (verification.status !== "PASS") {
|
|
102258
102231
|
return {
|
|
102259
102232
|
ok: false,
|
|
102260
|
-
error: `${
|
|
102233
|
+
error: `${KIND15}: proof.stages.verification.status is ${String(verification.status)}, not PASS`
|
|
102261
102234
|
};
|
|
102262
102235
|
}
|
|
102263
102236
|
if (review.status !== "completed") {
|
|
102264
102237
|
return {
|
|
102265
102238
|
ok: false,
|
|
102266
|
-
error: `${
|
|
102239
|
+
error: `${KIND15}: proof.stages.review.status is "${String(review.status)}" \u2014 a skipped or stale review is not completed evidence (0785 R4)`
|
|
102267
102240
|
};
|
|
102268
102241
|
}
|
|
102269
102242
|
const markerPath = join41(resolve15(workdir), ".spur", "run", `${context.runId}-review-proof.digest`);
|
|
@@ -102276,7 +102249,7 @@ class RunArtifactActionRunner {
|
|
|
102276
102249
|
if (marker === null || marker.trim() !== digest) {
|
|
102277
102250
|
return {
|
|
102278
102251
|
ok: false,
|
|
102279
|
-
error: `${
|
|
102252
|
+
error: `${KIND15}: review-proof marker ${context.runId}-review-proof.digest is missing or does not name ` + `the current digest \u2014 review completion is not evidenced for this run (0785 R4)`
|
|
102280
102253
|
};
|
|
102281
102254
|
}
|
|
102282
102255
|
const dao = this.artifactDao ?? new ArtifactDao(db);
|
|
@@ -102287,7 +102260,7 @@ class RunArtifactActionRunner {
|
|
|
102287
102260
|
registeredPath = await persistDurableArtifact(normalized, workdir, context.runId, this.fileSystem);
|
|
102288
102261
|
}
|
|
102289
102262
|
} catch (error) {
|
|
102290
|
-
return { ok: false, error: `${
|
|
102263
|
+
return { ok: false, error: `${KIND15}: durable persist failed: ${error.message}` };
|
|
102291
102264
|
}
|
|
102292
102265
|
const record = await dao.record({
|
|
102293
102266
|
path: registeredPath,
|
|
@@ -102308,7 +102281,7 @@ class RunArtifactActionRunner {
|
|
|
102308
102281
|
};
|
|
102309
102282
|
}
|
|
102310
102283
|
}
|
|
102311
|
-
var copyFile2, link, mkdir2, readFile5, unlink2, writeFile3,
|
|
102284
|
+
var copyFile2, link, mkdir2, readFile5, unlink2, writeFile3, KIND15 = "run.artifact", PROOF_DIGEST_RE;
|
|
102312
102285
|
var init_run_artifact = __esm(() => {
|
|
102313
102286
|
init_src2();
|
|
102314
102287
|
init_dist5();
|
|
@@ -102322,7 +102295,7 @@ var init_run_artifact = __esm(() => {
|
|
|
102322
102295
|
|
|
102323
102296
|
// ../../packages/app/src/workflow/guards/contract-violation.ts
|
|
102324
102297
|
class ContractViolationGuardRunner {
|
|
102325
|
-
kind =
|
|
102298
|
+
kind = KIND16;
|
|
102326
102299
|
constructor() {}
|
|
102327
102300
|
async evaluate(_options, context) {
|
|
102328
102301
|
const data = context.lastActionResult?.data;
|
|
@@ -102339,7 +102312,7 @@ class ContractViolationGuardRunner {
|
|
|
102339
102312
|
};
|
|
102340
102313
|
}
|
|
102341
102314
|
}
|
|
102342
|
-
var
|
|
102315
|
+
var KIND16 = "contract-violation";
|
|
102343
102316
|
|
|
102344
102317
|
// ../../packages/app/src/workflow/builtins.ts
|
|
102345
102318
|
function registerSpurBuiltins(host, options) {
|
|
@@ -102370,9 +102343,6 @@ function registerSpurBuiltins(host, options) {
|
|
|
102370
102343
|
if (options.httpRequester) {
|
|
102371
102344
|
host.registerAction(new HttpRequestActionRunner(options.httpRequester, options.hostAllowlist ?? new Set), "builtin");
|
|
102372
102345
|
}
|
|
102373
|
-
if (options.responseValidateEngine) {
|
|
102374
|
-
host.registerAction(new ResponseValidateActionRunner(options.responseValidateEngine), "builtin");
|
|
102375
|
-
}
|
|
102376
102346
|
}
|
|
102377
102347
|
var init_builtins2 = __esm(() => {
|
|
102378
102348
|
init_dist5();
|
|
@@ -108278,11 +108248,15 @@ function classifyProviderUsage(usage) {
|
|
|
108278
108248
|
const hasWindow = RATE_WINDOW_NAMES.some((n) => usage[n] !== null && usage[n] !== undefined);
|
|
108279
108249
|
return hasWindow ? { status: "headroom", exhaustedWindow: undefined } : { status: "no-usage", exhaustedWindow: undefined };
|
|
108280
108250
|
}
|
|
108251
|
+
function agentProviderSlug(agent) {
|
|
108252
|
+
const lower = (agent ?? "").toLowerCase();
|
|
108253
|
+
return PROVIDER_ALIASES[lower] ?? lower;
|
|
108254
|
+
}
|
|
108281
108255
|
function mapProvidersToExecutors(providers, executors) {
|
|
108282
108256
|
const mapping = new Map;
|
|
108283
108257
|
for (const provider of providers) {
|
|
108284
108258
|
const lower = provider.toLowerCase();
|
|
108285
|
-
const names = executors.filter((e) => (e.agent
|
|
108259
|
+
const names = executors.filter((e) => agentProviderSlug(e.agent) === lower || typeof e.model === "string" && e.model.split("/")[0]?.toLowerCase() === lower).map((e) => e.name);
|
|
108286
108260
|
if (names.length > 0)
|
|
108287
108261
|
mapping.set(provider, names);
|
|
108288
108262
|
}
|
|
@@ -108534,7 +108508,7 @@ async function finalizeOutcomes(entries, context) {
|
|
|
108534
108508
|
}
|
|
108535
108509
|
}
|
|
108536
108510
|
}
|
|
108537
|
-
var codexbarWindowSchema, codexbarEntrySchema, RATE_WINDOW_NAMES, AgentUsageProducerError;
|
|
108511
|
+
var codexbarWindowSchema, codexbarEntrySchema, RATE_WINDOW_NAMES, AgentUsageProducerError, PROVIDER_ALIASES;
|
|
108538
108512
|
var init_agent_usage_producer = __esm(() => {
|
|
108539
108513
|
init_src();
|
|
108540
108514
|
init_agent_quota_events();
|
|
@@ -108566,6 +108540,9 @@ var init_agent_usage_producer = __esm(() => {
|
|
|
108566
108540
|
this.name = "AgentUsageProducerError";
|
|
108567
108541
|
}
|
|
108568
108542
|
};
|
|
108543
|
+
PROVIDER_ALIASES = {
|
|
108544
|
+
"antigravity-cli": "antigravity"
|
|
108545
|
+
};
|
|
108569
108546
|
});
|
|
108570
108547
|
|
|
108571
108548
|
// ../../packages/app/src/services/agent-usage-source.ts
|
|
@@ -119962,36 +119939,6 @@ async function createFeatureLookup(workdir, fileSystem = createNodeFileSystem2()
|
|
|
119962
119939
|
return { path, content: await fileSystem.readFile(path) };
|
|
119963
119940
|
};
|
|
119964
119941
|
}
|
|
119965
|
-
function deriveFeatureConfidence(input) {
|
|
119966
|
-
const override = input.override === undefined || input.override === "" ? undefined : input.override;
|
|
119967
|
-
const measured = [];
|
|
119968
|
-
if (input.status === "PASS")
|
|
119969
|
-
measured.push("verification command exited 0");
|
|
119970
|
-
else
|
|
119971
|
-
measured.push("verification command did not exit 0");
|
|
119972
|
-
if (input.afterDigest !== undefined) {
|
|
119973
|
-
measured.push(input.afterDigest === input.inputDigest ? "proof-input digest unchanged across the pass" : "proof-input digest changed across the pass");
|
|
119974
|
-
}
|
|
119975
|
-
if (input.status !== "PASS")
|
|
119976
|
-
return { confidence: "LOW", basis: measured };
|
|
119977
|
-
const derived = input.effectiveCmd === input.configuredCmd ? "HIGH" : "MEDIUM";
|
|
119978
|
-
if (input.effectiveCmd !== input.configuredCmd) {
|
|
119979
|
-
measured.push(`caller command override: ${input.effectiveCmd}`);
|
|
119980
|
-
measured.push(`the selected definition declares: ${input.configuredCmd}`);
|
|
119981
|
-
}
|
|
119982
|
-
if (override === undefined)
|
|
119983
|
-
return { confidence: derived, basis: measured };
|
|
119984
|
-
return { confidence: override, basis: [`operator-declared ${override}`, ...measured] };
|
|
119985
|
-
}
|
|
119986
|
-
function assertConfidenceOverride(raw) {
|
|
119987
|
-
if (raw === undefined || raw === "")
|
|
119988
|
-
return;
|
|
119989
|
-
const upper = raw.toUpperCase();
|
|
119990
|
-
if (!FEATURE_RECEIPT_CONFIDENCE_LEVELS.includes(upper)) {
|
|
119991
|
-
throw new Error(`confidence must be HIGH, MEDIUM or LOW (got ${JSON.stringify(raw)})`);
|
|
119992
|
-
}
|
|
119993
|
-
return upper;
|
|
119994
|
-
}
|
|
119995
119942
|
async function defaultRegisterArtifact(workdir, artifact) {
|
|
119996
119943
|
const db = await openInlineRunProjectDb(workdir);
|
|
119997
119944
|
try {
|
|
@@ -120027,7 +119974,6 @@ async function runFeatureVerification(input) {
|
|
|
120027
119974
|
const vars = selected.workflow.vars;
|
|
120028
119975
|
const configuredCmd = typeof vars?.verificationCmd === "string" && vars.verificationCmd.length > 0 ? vars.verificationCmd : "bun run spur-check-feature";
|
|
120029
119976
|
const effectiveCmd = input.cmdOverride !== undefined && input.cmdOverride !== "" ? input.cmdOverride : configuredCmd;
|
|
120030
|
-
const confidenceOverride = assertConfidenceOverride(input.confidenceOverride);
|
|
120031
119977
|
const spec = await lookup(input.featureId);
|
|
120032
119978
|
if (spec === null) {
|
|
120033
119979
|
throw new Error(`feature ${input.featureId} not found in the configured features directory`);
|
|
@@ -120053,25 +119999,13 @@ async function runFeatureVerification(input) {
|
|
|
120053
119999
|
const base = { featureId: input.featureId, runId: input.runId, effectiveCmd, logPath, receiptPath: runScoped };
|
|
120054
120000
|
const launchSplit = split(effectiveCmd, "feature-verification verificationCmd");
|
|
120055
120001
|
if ("error" in launchSplit) {
|
|
120056
|
-
const { confidence, basis } = deriveFeatureConfidence({
|
|
120057
|
-
status: "FAIL",
|
|
120058
|
-
configuredCmd,
|
|
120059
|
-
effectiveCmd,
|
|
120060
|
-
inputDigest: beforeDigest,
|
|
120061
|
-
override: confidenceOverride
|
|
120062
|
-
});
|
|
120063
120002
|
await receipts.complete(fileSystem, runDir, receipt, {
|
|
120064
120003
|
status: "FAIL",
|
|
120065
|
-
inputDigest: beforeDigest
|
|
120066
|
-
confidence,
|
|
120067
|
-
confidenceBasis: basis,
|
|
120068
|
-
confidenceAcknowledged: input.ackLowConfidence === true
|
|
120004
|
+
inputDigest: beforeDigest
|
|
120069
120005
|
});
|
|
120070
120006
|
return {
|
|
120071
120007
|
...base,
|
|
120072
120008
|
status: "FAIL",
|
|
120073
|
-
confidence,
|
|
120074
|
-
confidenceBasis: basis,
|
|
120075
120009
|
message: `verification FAIL (${launchSplit.error})`
|
|
120076
120010
|
};
|
|
120077
120011
|
}
|
|
@@ -120085,27 +120019,14 @@ async function runFeatureVerification(input) {
|
|
|
120085
120019
|
const exit = result.status ?? 1;
|
|
120086
120020
|
const afterDigest = await receipts.captureDigest(input.workdir, spec.content, learningsContent);
|
|
120087
120021
|
const status = exit === 0 && afterDigest === beforeDigest ? "PASS" : "FAIL";
|
|
120088
|
-
const { confidence, basis } = deriveFeatureConfidence({
|
|
120089
|
-
status,
|
|
120090
|
-
configuredCmd,
|
|
120091
|
-
effectiveCmd,
|
|
120092
|
-
inputDigest: beforeDigest,
|
|
120093
|
-
afterDigest,
|
|
120094
|
-
override: confidenceOverride
|
|
120095
|
-
});
|
|
120096
120022
|
await receipts.complete(fileSystem, runDir, receipt, {
|
|
120097
120023
|
status,
|
|
120098
|
-
inputDigest: afterDigest
|
|
120099
|
-
confidence,
|
|
120100
|
-
confidenceBasis: basis,
|
|
120101
|
-
confidenceAcknowledged: input.ackLowConfidence === true
|
|
120024
|
+
inputDigest: afterDigest
|
|
120102
120025
|
});
|
|
120103
120026
|
return {
|
|
120104
120027
|
...base,
|
|
120105
120028
|
status,
|
|
120106
|
-
|
|
120107
|
-
confidenceBasis: basis,
|
|
120108
|
-
message: `verification ${status} (run ${input.runId}, confidence ${confidence}, log ${logPath})`
|
|
120029
|
+
message: `verification ${status} (run ${input.runId}, log ${logPath})`
|
|
120109
120030
|
};
|
|
120110
120031
|
} finally {
|
|
120111
120032
|
closeLog(logFd);
|
|
@@ -120950,7 +120871,6 @@ __export(exports_src, {
|
|
|
120950
120871
|
FEATURE_ACTION_NAMES: () => FEATURE_ACTION_NAMES,
|
|
120951
120872
|
FEATURE_LIFECYCLE_PROFILE: () => FEATURE_LIFECYCLE_PROFILE,
|
|
120952
120873
|
FEATURE_LIFECYCLE_WORKFLOW: () => FEATURE_LIFECYCLE_WORKFLOW,
|
|
120953
|
-
FEATURE_RECEIPT_CONFIDENCE_LEVELS: () => FEATURE_RECEIPT_CONFIDENCE_LEVELS,
|
|
120954
120874
|
FEATURE_RECEIPT_SCHEMA_VERSION: () => FEATURE_RECEIPT_SCHEMA_VERSION,
|
|
120955
120875
|
FINDING_CODES: () => FINDING_CODES,
|
|
120956
120876
|
FLEET_AUTO_TAG: () => FLEET_AUTO_TAG,
|
|
@@ -121014,7 +120934,6 @@ __export(exports_src, {
|
|
|
121014
120934
|
RUN_STORE_LIST_MAX_LIMIT: () => RUN_STORE_LIST_MAX_LIMIT,
|
|
121015
120935
|
RUN_STORE_WBS_DEFAULT_LIMIT: () => RUN_STORE_WBS_DEFAULT_LIMIT,
|
|
121016
120936
|
RUN_STORE_WBS_MAX_LIMIT: () => RUN_STORE_WBS_MAX_LIMIT,
|
|
121017
|
-
ResponseValidateActionRunner: () => ResponseValidateActionRunner,
|
|
121018
120937
|
RetroCorrelator: () => RetroCorrelator,
|
|
121019
120938
|
RuleCheckActionRunner: () => RuleCheckActionRunner,
|
|
121020
120939
|
RuleService: () => RuleService,
|
|
@@ -121142,7 +121061,6 @@ __export(exports_src, {
|
|
|
121142
121061
|
decodeRunListCursor: () => decodeRunListCursor,
|
|
121143
121062
|
decorateWorkflowEvent: () => decorateWorkflowEvent,
|
|
121144
121063
|
deduceCommandCategory: () => deduceCommandCategory,
|
|
121145
|
-
deriveFeatureConfidence: () => deriveFeatureConfidence,
|
|
121146
121064
|
deriveVerdict: () => deriveVerdict,
|
|
121147
121065
|
discoverEnabledClaudePlugins: () => discoverEnabledClaudePlugins,
|
|
121148
121066
|
disposeDecisionServices: () => disposeDecisionServices,
|
|
@@ -121205,6 +121123,7 @@ __export(exports_src, {
|
|
|
121205
121123
|
isInlineRunActionStatus: () => isInlineRunActionStatus,
|
|
121206
121124
|
isInlineRunCloseStatus: () => isInlineRunCloseStatus,
|
|
121207
121125
|
isInsideRoot: () => isInsideRoot,
|
|
121126
|
+
isLocalHost: () => isLocalHost,
|
|
121208
121127
|
isPortAvailable: () => isPortAvailable,
|
|
121209
121128
|
isPortLive: () => isPortLive,
|
|
121210
121129
|
isProofCaptureError: () => isProofCaptureError,
|
|
@@ -128491,7 +128410,7 @@ import {
|
|
|
128491
128410
|
statSync as statSync17,
|
|
128492
128411
|
writeFileSync as writeFileSync17
|
|
128493
128412
|
} from "fs";
|
|
128494
|
-
import { join as
|
|
128413
|
+
import { join as join81 } from "path";
|
|
128495
128414
|
function parseScalar(raw) {
|
|
128496
128415
|
const t = raw.trim();
|
|
128497
128416
|
if (t.startsWith('"') && t.endsWith('"'))
|
|
@@ -128700,10 +128619,6 @@ function checkReportStructure(reportMarkdown) {
|
|
|
128700
128619
|
if (!/(^|[\s`])P[123]([\s`.]|$)/.test(block) && !block.includes("symbolic-severity")) {
|
|
128701
128620
|
problems.push("finding-invalid-severity");
|
|
128702
128621
|
}
|
|
128703
|
-
const confValue = block.match(/`confidence`\s*[:=]\s*`?([^`\n]*?)`?\s*(?:\n|$)/)?.[1]?.trim();
|
|
128704
|
-
if (confValue !== undefined && confValue !== "" && !FINDING_CONFIDENCES.includes(confValue.toLowerCase())) {
|
|
128705
|
-
problems.push(`finding-invalid-confidence:${confValue}`);
|
|
128706
|
-
}
|
|
128707
128622
|
const catValue = block.match(/`category`\s*[:=]\s*`?([^`\n]+?)`?\s*(?:\n|$)/)?.[1];
|
|
128708
128623
|
if (catValue && !FINDING_CATEGORIES.includes(catValue.trim())) {
|
|
128709
128624
|
problems.push(`finding-invalid-category:${catValue.trim()}`);
|
|
@@ -128737,7 +128652,7 @@ function logicDigest(path) {
|
|
|
128737
128652
|
try {
|
|
128738
128653
|
const h = createHash17("sha256");
|
|
128739
128654
|
if (statSync17(path).isDirectory()) {
|
|
128740
|
-
const walk = (dir) => readdirSync13(dir, { withFileTypes: true }).flatMap((e) => e.isDirectory() ? walk(
|
|
128655
|
+
const walk = (dir) => readdirSync13(dir, { withFileTypes: true }).flatMap((e) => e.isDirectory() ? walk(join81(dir, e.name)) : /\.(md|ya?ml)$/.test(e.name) ? [join81(dir, e.name)] : []).sort();
|
|
128741
128656
|
for (const f of walk(path)) {
|
|
128742
128657
|
h.update(f.slice(path.length));
|
|
128743
128658
|
h.update(readFileSync25(f));
|
|
@@ -129042,7 +128957,7 @@ function diffPorcelain(before, now, expects) {
|
|
|
129042
128957
|
const beforePaths = porcelainPaths(before);
|
|
129043
128958
|
return [...porcelainPaths(now)].filter((p) => !beforePaths.has(p) && !expects.has(p)).sort();
|
|
129044
128959
|
}
|
|
129045
|
-
var REPORT_SECTIONS, FINDING_FIELDS, DISPOSITIONS, FINDING_CATEGORIES,
|
|
128960
|
+
var REPORT_SECTIONS, FINDING_FIELDS, DISPOSITIONS, FINDING_CATEGORIES, NOT_AVAILABLE = "not available", YAML_KEYS;
|
|
129046
128961
|
var init_history_anatomy = __esm(() => {
|
|
129047
128962
|
init_artifact_digest();
|
|
129048
128963
|
REPORT_SECTIONS = [
|
|
@@ -129066,7 +128981,6 @@ var init_history_anatomy = __esm(() => {
|
|
|
129066
128981
|
"trend",
|
|
129067
128982
|
"observation",
|
|
129068
128983
|
"inference",
|
|
129069
|
-
"confidence",
|
|
129070
128984
|
"contradictions",
|
|
129071
128985
|
"evidenceAnchor",
|
|
129072
128986
|
"severity",
|
|
@@ -129083,7 +128997,6 @@ var init_history_anatomy = __esm(() => {
|
|
|
129083
128997
|
"telemetry",
|
|
129084
128998
|
"positive"
|
|
129085
128999
|
];
|
|
129086
|
-
FINDING_CONFIDENCES = ["high", "medium", "low"];
|
|
129087
129000
|
YAML_KEYS = [
|
|
129088
129001
|
"windowState",
|
|
129089
129002
|
"generatedAt",
|
|
@@ -129108,7 +129021,7 @@ var init_history_anatomy = __esm(() => {
|
|
|
129108
129021
|
// ../../packages/app/src/services/history-anatomy-cli.ts
|
|
129109
129022
|
import { existsSync as existsSync29, mkdirSync as mkdirSync21, readFileSync as readFileSync26, writeFileSync as writeFileSync18 } from "fs";
|
|
129110
129023
|
import { homedir as homedir13 } from "os";
|
|
129111
|
-
import { join as
|
|
129024
|
+
import { join as join82 } from "path";
|
|
129112
129025
|
function parseFlags(args) {
|
|
129113
129026
|
const out = {};
|
|
129114
129027
|
for (let i = 0;i < args.length; i++) {
|
|
@@ -129130,9 +129043,9 @@ function resolveSkillDir(deps = {}) {
|
|
|
129130
129043
|
const cwd = deps.cwd ?? process.cwd();
|
|
129131
129044
|
const home = deps.home ?? homedir13();
|
|
129132
129045
|
const candidates = [
|
|
129133
|
-
|
|
129134
|
-
|
|
129135
|
-
|
|
129046
|
+
join82(cwd, "plugins", "sp", "skills", "history-anatomy"),
|
|
129047
|
+
join82(home, ".agents", "skills", "sp-history-anatomy"),
|
|
129048
|
+
join82(home, ".claude", "skills", "sp-history-anatomy")
|
|
129136
129049
|
];
|
|
129137
129050
|
return candidates.find((dir) => existsSync29(dir)) ?? candidates[0];
|
|
129138
129051
|
}
|
|
@@ -129363,7 +129276,7 @@ var init_history_anatomy_cli = __esm(() => {
|
|
|
129363
129276
|
|
|
129364
129277
|
// ../../packages/app/src/services/daily-summary.ts
|
|
129365
129278
|
import { existsSync as existsSync30, mkdirSync as mkdirSync22, readlinkSync as readlinkSync2, writeFileSync as writeFileSync19 } from "fs";
|
|
129366
|
-
import { dirname as dirname40, isAbsolute as isAbsolute14, join as
|
|
129279
|
+
import { dirname as dirname40, isAbsolute as isAbsolute14, join as join83, resolve as resolve30 } from "path";
|
|
129367
129280
|
function parseArgs2(argv) {
|
|
129368
129281
|
const args = argv;
|
|
129369
129282
|
const options = {
|
|
@@ -129750,8 +129663,8 @@ function ensureDir(path) {
|
|
|
129750
129663
|
}
|
|
129751
129664
|
function writeSummary(markdown, options) {
|
|
129752
129665
|
const filename = `summary_${options.date.replace(/-/g, "")}.md`;
|
|
129753
|
-
const outputPath = options.outputPath ||
|
|
129754
|
-
ensureDir(
|
|
129666
|
+
const outputPath = options.outputPath || join83(DAILY_DIR, filename);
|
|
129667
|
+
ensureDir(join83(outputPath, ".."));
|
|
129755
129668
|
writeFileSync19(outputPath, markdown, "utf-8");
|
|
129756
129669
|
return outputPath;
|
|
129757
129670
|
}
|
|
@@ -129900,7 +129813,7 @@ var init_daily_summary = __esm(() => {
|
|
|
129900
129813
|
// ../../packages/app/src/workflow/transcript.ts
|
|
129901
129814
|
import { existsSync as existsSync31, readdirSync as readdirSync14 } from "fs";
|
|
129902
129815
|
import { homedir as homedir14 } from "os";
|
|
129903
|
-
import { join as
|
|
129816
|
+
import { join as join84 } from "path";
|
|
129904
129817
|
function sniffTranscriptFormat(rows) {
|
|
129905
129818
|
for (const row of rows) {
|
|
129906
129819
|
if (row.type === "message" && PI_ROLES.has(row.message?.role ?? ""))
|
|
@@ -129929,7 +129842,7 @@ function parseTranscriptRows(lines) {
|
|
|
129929
129842
|
}
|
|
129930
129843
|
return { rows, skippedLines, format: sniffTranscriptFormat(rows.map(([r]) => r)) };
|
|
129931
129844
|
}
|
|
129932
|
-
function resolveTranscript(env, projectsRoot =
|
|
129845
|
+
function resolveTranscript(env, projectsRoot = join84(homedir14(), ".claude", "projects"), override) {
|
|
129933
129846
|
if (override)
|
|
129934
129847
|
return existsSync31(override) ? { ok: true, path: override } : { ok: false, reason: "no transcript" };
|
|
129935
129848
|
const id = env.CLAUDE_CODE_SESSION_ID;
|
|
@@ -129948,7 +129861,7 @@ function resolveTranscript(env, projectsRoot = join83(homedir14(), ".claude", "p
|
|
|
129948
129861
|
if (!existsSync31(projectsRoot))
|
|
129949
129862
|
return { ok: false, reason: `no transcript root ${projectsRoot}` };
|
|
129950
129863
|
for (const dir of readdirSync14(projectsRoot)) {
|
|
129951
|
-
const path =
|
|
129864
|
+
const path = join84(projectsRoot, dir, `${id}.jsonl`);
|
|
129952
129865
|
if (existsSync31(path))
|
|
129953
129866
|
return { ok: true, path };
|
|
129954
129867
|
}
|
|
@@ -130143,7 +130056,7 @@ var require_src9 = __commonJS(function(exports, module) {
|
|
|
130143
130056
|
|
|
130144
130057
|
// ../../packages/app/src/workflow/run-summary-render.ts
|
|
130145
130058
|
import { existsSync as existsSync32, mkdirSync as mkdirSync24, readFileSync as readFileSync29, writeFileSync as writeFileSync21 } from "fs";
|
|
130146
|
-
import { join as
|
|
130059
|
+
import { join as join99 } from "path";
|
|
130147
130060
|
function renderSummaryMarkdown(summary) {
|
|
130148
130061
|
const line = (r, bold = false) => {
|
|
130149
130062
|
const stage = bold ? `**${r.stage}**` : r.stage;
|
|
@@ -130229,9 +130142,9 @@ function renderRollupMarkdown(rows) {
|
|
|
130229
130142
|
}
|
|
130230
130143
|
function writeCloseSummary(run, opts = {}) {
|
|
130231
130144
|
const cwd = opts.cwd ?? process.cwd();
|
|
130232
|
-
const runDir =
|
|
130233
|
-
const rel =
|
|
130234
|
-
const abs =
|
|
130145
|
+
const runDir = join99(cwd, ".spur", "run");
|
|
130146
|
+
const rel = join99(".spur", "run", `${run.runId}-summary.md`);
|
|
130147
|
+
const abs = join99(cwd, rel);
|
|
130235
130148
|
const na = (reason) => {
|
|
130236
130149
|
mkdirSync24(runDir, { recursive: true });
|
|
130237
130150
|
writeFileSync21(abs, `Execution summary: n/a (${reason})
|
|
@@ -130256,14 +130169,14 @@ function writeCloseSummary(run, opts = {}) {
|
|
|
130256
130169
|
mkdirSync24(runDir, { recursive: true });
|
|
130257
130170
|
writeFileSync21(abs, `${renderSummaryMarkdown(summary)}
|
|
130258
130171
|
`);
|
|
130259
|
-
writeFileSync21(
|
|
130172
|
+
writeFileSync21(join99(runDir, `${run.runId}-summary.json`), JSON.stringify({ ...summary, runId: run.runId, startedAt: run.startedAt, completedAt: run.completedAt }, null, 2));
|
|
130260
130173
|
return rel;
|
|
130261
130174
|
} catch (error) {
|
|
130262
130175
|
return na(error instanceof Error ? error.message : String(error));
|
|
130263
130176
|
}
|
|
130264
130177
|
}
|
|
130265
130178
|
function gateFromReceipt(cwd, runId) {
|
|
130266
|
-
const path =
|
|
130179
|
+
const path = join99(cwd, ".spur", "run", `${runId}-check-receipt.json`);
|
|
130267
130180
|
if (!existsSync32(path))
|
|
130268
130181
|
return { ms: null, reason: "no matching check receipt" };
|
|
130269
130182
|
try {
|
|
@@ -138207,8 +138120,18 @@ init_src();
|
|
|
138207
138120
|
init_dist5();
|
|
138208
138121
|
import { homedir as homedir12 } from "os";
|
|
138209
138122
|
import { join as join67 } from "path";
|
|
138210
|
-
var CODEXBAR_ARGV = ["codexbar", "usage", "--format", "json"
|
|
138211
|
-
var DEFAULT_CAPTURE_TIMEOUT_MS =
|
|
138123
|
+
var CODEXBAR_ARGV = ["codexbar", "usage", "--format", "json"];
|
|
138124
|
+
var DEFAULT_CAPTURE_TIMEOUT_MS = 300000;
|
|
138125
|
+
var CAPTURE_TIMEOUT_OPTION = "agentUsageCaptureTimeoutMs";
|
|
138126
|
+
var MAX_TIMER_MS = 2147483647;
|
|
138127
|
+
function resolveCaptureTimeoutMs(spurConfig) {
|
|
138128
|
+
const raw = getAppOptions(spurConfig, CAPTURE_TIMEOUT_OPTION, undefined);
|
|
138129
|
+
if (raw === undefined)
|
|
138130
|
+
return DEFAULT_CAPTURE_TIMEOUT_MS;
|
|
138131
|
+
if (typeof raw === "number" && Number.isInteger(raw) && raw > 0 && raw <= MAX_TIMER_MS)
|
|
138132
|
+
return raw;
|
|
138133
|
+
throw new UsageSourceError(`bootstrap.options.${CAPTURE_TIMEOUT_OPTION} must be a positive integer number of milliseconds (at most ${MAX_TIMER_MS}); received ${JSON.stringify(raw)}`);
|
|
138134
|
+
}
|
|
138212
138135
|
|
|
138213
138136
|
class CodexbarUsageSource {
|
|
138214
138137
|
command;
|
|
@@ -138238,7 +138161,7 @@ class CodexbarUsageSource {
|
|
|
138238
138161
|
}
|
|
138239
138162
|
const outcome = result.outcome ?? "exit";
|
|
138240
138163
|
if (outcome === "timeout" || outcome === "cancelled") {
|
|
138241
|
-
throw new UsageSourceError(`\`${this.command.join(" ")}\` capture exceeded the ${this.timeoutMs} ms deadline and was terminated \u2014 ` +
|
|
138164
|
+
throw new UsageSourceError(`\`${this.command.join(" ")}\` capture exceeded the ${this.timeoutMs} ms deadline and was terminated \u2014 ` + `result is unusable (fail-closed); retry, raise bootstrap.options.${CAPTURE_TIMEOUT_OPTION}, or investigate the codexbar hang`);
|
|
138242
138165
|
}
|
|
138243
138166
|
if (outcome === "signal" || outcome === "error") {
|
|
138244
138167
|
throw new UsageSourceError(`\`${this.command.join(" ")}\` capture ended abnormally (${outcome}) \u2014 capture is unusable (fail-closed)`);
|
|
@@ -142424,13 +142347,13 @@ async function runAgentUsage(context, flags, deps) {
|
|
|
142424
142347
|
}
|
|
142425
142348
|
return 2;
|
|
142426
142349
|
}
|
|
142427
|
-
const options = {
|
|
142428
|
-
source: deps?.source ?? _testUsageDeps?.source ?? new CodexbarUsageSource,
|
|
142429
|
-
snapshotPath: deps?.snapshotPath ?? _testUsageDeps?.snapshotPath ?? defaultAgentUsageSnapshotPath(),
|
|
142430
|
-
dryRun: flags.dryRun === true
|
|
142431
|
-
};
|
|
142432
142350
|
let result;
|
|
142433
142351
|
try {
|
|
142352
|
+
const options = {
|
|
142353
|
+
source: deps?.source ?? _testUsageDeps?.source ?? new CodexbarUsageSource(undefined, resolveCaptureTimeoutMs(context.spurConfig)),
|
|
142354
|
+
snapshotPath: deps?.snapshotPath ?? _testUsageDeps?.snapshotPath ?? defaultAgentUsageSnapshotPath(),
|
|
142355
|
+
dryRun: flags.dryRun === true
|
|
142356
|
+
};
|
|
142434
142357
|
result = await runAgentUsageProducer({
|
|
142435
142358
|
getDb: () => context.getDb(),
|
|
142436
142359
|
projectRoot: context.cwd,
|
|
@@ -142725,12 +142648,33 @@ function registerAgentCommand(program, context) {
|
|
|
142725
142648
|
init_src();
|
|
142726
142649
|
init_dist5();
|
|
142727
142650
|
import { existsSync as existsSync26, readdirSync as readdirSync12, readFileSync as readFileSync23 } from "fs";
|
|
142728
|
-
import { join as
|
|
142651
|
+
import { join as join79 } from "path";
|
|
142652
|
+
|
|
142653
|
+
// ../../scripts/commands/bundle-plugins.ts
|
|
142654
|
+
import { cp, rm as rm4 } from "fs/promises";
|
|
142655
|
+
import { join as join77 } from "path";
|
|
142656
|
+
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
142657
|
+
var REPO_ROOT = fileURLToPath6(new URL("../../", import.meta.url));
|
|
142658
|
+
var PLUGIN_SOURCE = join77(REPO_ROOT, "plugins");
|
|
142659
|
+
var PLUGIN_TARGET = join77(REPO_ROOT, "apps/cli/plugins");
|
|
142660
|
+
var MARKETPLACE_SOURCE = join77(REPO_ROOT, ".claude-plugin");
|
|
142661
|
+
var MARKETPLACE_TARGET = join77(REPO_ROOT, "apps/cli/.claude-plugin");
|
|
142662
|
+
var EXCLUDE = /(^|\/)(\.DS_Store|tests|evals)($|\/)|\.test\.ts$/;
|
|
142663
|
+
async function bundlePlugins(pluginTarget = PLUGIN_TARGET, marketplaceTarget = MARKETPLACE_TARGET, pluginSource = PLUGIN_SOURCE, marketplaceSource = MARKETPLACE_SOURCE) {
|
|
142664
|
+
await rm4(pluginTarget, { recursive: true, force: true });
|
|
142665
|
+
await rm4(marketplaceTarget, { recursive: true, force: true });
|
|
142666
|
+
await cp(pluginSource, pluginTarget, {
|
|
142667
|
+
recursive: true,
|
|
142668
|
+
filter: (src) => !EXCLUDE.test(src.slice(pluginSource.length))
|
|
142669
|
+
});
|
|
142670
|
+
await cp(marketplaceSource, marketplaceTarget, { recursive: true });
|
|
142671
|
+
return { pluginTarget, marketplaceTarget };
|
|
142672
|
+
}
|
|
142729
142673
|
|
|
142730
142674
|
// src/version-carriers.ts
|
|
142731
142675
|
init_zod();
|
|
142732
142676
|
import { existsSync as existsSync25 } from "fs";
|
|
142733
|
-
import { join as
|
|
142677
|
+
import { join as join78 } from "path";
|
|
142734
142678
|
var defaultLiteralProbe = { file: "src/config.ts", identifier: "binaryVersion" };
|
|
142735
142679
|
var registry3 = new Map;
|
|
142736
142680
|
function registerCarrierType(type, def) {
|
|
@@ -142789,7 +142733,7 @@ registerCarrierType("plugin-manifest", {
|
|
|
142789
142733
|
schema: PluginManifestCarrierSchema,
|
|
142790
142734
|
async syncRepoWide(carrier, { repoRoot }, version, staged, output) {
|
|
142791
142735
|
for (const rel of carrier.paths) {
|
|
142792
|
-
const absPath =
|
|
142736
|
+
const absPath = join78(repoRoot, rel);
|
|
142793
142737
|
const manifest = await readJson(absPath);
|
|
142794
142738
|
if (manifest === null) {
|
|
142795
142739
|
output.write(` \u26A0 ${rel}: not found or malformed \u2014 skipping`);
|
|
@@ -142849,7 +142793,7 @@ async function run2(repoRoot, cmd, timeoutMs) {
|
|
|
142849
142793
|
};
|
|
142850
142794
|
}
|
|
142851
142795
|
async function verifyLockfile(ctx, output) {
|
|
142852
|
-
const lockPath =
|
|
142796
|
+
const lockPath = join79(ctx.repoRoot, "bun.lock");
|
|
142853
142797
|
if (!existsSync26(lockPath) || Bun.file(lockPath).size === 0)
|
|
142854
142798
|
return;
|
|
142855
142799
|
const result = await run2(ctx.repoRoot, ["bun", "install", "--frozen-lockfile"]);
|
|
@@ -142859,7 +142803,7 @@ async function verifyLockfile(ctx, output) {
|
|
|
142859
142803
|
output.write(" \u21B3 bun.lock: verified (bun install --frozen-lockfile)");
|
|
142860
142804
|
}
|
|
142861
142805
|
async function assertPluginInstallable(ctx, output) {
|
|
142862
|
-
const smokePath =
|
|
142806
|
+
const smokePath = join79(ctx.repoRoot, "scripts", "commands", "plugin-install-smoke.ts");
|
|
142863
142807
|
if (!existsSync26(smokePath)) {
|
|
142864
142808
|
output.write(" \u21B3 plugin-install-smoke: gate script not present in this repo \u2014 skipped");
|
|
142865
142809
|
return;
|
|
@@ -142901,7 +142845,7 @@ async function readJson2(path) {
|
|
|
142901
142845
|
function rootWorkspaceGlobs(repoRoot) {
|
|
142902
142846
|
let root;
|
|
142903
142847
|
try {
|
|
142904
|
-
root = JSON.parse(readFileSync23(
|
|
142848
|
+
root = JSON.parse(readFileSync23(join79(repoRoot, "package.json"), "utf8"));
|
|
142905
142849
|
} catch {
|
|
142906
142850
|
return [];
|
|
142907
142851
|
}
|
|
@@ -142918,11 +142862,11 @@ function workspaceDirs(repoRoot) {
|
|
|
142918
142862
|
if (!glob.endsWith("/*"))
|
|
142919
142863
|
continue;
|
|
142920
142864
|
const parent = glob.slice(0, -2);
|
|
142921
|
-
const parentAbs =
|
|
142865
|
+
const parentAbs = join79(repoRoot, parent);
|
|
142922
142866
|
if (!existsSync26(parentAbs))
|
|
142923
142867
|
continue;
|
|
142924
142868
|
for (const entry of readdirSync12(parentAbs, { withFileTypes: true })) {
|
|
142925
|
-
if (entry.isDirectory() && existsSync26(
|
|
142869
|
+
if (entry.isDirectory() && existsSync26(join79(parentAbs, entry.name, "package.json"))) {
|
|
142926
142870
|
dirs.push(`${parent}/${entry.name}`);
|
|
142927
142871
|
}
|
|
142928
142872
|
}
|
|
@@ -142930,14 +142874,14 @@ function workspaceDirs(repoRoot) {
|
|
|
142930
142874
|
return dirs;
|
|
142931
142875
|
}
|
|
142932
142876
|
async function releaseContext(repoRoot, bumpVerConfig = resolveBuilderBumpVerConfig(null)) {
|
|
142933
|
-
const rootManifest = await readJson2(
|
|
142877
|
+
const rootManifest = await readJson2(join79(repoRoot, "package.json"));
|
|
142934
142878
|
if (rootManifest === null) {
|
|
142935
142879
|
throw new Error(`no package.json found at ${repoRoot} \u2014 builder operates on a package workspace root.`);
|
|
142936
142880
|
}
|
|
142937
142881
|
const rootName = typeof rootManifest.name === "string" ? rootManifest.name : "root";
|
|
142938
142882
|
const workspaces = [];
|
|
142939
142883
|
for (const dir of workspaceDirs(repoRoot)) {
|
|
142940
|
-
const manifest = await readJson2(
|
|
142884
|
+
const manifest = await readJson2(join79(repoRoot, dir, "package.json"));
|
|
142941
142885
|
if (manifest === null)
|
|
142942
142886
|
continue;
|
|
142943
142887
|
workspaces.push({
|
|
@@ -142952,8 +142896,8 @@ async function releaseContext(repoRoot, bumpVerConfig = resolveBuilderBumpVerCon
|
|
|
142952
142896
|
const packages = new Map;
|
|
142953
142897
|
for (const ws of workspaces) {
|
|
142954
142898
|
const probe = findLiteralProbe(versionCarriers, ws.dir) ?? defaultLiteralProbe;
|
|
142955
|
-
const literalProbe =
|
|
142956
|
-
const versionSourceFile = existsSync26(
|
|
142899
|
+
const literalProbe = join79(ws.dir, probe.file);
|
|
142900
|
+
const versionSourceFile = existsSync26(join79(repoRoot, literalProbe)) ? literalProbe : undefined;
|
|
142957
142901
|
packages.set(shortName(ws.name), {
|
|
142958
142902
|
packageDir: ws.dir,
|
|
142959
142903
|
packageName: ws.name,
|
|
@@ -142991,7 +142935,7 @@ async function updateWorkspacePins(ctx, pkgName, oldVersion, newVersion, output)
|
|
|
142991
142935
|
const changed = [];
|
|
142992
142936
|
for (const dir of [".", ...workspaceDirs(ctx.repoRoot)]) {
|
|
142993
142937
|
const relPath = dir === "." ? "package.json" : `${dir}/package.json`;
|
|
142994
|
-
const manifest = await readJson2(
|
|
142938
|
+
const manifest = await readJson2(join79(ctx.repoRoot, relPath));
|
|
142995
142939
|
if (manifest === null)
|
|
142996
142940
|
continue;
|
|
142997
142941
|
let dirty = false;
|
|
@@ -143008,7 +142952,7 @@ async function updateWorkspacePins(ctx, pkgName, oldVersion, newVersion, output)
|
|
|
143008
142952
|
}
|
|
143009
142953
|
}
|
|
143010
142954
|
if (dirty) {
|
|
143011
|
-
await Bun.write(
|
|
142955
|
+
await Bun.write(join79(ctx.repoRoot, relPath), `${JSON.stringify(manifest, null, 4)}
|
|
143012
142956
|
`);
|
|
143013
142957
|
output.write(` \u21B3 ${relPath}: ${pkgName} workspace pin ${oldVersion} \u2192 ${newVersion}`);
|
|
143014
142958
|
changed.push(relPath);
|
|
@@ -143016,7 +142960,7 @@ async function updateWorkspacePins(ctx, pkgName, oldVersion, newVersion, output)
|
|
|
143016
142960
|
}
|
|
143017
142961
|
if (changed.length === 0)
|
|
143018
142962
|
return changed;
|
|
143019
|
-
const lockPath =
|
|
142963
|
+
const lockPath = join79(ctx.repoRoot, "bun.lock");
|
|
143020
142964
|
if (existsSync26(lockPath)) {
|
|
143021
142965
|
const lock = await Bun.file(lockPath).text();
|
|
143022
142966
|
const staleSpec = `"${pkgName}": "workspace:${oldVersion}"`;
|
|
@@ -143028,7 +142972,7 @@ async function updateWorkspacePins(ctx, pkgName, oldVersion, newVersion, output)
|
|
|
143028
142972
|
return changed;
|
|
143029
142973
|
}
|
|
143030
142974
|
async function updateVersionSourceFile(ctx, filePath, previous, next, output, identifier = "binaryVersion") {
|
|
143031
|
-
const absPath =
|
|
142975
|
+
const absPath = join79(ctx.repoRoot, filePath);
|
|
143032
142976
|
const content = await Bun.file(absPath).text();
|
|
143033
142977
|
const pattern = new RegExp(`(${identifier.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}:\\s*['"])\\d+\\.\\d+\\.\\d+(?:-[0-9A-Za-z-.]+)?(?:\\+[0-9A-Za-z-.]+)?(['"])`);
|
|
143034
142978
|
const updated = content.replace(pattern, `$1${next}$2`);
|
|
@@ -143046,7 +142990,7 @@ async function syncExtraCarriers(ctx, version, staged, output) {
|
|
|
143046
142990
|
}
|
|
143047
142991
|
}
|
|
143048
142992
|
async function syncMarketplaceAndPlugins(ctx, version, staged, output) {
|
|
143049
|
-
const marketplacePath =
|
|
142993
|
+
const marketplacePath = join79(ctx.repoRoot, ".claude-plugin", "marketplace.json");
|
|
143050
142994
|
const marketplaceFile = Bun.file(marketplacePath);
|
|
143051
142995
|
if (!await marketplaceFile.exists())
|
|
143052
142996
|
return;
|
|
@@ -143071,7 +143015,7 @@ async function syncMarketplaceAndPlugins(ctx, version, staged, output) {
|
|
|
143071
143015
|
output.write(`Bumped marketplace plugins to ${version}`);
|
|
143072
143016
|
}
|
|
143073
143017
|
for (const entry of plugins) {
|
|
143074
|
-
const pluginJsonPath =
|
|
143018
|
+
const pluginJsonPath = join79(ctx.repoRoot, entry.source, "plugin.json");
|
|
143075
143019
|
const pluginJson = await readJson2(pluginJsonPath);
|
|
143076
143020
|
if (pluginJson === null) {
|
|
143077
143021
|
output.write(` \u26A0 plugin.json not found at ${entry.source}/plugin.json \u2014 skipping`);
|
|
@@ -143085,6 +143029,14 @@ async function syncMarketplaceAndPlugins(ctx, version, staged, output) {
|
|
|
143085
143029
|
output.write(`Bumped ${entry.source}/plugin.json to ${version}`);
|
|
143086
143030
|
}
|
|
143087
143031
|
}
|
|
143032
|
+
for (const config of ctx.packages.values()) {
|
|
143033
|
+
const pkgDir = join79(ctx.repoRoot, config.packageDir);
|
|
143034
|
+
const stagedMarketplace = join79(pkgDir, ".claude-plugin", "marketplace.json");
|
|
143035
|
+
if (!existsSync26(stagedMarketplace))
|
|
143036
|
+
continue;
|
|
143037
|
+
await bundlePlugins(join79(pkgDir, "plugins"), join79(pkgDir, ".claude-plugin"), join79(ctx.repoRoot, "plugins"), join79(ctx.repoRoot, ".claude-plugin"));
|
|
143038
|
+
output.write(`Refreshed staged plugin copy in ${config.packageDir}`);
|
|
143039
|
+
}
|
|
143088
143040
|
}
|
|
143089
143041
|
async function assertCleanTreeOnBranch(ctx) {
|
|
143090
143042
|
const RUNTIME_NOISE = /^\?\? \.spur\/(?:spur\.db|spur\.db-shm|spur\.db-wal|logs\/)/;
|
|
@@ -143125,7 +143077,7 @@ async function bumpVersion(ctx, config, version, options, output) {
|
|
|
143125
143077
|
throw new Error(`${config.packageName}@${version} is already published on npm. Use a new version.`);
|
|
143126
143078
|
}
|
|
143127
143079
|
await assertPluginInstallable(ctx, output);
|
|
143128
|
-
const manifestPath =
|
|
143080
|
+
const manifestPath = join79(ctx.repoRoot, config.packageDir, "package.json");
|
|
143129
143081
|
const manifest = await readJson2(manifestPath);
|
|
143130
143082
|
if (manifest === null)
|
|
143131
143083
|
throw new Error(`missing or malformed manifest at ${config.packageDir}/package.json`);
|
|
@@ -143144,7 +143096,7 @@ async function bumpVersion(ctx, config, version, options, output) {
|
|
|
143144
143096
|
await syncMarketplaceAndPlugins(ctx, version, staged, output);
|
|
143145
143097
|
await syncExtraCarriers(ctx, version, staged, output);
|
|
143146
143098
|
await verifyLockfile(ctx, output);
|
|
143147
|
-
const lockPath =
|
|
143099
|
+
const lockPath = join79(ctx.repoRoot, "bun.lock");
|
|
143148
143100
|
if (existsSync26(lockPath) && Bun.file(lockPath).size > 0)
|
|
143149
143101
|
staged.push("bun.lock");
|
|
143150
143102
|
await git2(ctx.repoRoot, ["add", ...staged]);
|
|
@@ -143201,7 +143153,7 @@ async function bumpAll(ctx, version, options, output) {
|
|
|
143201
143153
|
}
|
|
143202
143154
|
const staged = [];
|
|
143203
143155
|
for (const config of configs) {
|
|
143204
|
-
const manifestPath =
|
|
143156
|
+
const manifestPath = join79(ctx.repoRoot, config.packageDir, "package.json");
|
|
143205
143157
|
const manifest = await readJson2(manifestPath);
|
|
143206
143158
|
if (manifest === null) {
|
|
143207
143159
|
throw new Error(`missing or malformed manifest at ${config.packageDir}/package.json`);
|
|
@@ -143222,7 +143174,7 @@ async function bumpAll(ctx, version, options, output) {
|
|
|
143222
143174
|
await syncMarketplaceAndPlugins(ctx, version, staged, output);
|
|
143223
143175
|
await syncExtraCarriers(ctx, version, staged, output);
|
|
143224
143176
|
await verifyLockfile(ctx, output);
|
|
143225
|
-
const lockPath =
|
|
143177
|
+
const lockPath = join79(ctx.repoRoot, "bun.lock");
|
|
143226
143178
|
if (existsSync26(lockPath) && Bun.file(lockPath).size > 0)
|
|
143227
143179
|
staged.push("bun.lock");
|
|
143228
143180
|
await git2(ctx.repoRoot, ["add", ...staged]);
|
|
@@ -143378,9 +143330,9 @@ async function checkVersionDrift(target, repoRoot = process.cwd(), output = cons
|
|
|
143378
143330
|
if (config === undefined) {
|
|
143379
143331
|
throw releaseUsage(target === undefined ? "bump-ver --check needs a package id (no aggregate package is configured)" : `unknown package "${target}". Package IDs: ${[...ctx.packages.keys()].join(", ")}`);
|
|
143380
143332
|
}
|
|
143381
|
-
const manifest = await readJson2(
|
|
143333
|
+
const manifest = await readJson2(join79(repoRoot, config.packageDir, "package.json"));
|
|
143382
143334
|
const pkgVersion = typeof manifest?.version === "string" ? manifest.version : "";
|
|
143383
|
-
const marketplacePath =
|
|
143335
|
+
const marketplacePath = join79(repoRoot, ".claude-plugin", "marketplace.json");
|
|
143384
143336
|
const marketplaceFile = Bun.file(marketplacePath);
|
|
143385
143337
|
if (!await marketplaceFile.exists()) {
|
|
143386
143338
|
output.write("No .claude-plugin/marketplace.json \u2014 nothing to check.");
|
|
@@ -143393,8 +143345,8 @@ async function checkVersionDrift(target, repoRoot = process.cwd(), output = cons
|
|
|
143393
143345
|
for (const entry of parsed.plugins ?? []) {
|
|
143394
143346
|
if (typeof entry.source !== "string")
|
|
143395
143347
|
continue;
|
|
143396
|
-
const label =
|
|
143397
|
-
const pluginJson = await readJson2(
|
|
143348
|
+
const label = join79(entry.source, "plugin.json").replace(/^\.\//, "");
|
|
143349
|
+
const pluginJson = await readJson2(join79(repoRoot, entry.source, "plugin.json"));
|
|
143398
143350
|
plugins.push({ label, version: typeof pluginJson?.version === "string" ? pluginJson.version : "" });
|
|
143399
143351
|
}
|
|
143400
143352
|
} catch {}
|
|
@@ -143462,8 +143414,8 @@ async function publishPackage(target, opts = {}) {
|
|
|
143462
143414
|
const repoRoot = opts.repoRoot ?? process.cwd();
|
|
143463
143415
|
const output = opts.output ?? consoleOutput;
|
|
143464
143416
|
const runner = opts.runner ?? defaultPublishRunner;
|
|
143465
|
-
const dir = (target.startsWith("/") ? target :
|
|
143466
|
-
const manifestPath =
|
|
143417
|
+
const dir = (target.startsWith("/") ? target : join79(repoRoot, target)).replace(/\/+$/, "");
|
|
143418
|
+
const manifestPath = join79(dir, "package.json");
|
|
143467
143419
|
const manifestFile = Bun.file(manifestPath);
|
|
143468
143420
|
if (!await manifestFile.exists())
|
|
143469
143421
|
throw new Error(`no package.json at ${dir}`);
|
|
@@ -143471,7 +143423,7 @@ async function publishPackage(target, opts = {}) {
|
|
|
143471
143423
|
const manifest = JSON.parse(original);
|
|
143472
143424
|
const workspaces = new Map;
|
|
143473
143425
|
for (const wsDir of workspaceDirs(repoRoot)) {
|
|
143474
|
-
const pkg = await readJson2(
|
|
143426
|
+
const pkg = await readJson2(join79(repoRoot, wsDir, "package.json"));
|
|
143475
143427
|
if (pkg !== null && typeof pkg.name === "string") {
|
|
143476
143428
|
workspaces.set(pkg.name, {
|
|
143477
143429
|
...typeof pkg.version === "string" ? { version: pkg.version } : {},
|
|
@@ -143479,7 +143431,7 @@ async function publishPackage(target, opts = {}) {
|
|
|
143479
143431
|
});
|
|
143480
143432
|
}
|
|
143481
143433
|
}
|
|
143482
|
-
const root = await readJson2(
|
|
143434
|
+
const root = await readJson2(join79(repoRoot, "package.json"));
|
|
143483
143435
|
const catalog = root?.workspaces?.catalog ?? {};
|
|
143484
143436
|
const changed = resolvePublishRanges(manifest, workspaces, catalog);
|
|
143485
143437
|
if (changed > 0) {
|
|
@@ -143514,7 +143466,7 @@ async function publishPackage(target, opts = {}) {
|
|
|
143514
143466
|
init_src();
|
|
143515
143467
|
import { spawnSync as spawnSync7 } from "child_process";
|
|
143516
143468
|
import { existsSync as existsSync27, readFileSync as readFileSync24, writeFileSync as writeFileSync16 } from "fs";
|
|
143517
|
-
import { join as
|
|
143469
|
+
import { join as join80, resolve as resolve28 } from "path";
|
|
143518
143470
|
function createSpawnRunner(context = {}) {
|
|
143519
143471
|
return (cmd) => {
|
|
143520
143472
|
const proc = spawnSync7(cmd[0] ?? "", [...cmd.slice(1)], {
|
|
@@ -143657,7 +143609,7 @@ function extractSeverity2(body) {
|
|
|
143657
143609
|
}
|
|
143658
143610
|
var DEFAULT_FOCUS = "for correctness and regressions, security boundaries, data-loss risks, concurrency or race conditions, " + "API/backward compatibility, migration safety, error handling, and missing high-value tests. " + "Prioritize actionable issues over style or nits.";
|
|
143659
143611
|
function hasCodeReviewRules(repoRoot) {
|
|
143660
|
-
const agentsPath =
|
|
143612
|
+
const agentsPath = join80(repoRoot, "AGENTS.md");
|
|
143661
143613
|
if (!existsSync27(agentsPath))
|
|
143662
143614
|
return false;
|
|
143663
143615
|
return /^## Code Review Rules\s*$/m.test(readFileSync24(agentsPath, "utf8"));
|
|
@@ -144717,9 +144669,9 @@ function registerFeatureVerifyCommand(feature, context) {
|
|
|
144717
144669
|
"Runs the selected feature-verification definition\u2019s command once (or --cmd) and writes the",
|
|
144718
144670
|
"run-scoped + feature-latest receipts and .spur/run/<runId>-feature-verification.status.",
|
|
144719
144671
|
"Exit: 0 after a completed pass (PASS or FAIL); 1 when the pass could not run.",
|
|
144720
|
-
"Flags fall back to the workflow env names: featureId, __runId, verificationCmd
|
|
144672
|
+
"Flags fall back to the workflow env names: featureId, __runId, verificationCmd."
|
|
144721
144673
|
].join(`
|
|
144722
|
-
`)).argument("[id]", "Feature ID (env: featureId)").option("--run-id <id>", "workflow run id the receipt binds to (env: __runId)").option("--cmd <command>", "verification command override (env: verificationCmd)").
|
|
144674
|
+
`)).argument("[id]", "Feature ID (env: featureId)").option("--run-id <id>", "workflow run id the receipt binds to (env: __runId)").option("--cmd <command>", "verification command override (env: verificationCmd)").action(async (id, options) => {
|
|
144723
144675
|
await Promise.resolve().then(() => init_src3());
|
|
144724
144676
|
const env = context.env;
|
|
144725
144677
|
const featureId = id || env.featureId || "";
|
|
@@ -144728,9 +144680,7 @@ function registerFeatureVerifyCommand(feature, context) {
|
|
|
144728
144680
|
workdir: context.cwd,
|
|
144729
144681
|
featureId,
|
|
144730
144682
|
runId: options.runId || env.__runId || "",
|
|
144731
|
-
cmdOverride: options.cmd || env.verificationCmd || ""
|
|
144732
|
-
confidenceOverride: options.confidence || env.confidence || "",
|
|
144733
|
-
ackLowConfidence: options.ackLowConfidence === true || env.ackLowConfidence === "true"
|
|
144683
|
+
cmdOverride: options.cmd || env.verificationCmd || ""
|
|
144734
144684
|
});
|
|
144735
144685
|
context.output.write(`feature verify: ${featureId} ${result.message}`);
|
|
144736
144686
|
} catch (err) {
|
|
@@ -145177,7 +145127,7 @@ import { createRequire as createRequire3 } from "module";
|
|
|
145177
145127
|
var CLI_CONFIG = {
|
|
145178
145128
|
binaryName: "spur",
|
|
145179
145129
|
binaryLabel: "spur",
|
|
145180
|
-
binaryVersion: "0.4.
|
|
145130
|
+
binaryVersion: "0.4.10",
|
|
145181
145131
|
configDir: ".spur",
|
|
145182
145132
|
configFile: ".spur/config.yaml",
|
|
145183
145133
|
databaseFile: ".spur/spur.db"
|
|
@@ -145721,7 +145671,7 @@ init_loader2();
|
|
|
145721
145671
|
init_src2();
|
|
145722
145672
|
init_dist11();
|
|
145723
145673
|
import { homedir as homedir15 } from "os";
|
|
145724
|
-
import { join as
|
|
145674
|
+
import { join as join85, resolve as resolve31 } from "path";
|
|
145725
145675
|
|
|
145726
145676
|
// src/config/scaffold-manifest.ts
|
|
145727
145677
|
var SCAFFOLD_MANIFEST = [
|
|
@@ -145750,8 +145700,8 @@ var SCAFFOLD_MANIFEST = [
|
|
|
145750
145700
|
];
|
|
145751
145701
|
|
|
145752
145702
|
// src/commands/init.ts
|
|
145753
|
-
var GLOBAL_CONFIG_DIR =
|
|
145754
|
-
var GLOBAL_RULES_DIR2 =
|
|
145703
|
+
var GLOBAL_CONFIG_DIR = join85(".config", "spur");
|
|
145704
|
+
var GLOBAL_RULES_DIR2 = join85(GLOBAL_CONFIG_DIR, "rules");
|
|
145755
145705
|
var GLOBAL_CONFIG_EXAMPLE = BUNDLED_GLOBAL_CONFIG;
|
|
145756
145706
|
var GLOBAL_CONFIG_FILE2 = "config.yaml";
|
|
145757
145707
|
var INDEXED_CONTEXT_MARKER = "## Indexed context";
|
|
@@ -145782,7 +145732,7 @@ If \`.spur/context/\` is absent, proceed normally. Never block work on its absen
|
|
|
145782
145732
|
`;
|
|
145783
145733
|
function globalRulesRoot(context) {
|
|
145784
145734
|
const override = context.env.SPUR_GLOBAL_RULES_DIR;
|
|
145785
|
-
return override !== undefined && override.length > 0 ? resolve31(context.cwd, override) :
|
|
145735
|
+
return override !== undefined && override.length > 0 ? resolve31(context.cwd, override) : join85(homedir15(), GLOBAL_RULES_DIR2);
|
|
145786
145736
|
}
|
|
145787
145737
|
async function writeIfNew(context, path, content, force, result) {
|
|
145788
145738
|
if (!force && await context.fs.exists(path)) {
|
|
@@ -145799,18 +145749,18 @@ async function seedGlobalRules(context) {
|
|
|
145799
145749
|
const target = globalRulesRoot(context);
|
|
145800
145750
|
let written = 0;
|
|
145801
145751
|
for (const relPath of await listBundledRuleFiles()) {
|
|
145802
|
-
const destination =
|
|
145752
|
+
const destination = join85(target, relPath);
|
|
145803
145753
|
if (await context.fs.exists(destination))
|
|
145804
145754
|
continue;
|
|
145805
|
-
await context.fs.ensureDir(
|
|
145806
|
-
await context.fs.writeFile(destination, await context.fs.readFile(
|
|
145755
|
+
await context.fs.ensureDir(join85(target, ...relPath.split("/").slice(0, -1)));
|
|
145756
|
+
await context.fs.writeFile(destination, await context.fs.readFile(join85(source, relPath)));
|
|
145807
145757
|
written += 1;
|
|
145808
145758
|
}
|
|
145809
145759
|
return written;
|
|
145810
145760
|
}
|
|
145811
145761
|
function resolveGlobalConfigDir(context) {
|
|
145812
145762
|
const globalOverride = context.env.SPUR_GLOBAL_RULES_DIR;
|
|
145813
|
-
return globalOverride !== undefined && globalOverride.length > 0 ? resolve31(context.cwd, globalOverride) :
|
|
145763
|
+
return globalOverride !== undefined && globalOverride.length > 0 ? resolve31(context.cwd, globalOverride) : join85(homedir15(), GLOBAL_CONFIG_DIR);
|
|
145814
145764
|
}
|
|
145815
145765
|
async function seedGlobalConfig(context) {
|
|
145816
145766
|
const source = bundledConfigRoot();
|
|
@@ -145821,15 +145771,15 @@ async function seedGlobalConfig(context) {
|
|
|
145821
145771
|
for (const relPath of listBundledConfigFiles()) {
|
|
145822
145772
|
if (relPath === GLOBAL_CONFIG_EXAMPLE)
|
|
145823
145773
|
continue;
|
|
145824
|
-
const destination =
|
|
145774
|
+
const destination = join85(target, relPath);
|
|
145825
145775
|
if (await context.fs.exists(destination))
|
|
145826
145776
|
continue;
|
|
145827
|
-
await context.fs.ensureDir(
|
|
145828
|
-
await context.fs.writeFile(destination, await context.fs.readFile(
|
|
145777
|
+
await context.fs.ensureDir(join85(target, ...relPath.split("/").slice(0, -1)));
|
|
145778
|
+
await context.fs.writeFile(destination, await context.fs.readFile(join85(source, relPath)));
|
|
145829
145779
|
written += 1;
|
|
145830
145780
|
}
|
|
145831
|
-
const examplePath =
|
|
145832
|
-
const globalConfigPath =
|
|
145781
|
+
const examplePath = join85(source, GLOBAL_CONFIG_EXAMPLE);
|
|
145782
|
+
const globalConfigPath = join85(target, GLOBAL_CONFIG_FILE2);
|
|
145833
145783
|
if (await context.fs.exists(examplePath) && !await context.fs.exists(globalConfigPath)) {
|
|
145834
145784
|
await context.fs.ensureDir(target);
|
|
145835
145785
|
await context.fs.writeFile(globalConfigPath, await context.fs.readFile(examplePath));
|
|
@@ -145844,10 +145794,10 @@ function registerInitCommand(program, context, options = {}) {
|
|
|
145844
145794
|
const minimal = options.minimal === true;
|
|
145845
145795
|
const adoptGlobal = options.adoptGlobalConfig === true;
|
|
145846
145796
|
const projectName = options.name ?? "default";
|
|
145847
|
-
const configPath =
|
|
145797
|
+
const configPath = join85(context.cwd, CLI_CONFIG.configFile);
|
|
145848
145798
|
const result = { created: [], skipped: [] };
|
|
145849
145799
|
const globalConfigDir = resolveGlobalConfigDir(context);
|
|
145850
|
-
const globalConfigPath =
|
|
145800
|
+
const globalConfigPath = join85(globalConfigDir, GLOBAL_CONFIG_FILE2);
|
|
145851
145801
|
const globalConfigExists = await context.fs.exists(globalConfigPath);
|
|
145852
145802
|
let misplacedKeys = [];
|
|
145853
145803
|
if (globalConfigExists) {
|
|
@@ -145860,20 +145810,20 @@ function registerInitCommand(program, context, options = {}) {
|
|
|
145860
145810
|
const configRoot = bundledConfigRoot();
|
|
145861
145811
|
if (configRoot !== null && !minimal) {
|
|
145862
145812
|
for (const relPath of listBundledProjectSeedFiles()) {
|
|
145863
|
-
const sourcePath =
|
|
145813
|
+
const sourcePath = join85(configRoot, relPath);
|
|
145864
145814
|
if (!await context.fs.exists(sourcePath))
|
|
145865
145815
|
continue;
|
|
145866
|
-
const targetPath =
|
|
145867
|
-
await context.fs.ensureDir(
|
|
145816
|
+
const targetPath = join85(context.cwd, CLI_CONFIG.configDir, relPath);
|
|
145817
|
+
await context.fs.ensureDir(join85(targetPath, ".."));
|
|
145868
145818
|
await writeIfNew(context, targetPath, await context.fs.readFile(sourcePath), false, result);
|
|
145869
145819
|
}
|
|
145870
145820
|
for (const entry of SCAFFOLD_MANIFEST) {
|
|
145871
|
-
const sourcePath =
|
|
145821
|
+
const sourcePath = join85(configRoot, entry.source);
|
|
145872
145822
|
if (!await context.fs.exists(sourcePath))
|
|
145873
145823
|
continue;
|
|
145874
|
-
const baseDir = entry.root === true ? context.cwd :
|
|
145875
|
-
const targetPath =
|
|
145876
|
-
await context.fs.ensureDir(
|
|
145824
|
+
const baseDir = entry.root === true ? context.cwd : join85(context.cwd, CLI_CONFIG.configDir);
|
|
145825
|
+
const targetPath = join85(baseDir, entry.target);
|
|
145826
|
+
await context.fs.ensureDir(join85(targetPath, ".."));
|
|
145877
145827
|
const entryForce = false;
|
|
145878
145828
|
let body = await context.fs.readFile(sourcePath);
|
|
145879
145829
|
if (entry.target === "AGENTS.md") {
|
|
@@ -145890,7 +145840,7 @@ function registerInitCommand(program, context, options = {}) {
|
|
|
145890
145840
|
const backupPath = `${globalConfigPath}.bak-${new Date().toISOString().replace(/[:.]/g, "-")}`;
|
|
145891
145841
|
await context.fs.writeFile(backupPath, await context.fs.readFile(globalConfigPath));
|
|
145892
145842
|
const bundledRoot = bundledConfigRoot();
|
|
145893
|
-
const examplePath = bundledRoot !== null ?
|
|
145843
|
+
const examplePath = bundledRoot !== null ? join85(bundledRoot, GLOBAL_CONFIG_EXAMPLE) : null;
|
|
145894
145844
|
if (examplePath !== null && await context.fs.exists(examplePath)) {
|
|
145895
145845
|
await context.fs.writeFile(globalConfigPath, await context.fs.readFile(examplePath));
|
|
145896
145846
|
adoptedGlobal = true;
|
|
@@ -145963,13 +145913,13 @@ function registerInitCommand(program, context, options = {}) {
|
|
|
145963
145913
|
].join(`
|
|
145964
145914
|
`)}
|
|
145965
145915
|
`;
|
|
145966
|
-
await context.fs.ensureDir(
|
|
145916
|
+
await context.fs.ensureDir(join85(context.cwd, CLI_CONFIG.configDir));
|
|
145967
145917
|
await context.fs.writeFile(configPath, configYaml);
|
|
145968
145918
|
result.created.push(configPath);
|
|
145969
|
-
const agentsDir =
|
|
145919
|
+
const agentsDir = join85(context.cwd, CLI_CONFIG.configDir, "agents");
|
|
145970
145920
|
await context.fs.ensureDir(agentsDir);
|
|
145971
|
-
await writeIfNew(context,
|
|
145972
|
-
const gitignorePath =
|
|
145921
|
+
await writeIfNew(context, join85(agentsDir, ".gitkeep"), "", force, result);
|
|
145922
|
+
const gitignorePath = join85(context.cwd, ".gitignore");
|
|
145973
145923
|
const contextEntry = ".spur/context/";
|
|
145974
145924
|
if (await context.fs.exists(gitignorePath)) {
|
|
145975
145925
|
const existing = await context.fs.readFile(gitignorePath);
|
|
@@ -145989,20 +145939,20 @@ ${contextEntry}
|
|
|
145989
145939
|
const configRoot = bundledConfigRoot();
|
|
145990
145940
|
if (configRoot !== null) {
|
|
145991
145941
|
for (const relPath of listBundledProjectSeedFiles()) {
|
|
145992
|
-
const sourcePath =
|
|
145942
|
+
const sourcePath = join85(configRoot, relPath);
|
|
145993
145943
|
if (!await context.fs.exists(sourcePath))
|
|
145994
145944
|
continue;
|
|
145995
|
-
const targetPath =
|
|
145996
|
-
await context.fs.ensureDir(
|
|
145945
|
+
const targetPath = join85(context.cwd, CLI_CONFIG.configDir, relPath);
|
|
145946
|
+
await context.fs.ensureDir(join85(targetPath, ".."));
|
|
145997
145947
|
await writeIfNew(context, targetPath, await context.fs.readFile(sourcePath), force, result);
|
|
145998
145948
|
}
|
|
145999
145949
|
for (const entry of SCAFFOLD_MANIFEST) {
|
|
146000
|
-
const sourcePath =
|
|
145950
|
+
const sourcePath = join85(configRoot, entry.source);
|
|
146001
145951
|
if (!await context.fs.exists(sourcePath))
|
|
146002
145952
|
continue;
|
|
146003
|
-
const baseDir = entry.root === true ? context.cwd :
|
|
146004
|
-
const targetPath =
|
|
146005
|
-
await context.fs.ensureDir(
|
|
145953
|
+
const baseDir = entry.root === true ? context.cwd : join85(context.cwd, CLI_CONFIG.configDir);
|
|
145954
|
+
const targetPath = join85(baseDir, entry.target);
|
|
145955
|
+
await context.fs.ensureDir(join85(targetPath, ".."));
|
|
146006
145956
|
const entryForce = entry.preserve === true ? false : force;
|
|
146007
145957
|
let body = await context.fs.readFile(sourcePath);
|
|
146008
145958
|
if (entry.target === "AGENTS.md") {
|
|
@@ -146015,7 +145965,7 @@ ${contextEntry}
|
|
|
146015
145965
|
}
|
|
146016
145966
|
}
|
|
146017
145967
|
}
|
|
146018
|
-
const agentsMdPath =
|
|
145968
|
+
const agentsMdPath = join85(context.cwd, "AGENTS.md");
|
|
146019
145969
|
if (await context.fs.exists(agentsMdPath)) {
|
|
146020
145970
|
const existing = await context.fs.readFile(agentsMdPath);
|
|
146021
145971
|
if (!existing.includes(INDEXED_CONTEXT_MARKER)) {
|
|
@@ -146056,11 +146006,11 @@ ${contextEntry}
|
|
|
146056
146006
|
// src/commands/maintain.ts
|
|
146057
146007
|
init_src();
|
|
146058
146008
|
init_src2();
|
|
146059
|
-
import { join as
|
|
146009
|
+
import { join as join86 } from "path";
|
|
146060
146010
|
function registerMaintainCommand(program, context, options = {}) {
|
|
146061
146011
|
program.command("maintain", { hidden: options.hidden === true }).summary("run database maintenance (optimize, WAL checkpoint truncation, optional VACUUM)").description("Run database maintenance: PRAGMA optimize, WAL checkpoint truncation; optionally VACUUM compaction.").option("--vacuum", "Run VACUUM defragmentation and page compaction", false).option(...SHARED_OPTIONS.jsonSupported).option(...SHARED_OPTIONS.jsonEnvelope).action(async (options) => {
|
|
146062
146012
|
const db = await context.getDb();
|
|
146063
|
-
const dbPath = context.env.DATABASE_URL ??
|
|
146013
|
+
const dbPath = context.env.DATABASE_URL ?? join86(context.cwd, DEFAULT_DATABASE_URL);
|
|
146064
146014
|
const result = await maintainDatabase(db, {
|
|
146065
146015
|
vacuum: options.vacuum === true,
|
|
146066
146016
|
dbPath: dbPath.startsWith(":memory:") ? undefined : dbPath
|
|
@@ -146484,14 +146434,14 @@ async function waitForPendingDrain(agentService, coordination, pin, timeoutMs, s
|
|
|
146484
146434
|
|
|
146485
146435
|
// src/commands/migrate.ts
|
|
146486
146436
|
init_src2();
|
|
146487
|
-
import { join as
|
|
146437
|
+
import { join as join88, relative as relative18 } from "path";
|
|
146488
146438
|
|
|
146489
146439
|
// src/context.ts
|
|
146490
146440
|
init_src3();
|
|
146491
146441
|
init_src();
|
|
146492
146442
|
init_src2();
|
|
146493
146443
|
init_dist5();
|
|
146494
|
-
import { dirname as dirname41, join as
|
|
146444
|
+
import { dirname as dirname41, join as join87, resolve as resolve32 } from "path";
|
|
146495
146445
|
import { isatty as isatty4 } from "tty";
|
|
146496
146446
|
|
|
146497
146447
|
// ../../node_modules/.bun/@clack+core@1.4.1/node_modules/@clack/core/dist/index.mjs
|
|
@@ -147476,7 +147426,7 @@ function createCliContext(options) {
|
|
|
147476
147426
|
}
|
|
147477
147427
|
function noopSetExitCode(_code) {}
|
|
147478
147428
|
function resolveDatabaseUrl(cwd, env) {
|
|
147479
|
-
return env.DATABASE_URL === undefined ?
|
|
147429
|
+
return env.DATABASE_URL === undefined ? join87(cwd, DEFAULT_DATABASE_URL) : buildConfigFromEnv(env).database.url;
|
|
147480
147430
|
}
|
|
147481
147431
|
async function createMigratedDbAdapter(cwd = process.cwd(), env = getEnvVars(), dbUrl) {
|
|
147482
147432
|
const url = dbUrl ?? resolveDatabaseUrl(cwd, env);
|
|
@@ -147539,7 +147489,7 @@ function registerMigrateCommand(program, context, options = {}) {
|
|
|
147539
147489
|
context.setExitCode(1);
|
|
147540
147490
|
return;
|
|
147541
147491
|
}
|
|
147542
|
-
const migrations = await loadSqlMigrations(
|
|
147492
|
+
const migrations = await loadSqlMigrations(join88(context.cwd, "drizzle")).catch(() => {
|
|
147543
147493
|
return;
|
|
147544
147494
|
});
|
|
147545
147495
|
const db = await context.getDb();
|
|
@@ -147554,22 +147504,28 @@ function registerMigrateCommand(program, context, options = {}) {
|
|
|
147554
147504
|
init_src3();
|
|
147555
147505
|
init_src2();
|
|
147556
147506
|
init_dist5();
|
|
147557
|
-
import { basename as basename19, dirname as dirname42, join as
|
|
147507
|
+
import { basename as basename19, dirname as dirname42, join as join89, resolve as resolve33 } from "path";
|
|
147558
147508
|
function registerProjectsCommand(program, context) {
|
|
147559
147509
|
const projectsCmd = program.command("projects").summary("manage the Spur multi-project registry");
|
|
147560
|
-
projectsCmd.command("add").argument("<path>", "Project root directory path").option(...SHARED_OPTIONS.nameProjectDisplay).option(...SHARED_OPTIONS.jsonProjectsResponse).option(...SHARED_OPTIONS.jsonEnvelope).action(async (pathArg, options) => {
|
|
147510
|
+
projectsCmd.command("add").argument("<path>", "Project root directory path").option("--host <host>", 'Host for local or remote project (default: "localhost")').option(...SHARED_OPTIONS.nameProjectDisplay).option(...SHARED_OPTIONS.jsonProjectsResponse).option(...SHARED_OPTIONS.jsonEnvelope).action(async (pathArg, options) => {
|
|
147561
147511
|
try {
|
|
147562
|
-
const
|
|
147563
|
-
|
|
147564
|
-
|
|
147512
|
+
const host = options.host ?? "localhost";
|
|
147513
|
+
const isLocal = isLocalHost(host);
|
|
147514
|
+
let absolutePath = pathArg;
|
|
147515
|
+
if (isLocal) {
|
|
147516
|
+
absolutePath = resolve33(context.cwd, pathArg);
|
|
147517
|
+
if (!await context.fs.exists(absolutePath)) {
|
|
147518
|
+
throw new Error(`Directory does not exist: ${absolutePath}`);
|
|
147519
|
+
}
|
|
147565
147520
|
}
|
|
147566
147521
|
const name = options.name ?? basename19(absolutePath);
|
|
147567
147522
|
const registry = new ProjectRegistry;
|
|
147568
|
-
const entry = await registry.upsert({ path: absolutePath, name, port: 0 });
|
|
147523
|
+
const entry = await registry.upsert({ path: absolutePath, name, host, port: 0 });
|
|
147569
147524
|
if (options.json) {
|
|
147570
147525
|
context.output.write(toEnvelopeJson({ ok: true, project: entry }, { enveloped: options.jsonEnvelope }));
|
|
147571
147526
|
} else {
|
|
147572
|
-
|
|
147527
|
+
const hostLabel = !isLocalHost(entry.host) ? ` on host ${entry.host}` : "";
|
|
147528
|
+
context.output.write(`Registered project "${entry.name}" at ${entry.path}${hostLabel}`);
|
|
147573
147529
|
}
|
|
147574
147530
|
} catch (err) {
|
|
147575
147531
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -147614,14 +147570,17 @@ function registerProjectsCommand(program, context) {
|
|
|
147614
147570
|
ok: true,
|
|
147615
147571
|
purgedCount: result.purgedProjects.length,
|
|
147616
147572
|
purgedProjects: result.purgedProjects,
|
|
147617
|
-
terminatedProcesses: result.terminatedProcesses
|
|
147573
|
+
terminatedProcesses: result.terminatedProcesses,
|
|
147574
|
+
...result.resetPorts && result.resetPorts.length > 0 ? { resetPorts: result.resetPorts } : {}
|
|
147618
147575
|
}, { enveloped: options.jsonEnvelope }));
|
|
147619
|
-
} else if (result.purgedProjects.length === 0) {
|
|
147576
|
+
} else if (result.purgedProjects.length === 0 && (!result.resetPorts || result.resetPorts.length === 0)) {
|
|
147620
147577
|
context.output.write("Registry is clean. No stale project entries found.");
|
|
147621
147578
|
} else {
|
|
147622
|
-
|
|
147623
|
-
|
|
147624
|
-
|
|
147579
|
+
if (result.purgedProjects.length > 0) {
|
|
147580
|
+
context.output.write(`Purged ${result.purgedProjects.length} stale project(s):`);
|
|
147581
|
+
for (const project of result.purgedProjects) {
|
|
147582
|
+
context.output.write(`- ${project.name} (${project.path})`);
|
|
147583
|
+
}
|
|
147625
147584
|
}
|
|
147626
147585
|
if (result.terminatedProcesses.length > 0) {
|
|
147627
147586
|
context.output.write(`Terminated ${result.terminatedProcesses.length} lingering process(es):`);
|
|
@@ -147629,6 +147588,13 @@ function registerProjectsCommand(program, context) {
|
|
|
147629
147588
|
context.output.write(`- PID ${proc.pid} on port ${proc.port} (${proc.signal})`);
|
|
147630
147589
|
}
|
|
147631
147590
|
}
|
|
147591
|
+
if (result.resetPorts && result.resetPorts.length > 0) {
|
|
147592
|
+
context.output.write(`Reset ${result.resetPorts.length} closed port(s):`);
|
|
147593
|
+
for (const project of result.resetPorts) {
|
|
147594
|
+
const hostLabel = !isLocalHost(project.host) ? `${project.host}:` : "";
|
|
147595
|
+
context.output.write(`- ${project.name} (${hostLabel}${project.port} -> 0)`);
|
|
147596
|
+
}
|
|
147597
|
+
}
|
|
147632
147598
|
}
|
|
147633
147599
|
} catch (err) {
|
|
147634
147600
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -147646,10 +147612,10 @@ function registerProjectsCommand(program, context) {
|
|
|
147646
147612
|
const rawProjects = await registry.list();
|
|
147647
147613
|
const projects = await Promise.all(rawProjects.map(async (p) => ({
|
|
147648
147614
|
...p,
|
|
147649
|
-
running: p.port > 0 ? await isPortLive(p.port) : false
|
|
147615
|
+
running: p.port > 0 ? await isPortLive(p.port, undefined, p.host) : false
|
|
147650
147616
|
})));
|
|
147651
147617
|
const openProjectDb = async (projectPath) => {
|
|
147652
|
-
const url =
|
|
147618
|
+
const url = join89(projectPath, ".spur", "spur.db");
|
|
147653
147619
|
await context.fs.ensureDir(dirname42(url));
|
|
147654
147620
|
return createMigratedDb({ url });
|
|
147655
147621
|
};
|
|
@@ -147715,8 +147681,9 @@ function registerProjectsCommand(program, context) {
|
|
|
147715
147681
|
context.output.write(`Registered Projects:
|
|
147716
147682
|
`);
|
|
147717
147683
|
for (const [i, p] of projects.entries()) {
|
|
147684
|
+
const hostLabel = !isLocalHost(p.host) ? ` (${p.host})` : "";
|
|
147718
147685
|
const status = p.running ? `[RUNNING: ${p.port}]` : "[STOPPED]";
|
|
147719
|
-
context.output.write(`- ${p.name} ${status} (${p.path})`);
|
|
147686
|
+
context.output.write(`- ${p.name}${hostLabel} ${status} (${p.path})`);
|
|
147720
147687
|
const f = fleets?.[i];
|
|
147721
147688
|
if (f === undefined)
|
|
147722
147689
|
continue;
|
|
@@ -148078,7 +148045,7 @@ function formatTraceDetail(detail) {
|
|
|
148078
148045
|
// src/commands/serve.ts
|
|
148079
148046
|
init_src();
|
|
148080
148047
|
import { statSync as statSync18 } from "fs";
|
|
148081
|
-
import { join as
|
|
148048
|
+
import { join as join94, resolve as resolve36 } from "path";
|
|
148082
148049
|
|
|
148083
148050
|
// ../server/src/index.ts
|
|
148084
148051
|
init_src();
|
|
@@ -148089,11 +148056,11 @@ init_src();
|
|
|
148089
148056
|
init_loader2();
|
|
148090
148057
|
init_src2();
|
|
148091
148058
|
import { homedir as homedir17 } from "os";
|
|
148092
|
-
import { basename as basename21, dirname as dirname44, isAbsolute as isAbsolute15, join as
|
|
148059
|
+
import { basename as basename21, dirname as dirname44, isAbsolute as isAbsolute15, join as join93 } from "path";
|
|
148093
148060
|
init_dist5();
|
|
148094
148061
|
|
|
148095
148062
|
// ../server/src/bootstrap.ts
|
|
148096
|
-
import { join as
|
|
148063
|
+
import { join as join91 } from "path";
|
|
148097
148064
|
|
|
148098
148065
|
// ../../node_modules/.bun/@orpc+shared@1.14.4+e40b0dfdd726a224/node_modules/@orpc/shared/dist/index.mjs
|
|
148099
148066
|
function resolveMaybeOptionalOptions(rest) {
|
|
@@ -154946,9 +154913,9 @@ init_loader2();
|
|
|
154946
154913
|
init_src2();
|
|
154947
154914
|
init_dist5();
|
|
154948
154915
|
import { homedir as homedir16 } from "os";
|
|
154949
|
-
import { basename as basename20, join as
|
|
154916
|
+
import { basename as basename20, join as join90, relative as relative19, resolve as resolve35 } from "path";
|
|
154950
154917
|
// ../server/package.json
|
|
154951
|
-
var version3 = "0.4.
|
|
154918
|
+
var version3 = "0.4.10";
|
|
154952
154919
|
|
|
154953
154920
|
// ../server/src/modules/health/index.ts
|
|
154954
154921
|
var startedAt = Date.now();
|
|
@@ -155421,8 +155388,8 @@ var healthModule = {
|
|
|
155421
155388
|
}
|
|
155422
155389
|
});
|
|
155423
155390
|
}
|
|
155424
|
-
const projectPath =
|
|
155425
|
-
const globalPath =
|
|
155391
|
+
const projectPath = join90(ctx.cwd, ".spur", "config.yaml");
|
|
155392
|
+
const globalPath = join90(homedir16(), ".config", "spur", "config.yaml");
|
|
155426
155393
|
let projectExists = false;
|
|
155427
155394
|
let projectContent = "";
|
|
155428
155395
|
let projectSizeBytes = 0;
|
|
@@ -155539,7 +155506,7 @@ var healthModule = {
|
|
|
155539
155506
|
const projectRoot = ctx.cwd ?? process.cwd();
|
|
155540
155507
|
const fs = ctx.fs ?? createNodeFileSystem2(projectRoot);
|
|
155541
155508
|
const files = [];
|
|
155542
|
-
const rootDesignPath =
|
|
155509
|
+
const rootDesignPath = join90(projectRoot, "DESIGN.md");
|
|
155543
155510
|
if (await fs.exists(rootDesignPath)) {
|
|
155544
155511
|
let title = "UI/UX Design System";
|
|
155545
155512
|
try {
|
|
@@ -155556,7 +155523,7 @@ var healthModule = {
|
|
|
155556
155523
|
category: "root"
|
|
155557
155524
|
});
|
|
155558
155525
|
}
|
|
155559
|
-
const doc04Path =
|
|
155526
|
+
const doc04Path = join90(projectRoot, "docs", "04_DESIGN.md");
|
|
155560
155527
|
if (await fs.exists(doc04Path)) {
|
|
155561
155528
|
let title = "Concrete Surfaces & Contracts";
|
|
155562
155529
|
try {
|
|
@@ -155573,7 +155540,7 @@ var healthModule = {
|
|
|
155573
155540
|
category: "architecture"
|
|
155574
155541
|
});
|
|
155575
155542
|
}
|
|
155576
|
-
const designDir =
|
|
155543
|
+
const designDir = join90(projectRoot, "docs", "design");
|
|
155577
155544
|
if (await fs.exists(designDir)) {
|
|
155578
155545
|
let mdFiles = [];
|
|
155579
155546
|
try {
|
|
@@ -155582,7 +155549,7 @@ var healthModule = {
|
|
|
155582
155549
|
} catch {}
|
|
155583
155550
|
mdFiles.sort((a, b) => a.localeCompare(b));
|
|
155584
155551
|
for (const filename of mdFiles) {
|
|
155585
|
-
const fullPath =
|
|
155552
|
+
const fullPath = join90(designDir, filename);
|
|
155586
155553
|
const relPath = `docs/design/${filename}`;
|
|
155587
155554
|
let title = filename.replace(/\.md$/, "").replace(/-/g, " ");
|
|
155588
155555
|
try {
|
|
@@ -155620,7 +155587,7 @@ var healthModule = {
|
|
|
155620
155587
|
try {
|
|
155621
155588
|
const projectRoot = ctx.cwd ?? process.cwd();
|
|
155622
155589
|
const fs = ctx.fs ?? createNodeFileSystem2(projectRoot);
|
|
155623
|
-
const fullPath =
|
|
155590
|
+
const fullPath = join90(projectRoot, reqPath);
|
|
155624
155591
|
if (!await fs.exists(fullPath)) {
|
|
155625
155592
|
return c.json({ ok: false, error: "File not found" }, 404);
|
|
155626
155593
|
}
|
|
@@ -155646,7 +155613,7 @@ var healthModule = {
|
|
|
155646
155613
|
const projectRoot = ctx.cwd ?? process.cwd();
|
|
155647
155614
|
const fs = ctx.fs ?? createNodeFileSystem2(projectRoot);
|
|
155648
155615
|
const files = [];
|
|
155649
|
-
const roadmapPath =
|
|
155616
|
+
const roadmapPath = join90(projectRoot, "docs", "02_ROADMAP.md");
|
|
155650
155617
|
if (await fs.exists(roadmapPath)) {
|
|
155651
155618
|
let title = "02 Roadmap \u2014 Spur";
|
|
155652
155619
|
try {
|
|
@@ -155663,7 +155630,7 @@ var healthModule = {
|
|
|
155663
155630
|
category: "roadmap"
|
|
155664
155631
|
});
|
|
155665
155632
|
}
|
|
155666
|
-
const plansDir =
|
|
155633
|
+
const plansDir = join90(projectRoot, "docs", "plans");
|
|
155667
155634
|
if (await fs.exists(plansDir)) {
|
|
155668
155635
|
let mdFiles = [];
|
|
155669
155636
|
try {
|
|
@@ -155672,7 +155639,7 @@ var healthModule = {
|
|
|
155672
155639
|
} catch {}
|
|
155673
155640
|
mdFiles.sort((a, b) => b.localeCompare(a));
|
|
155674
155641
|
for (const filename of mdFiles) {
|
|
155675
|
-
const fullPath =
|
|
155642
|
+
const fullPath = join90(plansDir, filename);
|
|
155676
155643
|
const relPath = `docs/plans/${filename}`;
|
|
155677
155644
|
let title = filename.replace(/\.md$/, "").replace(/-/g, " ");
|
|
155678
155645
|
try {
|
|
@@ -155710,7 +155677,7 @@ var healthModule = {
|
|
|
155710
155677
|
try {
|
|
155711
155678
|
const projectRoot = ctx.cwd ?? process.cwd();
|
|
155712
155679
|
const fs = ctx.fs ?? createNodeFileSystem2(projectRoot);
|
|
155713
|
-
const fullPath =
|
|
155680
|
+
const fullPath = join90(projectRoot, reqPath);
|
|
155714
155681
|
if (!await fs.exists(fullPath)) {
|
|
155715
155682
|
return c.json({ ok: false, error: "File not found" }, 404);
|
|
155716
155683
|
}
|
|
@@ -158224,7 +158191,7 @@ function createApp(appRt, opts) {
|
|
|
158224
158191
|
app.use("*", async (c, next) => {
|
|
158225
158192
|
const pathname = c.req.path === "/" ? "/index.html" : c.req.path;
|
|
158226
158193
|
try {
|
|
158227
|
-
const file = Bun.file(
|
|
158194
|
+
const file = Bun.file(join91(webDistPath, pathname));
|
|
158228
158195
|
if (await file.exists()) {
|
|
158229
158196
|
const headers = new Headers({ "content-type": file.type });
|
|
158230
158197
|
return new Response(file.stream(), { headers });
|
|
@@ -158237,7 +158204,7 @@ function createApp(appRt, opts) {
|
|
|
158237
158204
|
return c.json({ error: "Not Found" }, 404);
|
|
158238
158205
|
}
|
|
158239
158206
|
try {
|
|
158240
|
-
const indexFile = Bun.file(
|
|
158207
|
+
const indexFile = Bun.file(join91(webDistPath, "index.html"));
|
|
158241
158208
|
if (await indexFile.exists()) {
|
|
158242
158209
|
return new Response(indexFile.stream(), {
|
|
158243
158210
|
headers: { "content-type": "text/html; charset=utf-8" }
|
|
@@ -158259,7 +158226,7 @@ init_src();
|
|
|
158259
158226
|
init_loader2();
|
|
158260
158227
|
init_src2();
|
|
158261
158228
|
init_dist5();
|
|
158262
|
-
import { dirname as dirname43, join as
|
|
158229
|
+
import { dirname as dirname43, join as join92 } from "path";
|
|
158263
158230
|
var NOOP_OUTPUT = { write: (_s) => {}, error: (_s) => {} };
|
|
158264
158231
|
|
|
158265
158232
|
class LazyPlanningEventEmitter {
|
|
@@ -158295,7 +158262,7 @@ class NotConfiguredError extends Error {
|
|
|
158295
158262
|
function createServerContext(appRt, options) {
|
|
158296
158263
|
const cwd = options.cwd;
|
|
158297
158264
|
const fs = options.fs;
|
|
158298
|
-
const dbUrl = options.dbUrl ??
|
|
158265
|
+
const dbUrl = options.dbUrl ?? join92(cwd, DEFAULT_DATABASE_URL);
|
|
158299
158266
|
const eventsBus = options.eventsBus ?? appRt.events;
|
|
158300
158267
|
const jobQueueEnabled = options.jobQueueEnabled ?? false;
|
|
158301
158268
|
const eventProjectContext = systemEventProjectContext(cwd);
|
|
@@ -158331,7 +158298,7 @@ function createServerContext(appRt, options) {
|
|
|
158331
158298
|
return {
|
|
158332
158299
|
cwd,
|
|
158333
158300
|
fs,
|
|
158334
|
-
runDir:
|
|
158301
|
+
runDir: join92(cwd, ".spur", "run"),
|
|
158335
158302
|
webDistPath: options.webDistPath,
|
|
158336
158303
|
boardModules: options.boardModules,
|
|
158337
158304
|
logger: appRt.logger,
|
|
@@ -158695,7 +158662,7 @@ var BOARD_RUNTIME_MANIFEST_FILE = "board-runtime.json";
|
|
|
158695
158662
|
async function readBoardHostRuntime(webDistPath, hasContributions) {
|
|
158696
158663
|
if (!webDistPath)
|
|
158697
158664
|
return null;
|
|
158698
|
-
const manifestPath =
|
|
158665
|
+
const manifestPath = join93(webDistPath, BOARD_RUNTIME_MANIFEST_FILE);
|
|
158699
158666
|
try {
|
|
158700
158667
|
const file = Bun.file(manifestPath);
|
|
158701
158668
|
if (!await file.exists())
|
|
@@ -158732,7 +158699,7 @@ function defaultUsageSnapshotPath(env) {
|
|
|
158732
158699
|
const override = env.SPUR_AGENT_USAGE_SNAPSHOT;
|
|
158733
158700
|
if (override !== undefined && override.length > 0)
|
|
158734
158701
|
return override;
|
|
158735
|
-
return
|
|
158702
|
+
return join93(env.HOME ?? homedir17(), ".config", "spur", "agent-usage.json");
|
|
158736
158703
|
}
|
|
158737
158704
|
var JOB_WORKER_START_DELAY_MS = 30000;
|
|
158738
158705
|
var defaultDeps = {
|
|
@@ -158996,15 +158963,15 @@ async function handleFeatureActionJob(ctx, env, payload) {
|
|
|
158996
158963
|
await runFeatureActionJob(ctx, env, payload);
|
|
158997
158964
|
}
|
|
158998
158965
|
async function resolveWebDistPath(configuredPath, projectRoot = process.cwd()) {
|
|
158999
|
-
const candidates = configuredPath && configuredPath.trim() !== "" ? [isAbsolute15(configuredPath) ? configuredPath :
|
|
159000
|
-
|
|
159001
|
-
|
|
159002
|
-
|
|
159003
|
-
|
|
159004
|
-
|
|
158966
|
+
const candidates = configuredPath && configuredPath.trim() !== "" ? [isAbsolute15(configuredPath) ? configuredPath : join93(projectRoot, configuredPath)] : [
|
|
158967
|
+
join93(projectRoot, "dist/web"),
|
|
158968
|
+
join93(import.meta.dir, "web"),
|
|
158969
|
+
join93(dirname44(process.execPath), "web"),
|
|
158970
|
+
join93(dirname44(process.execPath), "../web"),
|
|
158971
|
+
join93(import.meta.dir, "../../../dist/web")
|
|
159005
158972
|
];
|
|
159006
158973
|
for (const candidate of candidates) {
|
|
159007
|
-
if (await Bun.file(
|
|
158974
|
+
if (await Bun.file(join93(candidate, "index.html")).exists()) {
|
|
159008
158975
|
return candidate;
|
|
159009
158976
|
}
|
|
159010
158977
|
}
|
|
@@ -159479,7 +159446,7 @@ var SQLITE_BUSY_MESSAGE_CONSTANTS = Object.freeze({
|
|
|
159479
159446
|
|
|
159480
159447
|
// src/commands/serve.ts
|
|
159481
159448
|
function resolveServeDbUrl(cwd, env, configuredUrl) {
|
|
159482
|
-
return env.DATABASE_URL === undefined ?
|
|
159449
|
+
return env.DATABASE_URL === undefined ? join94(cwd, DEFAULT_DATABASE_URL) : configuredUrl;
|
|
159483
159450
|
}
|
|
159484
159451
|
function resolveServeCwd(cwd) {
|
|
159485
159452
|
const resolved = resolve36(cwd);
|
|
@@ -159543,7 +159510,7 @@ function registerServeCommand(program, context, options = {}) {
|
|
|
159543
159510
|
}
|
|
159544
159511
|
|
|
159545
159512
|
// src/commands/status.ts
|
|
159546
|
-
import { join as
|
|
159513
|
+
import { join as join95 } from "path";
|
|
159547
159514
|
|
|
159548
159515
|
// src/git-context.ts
|
|
159549
159516
|
init_dist5();
|
|
@@ -159591,8 +159558,8 @@ function registerStatusCommand(program, context, options = {}) {
|
|
|
159591
159558
|
}
|
|
159592
159559
|
async function runStatusCore(path, options, context) {
|
|
159593
159560
|
const [packageJsonExists, spurConfigExists, git, agentSpecs] = await Promise.all([
|
|
159594
|
-
context.fs.exists(
|
|
159595
|
-
context.fs.exists(
|
|
159561
|
+
context.fs.exists(join95(context.cwd, "package.json")),
|
|
159562
|
+
context.fs.exists(join95(context.cwd, ".spur", "config.yaml")),
|
|
159596
159563
|
gitContext(context.cwd),
|
|
159597
159564
|
listAgentSpecIds(context)
|
|
159598
159565
|
]);
|
|
@@ -159623,14 +159590,14 @@ async function runStatusCore(path, options, context) {
|
|
|
159623
159590
|
return status.ok ? 0 : 1;
|
|
159624
159591
|
}
|
|
159625
159592
|
async function listAgentSpecIds(context) {
|
|
159626
|
-
const dir =
|
|
159593
|
+
const dir = join95(context.cwd, ".spur", "agents");
|
|
159627
159594
|
if (!await context.fs.exists(dir))
|
|
159628
159595
|
return [];
|
|
159629
159596
|
const entries = await context.fs.readDir(dir);
|
|
159630
159597
|
return entries.filter((entry) => entry.endsWith(".yaml") || entry.endsWith(".yml")).map((entry) => entry.replace(/\.ya?ml$/, "")).sort();
|
|
159631
159598
|
}
|
|
159632
159599
|
async function readTargetStatus(context, targetPath) {
|
|
159633
|
-
const resolved =
|
|
159600
|
+
const resolved = join95(context.cwd, targetPath);
|
|
159634
159601
|
const stat = await context.fs.stat(resolved);
|
|
159635
159602
|
if (stat === null)
|
|
159636
159603
|
throw new CommandError(`status failed: path does not exist at ${resolved}`);
|
|
@@ -159881,11 +159848,11 @@ function registerWorkflowCleanCommand(workflow, context) {
|
|
|
159881
159848
|
// ../../packages/app/src/workflow/commit-guard.ts
|
|
159882
159849
|
init_dist5();
|
|
159883
159850
|
import { mkdirSync as mkdirSync23, readFileSync as readFileSync28, renameSync as renameSync11, writeFileSync as writeFileSync20 } from "fs";
|
|
159884
|
-
import { join as
|
|
159851
|
+
import { join as join96 } from "path";
|
|
159885
159852
|
var COMMIT_GUARD_USAGE = "usage: spur workflow commit-guard start --run <id> | stage --run <id> [--base <sha>] -- <paths\u2026> | check --run <id> [--base <sha>]";
|
|
159886
159853
|
var isBookkeeping = (path) => path === ".spur" || path.startsWith(".spur/");
|
|
159887
|
-
var at2 = (options, rel) => options.cwd ?
|
|
159888
|
-
var artifactPath = (run) =>
|
|
159854
|
+
var at2 = (options, rel) => options.cwd ? join96(options.cwd, rel) : rel;
|
|
159855
|
+
var artifactPath = (run) => join96(".spur", "run", `${run}-tree-start.json`);
|
|
159889
159856
|
function gitOf(options) {
|
|
159890
159857
|
if (options.git)
|
|
159891
159858
|
return options.git;
|
|
@@ -159954,7 +159921,7 @@ function runContext(run, options) {
|
|
|
159954
159921
|
let base = options.base ?? "";
|
|
159955
159922
|
if (!base) {
|
|
159956
159923
|
try {
|
|
159957
|
-
const anchored = readFileSync28(at2(options,
|
|
159924
|
+
const anchored = readFileSync28(at2(options, join96(".spur", "run", `${run}-base.sha`)), "utf8").trim();
|
|
159958
159925
|
if (/^[0-9a-f]{7,40}$/i.test(anchored))
|
|
159959
159926
|
base = anchored;
|
|
159960
159927
|
} catch {}
|
|
@@ -159994,7 +159961,7 @@ function commitGuardStart(run, options = {}) {
|
|
|
159994
159961
|
dirty: parsePorcelainZ(status.stdout).map((path) => ({ path, sha: hashPath(path, git) }))
|
|
159995
159962
|
};
|
|
159996
159963
|
const abs = at2(options, artifactPath(run));
|
|
159997
|
-
mkdirSync23(
|
|
159964
|
+
mkdirSync23(join96(abs, ".."), { recursive: true });
|
|
159998
159965
|
writeFileSync20(`${abs}.tmp`, `${JSON.stringify(snapshot, null, 2)}
|
|
159999
159966
|
`);
|
|
160000
159967
|
renameSync11(`${abs}.tmp`, abs);
|
|
@@ -160266,14 +160233,14 @@ function registerWorkflowContinueCommand(workflow, context) {
|
|
|
160266
160233
|
}
|
|
160267
160234
|
|
|
160268
160235
|
// src/commands/workflow/cost.ts
|
|
160269
|
-
import { join as
|
|
160236
|
+
import { join as join98 } from "path";
|
|
160270
160237
|
|
|
160271
160238
|
// ../../packages/app/src/workflow/run-cost.ts
|
|
160272
160239
|
init_src2();
|
|
160273
160240
|
init_dist5();
|
|
160274
160241
|
init_dist2();
|
|
160275
160242
|
init_composition_baseline();
|
|
160276
|
-
import { join as
|
|
160243
|
+
import { join as join97 } from "path";
|
|
160277
160244
|
|
|
160278
160245
|
// ../../packages/app/src/workflow/run-measurements.ts
|
|
160279
160246
|
init_src2();
|
|
@@ -160507,7 +160474,7 @@ async function loadQueryCounts(workflowsDir, fs = createNodeFileSystem2()) {
|
|
|
160507
160474
|
const name = /^(.+)\.ya?ml$/.exec(entry)?.[1];
|
|
160508
160475
|
if (!name)
|
|
160509
160476
|
continue;
|
|
160510
|
-
const def = $parse(await fs.readFile(
|
|
160477
|
+
const def = $parse(await fs.readFile(join97(workflowsDir, entry)));
|
|
160511
160478
|
out[name] = extractResolvedWorkflowFacts(def).modelQueries.length;
|
|
160512
160479
|
}
|
|
160513
160480
|
return out;
|
|
@@ -160533,7 +160500,7 @@ async function runBudgetGate(opts) {
|
|
|
160533
160500
|
const fs = opts.fs ?? createNodeFileSystem2();
|
|
160534
160501
|
const gitShow = opts.gitShow ?? defaultGitShow;
|
|
160535
160502
|
const openDb = opts.openDb ?? defaultDbOpener;
|
|
160536
|
-
const absBudget = opts.budgetPath.startsWith("/") ? opts.budgetPath :
|
|
160503
|
+
const absBudget = opts.budgetPath.startsWith("/") ? opts.budgetPath : join97(opts.cwd, opts.budgetPath);
|
|
160537
160504
|
const relBudget = absBudget.startsWith(`${opts.cwd}/`) ? absBudget.slice(opts.cwd.length + 1) : opts.budgetPath;
|
|
160538
160505
|
const config = await loadPipelineBudgets(absBudget, fs);
|
|
160539
160506
|
let head = null;
|
|
@@ -160549,7 +160516,7 @@ async function runBudgetGate(opts) {
|
|
|
160549
160516
|
const queryCounts = await loadQueryCounts(opts.workflowsDir, fs);
|
|
160550
160517
|
let measured;
|
|
160551
160518
|
if (opts.measuredFile !== undefined) {
|
|
160552
|
-
const measuredPath = opts.measuredFile.startsWith("/") ? opts.measuredFile :
|
|
160519
|
+
const measuredPath = opts.measuredFile.startsWith("/") ? opts.measuredFile : join97(opts.cwd, opts.measuredFile);
|
|
160553
160520
|
measured = JSON.parse(await fs.readFile(measuredPath));
|
|
160554
160521
|
} else if (!await fs.exists(opts.dbPath)) {
|
|
160555
160522
|
measured = {};
|
|
@@ -160598,8 +160565,8 @@ function registerWorkflowCostCommand(workflow, context, deps = {}) {
|
|
|
160598
160565
|
].join(`
|
|
160599
160566
|
`)).option("--workflow <name>", "restrict to a workflow (repeatable)", collect3, []).option("--by-state", "report per-state rows instead of per-workflow rows").option("--include-bookkeeping", "keep bookkeeping workflows in the default cohort").option("--since <YYYY-MM-DD>", "only runs created on/after this UTC date").option("--budget <file>", "run the pipeline budget gate against this budget file").option("--measured-file <json>", "with --budget: read measured values from this file instead of the DB").option(...SHARED_OPTIONS.json).action(async (options) => {
|
|
160600
160567
|
const cwd = context.cwd;
|
|
160601
|
-
const dbPath =
|
|
160602
|
-
const workflowsDir =
|
|
160568
|
+
const dbPath = join98(cwd, ".spur/spur.db");
|
|
160569
|
+
const workflowsDir = join98(cwd, ".spur", "workflows");
|
|
160603
160570
|
const openDb = deps.openDb ?? defaultDbOpener;
|
|
160604
160571
|
try {
|
|
160605
160572
|
if (options.budget !== undefined) {
|
|
@@ -160663,9 +160630,9 @@ init_run_summary_render();
|
|
|
160663
160630
|
// ../../packages/app/src/workflow/persist-check.ts
|
|
160664
160631
|
init_dist5();
|
|
160665
160632
|
import { existsSync as existsSync33, readdirSync as readdirSync15, readFileSync as readFileSync30, statSync as statSync19 } from "fs";
|
|
160666
|
-
import { basename as basename22, dirname as dirname45, isAbsolute as isAbsolute16, join as
|
|
160667
|
-
var EVIDENCE_DIR =
|
|
160668
|
-
var RUN_DIR =
|
|
160633
|
+
import { basename as basename22, dirname as dirname45, isAbsolute as isAbsolute16, join as join100, relative as relative20, resolve as resolve37 } from "path";
|
|
160634
|
+
var EVIDENCE_DIR = join100(".spur", "memory", "evidence");
|
|
160635
|
+
var RUN_DIR = join100(".spur", "run");
|
|
160669
160636
|
var NAMED_CAP = 32;
|
|
160670
160637
|
var PREFIX_CAP = 64;
|
|
160671
160638
|
var DURABLE_FAMILY_RE = /^\d{4}-(?:verdict|feature-verification)\.json$/;
|
|
@@ -160680,7 +160647,7 @@ function walkFiles(dir, root) {
|
|
|
160680
160647
|
const out = [];
|
|
160681
160648
|
const walk = (cur) => {
|
|
160682
160649
|
for (const e of readdirSync15(cur, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
|
|
160683
|
-
const p =
|
|
160650
|
+
const p = join100(cur, e.name);
|
|
160684
160651
|
if (e.isDirectory())
|
|
160685
160652
|
walk(p);
|
|
160686
160653
|
else if (e.isFile())
|
|
@@ -160707,8 +160674,8 @@ function listObligations(wtRoot, wbsList, runIds) {
|
|
|
160707
160674
|
const prefixes = [...new Set([...wbsList, ...runIds].filter(Boolean))];
|
|
160708
160675
|
if (prefixes.length === 0)
|
|
160709
160676
|
return null;
|
|
160710
|
-
const evFiles = walkFiles(
|
|
160711
|
-
const runFiles = walkFiles(
|
|
160677
|
+
const evFiles = walkFiles(join100(wtRoot, EVIDENCE_DIR), wtRoot);
|
|
160678
|
+
const runFiles = walkFiles(join100(wtRoot, RUN_DIR), wtRoot);
|
|
160712
160679
|
const files = [];
|
|
160713
160680
|
const counts = new Map;
|
|
160714
160681
|
if (!collectOwned(evFiles, prefixes, counts, files))
|
|
@@ -160725,13 +160692,13 @@ function compareTrees(wtRoot, invokeRoot, files, foreignDivergent = new Set) {
|
|
|
160725
160692
|
for (const rel of files) {
|
|
160726
160693
|
if (foreignDivergent.has(rel) || foreignDivergent.has(basename22(rel)))
|
|
160727
160694
|
continue;
|
|
160728
|
-
const invPath =
|
|
160695
|
+
const invPath = join100(invokeRoot, rel);
|
|
160729
160696
|
if (!existsSync33(invPath)) {
|
|
160730
160697
|
missing.push(rel);
|
|
160731
160698
|
continue;
|
|
160732
160699
|
}
|
|
160733
160700
|
try {
|
|
160734
|
-
if (readFileSync30(
|
|
160701
|
+
if (readFileSync30(join100(wtRoot, rel)).equals(readFileSync30(invPath)))
|
|
160735
160702
|
ok++;
|
|
160736
160703
|
else
|
|
160737
160704
|
divergent.push(rel);
|
|
@@ -160748,7 +160715,7 @@ function defaultInvokeRoot(cwd) {
|
|
|
160748
160715
|
});
|
|
160749
160716
|
const common = run.stdout.trim();
|
|
160750
160717
|
if (run.exitCode === 0 && common)
|
|
160751
|
-
return dirname45(isAbsolute16(common) ? common :
|
|
160718
|
+
return dirname45(isAbsolute16(common) ? common : join100(cwd, common));
|
|
160752
160719
|
return cwd;
|
|
160753
160720
|
}
|
|
160754
160721
|
function readPersistOutSkips(candidates) {
|
|
@@ -160763,7 +160730,7 @@ function readPersistOutSkips(candidates) {
|
|
|
160763
160730
|
for (const item of parsed.evidenceSkipped) {
|
|
160764
160731
|
if (item.reason === "foreign-divergent" && typeof item.name === "string") {
|
|
160765
160732
|
foreignDivergent.add(item.name);
|
|
160766
|
-
foreignDivergent.add(
|
|
160733
|
+
foreignDivergent.add(join100(EVIDENCE_DIR, item.name));
|
|
160767
160734
|
}
|
|
160768
160735
|
}
|
|
160769
160736
|
}
|
|
@@ -160796,8 +160763,8 @@ function runPersistCheck(input) {
|
|
|
160796
160763
|
const invokeRoot = input.root === undefined ? defaultInvokeRoot(input.cwd) : resolve37(input.cwd, input.root);
|
|
160797
160764
|
const { foreignDivergent, skips } = readPersistOutSkips([
|
|
160798
160765
|
input.successJson === undefined || input.successJson === "" ? "" : resolve37(input.cwd, input.successJson),
|
|
160799
|
-
|
|
160800
|
-
|
|
160766
|
+
join100(invokeRoot, ".spur", "run", "persist-out.json"),
|
|
160767
|
+
join100(wtRoot, ".spur", "run", "persist-out.json")
|
|
160801
160768
|
].filter(Boolean));
|
|
160802
160769
|
const obligations = listObligations(wtRoot, wbsList, runIds);
|
|
160803
160770
|
if (obligations === null) {
|
|
@@ -161166,7 +161133,7 @@ init_src3();
|
|
|
161166
161133
|
init_capability_attestation();
|
|
161167
161134
|
init_src();
|
|
161168
161135
|
init_dist7();
|
|
161169
|
-
import { join as
|
|
161136
|
+
import { join as join101 } from "path";
|
|
161170
161137
|
import { createInterface } from "readline";
|
|
161171
161138
|
import { setTimeout as sleep6 } from "timers/promises";
|
|
161172
161139
|
var WORKFLOW_RUN_ACTIVE_ENV = "SPUR_WORKFLOW_RUN_ACTIVE";
|
|
@@ -161270,9 +161237,9 @@ async function writeWorkflowPlanArtifact(cwd, runId, resolved, secretValues, sta
|
|
|
161270
161237
|
}))
|
|
161271
161238
|
});
|
|
161272
161239
|
const { mkdir, writeFile } = await import("fs/promises");
|
|
161273
|
-
const artifactsDir =
|
|
161240
|
+
const artifactsDir = join101(cwd, ".spur", "run");
|
|
161274
161241
|
await mkdir(artifactsDir, { recursive: true });
|
|
161275
|
-
const artifactPath =
|
|
161242
|
+
const artifactPath = join101(artifactsDir, `${runId}-workflow-plan.json`);
|
|
161276
161243
|
await writeFile(artifactPath, payload);
|
|
161277
161244
|
return artifactPath;
|
|
161278
161245
|
}
|
|
@@ -161743,7 +161710,7 @@ function registerWorkflowSummaryCommand(workflow, context) {
|
|
|
161743
161710
|
// src/commands/workflow/trace.ts
|
|
161744
161711
|
init_src3();
|
|
161745
161712
|
import { closeSync as closeSync11, fstatSync as fstatSync5, openSync as openSync11, readFileSync as readFileSync32, readSync as readSync3 } from "fs";
|
|
161746
|
-
import { join as
|
|
161713
|
+
import { join as join102 } from "path";
|
|
161747
161714
|
import { setTimeout as sleep7 } from "timers/promises";
|
|
161748
161715
|
function formatTraceList2(result) {
|
|
161749
161716
|
if (result.entries.length === 0) {
|
|
@@ -161912,8 +161879,8 @@ function readRunLogChunk(logPath, offset) {
|
|
|
161912
161879
|
async function followRunLog(service, runId, dir, pollMs, write, wait = (ms) => sleep7(ms), now = Date.now, deadlineMs) {
|
|
161913
161880
|
const start = now();
|
|
161914
161881
|
const deadline = deadlineMs === undefined ? Number.POSITIVE_INFINITY : start + deadlineMs;
|
|
161915
|
-
const runDir =
|
|
161916
|
-
const pendingMdPath =
|
|
161882
|
+
const runDir = join102(dir, ".spur", "run");
|
|
161883
|
+
const pendingMdPath = join102(runDir, `${runId}.md`);
|
|
161917
161884
|
let logPath;
|
|
161918
161885
|
let record = { kind: "missing" };
|
|
161919
161886
|
let offset = 0;
|