@gobing-ai/spur 0.4.19 → 0.4.20
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/config.example.yaml +4 -0
- package/package.json +9 -9
- package/plugins/sp/plugin.json +1 -1
- package/plugins/sp/scripts/hooks/fleet-guest-stop.ts +32 -12
- package/plugins/sp/skills/fleet-join/SKILL.md +7 -4
- package/plugins/sp/skills/spur-cli/references/agent.md +6 -4
- package/schemas/spur-config.schema.json +5 -0
- package/spur.js +1810 -1684
- package/web/_astro/{BoardApp.DY8fwPfw.js → BoardApp.BDo7Cplz.js} +141 -141
- package/web/_astro/{BoardApp.CNF3sCnm.js → BoardApp.uR1DxOv6.js} +1 -1
- package/web/_astro/{TaskDetail.8z3DgYux.js → TaskDetail.BiGHeRcp.js} +1 -1
- package/web/_astro/{arc.CFEYbpFT.js → arc.BLw5ykEN.js} +1 -1
- package/web/_astro/{architectureDiagram-3BPJPVTR.3eDvG_X8.js → architectureDiagram-3BPJPVTR.C2BEgSVb.js} +1 -1
- package/web/_astro/{blockDiagram-GPEHLZMM.CMbLzMpk.js → blockDiagram-GPEHLZMM.D8G9DLH3.js} +1 -1
- package/web/_astro/{c4Diagram-AAUBKEIU.2lZW8nZ-.js → c4Diagram-AAUBKEIU.dU7HR9k5.js} +1 -1
- package/web/_astro/channel.DSbvfOEf.js +1 -0
- package/web/_astro/{chunk-2J33WTMH.BtM1eI3D.js → chunk-2J33WTMH.5BIk-HVp.js} +1 -1
- package/web/_astro/{chunk-4BX2VUAB.CKeckzAf.js → chunk-4BX2VUAB.BVIKX24s.js} +1 -1
- package/web/_astro/{chunk-55IACEB6.BBmHet8U.js → chunk-55IACEB6.Bz2tdkGM.js} +1 -1
- package/web/_astro/{chunk-727SXJPM.Bi8v7M_x.js → chunk-727SXJPM.R0d_FlTN.js} +1 -1
- package/web/_astro/{chunk-AQP2D5EJ.DOcBJyb6.js → chunk-AQP2D5EJ.C2kK8Ok-.js} +1 -1
- package/web/_astro/{chunk-FMBD7UC4.C-EdUBD7.js → chunk-FMBD7UC4.COAncjFn.js} +1 -1
- package/web/_astro/{chunk-ND2GUHAM.IqTyWQhk.js → chunk-ND2GUHAM.CeNg5VSN.js} +1 -1
- package/web/_astro/{chunk-QZHKN3VN.a2DShvIL.js → chunk-QZHKN3VN.DRGDDzrj.js} +1 -1
- package/web/_astro/{classDiagram-4FO5ZUOK.CQBnvIjk.js → classDiagram-4FO5ZUOK.DmM59Lgb.js} +1 -1
- package/web/_astro/{classDiagram-v2-Q7XG4LA2.CQBnvIjk.js → classDiagram-v2-Q7XG4LA2.DmM59Lgb.js} +1 -1
- package/web/_astro/{cose-bilkent-S5V4N54A.BW9Ryz7c.js → cose-bilkent-S5V4N54A.BjKuSb1i.js} +1 -1
- package/web/_astro/{cynefin-OW5HDTMX.BPhLeSep.js → cynefin-OW5HDTMX.DQevIJEc.js} +1 -1
- package/web/_astro/{dagre-BM42HDAG._oD9MsDB.js → dagre-BM42HDAG.CqOK1C5k.js} +1 -1
- package/web/_astro/{diagram-2AECGRRQ.iciSOlxY.js → diagram-2AECGRRQ.B7i8Xbcc.js} +1 -1
- package/web/_astro/{diagram-5GNKFQAL.ZzqeC45Q.js → diagram-5GNKFQAL.DXGFk2rU.js} +1 -1
- package/web/_astro/{diagram-KO2AKTUF.CMqWGOYB.js → diagram-KO2AKTUF.BDC9f2pC.js} +1 -1
- package/web/_astro/{diagram-LMA3HP47.B5MPN99W.js → diagram-LMA3HP47.D-Mrwnmt.js} +1 -1
- package/web/_astro/{diagram-OG6HWLK6.Zgi-ANAF.js → diagram-OG6HWLK6.XXGX1BQ_.js} +1 -1
- package/web/_astro/{erDiagram-TEJ5UH35.Bd5aeIyR.js → erDiagram-TEJ5UH35.DahWkk7M.js} +1 -1
- package/web/_astro/{flowDiagram-I6XJVG4X.B8LBo_PH.js → flowDiagram-I6XJVG4X.DaG5HfPs.js} +1 -1
- package/web/_astro/{ganttDiagram-6RSMTGT7.Di2HLHk4.js → ganttDiagram-6RSMTGT7.DZe_CQWm.js} +1 -1
- package/web/_astro/{gitGraphDiagram-PVQCEYII.BqDuU6fc.js → gitGraphDiagram-PVQCEYII.C4zSw3kS.js} +1 -1
- package/web/_astro/{infoDiagram-5YYISTIA.C_hw98uF.js → infoDiagram-5YYISTIA.CmaEULgT.js} +1 -1
- package/web/_astro/{ishikawaDiagram-YF4QCWOH.uK7JK9Kk.js → ishikawaDiagram-YF4QCWOH.DKKhZJ98.js} +1 -1
- package/web/_astro/{journeyDiagram-JHISSGLW.D03MNAmn.js → journeyDiagram-JHISSGLW.B6d7TuYQ.js} +1 -1
- package/web/_astro/{kanban-definition-UN3LZRKU.DuiDtKhq.js → kanban-definition-UN3LZRKU.xiG_jH03.js} +1 -1
- package/web/_astro/{linear.Der6_z5m.js → linear.DsbUmqlh.js} +1 -1
- package/web/_astro/{mermaid.core.CaxkEgwh.js → mermaid.core.CihlV6Wy.js} +4 -4
- package/web/_astro/{mindmap-definition-RKZ34NQL.DecT082u.js → mindmap-definition-RKZ34NQL.Cm2UvqJQ.js} +1 -1
- package/web/_astro/{pieDiagram-4H26LBE5.DuAEfZXh.js → pieDiagram-4H26LBE5.COyvnb_Q.js} +1 -1
- package/web/_astro/{quadrantDiagram-W4KKPZXB.CWXczU1k.js → quadrantDiagram-W4KKPZXB.BdaJr8EI.js} +1 -1
- package/web/_astro/{requirementDiagram-4Y6WPE33.BsAHUsR6.js → requirementDiagram-4Y6WPE33.aS-qeofA.js} +1 -1
- package/web/_astro/{sankeyDiagram-5OEKKPKP.ghBqvXN8.js → sankeyDiagram-5OEKKPKP.BxaOWF5N.js} +1 -1
- package/web/_astro/{sequenceDiagram-3UESZ5HK.BKbwb7ad.js → sequenceDiagram-3UESZ5HK.CfZPPWA1.js} +1 -1
- package/web/_astro/{stateDiagram-AJRCARHV.D5geOKAc.js → stateDiagram-AJRCARHV.D6VXy0-M.js} +1 -1
- package/web/_astro/{stateDiagram-v2-BHNVJYJU.Dc4Sjs4R.js → stateDiagram-v2-BHNVJYJU.DatbDxXn.js} +1 -1
- package/web/_astro/{timeline-definition-PNZ67QCA.ve2dW2tR.js → timeline-definition-PNZ67QCA.BxQ7-VRY.js} +1 -1
- package/web/_astro/{vennDiagram-CIIHVFJN.BdJumy3f.js → vennDiagram-CIIHVFJN.BJny6MAy.js} +1 -1
- package/web/_astro/{wardleyDiagram-YWT4CUSO.X8HgtN8V.js → wardleyDiagram-YWT4CUSO.gzTiYN-4.js} +1 -1
- package/web/_astro/{xychartDiagram-2RQKCTM6.C30VVwQ6.js → xychartDiagram-2RQKCTM6.DTR_zwDh.js} +1 -1
- package/web/index.html +1 -1
- package/web/_astro/channel.CdQ8ZO1m.js +0 -1
|
@@ -58,6 +58,10 @@ bootstrap:
|
|
|
58
58
|
command: >-
|
|
59
59
|
spur --no-logo history import --source all &&
|
|
60
60
|
spur --no-logo workflow run config/workflows/history-anatomy.yaml --quiet
|
|
61
|
+
# Without this the chain inherits the 10-minute scheduler default and dies mid-validate.
|
|
62
|
+
# 1h45m: enrich (45-52 min on a slow executor) + validate, still below the server
|
|
63
|
+
# queue's 2h visibility timeout.
|
|
64
|
+
timeoutMs: 6300000
|
|
61
65
|
|
|
62
66
|
# ── Spur app section (validated by spurConfigSchema in
|
|
63
67
|
# @gobing-ai/spur-config) ──
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gobing-ai/spur",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.20",
|
|
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",
|
|
@@ -58,14 +58,14 @@
|
|
|
58
58
|
},
|
|
59
59
|
"devDependencies": {
|
|
60
60
|
"@commander-js/extra-typings": "^14.0.0",
|
|
61
|
-
"@gobing-ai/ts-db": "^0.5.
|
|
62
|
-
"@gobing-ai/ts-ai-runner": "^0.5.
|
|
63
|
-
"@gobing-ai/ts-dual-workflow-engine": "^0.5.
|
|
64
|
-
"@gobing-ai/ts-infra": "^0.5.
|
|
65
|
-
"@gobing-ai/ts-llm-jsonl-importer": "^0.5.
|
|
66
|
-
"@gobing-ai/ts-rule-engine": "^0.5.
|
|
67
|
-
"@gobing-ai/ts-runtime": "^0.5.
|
|
68
|
-
"@gobing-ai/ts-utils": "^0.5.
|
|
61
|
+
"@gobing-ai/ts-db": "^0.5.33",
|
|
62
|
+
"@gobing-ai/ts-ai-runner": "^0.5.33",
|
|
63
|
+
"@gobing-ai/ts-dual-workflow-engine": "^0.5.33",
|
|
64
|
+
"@gobing-ai/ts-infra": "^0.5.33",
|
|
65
|
+
"@gobing-ai/ts-llm-jsonl-importer": "^0.5.33",
|
|
66
|
+
"@gobing-ai/ts-rule-engine": "^0.5.33",
|
|
67
|
+
"@gobing-ai/ts-runtime": "^0.5.33",
|
|
68
|
+
"@gobing-ai/ts-utils": "^0.5.33",
|
|
69
69
|
"@types/bun": "1.4.3",
|
|
70
70
|
"@types/figlet": "^1.7.0",
|
|
71
71
|
"@types/node-notifier": "8.0.5",
|
package/plugins/sp/plugin.json
CHANGED
|
@@ -37,7 +37,7 @@ export const GUEST_STOP_PROBE_MS = 2000;
|
|
|
37
37
|
/** How many pending messages the delivered reason names. */
|
|
38
38
|
export const GUEST_STOP_MAX_MESSAGES = 5;
|
|
39
39
|
|
|
40
|
-
/**
|
|
40
|
+
/** Preview length for legacy queued snapshots; an owned handoff carries the full body. */
|
|
41
41
|
export const GUEST_STOP_BODY_CHARS = 140;
|
|
42
42
|
|
|
43
43
|
/** Hard per-invocation timeout for a `spur` child the hook shells out to. */
|
|
@@ -108,20 +108,41 @@ export function stopDecisionFor(
|
|
|
108
108
|
} catch {
|
|
109
109
|
return null;
|
|
110
110
|
}
|
|
111
|
-
const
|
|
111
|
+
const payload = rows !== null && typeof rows === 'object' && 'data' in rows ? rows.data : rows;
|
|
112
|
+
const list = Array.isArray(payload)
|
|
113
|
+
? payload
|
|
114
|
+
: payload !== null && typeof payload === 'object' && 'messages' in payload && Array.isArray(payload.messages)
|
|
115
|
+
? payload.messages
|
|
116
|
+
: undefined;
|
|
112
117
|
if (list === undefined) return null;
|
|
118
|
+
const handedOff = payload !== null && typeof payload === 'object' && 'pending' in payload;
|
|
113
119
|
const pending = list
|
|
114
|
-
.
|
|
115
|
-
.
|
|
116
|
-
.
|
|
120
|
+
.filter((row) => row !== null && typeof row === 'object')
|
|
121
|
+
.map((row) => row as { id?: unknown; from_id?: unknown; fromId?: unknown; body?: unknown; status?: unknown })
|
|
122
|
+
.filter(
|
|
123
|
+
(row) =>
|
|
124
|
+
typeof row.body === 'string' &&
|
|
125
|
+
(row.status === undefined ||
|
|
126
|
+
(handedOff
|
|
127
|
+
? ['queued', 'claimed', 'delivered'].includes(String(row.status))
|
|
128
|
+
: row.status === 'queued')),
|
|
129
|
+
)
|
|
130
|
+
.slice(0, handedOff ? undefined : maxMessages);
|
|
117
131
|
if (pending.length === 0) return null;
|
|
118
132
|
const lines = pending.map((row) => {
|
|
119
133
|
const from =
|
|
120
134
|
typeof row.from_id === 'string' ? row.from_id : typeof row.fromId === 'string' ? row.fromId : 'fleet';
|
|
121
|
-
const body =
|
|
122
|
-
.
|
|
123
|
-
.
|
|
124
|
-
|
|
135
|
+
const body = handedOff
|
|
136
|
+
? String(row.body ?? '')
|
|
137
|
+
: String(row.body ?? '')
|
|
138
|
+
.replace(/\s+/g, ' ')
|
|
139
|
+
.trim();
|
|
140
|
+
const id = typeof row.id === 'string' ? ` [${row.id}]` : '';
|
|
141
|
+
const preview =
|
|
142
|
+
!handedOff && body.length > GUEST_STOP_BODY_CHARS
|
|
143
|
+
? `${body.slice(0, GUEST_STOP_BODY_CHARS)}… (read the full message with spur message show ${String(row.id ?? '<msgId>')} --json)`
|
|
144
|
+
: body;
|
|
145
|
+
return `- ${from}${id}: ${preview}`;
|
|
125
146
|
});
|
|
126
147
|
const reason = `Pending fleet messages for ${guestId}:\n${lines.join('\n')}\n\nHandle them, then \`spur message reply <msgId> …\` and continue.`;
|
|
127
148
|
return JSON.stringify({ decision: 'block', reason });
|
|
@@ -155,11 +176,10 @@ export function runFleetGuestStop(payload: GuestStopPayload, deps: GuestStopDeps
|
|
|
155
176
|
guest.id,
|
|
156
177
|
'--timeout',
|
|
157
178
|
String(deps.probeMs ?? GUEST_STOP_PROBE_MS),
|
|
179
|
+
'--json',
|
|
158
180
|
]);
|
|
159
181
|
if (probe.status !== 0) return null;
|
|
160
|
-
|
|
161
|
-
if (inbox.status !== 0) return null;
|
|
162
|
-
return stopDecisionFor(guest.id, inbox.stdout, deps.maxMessages);
|
|
182
|
+
return stopDecisionFor(guest.id, probe.stdout, deps.maxMessages);
|
|
163
183
|
} catch {
|
|
164
184
|
return null;
|
|
165
185
|
}
|
|
@@ -28,13 +28,16 @@ find this session, though the `wait --inbox` loop still works.
|
|
|
28
28
|
|
|
29
29
|
## Pull loop
|
|
30
30
|
|
|
31
|
-
`spur agent wait --inbox <id> --timeout 540000` returns **0**
|
|
32
|
-
|
|
31
|
+
`spur agent wait --inbox <id> --timeout 540000 --json` returns **0** with up to five claimed messages
|
|
32
|
+
in `{ id, pending, messages }`, settling their delivery after the output handoff. It returns **1**
|
|
33
|
+
on timeout. An output failure releases the exact claims for retry; inbox listing itself never
|
|
34
|
+
consumes messages. Each call heartbeats the lease, so a looping session stays joined. The Bash tool's
|
|
33
35
|
own ~10-minute limit is why the timeout stays under it: one bounded call per turn, re-armed after
|
|
34
36
|
the work is done.
|
|
35
37
|
|
|
36
|
-
1. `spur agent wait --inbox <id> --timeout 540000 --json` → exit 0
|
|
37
|
-
2. `spur message
|
|
38
|
+
1. `spur agent wait --inbox <id> --timeout 540000 --json` → exit 0 hands this session `messages`.
|
|
39
|
+
2. Read the returned messages, including their IDs. `spur message show <id> --json` can reread one;
|
|
40
|
+
`spur message inbox --agent <id> --json` is a non-consuming historical view.
|
|
38
41
|
3. Do the work the message asks for.
|
|
39
42
|
4. `spur message reply <msgId> "<result>"` → reply to each one.
|
|
40
43
|
5. Re-arm: go back to step 1 while the operator wants this session in the fleet.
|
|
@@ -239,10 +239,12 @@ is refused (R3), and `requiresCapabilities` stages therefore can never reach one
|
|
|
239
239
|
spur agent wait --inbox reviewer-g1 --timeout 540000 --json
|
|
240
240
|
```
|
|
241
241
|
|
|
242
|
-
Returns `0`
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
242
|
+
Returns `0` after handing off up to five queued messages, with `{ id, pending, messages }` in JSON;
|
|
243
|
+
`pending` is the handed-off batch count. Exact claims settle as delivered after output succeeds and
|
|
244
|
+
release for retry if output fails; further queued work stays for the next pull. Returns `1` on timeout
|
|
245
|
+
or when the id is not a joined guest. Every call heartbeats the guest's lease, so a session that keeps
|
|
246
|
+
looping stays joined; the timeout stays under the Bash tool's ~10-minute limit. The
|
|
247
|
+
`fleet-join` skill loops it (wait → work from returned messages → `spur message reply`),
|
|
246
248
|
and the `fleet-guest-stop` plugin hook delivers the same messages at a turn boundary on hosts that
|
|
247
249
|
support a blocking Stop hook.
|
|
248
250
|
|
|
@@ -120,6 +120,11 @@
|
|
|
120
120
|
"type": "string",
|
|
121
121
|
"minLength": 1,
|
|
122
122
|
"description": "Five-field cron expression (minute hour day-of-month month day-of-week) evaluated in local time. Mutually exclusive with `intervalMinutes`."
|
|
123
|
+
},
|
|
124
|
+
"timeoutMs": {
|
|
125
|
+
"type": ["integer", "null"],
|
|
126
|
+
"minimum": 1,
|
|
127
|
+
"description": "Total child budget for this job in milliseconds. At the deadline the runtime SIGTERMs the owned process group, then SIGKILLs after the scheduler grace. `null` is explicit unlimited; absent falls back to the global scheduler default (10 min). Keep a model-driven chain below the server queue's 2 h visibility timeout so it fails its own attempt instead of being re-delivered."
|
|
123
128
|
}
|
|
124
129
|
},
|
|
125
130
|
"oneOf": [
|