@gobing-ai/spur 0.3.11 → 0.3.13
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/package.json +1 -1
- package/spur.js +181 -20
- package/web/_astro/{BoardApp.DDpBMmZh.js → BoardApp.CqBOrPzf.js} +101 -101
- package/web/_astro/BoardApp.XG2FoRlA.js +1 -0
- package/web/_astro/{TaskDetail.BuXwnD5s.js → TaskDetail.BmxycoeS.js} +1 -1
- package/web/_astro/{arc.ROFFfvlC.js → arc.-ZkWbWcN.js} +1 -1
- package/web/_astro/{architectureDiagram-3BPJPVTR.BF8jS1C7.js → architectureDiagram-3BPJPVTR.fJ8ETc6n.js} +1 -1
- package/web/_astro/{blockDiagram-GPEHLZMM.BAZd0UR-.js → blockDiagram-GPEHLZMM.CdhoveXr.js} +1 -1
- package/web/_astro/{c4Diagram-AAUBKEIU.BQiuu65q.js → c4Diagram-AAUBKEIU.CWv7Jy0p.js} +1 -1
- package/web/_astro/channel.BrBVc6tg.js +1 -0
- package/web/_astro/{chunk-2J33WTMH.CaZ7Cj2C.js → chunk-2J33WTMH.CvYWDwal.js} +1 -1
- package/web/_astro/{chunk-4BX2VUAB.Dl95s7jh.js → chunk-4BX2VUAB.BVp_y1oc.js} +1 -1
- package/web/_astro/{chunk-55IACEB6.BS6cwIjQ.js → chunk-55IACEB6.CQCviGQk.js} +1 -1
- package/web/_astro/{chunk-727SXJPM.BodG8be9.js → chunk-727SXJPM.B3E6Bisv.js} +1 -1
- package/web/_astro/{chunk-AQP2D5EJ.D7bElS24.js → chunk-AQP2D5EJ.BvWfYj9E.js} +1 -1
- package/web/_astro/{chunk-FMBD7UC4.B4QCK-dI.js → chunk-FMBD7UC4.DcLP2Ypw.js} +1 -1
- package/web/_astro/{chunk-ND2GUHAM.X1msYFei.js → chunk-ND2GUHAM.DvXZJEqI.js} +1 -1
- package/web/_astro/{chunk-QZHKN3VN.B2c4-Dz1.js → chunk-QZHKN3VN.D4rK2HTI.js} +1 -1
- package/web/_astro/{classDiagram-4FO5ZUOK.b8-sWDxa.js → classDiagram-4FO5ZUOK.DSCYtdVf.js} +1 -1
- package/web/_astro/{classDiagram-v2-Q7XG4LA2.b8-sWDxa.js → classDiagram-v2-Q7XG4LA2.DSCYtdVf.js} +1 -1
- package/web/_astro/{cose-bilkent-S5V4N54A.CT4xXx1C.js → cose-bilkent-S5V4N54A.Bf7HMV4Q.js} +1 -1
- package/web/_astro/{dagre-BM42HDAG.DAukBvJI.js → dagre-BM42HDAG.DsBoJTBN.js} +1 -1
- package/web/_astro/{diagram-2AECGRRQ.iAkt-am3.js → diagram-2AECGRRQ.BMVxdKV3.js} +1 -1
- package/web/_astro/{diagram-5GNKFQAL.BbKJ0fPo.js → diagram-5GNKFQAL.BllW5zst.js} +1 -1
- package/web/_astro/{diagram-KO2AKTUF.DmAKP2Me.js → diagram-KO2AKTUF.CNHoSD6q.js} +1 -1
- package/web/_astro/{diagram-LMA3HP47.CJzAaFxI.js → diagram-LMA3HP47.DZT6otFZ.js} +1 -1
- package/web/_astro/{diagram-OG6HWLK6.z2Yhlt_S.js → diagram-OG6HWLK6.EwFWHBRO.js} +1 -1
- package/web/_astro/{erDiagram-TEJ5UH35.C1inBaHt.js → erDiagram-TEJ5UH35.CaXPcR5W.js} +1 -1
- package/web/_astro/{flowDiagram-I6XJVG4X.D6y2SP1V.js → flowDiagram-I6XJVG4X.FEcfML44.js} +1 -1
- package/web/_astro/{ganttDiagram-6RSMTGT7.BWgsQaJO.js → ganttDiagram-6RSMTGT7.Bs7t3AQM.js} +1 -1
- package/web/_astro/{gitGraphDiagram-PVQCEYII.So8AZT3S.js → gitGraphDiagram-PVQCEYII.DmxLV9JC.js} +1 -1
- package/web/_astro/index.BOgTYz24.css +1 -0
- package/web/_astro/{infoDiagram-5YYISTIA.alTk5ecq.js → infoDiagram-5YYISTIA.E7_NrFUE.js} +1 -1
- package/web/_astro/{ishikawaDiagram-YF4QCWOH.X82FajaS.js → ishikawaDiagram-YF4QCWOH.Bs5Q4r-U.js} +1 -1
- package/web/_astro/{journeyDiagram-JHISSGLW.74uvU6ld.js → journeyDiagram-JHISSGLW.D3U7T1y1.js} +1 -1
- package/web/_astro/{kanban-definition-UN3LZRKU.D6BjF9UG.js → kanban-definition-UN3LZRKU.DfdWsO2E.js} +1 -1
- package/web/_astro/{linear.BpRIYzdb.js → linear.CAqUTUf3.js} +1 -1
- package/web/_astro/{mermaid.core.fdUDKzul.js → mermaid.core.ZlPUJ21K.js} +4 -4
- package/web/_astro/{mindmap-definition-RKZ34NQL.Df5SdMrH.js → mindmap-definition-RKZ34NQL.ClTJkcTG.js} +1 -1
- package/web/_astro/{pieDiagram-4H26LBE5.xNCa7QSz.js → pieDiagram-4H26LBE5.B9kJfBw_.js} +1 -1
- package/web/_astro/{quadrantDiagram-W4KKPZXB.C_3DSB9b.js → quadrantDiagram-W4KKPZXB.CtTJH5Fo.js} +1 -1
- package/web/_astro/{requirementDiagram-4Y6WPE33.C2MFScI8.js → requirementDiagram-4Y6WPE33.WVLq9rr2.js} +1 -1
- package/web/_astro/{sankeyDiagram-5OEKKPKP.DEy6YHUG.js → sankeyDiagram-5OEKKPKP.L_XUdyqe.js} +1 -1
- package/web/_astro/{sequenceDiagram-3UESZ5HK.CdQjeOkf.js → sequenceDiagram-3UESZ5HK.DCdtsEdj.js} +1 -1
- package/web/_astro/{stateDiagram-AJRCARHV.C-j_0D_L.js → stateDiagram-AJRCARHV.-Koi9kLh.js} +1 -1
- package/web/_astro/{stateDiagram-v2-BHNVJYJU.BMzYaYSQ.js → stateDiagram-v2-BHNVJYJU.DHXu_l8w.js} +1 -1
- package/web/_astro/{timeline-definition-PNZ67QCA.BaAa5dDC.js → timeline-definition-PNZ67QCA.Czotaq0j.js} +1 -1
- package/web/_astro/{vennDiagram-CIIHVFJN.GZL7T0cb.js → vennDiagram-CIIHVFJN.BfEI3zhc.js} +1 -1
- package/web/_astro/{wardley-L42UT6IY.STDdK5oO.js → wardley-L42UT6IY.C33mahwT.js} +1 -1
- package/web/_astro/{wardleyDiagram-YWT4CUSO.BJLlSFei.js → wardleyDiagram-YWT4CUSO.D8IzxB1w.js} +1 -1
- package/web/_astro/{xychartDiagram-2RQKCTM6.CRGBFqsD.js → xychartDiagram-2RQKCTM6.D-cfi9uT.js} +1 -1
- package/web/index.html +2 -2
- package/web/_astro/BoardApp.CXm9NttC.js +0 -1
- package/web/_astro/channel.e58tDmJn.js +0 -1
- package/web/_astro/index.BaoJPQzM.css +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gobing-ai/spur",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.13",
|
|
4
4
|
"description": "Spur CLI — local-first harness for mainstream coding agents: constraint checking, workflow orchestration, agent health, and history analytics. Bun-native; exposes the `spur` command.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"spur",
|
package/spur.js
CHANGED
|
@@ -52515,6 +52515,27 @@ class InboxRecentDao {
|
|
|
52515
52515
|
throw error51;
|
|
52516
52516
|
}
|
|
52517
52517
|
}
|
|
52518
|
+
async countReplies(ids) {
|
|
52519
|
+
const result = new Map;
|
|
52520
|
+
if (ids.length === 0)
|
|
52521
|
+
return result;
|
|
52522
|
+
const placeholders = ids.map((_, i2) => `?${i2 + 1}`).join(", ");
|
|
52523
|
+
try {
|
|
52524
|
+
const rows = await this.db.queryAll(`SELECT in_reply_to, COUNT(*) AS n
|
|
52525
|
+
FROM inbox_messages
|
|
52526
|
+
WHERE in_reply_to IS NOT NULL AND in_reply_to IN (${placeholders})
|
|
52527
|
+
GROUP BY in_reply_to`, ...ids);
|
|
52528
|
+
for (const row of rows) {
|
|
52529
|
+
result.set(row.in_reply_to, row.n);
|
|
52530
|
+
}
|
|
52531
|
+
} catch (error51) {
|
|
52532
|
+
if (error51 instanceof Error && error51.message.includes("no such table: inbox_messages")) {
|
|
52533
|
+
return result;
|
|
52534
|
+
}
|
|
52535
|
+
throw error51;
|
|
52536
|
+
}
|
|
52537
|
+
return result;
|
|
52538
|
+
}
|
|
52518
52539
|
}
|
|
52519
52540
|
|
|
52520
52541
|
// ../../packages/domain/src/schema/phase-runs.ts
|
|
@@ -61170,16 +61191,28 @@ class SupervisorService {
|
|
|
61170
61191
|
status: "running",
|
|
61171
61192
|
startedAt: new Date().toISOString(),
|
|
61172
61193
|
ringBuffer: frames,
|
|
61173
|
-
teamId
|
|
61194
|
+
teamId,
|
|
61195
|
+
agentType: spec.type
|
|
61174
61196
|
};
|
|
61175
61197
|
this.processes.set(agentId, { handle, entry });
|
|
61176
61198
|
this.restartAttempts.delete(agentId);
|
|
61177
61199
|
this.pipeStream(handle.stdout, "stdout", agentId, frames);
|
|
61178
61200
|
this.pipeStream(handle.stderr, "stderr", agentId, frames);
|
|
61179
|
-
this.emit("process.spawned", {
|
|
61201
|
+
this.emit("process.spawned", {
|
|
61202
|
+
agentId,
|
|
61203
|
+
pid,
|
|
61204
|
+
teamId,
|
|
61205
|
+
agentType: spec.type
|
|
61206
|
+
});
|
|
61180
61207
|
handle.exited.then(async (code) => {
|
|
61181
61208
|
entry.exitCode = code;
|
|
61182
|
-
this.emit("process.exited", {
|
|
61209
|
+
this.emit("process.exited", {
|
|
61210
|
+
agentId,
|
|
61211
|
+
pid,
|
|
61212
|
+
exitCode: code,
|
|
61213
|
+
teamId: entry.teamId ?? null,
|
|
61214
|
+
...entry.agentType ? { agentType: entry.agentType } : {}
|
|
61215
|
+
});
|
|
61183
61216
|
if (code === 0 || entry.status === "stopped") {
|
|
61184
61217
|
entry.status = "exited";
|
|
61185
61218
|
this.scheduleCleanup(agentId, entry);
|
|
@@ -61226,7 +61259,12 @@ class SupervisorService {
|
|
|
61226
61259
|
this.restartTimers.delete(agentId);
|
|
61227
61260
|
}
|
|
61228
61261
|
this.restartAttempts.delete(agentId);
|
|
61229
|
-
this.emit("process.stopped", {
|
|
61262
|
+
this.emit("process.stopped", {
|
|
61263
|
+
agentId,
|
|
61264
|
+
pid: proc.entry.pid,
|
|
61265
|
+
teamId: proc.entry.teamId ?? null,
|
|
61266
|
+
...proc.entry.agentType ? { agentType: proc.entry.agentType } : {}
|
|
61267
|
+
});
|
|
61230
61268
|
}
|
|
61231
61269
|
async startAutostart(ids) {
|
|
61232
61270
|
const results = [];
|
|
@@ -61369,9 +61407,11 @@ function safeStringify(event2) {
|
|
|
61369
61407
|
}
|
|
61370
61408
|
function extractSystemEventActor(event2) {
|
|
61371
61409
|
if (event2 && typeof event2 === "object") {
|
|
61372
|
-
const
|
|
61373
|
-
if (typeof
|
|
61374
|
-
return
|
|
61410
|
+
const obj = event2;
|
|
61411
|
+
if (typeof obj.actor === "string" && obj.actor.length > 0)
|
|
61412
|
+
return obj.actor;
|
|
61413
|
+
if (typeof obj.agentId === "string" && obj.agentId.length > 0)
|
|
61414
|
+
return obj.agentId;
|
|
61375
61415
|
}
|
|
61376
61416
|
return null;
|
|
61377
61417
|
}
|
|
@@ -61442,6 +61482,10 @@ function hasPopulatedPriorityTable(body) {
|
|
|
61442
61482
|
}
|
|
61443
61483
|
return false;
|
|
61444
61484
|
}
|
|
61485
|
+
function extractReviewSectionBody(markdown) {
|
|
61486
|
+
const match = markdown.match(/^### Review[ \t]*\n([\s\S]*?)(?=^### |Z)/m);
|
|
61487
|
+
return match ? match[1] ?? "" : null;
|
|
61488
|
+
}
|
|
61445
61489
|
function isReviewScaffold(body) {
|
|
61446
61490
|
if (isPlaceholderBody(body))
|
|
61447
61491
|
return true;
|
|
@@ -63016,16 +63060,50 @@ class TeamService {
|
|
|
63016
63060
|
async listRecent(limit = 50) {
|
|
63017
63061
|
const dao2 = await this.inboxRecentDao();
|
|
63018
63062
|
const rows = await dao2.listRecent(limit);
|
|
63063
|
+
if (rows.length === 0) {
|
|
63064
|
+
return { messages: [], count: 0 };
|
|
63065
|
+
}
|
|
63066
|
+
const [teams, specs] = await Promise.all([this.listTeams(), this.listAgentSpecs()]);
|
|
63067
|
+
const identityById = new Map;
|
|
63068
|
+
for (const team of teams) {
|
|
63069
|
+
for (const spec of team.specs) {
|
|
63070
|
+
identityById.set(spec.id, {
|
|
63071
|
+
agentId: spec.id,
|
|
63072
|
+
teamId: team.teamId,
|
|
63073
|
+
teamName: team.name,
|
|
63074
|
+
memberLabel: spec.name,
|
|
63075
|
+
agentType: spec.type
|
|
63076
|
+
});
|
|
63077
|
+
}
|
|
63078
|
+
}
|
|
63079
|
+
for (const spec of specs) {
|
|
63080
|
+
if (!identityById.has(spec.id)) {
|
|
63081
|
+
identityById.set(spec.id, {
|
|
63082
|
+
agentId: spec.id,
|
|
63083
|
+
memberLabel: spec.name,
|
|
63084
|
+
agentType: spec.type
|
|
63085
|
+
});
|
|
63086
|
+
}
|
|
63087
|
+
}
|
|
63088
|
+
const replyCounts = await dao2.countReplies(rows.map((r) => r.id));
|
|
63089
|
+
const resolveEndpoint = (agentId) => identityById.get(agentId) ?? { agentId };
|
|
63019
63090
|
return {
|
|
63020
|
-
messages: rows.map((row) =>
|
|
63021
|
-
|
|
63022
|
-
|
|
63023
|
-
|
|
63024
|
-
|
|
63025
|
-
|
|
63026
|
-
|
|
63027
|
-
|
|
63028
|
-
|
|
63091
|
+
messages: rows.map((row) => {
|
|
63092
|
+
const replyCount = replyCounts.get(row.id) ?? 0;
|
|
63093
|
+
return {
|
|
63094
|
+
id: row.id,
|
|
63095
|
+
fromId: row.from_id,
|
|
63096
|
+
toId: row.to_id,
|
|
63097
|
+
body: row.body,
|
|
63098
|
+
status: row.status,
|
|
63099
|
+
createdAt: new Date(row.created_at).toISOString(),
|
|
63100
|
+
inReplyTo: row.in_reply_to,
|
|
63101
|
+
...row.from_id !== null ? { from: resolveEndpoint(row.from_id) } : {},
|
|
63102
|
+
to: resolveEndpoint(row.to_id),
|
|
63103
|
+
hasReply: replyCount > 0,
|
|
63104
|
+
replyCount
|
|
63105
|
+
};
|
|
63106
|
+
}),
|
|
63029
63107
|
count: rows.length
|
|
63030
63108
|
};
|
|
63031
63109
|
}
|
|
@@ -64805,6 +64883,8 @@ var init_workflow_service = __esm(() => {
|
|
|
64805
64883
|
});
|
|
64806
64884
|
|
|
64807
64885
|
// ../../packages/app/src/workflow/lifecycle-adapter.ts
|
|
64886
|
+
import { readFile as readFile3 } from "fs/promises";
|
|
64887
|
+
|
|
64808
64888
|
class LifecycleAdapter {
|
|
64809
64889
|
opts;
|
|
64810
64890
|
workflowCache;
|
|
@@ -64840,6 +64920,15 @@ class LifecycleAdapter {
|
|
|
64840
64920
|
};
|
|
64841
64921
|
}
|
|
64842
64922
|
}
|
|
64923
|
+
const reviewDenial = await this.checkReviewReadyForDone(ref);
|
|
64924
|
+
if (reviewDenial !== null) {
|
|
64925
|
+
return {
|
|
64926
|
+
allowed: false,
|
|
64927
|
+
from: currentStatus,
|
|
64928
|
+
to,
|
|
64929
|
+
report: reviewDenial
|
|
64930
|
+
};
|
|
64931
|
+
}
|
|
64843
64932
|
}
|
|
64844
64933
|
const existing = await svc.findRunByKey(profile.workflowName, externalKey);
|
|
64845
64934
|
const runId = existing?.id ?? createId("run");
|
|
@@ -64890,6 +64979,27 @@ class LifecycleAdapter {
|
|
|
64890
64979
|
vars: { ...workflow.vars, [this.opts.profile.varKey]: id, spurBin: this.opts.spurBin }
|
|
64891
64980
|
};
|
|
64892
64981
|
}
|
|
64982
|
+
async checkReviewReadyForDone(ref) {
|
|
64983
|
+
const markdown = await this.loadTaskMarkdown(ref);
|
|
64984
|
+
if (markdown === null) {
|
|
64985
|
+
return null;
|
|
64986
|
+
}
|
|
64987
|
+
const reviewBody = extractReviewSectionBody(markdown);
|
|
64988
|
+
if (reviewBody === null || !hasPopulatedPriorityTable(reviewBody)) {
|
|
64989
|
+
return `Review L3 gate failed for task ${ref.id}: ### Review must contain a populated ` + `P1\u2013P4 priority findings table before testing\u2192done (strict-core). ` + `Author the table via /sp:dev-review, then re-run the done transition.`;
|
|
64990
|
+
}
|
|
64991
|
+
return null;
|
|
64992
|
+
}
|
|
64993
|
+
async loadTaskMarkdown(ref) {
|
|
64994
|
+
if (this.opts.readTaskMarkdown) {
|
|
64995
|
+
return this.opts.readTaskMarkdown(ref);
|
|
64996
|
+
}
|
|
64997
|
+
try {
|
|
64998
|
+
return await readFile3(ref.filePath, "utf8");
|
|
64999
|
+
} catch {
|
|
65000
|
+
return null;
|
|
65001
|
+
}
|
|
65002
|
+
}
|
|
64893
65003
|
formatDenial(detail, guardReport) {
|
|
64894
65004
|
if (guardReport === undefined || guardReport === null)
|
|
64895
65005
|
return detail;
|
|
@@ -64901,6 +65011,7 @@ var TASK_LIFECYCLE_PROFILE, FEATURE_LIFECYCLE_PROFILE;
|
|
|
64901
65011
|
var init_lifecycle_adapter = __esm(() => {
|
|
64902
65012
|
init_src2();
|
|
64903
65013
|
init_dist8();
|
|
65014
|
+
init_task_check();
|
|
64904
65015
|
TASK_LIFECYCLE_PROFILE = {
|
|
64905
65016
|
linkKind: "lifecycle",
|
|
64906
65017
|
workflowName: "task-lifecycle",
|
|
@@ -64965,7 +65076,9 @@ __export(exports_src2, {
|
|
|
64965
65076
|
normalizeSystemEventPayload: () => normalizeSystemEventPayload,
|
|
64966
65077
|
hitlConfirmDefault: () => hitlConfirmDefault,
|
|
64967
65078
|
hitlAutoApproveEnabled: () => hitlAutoApproveEnabled,
|
|
65079
|
+
hasPopulatedPriorityTable: () => hasPopulatedPriorityTable,
|
|
64968
65080
|
extractSystemEventActor: () => extractSystemEventActor,
|
|
65081
|
+
extractReviewSectionBody: () => extractReviewSectionBody,
|
|
64969
65082
|
deriveVerdict: () => deriveVerdict,
|
|
64970
65083
|
createPsProcessInspector: () => createPsProcessInspector,
|
|
64971
65084
|
clampToolUseLimit: () => clampToolUseLimit,
|
|
@@ -73009,7 +73122,7 @@ import { join as join15, resolve as resolve5 } from "path";
|
|
|
73009
73122
|
var CLI_CONFIG = {
|
|
73010
73123
|
binaryName: "spur",
|
|
73011
73124
|
binaryLabel: "spur",
|
|
73012
|
-
binaryVersion: "0.3.
|
|
73125
|
+
binaryVersion: "0.3.13",
|
|
73013
73126
|
configDir: ".spur",
|
|
73014
73127
|
configFile: ".spur/config.yaml",
|
|
73015
73128
|
databaseFile: ".spur/spur.db"
|
|
@@ -79036,9 +79149,11 @@ init_event_names();
|
|
|
79036
79149
|
// ../server/src/modules/events/index.ts
|
|
79037
79150
|
function extractSystemEventActor2(event2) {
|
|
79038
79151
|
if (event2 && typeof event2 === "object") {
|
|
79039
|
-
const
|
|
79040
|
-
if (typeof
|
|
79041
|
-
return
|
|
79152
|
+
const obj = event2;
|
|
79153
|
+
if (typeof obj.actor === "string" && obj.actor.length > 0)
|
|
79154
|
+
return obj.actor;
|
|
79155
|
+
if (typeof obj.agentId === "string" && obj.agentId.length > 0)
|
|
79156
|
+
return obj.agentId;
|
|
79042
79157
|
}
|
|
79043
79158
|
return null;
|
|
79044
79159
|
}
|
|
@@ -79650,6 +79765,7 @@ var featureModule = {
|
|
|
79650
79765
|
};
|
|
79651
79766
|
|
|
79652
79767
|
// ../server/src/modules/health/index.ts
|
|
79768
|
+
import { basename as basename3 } from "path";
|
|
79653
79769
|
var startedAt = Date.now();
|
|
79654
79770
|
var healthModule = {
|
|
79655
79771
|
name: "health",
|
|
@@ -79674,6 +79790,9 @@ var healthModule = {
|
|
|
79674
79790
|
}
|
|
79675
79791
|
return c3.json({ status: "error", db: "unreachable" }, 503);
|
|
79676
79792
|
});
|
|
79793
|
+
app.get("/api/project", (c3) => {
|
|
79794
|
+
return c3.json({ name: ctx ? basename3(ctx.cwd) : null });
|
|
79795
|
+
});
|
|
79677
79796
|
}
|
|
79678
79797
|
};
|
|
79679
79798
|
|
|
@@ -80174,11 +80293,13 @@ var teamModule = {
|
|
|
80174
80293
|
name: team.name,
|
|
80175
80294
|
members: team.specs.map((spec) => {
|
|
80176
80295
|
const proc = processes.find((p2) => p2.agentId === spec.id);
|
|
80296
|
+
const configModel = spec.config?.model;
|
|
80177
80297
|
return {
|
|
80178
80298
|
id: spec.id,
|
|
80179
80299
|
type: spec.type,
|
|
80180
80300
|
status: proc?.status ?? "unknown",
|
|
80181
80301
|
autoStart: spec.autoStart === true,
|
|
80302
|
+
...typeof configModel === "string" && configModel.length > 0 ? { model: configModel } : {},
|
|
80182
80303
|
...proc?.pid !== undefined ? { pid: proc.pid } : {}
|
|
80183
80304
|
};
|
|
80184
80305
|
})
|
|
@@ -83810,6 +83931,46 @@ ${result.wbs} (${result.status}): ${result.pass ? "PASS" : "FAIL"}`);
|
|
|
83810
83931
|
context4.setExitCode(1);
|
|
83811
83932
|
}
|
|
83812
83933
|
});
|
|
83934
|
+
task.command("run-link").summary("Record a pipeline provenance link for a task (used by --next auto chains to satisfy testing\u2192done guard).").argument("<wbs>", "Task WBS number").option("--source <source>", "Link source identifier (e.g. next-auto)", "chain").option("--run-id <id>", "Explicit run_id (auto-generated when omitted)").option("--json", "Output machine-readable JSON").action(async (wbs, options) => {
|
|
83935
|
+
try {
|
|
83936
|
+
const db2 = await context4.getDb();
|
|
83937
|
+
const dao2 = new TaskRunLinkDao(db2);
|
|
83938
|
+
const existing = await dao2.listByWbs(wbs, 20);
|
|
83939
|
+
if (existing.some((row) => row.kind === "pipeline")) {
|
|
83940
|
+
const entry = existing.find((row) => row.kind === "pipeline");
|
|
83941
|
+
if (!entry) {
|
|
83942
|
+
context4.output.error(`Pipeline link lookup inconsistent for ${wbs}`);
|
|
83943
|
+
context4.setExitCode(1);
|
|
83944
|
+
return;
|
|
83945
|
+
}
|
|
83946
|
+
const result2 = {
|
|
83947
|
+
id: entry.id,
|
|
83948
|
+
wbs: entry.wbs,
|
|
83949
|
+
runId: entry.run_id,
|
|
83950
|
+
kind: entry.kind,
|
|
83951
|
+
existed: true
|
|
83952
|
+
};
|
|
83953
|
+
if (options.json) {
|
|
83954
|
+
context4.output.write(toJson2(result2));
|
|
83955
|
+
} else {
|
|
83956
|
+
context4.output.write(`Pipeline run-link already exists for ${wbs} (${entry.id}). Skipped.`);
|
|
83957
|
+
}
|
|
83958
|
+
return;
|
|
83959
|
+
}
|
|
83960
|
+
const id = createId("trl");
|
|
83961
|
+
const runId = options.runId ?? `chain:${options.source}:${wbs}:${Date.now()}`;
|
|
83962
|
+
await dao2.insert({ id, wbs, run_id: runId, kind: "pipeline", created_at: new Date().toISOString() });
|
|
83963
|
+
const result = { id, wbs, runId, kind: "pipeline" };
|
|
83964
|
+
if (options.json) {
|
|
83965
|
+
context4.output.write(toJson2(result));
|
|
83966
|
+
} else {
|
|
83967
|
+
context4.output.write(`Recorded pipeline run-link ${id} for task ${wbs} (source: ${options.source})`);
|
|
83968
|
+
}
|
|
83969
|
+
} catch (err) {
|
|
83970
|
+
context4.output.error(String(err));
|
|
83971
|
+
context4.setExitCode(1);
|
|
83972
|
+
}
|
|
83973
|
+
});
|
|
83813
83974
|
}
|
|
83814
83975
|
async function makeService2(context4, folderOverride, noLifecycle = false) {
|
|
83815
83976
|
const foldersConfig = (await resolvePlanningFolders(context4.fs)).foldersConfig;
|