@thehammer/danx-dashboard-mcp 0.1.85 → 0.1.86
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/dist/listen.js +74 -1
- package/package.json +1 -1
package/dist/listen.js
CHANGED
|
@@ -11,7 +11,10 @@
|
|
|
11
11
|
* - `{type:"event", id, text}` — exactly one per event, the moment it arrives.
|
|
12
12
|
* `text` is what the session reads; an event it cannot read still produces one
|
|
13
13
|
* (`could not read event …`), never silence. `id` is `null` only for a frame
|
|
14
|
-
* that carried no usable id
|
|
14
|
+
* that carried no usable id — which includes DX-2885's `nudge` frame
|
|
15
|
+
* (`formatNudgeLine`): it is advisory, re-derived fresh every tick by the
|
|
16
|
+
* dashboard's own keep-alive tick, and never replayed, so it rides this
|
|
17
|
+
* same shape with `id: null` rather than growing a THIRD output variant.
|
|
15
18
|
* - `{type:"stopped", reason, detail, refusal?, boards?}` — once, last. `reason`
|
|
16
19
|
* is the dashboard's own end reason (`superseded`, `replaced`, `revoked`,
|
|
17
20
|
* `scope_narrowed`, `not_connected`), `refused` (the ticket was not
|
|
@@ -219,6 +222,46 @@ export function formatActivityLine(event) {
|
|
|
219
222
|
throw new Error(`malformed activity event: ${invalid}`);
|
|
220
223
|
return oneLine(`[${event.cardId} "${event.cardTitle}" ${event.boardId}] ${describe(event)}`);
|
|
221
224
|
}
|
|
225
|
+
function isNudgeCardRef(value) {
|
|
226
|
+
return isRecord(value) && typeof value.id === "string" && typeof value.title === "string";
|
|
227
|
+
}
|
|
228
|
+
function isNudgeCardRefArray(value) {
|
|
229
|
+
return Array.isArray(value) && value.every(isNudgeCardRef);
|
|
230
|
+
}
|
|
231
|
+
/** Why a nudge cannot be read, or `null` when it can — same checked-before-describing shape as `invalidEventReason`. */
|
|
232
|
+
export function invalidNudgeReason(value) {
|
|
233
|
+
if (!isRecord(value))
|
|
234
|
+
return "the nudge is not an object";
|
|
235
|
+
if (typeof value.idleMinutes !== "number" || !Number.isFinite(value.idleMinutes)) {
|
|
236
|
+
return "idleMinutes is not a number";
|
|
237
|
+
}
|
|
238
|
+
if (!isNudgeCardRefArray(value.startable))
|
|
239
|
+
return "startable is not a {id,title}[] array";
|
|
240
|
+
if (!isNudgeCardRefArray(value.held))
|
|
241
|
+
return "held is not a {id,title}[] array";
|
|
242
|
+
return null;
|
|
243
|
+
}
|
|
244
|
+
function cardList(cards) {
|
|
245
|
+
return cards.map((c) => `${c.id} "${c.title}"`).join(", ");
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* The one readable line for a nudge (DX-2885). Always names the actual
|
|
249
|
+
* waiting cards — never a generic "keep going" — because that is the whole
|
|
250
|
+
* point of the feature: a session that only sees "you're idle" learns
|
|
251
|
+
* nothing a bare timer couldn't have told it. Always a single line; throws,
|
|
252
|
+
* naming the fault, on a malformed nudge.
|
|
253
|
+
*/
|
|
254
|
+
export function formatNudgeLine(event) {
|
|
255
|
+
const invalid = invalidNudgeReason(event);
|
|
256
|
+
if (invalid !== null)
|
|
257
|
+
throw new Error(`malformed nudge event: ${invalid}`);
|
|
258
|
+
const parts = [];
|
|
259
|
+
if (event.startable.length > 0)
|
|
260
|
+
parts.push(`startable: ${cardList(event.startable)}`);
|
|
261
|
+
if (event.held.length > 0)
|
|
262
|
+
parts.push(`held: ${cardList(event.held)}`);
|
|
263
|
+
return oneLine(`${LINE_PREFIX} this session has been idle ${event.idleMinutes}m with work waiting — ${parts.join("; ")}`);
|
|
264
|
+
}
|
|
222
265
|
/**
|
|
223
266
|
* Incremental SSE parser. Comment lines (keep-alives, the connect marker) and
|
|
224
267
|
* field-less blocks produce no message, which is what keeps them out of the output.
|
|
@@ -347,6 +390,36 @@ export async function runListener(options, deps) {
|
|
|
347
390
|
// elsewhere — see `ListenStopReason`'s docblock.
|
|
348
391
|
return { kind: "ended", reason: isKnownWireEndReason(reason) ? reason : "unknown" };
|
|
349
392
|
}
|
|
393
|
+
if (message.event === "nudge") {
|
|
394
|
+
// DX-2885 — a nudge carries no usable id (it is advisory, re-derived
|
|
395
|
+
// fresh every tick, never replayed — see `plan-session-stream.ts`'s
|
|
396
|
+
// module doc), so it rides the SAME `{type:"event", id:null, text}`
|
|
397
|
+
// shape an ordinary activity line uses (`id` is `null` only for a frame
|
|
398
|
+
// that carried no usable id — a nudge is exactly that case). That is
|
|
399
|
+
// what lets `bridge.ts` and the plugin's relay
|
|
400
|
+
// (`danxbot/scripts/plan-event-bridge.mjs`, outside this repo) forward
|
|
401
|
+
// it with NO changes at all: both already forward any `{type:"event",
|
|
402
|
+
// id, text}` line generically, keyed only on `type`.
|
|
403
|
+
//
|
|
404
|
+
// DX-2885 — this frame must stay exempt from DX-2730 D4's digest-
|
|
405
|
+
// batching when that lands (see DX-2730 AC 29939, and the matching note
|
|
406
|
+
// at the dashboard's own emit site): it must keep delivering
|
|
407
|
+
// immediately, never held for a later digest flush.
|
|
408
|
+
let parsed;
|
|
409
|
+
let invalid;
|
|
410
|
+
try {
|
|
411
|
+
parsed = JSON.parse(message.data);
|
|
412
|
+
invalid = invalidNudgeReason(parsed);
|
|
413
|
+
}
|
|
414
|
+
catch (err) {
|
|
415
|
+
invalid = `not JSON: ${err.message}`;
|
|
416
|
+
}
|
|
417
|
+
const text = invalid === null
|
|
418
|
+
? formatNudgeLine(parsed)
|
|
419
|
+
: `${LINE_PREFIX} could not read nudge (${invalid}): ${oneLine(message.data, 300)}`;
|
|
420
|
+
deps.write({ type: "event", id: null, text });
|
|
421
|
+
return null;
|
|
422
|
+
}
|
|
350
423
|
if (message.event !== "activity")
|
|
351
424
|
return null;
|
|
352
425
|
// `Number(null)` is 0, so a frame with no id must never be read as event 0: that id
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@thehammer/danx-dashboard-mcp",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.86",
|
|
4
4
|
"description": "Stdio MCP server wrapping danxbot's dashboard /api/issues/* normalized DB-backed HTTP routes for dispatched agents (DX-704 Phase 2).",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|