@thehammer/danx-dashboard-mcp 0.1.83 → 0.1.84

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/handlers.js CHANGED
@@ -392,7 +392,11 @@ export async function issueComment(client, args) {
392
392
  return client.request({
393
393
  method: "POST",
394
394
  path: `/${idEnc}/comments`,
395
- body: { text, ...(args.metadata !== undefined ? { metadata: args.metadata } : {}) },
395
+ body: {
396
+ text,
397
+ ...(args.metadata !== undefined ? { metadata: args.metadata } : {}),
398
+ ...(args.problem_id !== undefined ? { problem_id: args.problem_id } : {}),
399
+ },
396
400
  board,
397
401
  });
398
402
  }
package/dist/index.js CHANGED
@@ -504,12 +504,13 @@ server.tool("issue_triage", "Record a triage confidence score via POST /api/issu
504
504
  ...boardField,
505
505
  }, async (args) => jsonResult(await issueTriage(client, args)));
506
506
  // ---------------- issue_comment ----------------
507
- server.tool("issue_comment", "Comment CRUD via /api/issues/:id/comments[/:cid]. action=add → POST {text, metadata?} (server stamps author from bearer + auto-incrementing ordinal); action=edit → PATCH /:cid {text}; action=delete → DELETE /:cid (soft-delete, audit trail preserved — comments are NEVER hard-deleted). Client-supplied author is IGNORED (server-stamped to prevent impersonation). `metadata` (DX-2157, action=add only) is an OPTIONAL opaque JSON object a calling app attaches to the comment — e.g. a generated `{sql, explanation}` packet its own UI renders specially. danxbot stores + returns it verbatim and enforces NO shape on its contents; omit for a plain markdown-only comment (unaffected either way).", {
507
+ server.tool("issue_comment", "Comment CRUD via /api/issues/:id/comments[/:cid]. action=add → POST {text, metadata?, problem_id?} (server stamps author from bearer + auto-incrementing ordinal); action=edit → PATCH /:cid {text}; action=delete → DELETE /:cid (soft-delete, audit trail preserved — comments are NEVER hard-deleted). Client-supplied author is IGNORED (server-stamped to prevent impersonation). `metadata` (DX-2157, action=add only) is an OPTIONAL opaque JSON object a calling app attaches to the comment — e.g. a generated `{sql, explanation}` packet its own UI renders specially. danxbot stores + returns it verbatim and enforces NO shape on its contents; omit for a plain markdown-only comment (unaffected either way). `problem_id` (DX-2906, action=add only) OPTIONALLY threads the comment as a follow-up question under one of this SAME card's problems (from `issue_problem` list/add) WITHOUT answering it — commenting never changes open_problem_count, blocked, or records a decision; use issue_problem's answer route for that. An unknown id, another card's id, or a removed problem's id all 404 naming the problem id — an ANSWERED (but not removed) problem still accepts a follow-up comment, since the thread continues after a decision.", {
508
508
  id: z.string().min(1),
509
509
  action: z.enum(["add", "edit", "delete"]),
510
510
  comment_id: z.number().int().positive().optional(),
511
511
  text: z.string().min(1).optional(),
512
512
  metadata: z.record(z.unknown()).optional(),
513
+ problem_id: z.number().int().positive().optional().describe("action=add only — thread this comment as a follow-up under a LIVE problem of this same card, without answering it"),
513
514
  ...boardField,
514
515
  }, async (args) => jsonResult(await issueComment(client, args)));
515
516
  // ---------------- issue_checklist ----------------
package/dist/listen.js CHANGED
@@ -92,8 +92,18 @@ export function invalidEventReason(value) {
92
92
  if (!isRecord(d))
93
93
  return "detail is not an object";
94
94
  switch (value.kind) {
95
- case "comment_added":
96
- return isCappedText(d.excerpt) ? null : "detail.excerpt is not capped text";
95
+ case "comment_added": {
96
+ if (!isCappedText(d.excerpt))
97
+ return "detail.excerpt is not capped text";
98
+ // DX-2906 — `problem` is OPTIONAL (only present when the comment named
99
+ // one); absent is valid, but a present-and-malformed one is not.
100
+ if (d.problem === undefined)
101
+ return null;
102
+ const problem = d.problem;
103
+ return isRecord(problem) && typeof problem.id === "number" && isCappedText(problem.statement)
104
+ ? null
105
+ : "detail.problem is not {id, statement}";
106
+ }
97
107
  case "solution_answered": {
98
108
  const problem = d.problem;
99
109
  if (!isRecord(problem) || typeof problem.id !== "number" || !isCappedText(problem.statement)) {
@@ -122,8 +132,16 @@ export function invalidEventReason(value) {
122
132
  function describe(event) {
123
133
  const d = event.detail;
124
134
  switch (event.kind) {
125
- case "comment_added":
126
- return `${event.actor} commented: ${quoted(d.excerpt)}`;
135
+ case "comment_added": {
136
+ // DX-2906 — a comment naming a problem (a follow-up question, without
137
+ // answering it) relays distinctly from a plain comment, so an agent
138
+ // listening to the stream can tell which of its open problems just
139
+ // got a reply.
140
+ const problem = d.problem;
141
+ if (problem === undefined)
142
+ return `${event.actor} commented: ${quoted(d.excerpt)}`;
143
+ return `${event.actor} commented on problem ${quoted(problem.statement)}: ${quoted(d.excerpt)}`;
144
+ }
127
145
  case "solution_answered": {
128
146
  // DX-2735: every answer answers ONE problem on a card that may carry several,
129
147
  // so the line names the problem — otherwise the agent cannot tell which of its
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thehammer/danx-dashboard-mcp",
3
- "version": "0.1.83",
3
+ "version": "0.1.84",
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",