@thehammer/danx-dashboard-mcp 0.1.112 → 0.1.115

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
@@ -591,6 +591,16 @@ export async function issueChecklist(client, args) {
591
591
  * context while `null` clears it — the same omit/null semantics
592
592
  * `plan_update_record` already gives a plan record's context.
593
593
  *
594
+ * DX-3309 — `add` also takes `type`: `question` (the operator decides) or
595
+ * `action` (only a person can carry it out). Sent only when given — the
596
+ * server itself defaults an omitted value to `question`, so a caller that
597
+ * predates this field keeps creating exactly what it always created. It is
598
+ * set once, at creation, and never editable afterward. The FULL teaching
599
+ * text (the could-I-do-this-myself test, and what `statement`/`context`/
600
+ * `solutions[]` mean under each type) lives on the tool's own `type` field
601
+ * `.describe()` in `index.ts` — that description, not this comment, is what
602
+ * a calling agent actually reads.
603
+ *
594
604
  * No answer action, for the reason `issueSolution` gives.
595
605
  */
596
606
  export async function issueProblem(client, args) {
@@ -604,6 +614,11 @@ export async function issueProblem(client, args) {
604
614
  const body = { statement: need.string(args.statement, "statement") };
605
615
  if (args.context !== undefined)
606
616
  body.context = args.context;
617
+ // DX-3309 — sent only when given; the server itself defaults an omitted
618
+ // value to "question" (today's behavior, unchanged for any caller that
619
+ // doesn't yet know about this field).
620
+ if (args.type !== undefined)
621
+ body.type = args.type;
607
622
  if (args.solutions !== undefined)
608
623
  body.solutions = args.solutions;
609
624
  const result = await client.request({ method: "POST", path: `/${idEnc}/problems`, body, board });
package/dist/index.js CHANGED
@@ -610,7 +610,7 @@ const SOLUTION_FIELDS = {
610
610
  con: z.string().optional(),
611
611
  recommended: z.boolean().optional(),
612
612
  };
613
- strictTool("issue_problem", "A card's PROBLEMS via /api/issues/:id/problems[/:pid]: one statement the operator must resolve (a question, or a flaw in the plan), each with its own solutions/answers — one problem per question. OPEN = not yet answered; the card needs a human exactly while open_problem_count > 0 — there is no separate flag to set, adding a problem IS putting the card in front of a human. list → live problems in order, each {id, statement, context, content_hash, open, solutions[], decisions[]}; add {statement, context?, solutions?} → problem_id + solution_ids in one transaction (zero solutions is valid: the operator answers free-form) plus `problems_reminder: {open_problem_count, instruction}` naming each open problem's solution count; edit :pid {base_hash, statement, context?}; remove :pid {base_hash} — always allowed, even as the card's last open problem (DX-2830: removing it just means the card no longer needs a human). A stale base_hash → 409 `stale_problem` with currentHash + currentProblem: merge, then retry. No answer action — the operator answers in the dashboard. DX-2942 — `statement` is capped at 200 characters (a 400 names the actual length otherwise): write it as ONE plain question, and put any investigation detail in `context` (markdown, no cap) instead of running it on. Good: statement \"Which cache should we use?\", context \"Redis fits the read-heavy path; see benchmark in #123. Memcached is simpler ops but no persistence.\" Bad: statement \"We looked at Redis vs Memcached, ran benchmarks showing Redis 3x faster on reads, but Memcached has simpler ops and we're not sure persistence matters here since the cache is fully rebuildable from Postgres...\" (too long, refused — move it to context).", {
613
+ strictTool("issue_problem", "A card's PROBLEMS via /api/issues/:id/problems[/:pid]: one statement the operator must resolve (a question, or a flaw in the plan), each with its own solutions/answers — one problem per question. OPEN = not yet answered; the card needs a human exactly while open_problem_count > 0 — there is no separate flag to set, adding a problem IS putting the card in front of a human. list → live problems in order, each {id, statement, context, type, content_hash, open, solutions[], decisions[]}; add {statement, context?, type?, solutions?} → problem_id + solution_ids in one transaction (zero solutions is valid: the operator answers free-form) plus `problems_reminder: {open_problem_count, instruction}` naming each open problem's solution count; edit :pid {base_hash, statement, context?}; remove :pid {base_hash} — always allowed, even as the card's last open problem (DX-2830: removing it just means the card no longer needs a human). A stale base_hash → 409 `stale_problem` with currentHash + currentProblem: merge, then retry. No answer action — the operator answers in the dashboard. DX-2942 — `statement` is capped at 200 characters (a 400 names the actual length otherwise): write it as ONE plain question, and put any investigation detail in `context` (markdown, no cap) instead of running it on. Good: statement \"Which cache should we use?\", context \"Redis fits the read-heavy path; see benchmark in #123. Memcached is simpler ops but no persistence.\" Bad: statement \"We looked at Redis vs Memcached, ran benchmarks showing Redis 3x faster on reads, but Memcached has simpler ops and we're not sure persistence matters here since the cache is fully rebuildable from Postgres...\" (too long, refused — move it to context). DX-3309 — every problem is also a `type`: read the `type` field's own description below before your first `add`, it teaches which one this is and why the choice matters.", {
614
614
  id: z.string().min(1),
615
615
  action: z.enum(["list", "add", "edit", "remove"]),
616
616
  problem_id: z.number().int().positive().optional().describe("edit/remove"),
@@ -621,6 +621,22 @@ strictTool("issue_problem", "A card's PROBLEMS via /api/issues/:id/problems[/:pi
621
621
  .nullable()
622
622
  .optional()
623
623
  .describe("add/edit; markdown detail behind statement — file:line refs, code excerpts, root-cause writeups. add: sent only when given (null/omit means none). edit: omit to keep the stored context, null to clear it."),
624
+ type: z
625
+ .enum(["question", "action"])
626
+ .optional()
627
+ .describe("add only. Before you raise this problem, apply the test: could I do this myself if I tried harder, and is the " +
628
+ "only thing missing a decision? If yes, this is a \"question\": statement is the question itself (one plain " +
629
+ "sentence); context is the evidence behind it; solutions[] are candidate ANSWERS, each with its own pro/con, " +
630
+ "and the operator is done the moment they pick one. If the blocker is access, credentials, hardware, a " +
631
+ "human's authority, or a system you genuinely cannot reach, this is an \"action\": statement is WHAT IS TO " +
632
+ "BE DONE — the deed itself, never phrased as a question (\"Rotate the staging DB credential\", not \"Should " +
633
+ "we rotate it?\"); context is the action's summary and MUST say WHY YOU CANNOT DO THIS YOURSELF — that " +
634
+ "sentence is what tells the operator this is not you being lazy; solutions[] are the possible ROUTES a " +
635
+ "person could take to carry it out (e.g. \"rotate by hand in the console\" vs \"run the provisioning " +
636
+ "script\" vs \"ask the vendor\") — each will carry its own ordered steps to completion once the operator " +
637
+ "picks one (a later release; for now, describe the route in the solution's body). Omitted defaults to " +
638
+ "\"question\" (today's behavior) — but that default exists for the caller who has never heard of this " +
639
+ "field, not as permission to skip the choice: decide deliberately every time."),
624
640
  solutions: z
625
641
  .array(z.object({ title: z.string().min(1), ...SOLUTION_FIELDS }).strict())
626
642
  .optional()
package/dist/listen.js CHANGED
@@ -276,6 +276,20 @@ function cardList(cards) {
276
276
  * point of the feature: a session that only sees "you're idle" learns
277
277
  * nothing a bare timer couldn't have told it. Always a single line; throws,
278
278
  * naming the fault, on a malformed nudge.
279
+ *
280
+ * DX-3274 — the line asserts only what `evaluateIdleNudge` actually measured
281
+ * (no real MCP call from THIS session in `idleMinutes`), never "work is
282
+ * waiting" or "work has stalled". `last_agent_call_at` cannot see a
283
+ * sub-agent/dispatch working the same card from underneath an orchestrating
284
+ * session — an orchestrator that delegates and then correctly waits without
285
+ * polling (the project's own mandated style, `base:monitor-polling`) looks
286
+ * identical, on this signal, to one that actually stalled. The card names
287
+ * are still worth reporting (a real signal: these are the cards this session
288
+ * itself has not touched), but the claim about THEM stops at "not checked
289
+ * in" — never a claim about whether the work itself is progressing, which
290
+ * this signal cannot support. See `evaluateIdleNudge`'s own doc comment
291
+ * (`src/issues/db/session-idle-nudge.ts`) for why a sub-agent-awareness
292
+ * special case was rejected instead of this wording fix.
279
293
  */
280
294
  export function formatNudgeLine(event) {
281
295
  const invalid = invalidNudgeReason(event);
@@ -286,7 +300,7 @@ export function formatNudgeLine(event) {
286
300
  parts.push(`startable: ${cardList(event.startable)}`);
287
301
  if (event.held.length > 0)
288
302
  parts.push(`held: ${cardList(event.held)}`);
289
- return oneLine(`${LINE_PREFIX} this session has been idle ${event.idleMinutes}m with work waiting — ${parts.join("; ")}`);
303
+ return oneLine(`${LINE_PREFIX} this session has not made an MCP call in ${event.idleMinutes}m — ${parts.join("; ")}`);
290
304
  }
291
305
  /**
292
306
  * Incremental SSE parser. Comment lines (keep-alives, the connect marker) and
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thehammer/danx-dashboard-mcp",
3
- "version": "0.1.112",
3
+ "version": "0.1.115",
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",