@mu-crew/mu 3.3.0 → 3.5.0

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.
Files changed (45) hide show
  1. package/README.md +45 -31
  2. package/dist/cli.js +812 -429
  3. package/dist/cli.js.map +1 -1
  4. package/dist/extension/mu-pi.js +221 -55
  5. package/dist/extension/mu-pi.js.map +1 -1
  6. package/dist/index.d.ts +33 -4
  7. package/dist/index.js +76 -5
  8. package/dist/index.js.map +1 -1
  9. package/docs/VISION.md +5 -1
  10. package/docs/VOCABULARY.md +4 -1
  11. package/docs/architecture/dag.md +3 -3
  12. package/docs/architecture/ops-log.md +26 -7
  13. package/docs/guide/cleanup.md +19 -0
  14. package/docs/guide/delegate.md +43 -4
  15. package/docs/reference/env.md +1 -0
  16. package/docs/reference/naming.md +18 -0
  17. package/docs/specs/2026-09-29-task-substates.md +1 -1
  18. package/package.json +1 -1
  19. package/prompts/mu-debug.md +8 -0
  20. package/prompts/mu-sweep.md +8 -0
  21. package/prompts/mu-until.md +8 -0
  22. package/prompts/ultrathink.md +2 -1
  23. package/skills/mu/SKILL.md +70 -31
  24. package/skills/mu/recipes/adversarial-review.md +27 -19
  25. package/skills/mu/recipes/{triage.md → backlog-triage.md} +9 -4
  26. package/skills/mu/recipes/codemode-driver.md +68 -20
  27. package/skills/mu/recipes/deep-research.md +23 -8
  28. package/skills/mu/recipes/fan-out.md +15 -11
  29. package/skills/mu/recipes/findings.md +103 -0
  30. package/skills/mu/recipes/hypothesis-panel.md +18 -13
  31. package/skills/mu/recipes/long-run.md +4 -4
  32. package/skills/mu/recipes/loop-until-done.md +7 -3
  33. package/skills/mu/recipes/orchestrator-loop.md +44 -27
  34. package/skills/mu/recipes/plan.md +8 -3
  35. package/skills/mu/recipes/recovery.md +5 -0
  36. package/skills/mu/recipes/refute.md +38 -36
  37. package/skills/mu/recipes/remote-workers.md +7 -2
  38. package/skills/mu/recipes/review-panel.md +36 -31
  39. package/skills/mu/recipes/rules-audit.md +30 -17
  40. package/skills/mu/recipes/tasks-or-calls.md +71 -0
  41. package/skills/mu/recipes/tournament.md +13 -10
  42. package/skills/mu/recipes/ultrathink.md +20 -11
  43. package/skills/mu/recipes/watcher.md +4 -1
  44. package/skills/mu/recipes/waves.md +6 -5
  45. package/skills/mu/recipes/worker.md +3 -1
package/dist/index.js CHANGED
@@ -221,6 +221,11 @@ function fullOldPayload(cols) {
221
221
  const pairs = cols.map((c) => `'${c}', OLD.${c}`).join(", ");
222
222
  return `json_object(${pairs})`;
223
223
  }
224
+ function noteTombstonePayload(keyExpr, cols) {
225
+ return `CASE WHEN EXISTS (
226
+ SELECT 1 FROM ops WHERE entity = 'note' AND key = ${keyExpr} AND op = 'put'
227
+ ) THEN '{}' ELSE ${fullOldPayload(cols)} END`;
228
+ }
224
229
  function anyChanged(cols) {
225
230
  return cols.map((c) => `NEW.${c} IS NOT OLD.${c}`).join("\n OR ");
226
231
  }
@@ -321,7 +326,7 @@ END;
321
326
  CREATE TEMP TRIGGER IF NOT EXISTS _cap_task_notes_del
322
327
  BEFORE DELETE ON task_notes WHEN ${NOT_APPLYING}
323
328
  BEGIN
324
- ${emitOp("note", "del", `${taskKey("OLD.task_id")} || '#' || OLD.id`, fullOldPayload(noteCols))}
329
+ ${emitOp("note", "del", `${taskKey("OLD.task_id")} || '#' || OLD.id`, noteTombstonePayload(`${taskKey("OLD.task_id")} || '#' || OLD.id`, noteCols))}
325
330
  END;
326
331
 
327
332
  -- \u2500\u2500\u2500 task_edges \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
@@ -359,7 +364,7 @@ function isTaskStatus(s) {
359
364
  }
360
365
  var TASK_STATUS_LIST = TASK_STATUSES.join(" | ");
361
366
  var TASK_SUBSTATES = {
362
- OPEN: ["todo", "parked"],
367
+ OPEN: ["todo", "parked", "triage"],
363
368
  IN_PROGRESS: ["active"],
364
369
  CLOSED: ["done", "rejected", "wontfix", "duplicate", "superseded"]
365
370
  };
@@ -590,7 +595,7 @@ CREATE VIEW ready AS
590
595
  SELECT t.*
591
596
  FROM tasks t
592
597
  WHERE t.status = 'OPEN'
593
- AND t.substate <> 'parked'
598
+ AND t.substate NOT IN ('parked', 'triage')
594
599
  AND NOT EXISTS (
595
600
  SELECT 1
596
601
  FROM task_edges e
@@ -4560,6 +4565,30 @@ var TaskParkStateError = class extends Error {
4560
4565
  ];
4561
4566
  }
4562
4567
  };
4568
+ var TaskInTriageError = class extends Error {
4569
+ constructor(taskId, workstream) {
4570
+ super(`task ${taskId} is in triage (OPEN/triage); accept it or claim with --force`);
4571
+ this.taskId = taskId;
4572
+ this.workstream = workstream;
4573
+ }
4574
+ taskId;
4575
+ workstream;
4576
+ name = "TaskInTriageError";
4577
+ errorNextSteps() {
4578
+ const ws = this.workstream;
4579
+ return [
4580
+ {
4581
+ intent: "Accept it as work, then claim",
4582
+ command: `mu task accept ${this.taskId} -w ${ws}`
4583
+ },
4584
+ {
4585
+ intent: "Decline it",
4586
+ command: `mu task close ${this.taskId} --as rejected --why "<reason>" -w ${ws}`
4587
+ },
4588
+ { intent: "Claim it anyway", command: `mu task claim ${this.taskId} --force -w ${ws}` }
4589
+ ];
4590
+ }
4591
+ };
4563
4592
  var TaskParkedError = class extends Error {
4564
4593
  constructor(taskId, workstream) {
4565
4594
  super(`task ${taskId} is parked (OPEN/parked); unpark it or claim with --force`);
@@ -5015,8 +5044,17 @@ function addTaskImpl(db, opts) {
5015
5044
  const now = (/* @__PURE__ */ new Date()).toISOString();
5016
5045
  const insertResult = db.prepare(
5017
5046
  `INSERT INTO tasks (workstream_id, local_id, title, status, substate, impact, effort_days, created_at, updated_at)
5018
- VALUES (?, ?, ?, 'OPEN', 'todo', ?, ?, ?, ?)`
5019
- ).run(wsId, opts.localId, opts.title, opts.impact, opts.effortDays, now, now);
5047
+ VALUES (?, ?, ?, 'OPEN', ?, ?, ?, ?, ?)`
5048
+ ).run(
5049
+ wsId,
5050
+ opts.localId,
5051
+ opts.title,
5052
+ opts.triage === true ? "triage" : "todo",
5053
+ opts.impact,
5054
+ opts.effortDays,
5055
+ now,
5056
+ now
5057
+ );
5020
5058
  const newTaskId = Number(insertResult.lastInsertRowid);
5021
5059
  if (opts.blockedBy && opts.blockedBy.length > 0) {
5022
5060
  const blockerLookupSameWs = db.prepare(
@@ -5291,6 +5329,28 @@ function parkTask(db, localId, opts) {
5291
5329
  })()
5292
5330
  );
5293
5331
  }
5332
+ function acceptTask(db, localId, opts) {
5333
+ return withOpContext(
5334
+ db,
5335
+ { intent: "task.accept", group: "new" },
5336
+ () => db.transaction(() => {
5337
+ const before = getTask(db, localId, opts.workstream);
5338
+ if (!before) throw new TaskNotFoundError(localId);
5339
+ if (before.status !== "OPEN" || before.substate !== "triage") {
5340
+ return {
5341
+ previousStatus: before.status,
5342
+ status: before.status,
5343
+ previousSubstate: before.substate,
5344
+ substate: before.substate,
5345
+ changed: false
5346
+ };
5347
+ }
5348
+ const r = setTaskStatus(db, localId, "OPEN", { workstream: opts.workstream });
5349
+ recordEvidenceNote(db, localId, before.workstreamName, "ACCEPT", opts);
5350
+ return r;
5351
+ })()
5352
+ );
5353
+ }
5294
5354
  function unparkTask(db, localId, opts) {
5295
5355
  return withOpContext(
5296
5356
  db,
@@ -5421,6 +5481,9 @@ function assertNotParked(task, opts) {
5421
5481
  if (task.status === "OPEN" && task.substate === "parked" && opts.force !== true) {
5422
5482
  throw new TaskParkedError(task.name, task.workstreamName);
5423
5483
  }
5484
+ if (task.status === "OPEN" && task.substate === "triage" && opts.force !== true) {
5485
+ throw new TaskInTriageError(task.name, task.workstreamName);
5486
+ }
5424
5487
  }
5425
5488
  async function resolveActorIdentity() {
5426
5489
  const worker = await resolveWorkerIdentity();
@@ -10022,6 +10085,10 @@ async function loadWorkstreamSnapshotFast(db, workstream, opts = {}) {
10022
10085
  blocked: listBlocked(db, workstream),
10023
10086
  recentClosed: listRecentClosed(db, workstream),
10024
10087
  parkedCount: listTasks(db, workstream, { status: "OPEN", substate: "parked" }).length,
10088
+ triage: listTasks(db, workstream, { status: "OPEN", substate: "triage" }),
10089
+ taskCount: db.prepare(
10090
+ "SELECT count(*) AS n FROM tasks t JOIN workstreams w ON w.id = t.workstream_id WHERE w.name = ?"
10091
+ ).get(workstream).n,
10025
10092
  allTasks: opts.withAllTasks === true ? listTasks(db, workstream) : [],
10026
10093
  workspaces: listWorkspaces2(db, workstream),
10027
10094
  workspaceOrphans: listWorkspaceOrphans(db, workstream),
@@ -10095,6 +10162,8 @@ function minimalSnapshot(workstream) {
10095
10162
  blocked: [],
10096
10163
  recentClosed: [],
10097
10164
  parkedCount: 0,
10165
+ triage: [],
10166
+ taskCount: 0,
10098
10167
  allTasks: [],
10099
10168
  workspaces: [],
10100
10169
  workspaceOrphans: [],
@@ -10991,6 +11060,7 @@ export {
10991
11060
  TASK_SUBSTATE_ROWS,
10992
11061
  TaskAlreadyOwnedError,
10993
11062
  TaskExistsError,
11063
+ TaskInTriageError,
10994
11064
  TaskNotFoundError,
10995
11065
  TaskNotInWorkstreamError,
10996
11066
  TaskParkStateError,
@@ -11007,6 +11077,7 @@ export {
11007
11077
  WorkstreamExistsError,
11008
11078
  WorkstreamNameInvalidError,
11009
11079
  abortAgent,
11080
+ acceptTask,
11010
11081
  activeMux,
11011
11082
  addBlockEdge,
11012
11083
  addNote,