@wrongstack/tools 0.287.0 → 0.289.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 (93) hide show
  1. package/dist/_fetch-guard.d.ts.map +1 -1
  2. package/dist/_redact-command.d.ts.map +1 -1
  3. package/dist/_spawn-stream.d.ts.map +1 -1
  4. package/dist/audit.js +61 -14
  5. package/dist/audit.js.map +3 -3
  6. package/dist/bash.d.ts +1 -1
  7. package/dist/bash.d.ts.map +1 -1
  8. package/dist/bash.js +97 -19
  9. package/dist/bash.js.map +3 -3
  10. package/dist/builtin.d.ts +12 -10
  11. package/dist/builtin.d.ts.map +1 -1
  12. package/dist/builtin.js +1620 -803
  13. package/dist/builtin.js.map +3 -3
  14. package/dist/codebase-index/background-indexer.d.ts.map +1 -1
  15. package/dist/codebase-index/bm25.d.ts +1 -2
  16. package/dist/codebase-index/bm25.d.ts.map +1 -1
  17. package/dist/codebase-index/codebase-index-tool.d.ts.map +1 -1
  18. package/dist/codebase-index/codebase-search-tool.d.ts.map +1 -1
  19. package/dist/codebase-index/codebase-stats-tool.d.ts +7 -0
  20. package/dist/codebase-index/codebase-stats-tool.d.ts.map +1 -1
  21. package/dist/codebase-index/index-service.d.ts +11 -1
  22. package/dist/codebase-index/index-service.d.ts.map +1 -1
  23. package/dist/codebase-index/index.d.ts +2 -1
  24. package/dist/codebase-index/index.d.ts.map +1 -1
  25. package/dist/codebase-index/index.js +749 -192
  26. package/dist/codebase-index/index.js.map +3 -3
  27. package/dist/codebase-index/indexer.d.ts.map +1 -1
  28. package/dist/codebase-index/schema.d.ts +43 -1
  29. package/dist/codebase-index/schema.d.ts.map +1 -1
  30. package/dist/codebase-index/ts-parser.d.ts.map +1 -1
  31. package/dist/codebase-index/worker-protocol.d.ts +1 -0
  32. package/dist/codebase-index/worker-protocol.d.ts.map +1 -1
  33. package/dist/codebase-index/worker.js +610 -152
  34. package/dist/codebase-index/worker.js.map +3 -3
  35. package/dist/codebase-index/writer.d.ts +27 -1
  36. package/dist/codebase-index/writer.d.ts.map +1 -1
  37. package/dist/e2e.js +477 -0
  38. package/dist/e2e.js.map +7 -0
  39. package/dist/exec.d.ts +1 -1
  40. package/dist/exec.d.ts.map +1 -1
  41. package/dist/exec.js +60 -5
  42. package/dist/exec.js.map +3 -3
  43. package/dist/fetch.js +2 -2
  44. package/dist/fetch.js.map +2 -2
  45. package/dist/format.js +61 -14
  46. package/dist/format.js.map +3 -3
  47. package/dist/glob.js +2 -2
  48. package/dist/glob.js.map +1 -1
  49. package/dist/grep.d.ts.map +1 -1
  50. package/dist/grep.js +2 -2
  51. package/dist/grep.js.map +2 -2
  52. package/dist/index.d.ts +3 -2
  53. package/dist/index.d.ts.map +1 -1
  54. package/dist/index.js +1786 -809
  55. package/dist/index.js.map +3 -3
  56. package/dist/install.js +61 -14
  57. package/dist/install.js.map +3 -3
  58. package/dist/kanban.d.ts +11 -2
  59. package/dist/kanban.d.ts.map +1 -1
  60. package/dist/kanban.js +695 -571
  61. package/dist/kanban.js.map +3 -3
  62. package/dist/languages/detect.d.ts.map +1 -1
  63. package/dist/languages/index.js +61 -14
  64. package/dist/languages/index.js.map +3 -3
  65. package/dist/languages/types.d.ts +7 -0
  66. package/dist/languages/types.d.ts.map +1 -1
  67. package/dist/lint.js +61 -14
  68. package/dist/lint.js.map +3 -3
  69. package/dist/outdated.js +61 -14
  70. package/dist/outdated.js.map +3 -3
  71. package/dist/pack.js +1617 -800
  72. package/dist/pack.js.map +3 -3
  73. package/dist/plan.js +4 -0
  74. package/dist/plan.js.map +2 -2
  75. package/dist/process-registry.js +12 -5
  76. package/dist/process-registry.js.map +2 -2
  77. package/dist/search.js +2 -2
  78. package/dist/search.js.map +2 -2
  79. package/dist/session-kanban.d.ts +9 -0
  80. package/dist/session-kanban.d.ts.map +1 -1
  81. package/dist/session-kanban.js +137 -0
  82. package/dist/session-kanban.js.map +2 -2
  83. package/dist/task.js +4 -0
  84. package/dist/task.js.map +2 -2
  85. package/dist/test.js +61 -14
  86. package/dist/test.js.map +3 -3
  87. package/dist/todo.js +4 -0
  88. package/dist/todo.js.map +2 -2
  89. package/dist/tree.js +2 -2
  90. package/dist/tree.js.map +1 -1
  91. package/dist/typecheck.js +61 -14
  92. package/dist/typecheck.js.map +3 -3
  93. package/package.json +7 -3
package/dist/kanban.js CHANGED
@@ -1,4 +1,5 @@
1
1
  // src/kanban.ts
2
+ import { randomUUID } from "node:crypto";
2
3
  import { deserializeTaskGraph as deserializeTaskGraph2, loadTasks as loadTasks2, serializeTaskGraph } from "@wrongstack/core";
3
4
  import {
4
5
  addCheckToTask,
@@ -38,7 +39,9 @@ import {
38
39
  setTaskChain,
39
40
  splitTask,
40
41
  syncBoardFromTaskGraph as syncBoardFromTaskGraph2,
42
+ transitionTask,
41
43
  transferTaskToBoard,
44
+ touchKanbanPresence as touchKanbanPresence2,
42
45
  updateBoard as updateBoard2,
43
46
  updateCheckOnTask,
44
47
  updateColumn,
@@ -50,17 +53,21 @@ import {
50
53
  // src/session-kanban.ts
51
54
  import {
52
55
  deserializeTaskGraph,
56
+ GlobalMailbox,
53
57
  loadPlan,
54
58
  loadTasks,
55
59
  mutatePlan,
56
60
  mutateTasks
57
61
  } from "@wrongstack/core";
62
+ import { resolveWstackPaths } from "@wrongstack/core/utils";
58
63
  import {
59
64
  createBoard,
60
65
  getBoard,
66
+ getKanbanDir,
61
67
  listBoards,
62
68
  removeBoard,
63
69
  syncBoardFromTaskGraph,
70
+ touchKanbanPresence,
64
71
  updateBoard
65
72
  } from "@wrongstack/kanban";
66
73
  function taskFileToSerializedGraph(tasks, sessionId) {
@@ -104,8 +111,8 @@ function taskFileToSerializedGraph(tasks, sessionId) {
104
111
  var kanbanTool = {
105
112
  name: "kanban",
106
113
  category: "Project",
107
- description: "Manage project-scoped multi-kanban boards stored under .wrongstack/kanbans. Supports board/task/column CRUD, ready-task queues, dependency chains, split/merge, assignment metadata, provider/model/fallback routing hints, goal metrics, success checks, notes, links, and run status updates.",
108
- usageHint: "Use this for durable project kanban state. Agents should call snapshot/ready_tasks, then claim_task before working. Use set_chain for ordered work, split_task/merge_tasks when task scope changes, assign_task with provider/model/fallback hints before spawning, mark_assignment when starting or finishing, and release_task if the claim cannot be worked.",
114
+ description: "Manage and audit project-scoped multi-kanban boards stored under .wrongstack/kanbans. Managed cards enforce fully specified details and adjacent Backlog \u2192 Todo \u2192 Running \u2192 Review \u2192 Done transitions with persistent comments and evidence. Successful board access records live agent/session presence.",
115
+ usageHint: "Use this for durable project kanban state. Reassess with get_board whenever evidence changes; agents may add, update, split, merge, reprioritize, or remove tasks so the board remains a live plan. Presence includes active/last-seen session and agent metadata. For managed boards, fully fill card details, use transition_task after every material step, attach truthful evidence, and never use update_task/move_task to bypass lifecycle guards. Worker completion enters Review; only passed acceptance criteria plus review evidence allow Done.",
109
116
  permission: "confirm",
110
117
  mutating: true,
111
118
  capabilities: ["fs.write"],
@@ -142,6 +149,7 @@ var kanbanTool = {
142
149
  "transfer_task",
143
150
  "get_task",
144
151
  "update_task",
152
+ "transition_task",
145
153
  "move_task",
146
154
  "delete_task",
147
155
  "set_chain",
@@ -172,6 +180,7 @@ var kanbanTool = {
172
180
  targetColumnId: { type: "string" },
173
181
  title: { type: "string" },
174
182
  description: { type: "string" },
183
+ dueDate: { type: "string" },
175
184
  tags: { type: "array", items: { type: "string" } },
176
185
  labels: { type: "array", items: { type: "string" } },
177
186
  priority: { type: "string", enum: ["critical", "high", "medium", "low"] },
@@ -218,6 +227,18 @@ var kanbanTool = {
218
227
  type: "string",
219
228
  enum: ["assigned", "queued", "running", "completed", "failed", "cancelled"]
220
229
  },
230
+ lifecycleStage: {
231
+ type: "string",
232
+ enum: ["backlog", "todo", "running", "review", "done"]
233
+ },
234
+ transitionAction: { type: "string" },
235
+ transitionComment: { type: "string" },
236
+ attachmentUrl: { type: "string" },
237
+ attachmentTitle: { type: "string" },
238
+ attachmentType: {
239
+ type: "string",
240
+ enum: ["issue", "pr", "doc", "commit", "design", "file", "url", "other"]
241
+ },
221
242
  releaseStatus: { type: "string", enum: ["pending", "ready", "blocked"] },
222
243
  releaseReason: { type: "string" },
223
244
  clearAssignee: { type: "boolean" },
@@ -288,591 +309,632 @@ var kanbanTool = {
288
309
  async execute(input, ctx) {
289
310
  const projectRoot = ctx.projectRoot;
290
311
  if (!projectRoot) return fail("No project root is available.");
312
+ const withPresence = async (result) => {
313
+ const boardId = result.board?.id ?? input.boardId;
314
+ if (!result.ok || !boardId || !ctx.session?.id || !ctx.agentId) return result;
315
+ try {
316
+ const board = await touchKanbanPresence2(projectRoot, boardId, {
317
+ sessionId: ctx.session.id,
318
+ agentId: ctx.agentId,
319
+ agentName: ctx.agentName,
320
+ taskId: input.taskId ?? result.task?.id,
321
+ runTaskId: input.runTaskId
322
+ });
323
+ return board ? { ...result, board } : result;
324
+ } catch {
325
+ return result;
326
+ }
327
+ };
291
328
  try {
292
- switch (input.action) {
293
- case "list_boards": {
294
- const boards = await listBoards2(projectRoot);
295
- return { ok: true, message: `${boards.length} board(s).`, boards };
296
- }
297
- case "get_board": {
298
- const board = await requireBoard(projectRoot, input.boardId);
299
- return board ? okBoard(board) : fail("Board not found.");
300
- }
301
- case "create_board": {
302
- if (!input.title) return fail("create_board requires title.");
303
- const board = await createBoard2(projectRoot, {
304
- title: input.title,
305
- ...input.description !== void 0 ? { description: input.description } : {},
306
- ...input.tags !== void 0 ? { tags: input.tags } : {},
307
- ...input.generatedBy !== void 0 ? { generatedBy: input.generatedBy } : {}
308
- });
309
- return { ok: true, message: `Board created: ${board.title}`, board };
310
- }
311
- case "update_board": {
312
- if (!input.boardId) return fail("update_board requires boardId.");
313
- const board = await updateBoard2(projectRoot, input.boardId, {
314
- ...input.title !== void 0 ? { title: input.title } : {},
315
- ...input.description !== void 0 ? { description: input.description } : {},
316
- ...input.tags !== void 0 ? { tags: input.tags } : {}
317
- });
318
- return board ? okBoard(board, "Board updated.") : fail("Board not found.");
319
- }
320
- case "duplicate_board": {
321
- if (!input.boardId) return fail("duplicate_board requires boardId.");
322
- const board = await duplicateBoard(projectRoot, input.boardId, {
323
- ...input.title !== void 0 ? { title: input.title } : {},
324
- ...input.generatedBy !== void 0 ? { generatedBy: input.generatedBy } : {},
325
- ...input.includeTasks !== void 0 ? { includeTasks: input.includeTasks } : {},
326
- ...input.includeCompletedTasks !== void 0 ? { includeCompletedTasks: input.includeCompletedTasks } : {},
327
- ...input.preserveAssignment !== void 0 ? { preserveAssignment: input.preserveAssignment } : {}
328
- });
329
- return board ? okBoard(board, "Board duplicated.") : fail("Board not found.");
330
- }
331
- case "delete_board": {
332
- if (!input.boardId) return fail("delete_board requires boardId.");
333
- const removed = await removeBoard2(projectRoot, input.boardId);
334
- return { ok: removed, message: removed ? "Board deleted." : "Board not found." };
335
- }
336
- case "generate_board": {
337
- if (!input.description) return fail("generate_board requires description.");
338
- const boardInput = generateBoardFromDescription({
339
- description: input.description,
340
- ...input.title !== void 0 ? { title: input.title } : {},
341
- ...input.context !== void 0 ? { context: input.context } : {},
342
- ...input.columns !== void 0 ? { columns: input.columns } : {}
343
- });
344
- const board = await createBoard2(projectRoot, boardInput);
345
- for (const taskInput2 of parseLinesIntoTasks(
346
- input.description,
347
- board.columns[0]?.id ?? "backlog"
348
- )) {
349
- await addTask(projectRoot, board.id, taskInput2);
350
- }
351
- return okBoard(await getBoard2(projectRoot, board.id) ?? board, "Board generated.");
352
- }
353
- case "export_markdown": {
354
- const board = await requireBoard(projectRoot, input.boardId);
355
- if (!board) return fail("Board not found.");
356
- return {
357
- ok: true,
358
- message: "Board exported.",
359
- board,
360
- markdown: exportBoardAsMarkdown(board)
361
- };
362
- }
363
- case "export_task_graph": {
364
- if (!input.boardId) return fail("export_task_graph requires boardId.");
365
- const exported = await exportBoardToTaskGraph(projectRoot, input.boardId, {
366
- ...input.graphId !== void 0 ? { graphId: input.graphId } : {},
367
- ...input.specId !== void 0 ? { specId: input.specId } : {},
368
- ...input.title !== void 0 ? { title: input.title } : {},
369
- ...input.preserveOriginTaskIds !== void 0 ? { preserveOriginTaskIds: input.preserveOriginTaskIds } : {},
370
- ...input.includeArchived !== void 0 ? { includeArchived: input.includeArchived } : {}
371
- });
372
- if (!exported) return fail("Board not found.");
373
- return {
374
- ok: true,
375
- message: `Task graph exported with ${exported.graph.nodes.size} node(s).`,
376
- board: exported.board,
377
- taskGraph: serializeTaskGraph(exported.graph)
378
- };
379
- }
380
- case "sync_task_graph": {
381
- if (!input.boardId || !input.taskGraph) {
382
- return fail("sync_task_graph requires boardId and taskGraph.");
383
- }
384
- const graph = deserializeTaskGraph2(input.taskGraph);
385
- const result = await syncBoardFromTaskGraph2(projectRoot, input.boardId, graph, {
386
- ...input.title !== void 0 ? { title: input.title } : {},
387
- ...input.description !== void 0 ? { description: input.description } : {},
388
- ...input.tags !== void 0 ? { tags: input.tags } : {},
389
- ...input.generatedBy !== void 0 ? { generatedBy: input.generatedBy } : {},
390
- ...input.sourceSystem !== void 0 ? { sourceSystem: input.sourceSystem } : {},
391
- ...input.phaseId !== void 0 ? { phaseId: input.phaseId } : {},
392
- ...input.includeCompletedTasks !== void 0 ? { includeCompletedTasks: input.includeCompletedTasks } : {},
393
- ...input.archiveMissingTasks !== void 0 ? { archiveMissingTasks: input.archiveMissingTasks } : {},
394
- ...input.preserveManualDependencies !== void 0 ? { preserveManualDependencies: input.preserveManualDependencies } : {}
395
- });
396
- return result ? {
397
- ok: true,
398
- message: `Task graph synced: ${result.createdTaskIds.length} created, ${result.updatedTaskIds.length} updated, ${result.archivedTaskIds.length} archived.`,
399
- board: result.board
400
- } : fail("Board not found.");
401
- }
402
- case "create_from_graph": {
403
- if (!input.taskGraph) return fail("create_from_graph requires taskGraph.");
404
- const graph = deserializeTaskGraph2(input.taskGraph);
405
- const { board } = await createBoardFromTaskGraph(projectRoot, graph, {
406
- ...input.title !== void 0 ? { title: input.title } : {},
407
- ...input.description !== void 0 ? { description: input.description } : {},
408
- ...input.tags !== void 0 ? { tags: input.tags } : {},
409
- ...input.generatedBy !== void 0 ? { generatedBy: input.generatedBy } : {},
410
- ...input.sourceSystem !== void 0 ? { sourceSystem: input.sourceSystem } : {},
411
- ...input.phaseId !== void 0 ? { phaseId: input.phaseId } : {},
412
- ...input.includeCompletedTasks !== void 0 ? { includeCompletedTasks: input.includeCompletedTasks } : {}
413
- });
414
- return {
415
- ok: true,
416
- message: `Created board "${board.title}" from task graph with ${board.tasks.length} tasks.`,
417
- board
418
- };
419
- }
420
- case "import_session_tasks": {
421
- const taskPath = ctx.meta?.["task.path"];
422
- if (!taskPath) return fail("No session task file for this session.");
423
- const file = await loadTasks2(taskPath);
424
- if (!file || file.tasks.length === 0) return fail("No session tasks to import.");
425
- const sessionId = ctx.session?.id ?? file.sessionId ?? "session";
426
- const graph = deserializeTaskGraph2(taskFileToSerializedGraph(file.tasks, sessionId));
427
- const tags = ["session", `session:${sessionId}`];
428
- const existing = (await listBoards2(projectRoot)).find(
429
- (b) => b.tags?.includes(`session:${sessionId}`)
430
- );
431
- if (existing) {
432
- const result = await syncBoardFromTaskGraph2(projectRoot, existing.id, graph, {
329
+ const result = await (async () => {
330
+ switch (input.action) {
331
+ case "list_boards": {
332
+ const boards = await listBoards2(projectRoot);
333
+ return { ok: true, message: `${boards.length} board(s).`, boards };
334
+ }
335
+ case "get_board": {
336
+ const board = await requireBoard(projectRoot, input.boardId);
337
+ return board ? okBoard(board) : fail("Board not found.");
338
+ }
339
+ case "create_board": {
340
+ if (!input.title) return fail("create_board requires title.");
341
+ const board = await createBoard2(projectRoot, {
342
+ title: input.title,
343
+ ...input.description !== void 0 ? { description: input.description } : {},
344
+ ...input.tags !== void 0 ? { tags: input.tags } : {},
345
+ ...input.generatedBy !== void 0 ? { generatedBy: input.generatedBy } : {}
346
+ });
347
+ return { ok: true, message: `Board created: ${board.title}`, board };
348
+ }
349
+ case "update_board": {
350
+ if (!input.boardId) return fail("update_board requires boardId.");
351
+ const board = await updateBoard2(projectRoot, input.boardId, {
352
+ ...input.title !== void 0 ? { title: input.title } : {},
353
+ ...input.description !== void 0 ? { description: input.description } : {},
354
+ ...input.tags !== void 0 ? { tags: input.tags } : {}
355
+ });
356
+ return board ? okBoard(board, "Board updated.") : fail("Board not found.");
357
+ }
358
+ case "duplicate_board": {
359
+ if (!input.boardId) return fail("duplicate_board requires boardId.");
360
+ const board = await duplicateBoard(projectRoot, input.boardId, {
361
+ ...input.title !== void 0 ? { title: input.title } : {},
362
+ ...input.generatedBy !== void 0 ? { generatedBy: input.generatedBy } : {},
363
+ ...input.includeTasks !== void 0 ? { includeTasks: input.includeTasks } : {},
364
+ ...input.includeCompletedTasks !== void 0 ? { includeCompletedTasks: input.includeCompletedTasks } : {},
365
+ ...input.preserveAssignment !== void 0 ? { preserveAssignment: input.preserveAssignment } : {}
366
+ });
367
+ return board ? okBoard(board, "Board duplicated.") : fail("Board not found.");
368
+ }
369
+ case "delete_board": {
370
+ if (!input.boardId) return fail("delete_board requires boardId.");
371
+ const removed = await removeBoard2(projectRoot, input.boardId);
372
+ return { ok: removed, message: removed ? "Board deleted." : "Board not found." };
373
+ }
374
+ case "generate_board": {
375
+ if (!input.description) return fail("generate_board requires description.");
376
+ const boardInput = generateBoardFromDescription({
377
+ description: input.description,
378
+ ...input.title !== void 0 ? { title: input.title } : {},
379
+ ...input.context !== void 0 ? { context: input.context } : {},
380
+ ...input.columns !== void 0 ? { columns: input.columns } : {}
381
+ });
382
+ const board = await createBoard2(projectRoot, boardInput);
383
+ for (const taskInput2 of parseLinesIntoTasks(
384
+ input.description,
385
+ board.columns[0]?.id ?? "backlog"
386
+ )) {
387
+ await addTask(projectRoot, board.id, taskInput2);
388
+ }
389
+ return okBoard(await getBoard2(projectRoot, board.id) ?? board, "Board generated.");
390
+ }
391
+ case "export_markdown": {
392
+ const board = await requireBoard(projectRoot, input.boardId);
393
+ if (!board) return fail("Board not found.");
394
+ return {
395
+ ok: true,
396
+ message: "Board exported.",
397
+ board,
398
+ markdown: exportBoardAsMarkdown(board)
399
+ };
400
+ }
401
+ case "export_task_graph": {
402
+ if (!input.boardId) return fail("export_task_graph requires boardId.");
403
+ const exported = await exportBoardToTaskGraph(projectRoot, input.boardId, {
404
+ ...input.graphId !== void 0 ? { graphId: input.graphId } : {},
405
+ ...input.specId !== void 0 ? { specId: input.specId } : {},
406
+ ...input.title !== void 0 ? { title: input.title } : {},
407
+ ...input.preserveOriginTaskIds !== void 0 ? { preserveOriginTaskIds: input.preserveOriginTaskIds } : {},
408
+ ...input.includeArchived !== void 0 ? { includeArchived: input.includeArchived } : {}
409
+ });
410
+ if (!exported) return fail("Board not found.");
411
+ return {
412
+ ok: true,
413
+ message: `Task graph exported with ${exported.graph.nodes.size} node(s).`,
414
+ board: exported.board,
415
+ taskGraph: serializeTaskGraph(exported.graph)
416
+ };
417
+ }
418
+ case "sync_task_graph": {
419
+ if (!input.boardId || !input.taskGraph) {
420
+ return fail("sync_task_graph requires boardId and taskGraph.");
421
+ }
422
+ const graph = deserializeTaskGraph2(input.taskGraph);
423
+ const result2 = await syncBoardFromTaskGraph2(projectRoot, input.boardId, graph, {
424
+ ...input.title !== void 0 ? { title: input.title } : {},
425
+ ...input.description !== void 0 ? { description: input.description } : {},
426
+ ...input.tags !== void 0 ? { tags: input.tags } : {},
427
+ ...input.generatedBy !== void 0 ? { generatedBy: input.generatedBy } : {},
428
+ ...input.sourceSystem !== void 0 ? { sourceSystem: input.sourceSystem } : {},
429
+ ...input.phaseId !== void 0 ? { phaseId: input.phaseId } : {},
430
+ ...input.includeCompletedTasks !== void 0 ? { includeCompletedTasks: input.includeCompletedTasks } : {},
431
+ ...input.archiveMissingTasks !== void 0 ? { archiveMissingTasks: input.archiveMissingTasks } : {},
432
+ ...input.preserveManualDependencies !== void 0 ? { preserveManualDependencies: input.preserveManualDependencies } : {}
433
+ });
434
+ return result2 ? {
435
+ ok: true,
436
+ message: `Task graph synced: ${result2.createdTaskIds.length} created, ${result2.updatedTaskIds.length} updated, ${result2.archivedTaskIds.length} archived.`,
437
+ board: result2.board
438
+ } : fail("Board not found.");
439
+ }
440
+ case "create_from_graph": {
441
+ if (!input.taskGraph) return fail("create_from_graph requires taskGraph.");
442
+ const graph = deserializeTaskGraph2(input.taskGraph);
443
+ const { board } = await createBoardFromTaskGraph(projectRoot, graph, {
444
+ ...input.title !== void 0 ? { title: input.title } : {},
445
+ ...input.description !== void 0 ? { description: input.description } : {},
446
+ ...input.tags !== void 0 ? { tags: input.tags } : {},
447
+ ...input.generatedBy !== void 0 ? { generatedBy: input.generatedBy } : {},
448
+ ...input.sourceSystem !== void 0 ? { sourceSystem: input.sourceSystem } : {},
449
+ ...input.phaseId !== void 0 ? { phaseId: input.phaseId } : {},
450
+ ...input.includeCompletedTasks !== void 0 ? { includeCompletedTasks: input.includeCompletedTasks } : {}
451
+ });
452
+ return {
453
+ ok: true,
454
+ message: `Created board "${board.title}" from task graph with ${board.tasks.length} tasks.`,
455
+ board
456
+ };
457
+ }
458
+ case "import_session_tasks": {
459
+ const taskPath = ctx.meta?.["task.path"];
460
+ if (!taskPath) return fail("No session task file for this session.");
461
+ const file = await loadTasks2(taskPath);
462
+ if (!file || file.tasks.length === 0) return fail("No session tasks to import.");
463
+ const sessionId = ctx.session?.id ?? file.sessionId ?? "session";
464
+ const graph = deserializeTaskGraph2(taskFileToSerializedGraph(file.tasks, sessionId));
465
+ const tags = ["session", `session:${sessionId}`];
466
+ const existing = (await listBoards2(projectRoot)).find(
467
+ (b) => b.tags?.includes(`session:${sessionId}`)
468
+ );
469
+ if (existing) {
470
+ const result2 = await syncBoardFromTaskGraph2(projectRoot, existing.id, graph, {
471
+ sourceSystem: "session",
472
+ tags,
473
+ archiveMissingTasks: true,
474
+ includeCompletedTasks: true
475
+ });
476
+ return result2 ? {
477
+ ok: true,
478
+ message: `Synced ${file.tasks.length} session tasks into board "${result2.board.title}".`,
479
+ board: result2.board
480
+ } : fail("Session board vanished mid-sync.");
481
+ }
482
+ const { board } = await createBoardFromTaskGraph(projectRoot, graph, {
483
+ title: `Session tasks (${sessionId.slice(0, 8)})`,
433
484
  sourceSystem: "session",
434
- tags,
435
- archiveMissingTasks: true,
436
- includeCompletedTasks: true
485
+ tags
437
486
  });
438
- return result ? {
487
+ return {
439
488
  ok: true,
440
- message: `Synced ${file.tasks.length} session tasks into board "${result.board.title}".`,
441
- board: result.board
442
- } : fail("Session board vanished mid-sync.");
443
- }
444
- const { board } = await createBoardFromTaskGraph(projectRoot, graph, {
445
- title: `Session tasks (${sessionId.slice(0, 8)})`,
446
- sourceSystem: "session",
447
- tags
448
- });
449
- return {
450
- ok: true,
451
- message: `Imported ${file.tasks.length} session tasks into new board "${board.title}".`,
452
- board
453
- };
454
- }
455
- case "search_tasks": {
456
- const tasks = await searchKanban(projectRoot, {
457
- query: input.query,
458
- boardId: input.boardId,
459
- assignedAgent: input.agentId,
460
- status: input.status,
461
- priority: input.priority,
462
- label: input.labels?.[0],
463
- chainId: input.chainId
464
- });
465
- return { ok: true, message: `${tasks.length} task(s) matched.`, tasks };
466
- }
467
- case "ready_tasks": {
468
- const tasks = await listReadyTasks(projectRoot, {
469
- query: input.query,
470
- boardId: input.boardId,
471
- assignedAgent: input.agentId,
472
- priority: input.priority,
473
- label: input.labels?.[0],
474
- chainId: input.chainId,
475
- limit: input.limit
476
- });
477
- return { ok: true, message: `${tasks.length} ready task(s).`, tasks };
478
- }
479
- case "snapshot": {
480
- const snapshot = await getKanbanOrchestrationSnapshot(projectRoot, {
481
- query: input.query,
482
- boardId: input.boardId,
483
- assignedAgent: input.agentId,
484
- status: input.status,
485
- priority: input.priority,
486
- label: input.labels?.[0],
487
- chainId: input.chainId
488
- });
489
- return {
490
- ok: true,
491
- message: `${snapshot.ready.length} ready, ${snapshot.running.length} running, ${snapshot.blocked.length} blocked.`,
492
- snapshot
493
- };
494
- }
495
- case "add_column": {
496
- if (!input.boardId || !input.title) return fail("add_column requires boardId and title.");
497
- const result = await addColumn(projectRoot, input.boardId, {
498
- title: input.title,
499
- ...input.description !== void 0 ? { description: input.description } : {}
500
- });
501
- return result ? okBoard(result.board, "Column added.") : fail("Board not found.");
502
- }
503
- case "update_column": {
504
- if (!input.boardId || !input.columnId)
505
- return fail("update_column requires boardId and columnId.");
506
- const board = await updateColumn(projectRoot, input.boardId, input.columnId, {
507
- ...input.title !== void 0 ? { title: input.title } : {},
508
- ...input.description !== void 0 ? { description: input.description } : {},
509
- ...input.order !== void 0 ? { order: input.order } : {}
510
- });
511
- return board ? okBoard(board, "Column updated.") : fail("Column not found.");
512
- }
513
- case "delete_column": {
514
- if (!input.boardId || !input.columnId)
515
- return fail("delete_column requires boardId and columnId.");
516
- const board = await removeColumn(projectRoot, input.boardId, input.columnId, {
517
- moveTasksToColumnId: input.moveTasksToColumnId
518
- });
519
- return board ? okBoard(board, "Column deleted.") : fail("Column not found.");
520
- }
521
- case "add_task": {
522
- if (!input.boardId || !input.title) return fail("add_task requires boardId and title.");
523
- const result = await addTask(projectRoot, input.boardId, taskInput(input));
524
- return result ? okTask(result.board, result.task, "Task added.") : fail("Board not found.");
525
- }
526
- case "split_task": {
527
- if (!input.boardId || !input.taskId || !input.childTitles?.length) {
528
- return fail("split_task requires boardId, taskId, and childTitles.");
529
- }
530
- const result = await splitTask(projectRoot, input.boardId, input.taskId, {
531
- titles: input.childTitles,
532
- ...input.targetColumnId !== void 0 ? { columnId: input.targetColumnId } : {},
533
- ...input.inheritAssignment !== void 0 ? { inheritAssignment: input.inheritAssignment } : {},
534
- ...input.inheritLabels !== void 0 ? { inheritLabels: input.inheritLabels } : {},
535
- ...input.inheritSuccessCriteria !== void 0 ? { inheritSuccessCriteria: input.inheritSuccessCriteria } : {},
536
- ...input.inheritGoalMetrics !== void 0 ? { inheritGoalMetrics: input.inheritGoalMetrics } : {},
537
- ...input.inheritDependencies !== void 0 ? { inheritDependencies: input.inheritDependencies } : {},
538
- ...input.chainChildren !== void 0 ? { chainChildren: input.chainChildren } : {},
539
- ...input.rewireDependents !== void 0 ? { rewireDependents: input.rewireDependents } : {}
540
- });
541
- return result ? {
542
- ok: true,
543
- message: `${result.children.length} child task(s) created.`,
544
- board: result.board,
545
- task: result.parent,
546
- children: result.children
547
- } : fail("Task not found.");
548
- }
549
- case "merge_tasks": {
550
- if (!input.boardId || !input.taskIds?.length || !input.title) {
551
- return fail("merge_tasks requires boardId, taskIds, and title.");
552
- }
553
- const result = await mergeTasks(projectRoot, input.boardId, {
554
- taskIds: input.taskIds,
555
- title: input.title,
556
- ...input.description !== void 0 ? { description: input.description } : {},
557
- ...input.targetColumnId !== void 0 ? { targetColumnId: input.targetColumnId } : {},
558
- ...input.preserveAssignment !== void 0 ? { preserveAssignment: input.preserveAssignment } : {},
559
- ...input.closeSourceTasks !== void 0 ? { closeSourceTasks: input.closeSourceTasks } : {}
560
- });
561
- return result ? okTask(result.board, result.task, "Tasks merged.") : fail("Board or task not found.");
562
- }
563
- case "copy_task": {
564
- if (!input.boardId || !input.taskId || !input.targetBoardId) {
565
- return fail("copy_task requires boardId, taskId, and targetBoardId.");
566
- }
567
- const result = await copyTaskToBoard(
568
- projectRoot,
569
- input.boardId,
570
- input.taskId,
571
- input.targetBoardId,
572
- {
573
- ...input.targetColumnId !== void 0 ? { targetColumnId: input.targetColumnId } : {},
574
- ...input.order !== void 0 ? { targetOrder: input.order } : {},
575
- ...input.preserveAssignment !== void 0 ? { preserveAssignment: input.preserveAssignment } : {},
576
- ...input.preserveDependencies !== void 0 ? { preserveDependencies: input.preserveDependencies } : {}
489
+ message: `Imported ${file.tasks.length} session tasks into new board "${board.title}".`,
490
+ board
491
+ };
492
+ }
493
+ case "search_tasks": {
494
+ const tasks = await searchKanban(projectRoot, {
495
+ query: input.query,
496
+ boardId: input.boardId,
497
+ assignedAgent: input.agentId,
498
+ status: input.status,
499
+ priority: input.priority,
500
+ label: input.labels?.[0],
501
+ chainId: input.chainId
502
+ });
503
+ return { ok: true, message: `${tasks.length} task(s) matched.`, tasks };
504
+ }
505
+ case "ready_tasks": {
506
+ const tasks = await listReadyTasks(projectRoot, {
507
+ query: input.query,
508
+ boardId: input.boardId,
509
+ assignedAgent: input.agentId,
510
+ priority: input.priority,
511
+ label: input.labels?.[0],
512
+ chainId: input.chainId,
513
+ limit: input.limit
514
+ });
515
+ return { ok: true, message: `${tasks.length} ready task(s).`, tasks };
516
+ }
517
+ case "snapshot": {
518
+ const snapshot = await getKanbanOrchestrationSnapshot(projectRoot, {
519
+ query: input.query,
520
+ boardId: input.boardId,
521
+ assignedAgent: input.agentId,
522
+ status: input.status,
523
+ priority: input.priority,
524
+ label: input.labels?.[0],
525
+ chainId: input.chainId
526
+ });
527
+ return {
528
+ ok: true,
529
+ message: `${snapshot.ready.length} ready, ${snapshot.running.length} running, ${snapshot.blocked.length} blocked.`,
530
+ snapshot
531
+ };
532
+ }
533
+ case "add_column": {
534
+ if (!input.boardId || !input.title) return fail("add_column requires boardId and title.");
535
+ const result2 = await addColumn(projectRoot, input.boardId, {
536
+ title: input.title,
537
+ ...input.description !== void 0 ? { description: input.description } : {}
538
+ });
539
+ return result2 ? okBoard(result2.board, "Column added.") : fail("Board not found.");
540
+ }
541
+ case "update_column": {
542
+ if (!input.boardId || !input.columnId)
543
+ return fail("update_column requires boardId and columnId.");
544
+ const board = await updateColumn(projectRoot, input.boardId, input.columnId, {
545
+ ...input.title !== void 0 ? { title: input.title } : {},
546
+ ...input.description !== void 0 ? { description: input.description } : {},
547
+ ...input.order !== void 0 ? { order: input.order } : {}
548
+ });
549
+ return board ? okBoard(board, "Column updated.") : fail("Column not found.");
550
+ }
551
+ case "delete_column": {
552
+ if (!input.boardId || !input.columnId)
553
+ return fail("delete_column requires boardId and columnId.");
554
+ const board = await removeColumn(projectRoot, input.boardId, input.columnId, {
555
+ moveTasksToColumnId: input.moveTasksToColumnId
556
+ });
557
+ return board ? okBoard(board, "Column deleted.") : fail("Column not found.");
558
+ }
559
+ case "add_task": {
560
+ if (!input.boardId || !input.title) return fail("add_task requires boardId and title.");
561
+ const result2 = await addTask(projectRoot, input.boardId, taskInput(input));
562
+ return result2 ? okTask(result2.board, result2.task, "Task added.") : fail("Board not found.");
563
+ }
564
+ case "split_task": {
565
+ if (!input.boardId || !input.taskId || !input.childTitles?.length) {
566
+ return fail("split_task requires boardId, taskId, and childTitles.");
577
567
  }
578
- );
579
- return result ? okTask(result.targetBoard, result.task, "Task copied to target board.") : fail("Board or task not found.");
580
- }
581
- case "transfer_task": {
582
- if (!input.boardId || !input.taskId || !input.targetBoardId) {
583
- return fail("transfer_task requires boardId, taskId, and targetBoardId.");
584
- }
585
- const result = await transferTaskToBoard(
586
- projectRoot,
587
- input.boardId,
588
- input.taskId,
589
- input.targetBoardId,
590
- {
568
+ const result2 = await splitTask(projectRoot, input.boardId, input.taskId, {
569
+ titles: input.childTitles,
570
+ ...input.targetColumnId !== void 0 ? { columnId: input.targetColumnId } : {},
571
+ ...input.inheritAssignment !== void 0 ? { inheritAssignment: input.inheritAssignment } : {},
572
+ ...input.inheritLabels !== void 0 ? { inheritLabels: input.inheritLabels } : {},
573
+ ...input.inheritSuccessCriteria !== void 0 ? { inheritSuccessCriteria: input.inheritSuccessCriteria } : {},
574
+ ...input.inheritGoalMetrics !== void 0 ? { inheritGoalMetrics: input.inheritGoalMetrics } : {},
575
+ ...input.inheritDependencies !== void 0 ? { inheritDependencies: input.inheritDependencies } : {},
576
+ ...input.chainChildren !== void 0 ? { chainChildren: input.chainChildren } : {},
577
+ ...input.rewireDependents !== void 0 ? { rewireDependents: input.rewireDependents } : {}
578
+ });
579
+ return result2 ? {
580
+ ok: true,
581
+ message: `${result2.children.length} child task(s) created.`,
582
+ board: result2.board,
583
+ task: result2.parent,
584
+ children: result2.children
585
+ } : fail("Task not found.");
586
+ }
587
+ case "merge_tasks": {
588
+ if (!input.boardId || !input.taskIds?.length || !input.title) {
589
+ return fail("merge_tasks requires boardId, taskIds, and title.");
590
+ }
591
+ const result2 = await mergeTasks(projectRoot, input.boardId, {
592
+ taskIds: input.taskIds,
593
+ title: input.title,
594
+ ...input.description !== void 0 ? { description: input.description } : {},
591
595
  ...input.targetColumnId !== void 0 ? { targetColumnId: input.targetColumnId } : {},
592
- ...input.order !== void 0 ? { targetOrder: input.order } : {},
593
596
  ...input.preserveAssignment !== void 0 ? { preserveAssignment: input.preserveAssignment } : {},
594
- ...input.preserveDependencies !== void 0 ? { preserveDependencies: input.preserveDependencies } : {}
597
+ ...input.closeSourceTasks !== void 0 ? { closeSourceTasks: input.closeSourceTasks } : {}
598
+ });
599
+ return result2 ? okTask(result2.board, result2.task, "Tasks merged.") : fail("Board or task not found.");
600
+ }
601
+ case "copy_task": {
602
+ if (!input.boardId || !input.taskId || !input.targetBoardId) {
603
+ return fail("copy_task requires boardId, taskId, and targetBoardId.");
595
604
  }
596
- );
597
- return result ? okTask(result.targetBoard, result.task, "Task transferred to target board.") : fail("Board or task not found.");
598
- }
599
- case "get_task": {
600
- if (!input.boardId || !input.taskId) return fail("get_task requires boardId and taskId.");
601
- const task = await getTask(projectRoot, input.boardId, input.taskId);
602
- return task ? { ok: true, message: "Task loaded.", task } : fail("Task not found.");
603
- }
604
- case "update_task": {
605
- if (!input.boardId || !input.taskId)
606
- return fail("update_task requires boardId and taskId.");
607
- const board = await updateTask(
608
- projectRoot,
609
- input.boardId,
610
- input.taskId,
611
- taskPatch(input)
612
- );
613
- return board ? okBoard(board, "Task updated.") : fail("Task not found.");
614
- }
615
- case "move_task": {
616
- if (!input.boardId || !input.taskId || !input.targetColumnId) {
617
- return fail("move_task requires boardId, taskId, and targetColumnId.");
618
- }
619
- const board = await moveTask(
620
- projectRoot,
621
- input.boardId,
622
- input.taskId,
623
- input.targetColumnId,
624
- input.order
625
- );
626
- return board ? okBoard(board, "Task moved.") : fail("Move failed.");
627
- }
628
- case "delete_task": {
629
- if (!input.boardId || !input.taskId)
630
- return fail("delete_task requires boardId and taskId.");
631
- const board = await removeTask(projectRoot, input.boardId, input.taskId);
632
- return board ? okBoard(board, "Task deleted.") : fail("Task not found.");
633
- }
634
- case "set_chain": {
635
- if (!input.boardId || !input.taskIds?.length) {
636
- return fail("set_chain requires boardId and taskIds.");
637
- }
638
- const result = await setTaskChain(projectRoot, input.boardId, {
639
- taskIds: input.taskIds,
640
- ...input.chainId !== void 0 ? { chainId: input.chainId } : {},
641
- ...input.enforceDependencies !== void 0 ? { enforceDependencies: input.enforceDependencies } : {}
642
- });
643
- return result ? {
644
- ok: true,
645
- message: `Chain set: ${result.chainId}`,
646
- board: result.board,
647
- chain: result.tasks
648
- } : fail("Board or task not found.");
649
- }
650
- case "get_chain": {
651
- if (!input.boardId || !(input.taskId || input.chainId)) {
652
- return fail("get_chain requires boardId and taskId or chainId.");
653
- }
654
- const result = await getTaskChain(
655
- projectRoot,
656
- input.boardId,
657
- input.taskId ?? input.chainId ?? ""
658
- );
659
- return result ? {
660
- ok: true,
661
- message: `Chain loaded: ${result.chainId}`,
662
- board: result.board,
663
- chain: result.tasks
664
- } : fail("Chain not found.");
665
- }
666
- case "claim_task": {
667
- const result = await claimReadyTask(projectRoot, {
668
- ...input.boardId !== void 0 ? { boardId: input.boardId } : {},
669
- ...input.taskId !== void 0 ? { taskId: input.taskId } : {},
670
- ...assignmentInput(input),
671
- status: input.assignmentStatus ?? "queued"
672
- });
673
- return result ? okTask(result.board, result.task, "Task claimed.") : fail("No ready kanban task matched the claim.");
674
- }
675
- case "release_task": {
676
- if (!input.boardId || !input.taskId) {
677
- return fail("release_task requires boardId and taskId.");
678
- }
679
- const board = await releaseTaskClaim(projectRoot, input.boardId, input.taskId, {
680
- ...input.releaseStatus !== void 0 ? { status: input.releaseStatus } : {},
681
- ...input.releaseReason !== void 0 ? { reason: input.releaseReason } : {},
682
- ...input.clearAssignee !== void 0 ? { clearAssignee: input.clearAssignee } : {}
683
- });
684
- return board ? okBoard(board, "Task claim released.") : fail("Task not found.");
685
- }
686
- case "assign_task": {
687
- if (!input.boardId || !input.taskId)
688
- return fail("assign_task requires boardId and taskId.");
689
- const board = await assignTask(
690
- projectRoot,
691
- input.boardId,
692
- input.taskId,
693
- assignmentInput(input)
694
- );
695
- return board ? okBoard(board, "Task assigned.") : fail("Task not found.");
696
- }
697
- case "mark_assignment": {
698
- if (!input.boardId || !input.taskId)
699
- return fail("mark_assignment requires boardId and taskId.");
700
- const assignmentStatus = input.assignmentStatus ?? (input.status === "completed" ? "completed" : input.error ? "failed" : void 0);
701
- const board = await updateTaskAssignment(projectRoot, input.boardId, input.taskId, {
702
- ...assignmentStatus !== void 0 ? { status: assignmentStatus } : {},
703
- ...input.subagentId !== void 0 ? { subagentId: input.subagentId } : {},
704
- ...input.runTaskId !== void 0 ? { runTaskId: input.runTaskId } : {},
705
- ...input.lastResult !== void 0 ? { lastResult: input.lastResult } : {},
706
- ...input.error !== void 0 ? { error: input.error } : {},
707
- ...input.agentId !== void 0 ? { agentId: input.agentId } : {},
708
- ...input.leaseId !== void 0 ? { leaseId: input.leaseId } : {},
709
- ...input.claimedAt !== void 0 ? { claimedAt: input.claimedAt } : {},
710
- ...input.heartbeatAt !== void 0 ? { heartbeatAt: input.heartbeatAt } : {},
711
- ...input.leaseExpiresAt !== void 0 ? { leaseExpiresAt: input.leaseExpiresAt } : {},
712
- ...input.attempt !== void 0 ? { attempt: input.attempt } : {},
713
- ...input.maxAttempts !== void 0 ? { maxAttempts: input.maxAttempts } : {}
714
- });
715
- return board ? okBoard(board, "Assignment updated.") : fail("Task not found.");
716
- }
717
- case "heartbeat_assignment": {
718
- if (!input.boardId || !input.taskId) {
719
- return fail("heartbeat_assignment requires boardId and taskId.");
720
- }
721
- const board = await heartbeatTaskAssignment(projectRoot, input.boardId, input.taskId, {
722
- ...input.heartbeatAt !== void 0 ? { heartbeatAt: input.heartbeatAt } : {},
723
- ...input.leaseExpiresAt !== void 0 ? { leaseExpiresAt: input.leaseExpiresAt } : {}
724
- });
725
- return board ? okBoard(board, "Assignment heartbeat updated.") : fail("Task assignment not found.");
726
- }
727
- case "recover_stale": {
728
- if (!input.boardId) return fail("recover_stale requires boardId.");
729
- const policyFields = [
730
- input.recoveryPolicyFailOnCostCeiling !== void 0,
731
- input.recoveryPolicyReleaseOnFailureKinds !== void 0,
732
- input.recoveryPolicyReleaseOnHeartbeatDue !== void 0,
733
- input.recoveryPolicyRetryPolicyOverride !== void 0
734
- ].some(Boolean);
735
- const result = await recoverStaleTaskAssignments(projectRoot, input.boardId, {
736
- ...input.recoveryMode !== void 0 ? { mode: input.recoveryMode } : {},
737
- ...input.recoveryNow !== void 0 ? { now: input.recoveryNow } : {},
738
- ...input.releaseReason !== void 0 ? { reason: input.releaseReason } : {},
739
- ...input.clearAssignee !== void 0 ? { clearAssignee: input.clearAssignee } : {},
740
- ...policyFields ? {
741
- policy: {
742
- ...input.recoveryPolicyFailOnCostCeiling !== void 0 ? { failWhenCostCeilingSet: input.recoveryPolicyFailOnCostCeiling } : {},
743
- ...input.recoveryPolicyReleaseOnFailureKinds !== void 0 ? {
744
- releaseOnFailureKinds: input.recoveryPolicyReleaseOnFailureKinds
745
- } : {},
746
- ...input.recoveryPolicyReleaseOnHeartbeatDue !== void 0 ? {
747
- releaseOnHeartbeatDue: input.recoveryPolicyReleaseOnHeartbeatDue
748
- } : {},
749
- ...input.recoveryPolicyRetryPolicyOverride !== void 0 ? {
750
- retryPolicyOverride: input.recoveryPolicyRetryPolicyOverride
751
- } : {}
605
+ const result2 = await copyTaskToBoard(
606
+ projectRoot,
607
+ input.boardId,
608
+ input.taskId,
609
+ input.targetBoardId,
610
+ {
611
+ ...input.targetColumnId !== void 0 ? { targetColumnId: input.targetColumnId } : {},
612
+ ...input.order !== void 0 ? { targetOrder: input.order } : {},
613
+ ...input.preserveAssignment !== void 0 ? { preserveAssignment: input.preserveAssignment } : {},
614
+ ...input.preserveDependencies !== void 0 ? { preserveDependencies: input.preserveDependencies } : {}
752
615
  }
753
- } : {}
754
- });
755
- return result ? {
756
- ok: true,
757
- message: `Recovered ${result.tasks.length} stale assignment(s).`,
758
- board: result.board,
759
- recoveredTasks: result.tasks
760
- } : { ok: true, message: "No stale assignment matched.", recoveredTasks: [] };
761
- }
762
- case "events": {
763
- if (!input.boardId) return fail("events requires boardId.");
764
- const eventList = await listKanbanEvents(projectRoot, input.boardId);
765
- return {
766
- ok: true,
767
- message: `${eventList.length} event(s).`,
768
- events: eventList
769
- };
770
- }
771
- case "queue_health": {
772
- const health = await getKanbanQueueHealth(projectRoot, {
773
- ...input.boardId !== void 0 ? { boardId: input.boardId } : {}
774
- });
775
- return {
776
- ok: true,
777
- message: `Counts: ready=${health.counts.ready}, running=${health.counts.running}, stale=${health.staleAssignments.count}.`,
778
- queueHealth: health
779
- };
780
- }
781
- case "add_dependency": {
782
- if (!input.boardId || !input.taskId || !input.dependencyTaskId) {
783
- return fail("add_dependency requires boardId, taskId, and dependencyTaskId.");
784
- }
785
- const board = await addDependency(
786
- projectRoot,
787
- input.boardId,
788
- input.taskId,
789
- input.dependencyTaskId
790
- );
791
- return board ? okBoard(board, "Dependency added.") : fail("Task not found.");
792
- }
793
- case "add_goal_metric": {
794
- if (!input.boardId || !input.taskId || !input.metricName) {
795
- return fail("add_goal_metric requires boardId, taskId, and metricName.");
796
- }
797
- const board = await addGoalMetricToTask(projectRoot, input.boardId, input.taskId, {
798
- name: input.metricName,
799
- ...input.metricStatus !== void 0 ? { status: input.metricStatus } : {},
800
- ...input.metricTarget !== void 0 ? { target: input.metricTarget } : {},
801
- ...input.metricCurrent !== void 0 ? { current: input.metricCurrent } : {},
802
- ...input.metricUnit !== void 0 ? { unit: input.metricUnit } : {},
803
- ...input.metricNotes !== void 0 ? { notes: input.metricNotes } : {}
804
- });
805
- return board ? okBoard(board, "Goal metric added.") : fail("Task not found.");
806
- }
807
- case "update_goal_metric": {
808
- if (!input.boardId || !input.taskId || !input.metricId) {
809
- return fail("update_goal_metric requires boardId, taskId, and metricId.");
810
- }
811
- const board = await updateGoalMetricOnTask(
812
- projectRoot,
813
- input.boardId,
814
- input.taskId,
815
- input.metricId,
816
- {
817
- ...input.metricName !== void 0 ? { name: input.metricName } : {},
616
+ );
617
+ return result2 ? okTask(result2.targetBoard, result2.task, "Task copied to target board.") : fail("Board or task not found.");
618
+ }
619
+ case "transfer_task": {
620
+ if (!input.boardId || !input.taskId || !input.targetBoardId) {
621
+ return fail("transfer_task requires boardId, taskId, and targetBoardId.");
622
+ }
623
+ const result2 = await transferTaskToBoard(
624
+ projectRoot,
625
+ input.boardId,
626
+ input.taskId,
627
+ input.targetBoardId,
628
+ {
629
+ ...input.targetColumnId !== void 0 ? { targetColumnId: input.targetColumnId } : {},
630
+ ...input.order !== void 0 ? { targetOrder: input.order } : {},
631
+ ...input.preserveAssignment !== void 0 ? { preserveAssignment: input.preserveAssignment } : {},
632
+ ...input.preserveDependencies !== void 0 ? { preserveDependencies: input.preserveDependencies } : {}
633
+ }
634
+ );
635
+ return result2 ? okTask(result2.targetBoard, result2.task, "Task transferred to target board.") : fail("Board or task not found.");
636
+ }
637
+ case "get_task": {
638
+ if (!input.boardId || !input.taskId) return fail("get_task requires boardId and taskId.");
639
+ const task = await getTask(projectRoot, input.boardId, input.taskId);
640
+ return task ? { ok: true, message: "Task loaded.", task } : fail("Task not found.");
641
+ }
642
+ case "update_task": {
643
+ if (!input.boardId || !input.taskId)
644
+ return fail("update_task requires boardId and taskId.");
645
+ const board = await updateTask(
646
+ projectRoot,
647
+ input.boardId,
648
+ input.taskId,
649
+ taskPatch(input)
650
+ );
651
+ return board ? okBoard(board, "Task updated.") : fail("Task not found.");
652
+ }
653
+ case "transition_task": {
654
+ if (!input.boardId || !input.taskId || !input.lifecycleStage || !input.author || !input.transitionComment) {
655
+ return fail(
656
+ "transition_task requires boardId, taskId, lifecycleStage, author, and transitionComment."
657
+ );
658
+ }
659
+ const result2 = await transitionTask(projectRoot, input.boardId, input.taskId, {
660
+ to: input.lifecycleStage,
661
+ actor: input.author,
662
+ comment: input.transitionComment,
663
+ ...input.transitionAction !== void 0 ? { action: input.transitionAction } : {},
664
+ ...input.attachmentUrl !== void 0 ? {
665
+ attachment: {
666
+ url: input.attachmentUrl,
667
+ type: input.attachmentType ?? "url",
668
+ ...input.attachmentTitle !== void 0 ? { title: input.attachmentTitle } : {}
669
+ }
670
+ } : {},
671
+ patch: taskPatch(input)
672
+ });
673
+ return result2 ? okTask(result2.board, result2.task, `Task advanced to ${result2.transition.to}.`) : fail("Board or task not found.");
674
+ }
675
+ case "move_task": {
676
+ if (!input.boardId || !input.taskId || !input.targetColumnId) {
677
+ return fail("move_task requires boardId, taskId, and targetColumnId.");
678
+ }
679
+ const board = await moveTask(
680
+ projectRoot,
681
+ input.boardId,
682
+ input.taskId,
683
+ input.targetColumnId,
684
+ input.order
685
+ );
686
+ return board ? okBoard(board, "Task moved.") : fail("Move failed.");
687
+ }
688
+ case "delete_task": {
689
+ if (!input.boardId || !input.taskId)
690
+ return fail("delete_task requires boardId and taskId.");
691
+ const board = await removeTask(projectRoot, input.boardId, input.taskId);
692
+ return board ? okBoard(board, "Task deleted.") : fail("Task not found.");
693
+ }
694
+ case "set_chain": {
695
+ if (!input.boardId || !input.taskIds?.length) {
696
+ return fail("set_chain requires boardId and taskIds.");
697
+ }
698
+ const result2 = await setTaskChain(projectRoot, input.boardId, {
699
+ taskIds: input.taskIds,
700
+ ...input.chainId !== void 0 ? { chainId: input.chainId } : {},
701
+ ...input.enforceDependencies !== void 0 ? { enforceDependencies: input.enforceDependencies } : {}
702
+ });
703
+ return result2 ? {
704
+ ok: true,
705
+ message: `Chain set: ${result2.chainId}`,
706
+ board: result2.board,
707
+ chain: result2.tasks
708
+ } : fail("Board or task not found.");
709
+ }
710
+ case "get_chain": {
711
+ if (!input.boardId || !(input.taskId || input.chainId)) {
712
+ return fail("get_chain requires boardId and taskId or chainId.");
713
+ }
714
+ const result2 = await getTaskChain(
715
+ projectRoot,
716
+ input.boardId,
717
+ input.taskId ?? input.chainId ?? ""
718
+ );
719
+ return result2 ? {
720
+ ok: true,
721
+ message: `Chain loaded: ${result2.chainId}`,
722
+ board: result2.board,
723
+ chain: result2.tasks
724
+ } : fail("Chain not found.");
725
+ }
726
+ case "claim_task": {
727
+ const result2 = await claimReadyTask(projectRoot, {
728
+ ...input.boardId !== void 0 ? { boardId: input.boardId } : {},
729
+ ...input.taskId !== void 0 ? { taskId: input.taskId } : {},
730
+ ...assignmentInput(input),
731
+ status: input.assignmentStatus ?? "queued"
732
+ });
733
+ return result2 ? okTask(result2.board, result2.task, "Task claimed.") : fail("No ready kanban task matched the claim.");
734
+ }
735
+ case "release_task": {
736
+ if (!input.boardId || !input.taskId) {
737
+ return fail("release_task requires boardId and taskId.");
738
+ }
739
+ const board = await releaseTaskClaim(projectRoot, input.boardId, input.taskId, {
740
+ ...input.releaseStatus !== void 0 ? { status: input.releaseStatus } : {},
741
+ ...input.releaseReason !== void 0 ? { reason: input.releaseReason } : {},
742
+ ...input.clearAssignee !== void 0 ? { clearAssignee: input.clearAssignee } : {}
743
+ });
744
+ return board ? okBoard(board, "Task claim released.") : fail("Task not found.");
745
+ }
746
+ case "assign_task": {
747
+ if (!input.boardId || !input.taskId)
748
+ return fail("assign_task requires boardId and taskId.");
749
+ const board = await assignTask(
750
+ projectRoot,
751
+ input.boardId,
752
+ input.taskId,
753
+ assignmentInput(input)
754
+ );
755
+ return board ? okBoard(board, "Task assigned.") : fail("Task not found.");
756
+ }
757
+ case "mark_assignment": {
758
+ if (!input.boardId || !input.taskId)
759
+ return fail("mark_assignment requires boardId and taskId.");
760
+ const assignmentStatus = input.assignmentStatus ?? (input.status === "completed" ? "completed" : input.error ? "failed" : void 0);
761
+ const board = await updateTaskAssignment(projectRoot, input.boardId, input.taskId, {
762
+ ...assignmentStatus !== void 0 ? { status: assignmentStatus } : {},
763
+ ...input.subagentId !== void 0 ? { subagentId: input.subagentId } : {},
764
+ ...input.runTaskId !== void 0 ? { runTaskId: input.runTaskId } : {},
765
+ ...input.lastResult !== void 0 ? { lastResult: input.lastResult } : {},
766
+ ...input.error !== void 0 ? { error: input.error } : {},
767
+ ...input.agentId !== void 0 ? { agentId: input.agentId } : {},
768
+ ...input.leaseId !== void 0 ? { leaseId: input.leaseId } : {},
769
+ ...input.claimedAt !== void 0 ? { claimedAt: input.claimedAt } : {},
770
+ ...input.heartbeatAt !== void 0 ? { heartbeatAt: input.heartbeatAt } : {},
771
+ ...input.leaseExpiresAt !== void 0 ? { leaseExpiresAt: input.leaseExpiresAt } : {},
772
+ ...input.attempt !== void 0 ? { attempt: input.attempt } : {},
773
+ ...input.maxAttempts !== void 0 ? { maxAttempts: input.maxAttempts } : {}
774
+ });
775
+ return board ? okBoard(board, "Assignment updated.") : fail("Task not found.");
776
+ }
777
+ case "heartbeat_assignment": {
778
+ if (!input.boardId || !input.taskId) {
779
+ return fail("heartbeat_assignment requires boardId and taskId.");
780
+ }
781
+ const board = await heartbeatTaskAssignment(projectRoot, input.boardId, input.taskId, {
782
+ ...input.heartbeatAt !== void 0 ? { heartbeatAt: input.heartbeatAt } : {},
783
+ ...input.leaseExpiresAt !== void 0 ? { leaseExpiresAt: input.leaseExpiresAt } : {}
784
+ });
785
+ return board ? okBoard(board, "Assignment heartbeat updated.") : fail("Task assignment not found.");
786
+ }
787
+ case "recover_stale": {
788
+ if (!input.boardId) return fail("recover_stale requires boardId.");
789
+ const policyFields = [
790
+ input.recoveryPolicyFailOnCostCeiling !== void 0,
791
+ input.recoveryPolicyReleaseOnFailureKinds !== void 0,
792
+ input.recoveryPolicyReleaseOnHeartbeatDue !== void 0,
793
+ input.recoveryPolicyRetryPolicyOverride !== void 0
794
+ ].some(Boolean);
795
+ const result2 = await recoverStaleTaskAssignments(projectRoot, input.boardId, {
796
+ ...input.recoveryMode !== void 0 ? { mode: input.recoveryMode } : {},
797
+ ...input.recoveryNow !== void 0 ? { now: input.recoveryNow } : {},
798
+ ...input.releaseReason !== void 0 ? { reason: input.releaseReason } : {},
799
+ ...input.clearAssignee !== void 0 ? { clearAssignee: input.clearAssignee } : {},
800
+ ...policyFields ? {
801
+ policy: {
802
+ ...input.recoveryPolicyFailOnCostCeiling !== void 0 ? { failWhenCostCeilingSet: input.recoveryPolicyFailOnCostCeiling } : {},
803
+ ...input.recoveryPolicyReleaseOnFailureKinds !== void 0 ? {
804
+ releaseOnFailureKinds: input.recoveryPolicyReleaseOnFailureKinds
805
+ } : {},
806
+ ...input.recoveryPolicyReleaseOnHeartbeatDue !== void 0 ? {
807
+ releaseOnHeartbeatDue: input.recoveryPolicyReleaseOnHeartbeatDue
808
+ } : {},
809
+ ...input.recoveryPolicyRetryPolicyOverride !== void 0 ? {
810
+ retryPolicyOverride: input.recoveryPolicyRetryPolicyOverride
811
+ } : {}
812
+ }
813
+ } : {}
814
+ });
815
+ return result2 ? {
816
+ ok: true,
817
+ message: `Recovered ${result2.tasks.length} stale assignment(s).`,
818
+ board: result2.board,
819
+ recoveredTasks: result2.tasks
820
+ } : { ok: true, message: "No stale assignment matched.", recoveredTasks: [] };
821
+ }
822
+ case "events": {
823
+ if (!input.boardId) return fail("events requires boardId.");
824
+ const eventList = await listKanbanEvents(projectRoot, input.boardId);
825
+ return {
826
+ ok: true,
827
+ message: `${eventList.length} event(s).`,
828
+ events: eventList
829
+ };
830
+ }
831
+ case "queue_health": {
832
+ const health = await getKanbanQueueHealth(projectRoot, {
833
+ ...input.boardId !== void 0 ? { boardId: input.boardId } : {}
834
+ });
835
+ return {
836
+ ok: true,
837
+ message: `Counts: ready=${health.counts.ready}, running=${health.counts.running}, stale=${health.staleAssignments.count}.`,
838
+ queueHealth: health
839
+ };
840
+ }
841
+ case "add_dependency": {
842
+ if (!input.boardId || !input.taskId || !input.dependencyTaskId) {
843
+ return fail("add_dependency requires boardId, taskId, and dependencyTaskId.");
844
+ }
845
+ const board = await addDependency(
846
+ projectRoot,
847
+ input.boardId,
848
+ input.taskId,
849
+ input.dependencyTaskId
850
+ );
851
+ return board ? okBoard(board, "Dependency added.") : fail("Task not found.");
852
+ }
853
+ case "add_goal_metric": {
854
+ if (!input.boardId || !input.taskId || !input.metricName) {
855
+ return fail("add_goal_metric requires boardId, taskId, and metricName.");
856
+ }
857
+ const board = await addGoalMetricToTask(projectRoot, input.boardId, input.taskId, {
858
+ name: input.metricName,
818
859
  ...input.metricStatus !== void 0 ? { status: input.metricStatus } : {},
819
860
  ...input.metricTarget !== void 0 ? { target: input.metricTarget } : {},
820
861
  ...input.metricCurrent !== void 0 ? { current: input.metricCurrent } : {},
821
862
  ...input.metricUnit !== void 0 ? { unit: input.metricUnit } : {},
822
863
  ...input.metricNotes !== void 0 ? { notes: input.metricNotes } : {}
864
+ });
865
+ return board ? okBoard(board, "Goal metric added.") : fail("Task not found.");
866
+ }
867
+ case "update_goal_metric": {
868
+ if (!input.boardId || !input.taskId || !input.metricId) {
869
+ return fail("update_goal_metric requires boardId, taskId, and metricId.");
823
870
  }
824
- );
825
- return board ? okBoard(board, "Goal metric updated.") : fail("Metric not found.");
826
- }
827
- case "add_check": {
828
- if (!input.boardId || !input.taskId || !input.checkDescription) {
829
- return fail("add_check requires boardId, taskId, and checkDescription.");
830
- }
831
- const board = await addCheckToTask(projectRoot, input.boardId, input.taskId, {
832
- description: input.checkDescription,
833
- type: "manual",
834
- status: input.checkStatus
835
- });
836
- return board ? okBoard(board, "Check added.") : fail("Task not found.");
837
- }
838
- case "update_check": {
839
- if (!input.boardId || !input.taskId || !input.checkId) {
840
- return fail("update_check requires boardId, taskId, and checkId.");
841
- }
842
- const board = await updateCheckOnTask(
843
- projectRoot,
844
- input.boardId,
845
- input.taskId,
846
- input.checkId,
847
- {
848
- ...input.checkDescription !== void 0 ? { description: input.checkDescription } : {},
849
- ...input.checkStatus !== void 0 ? { status: input.checkStatus } : {}
871
+ const board = await updateGoalMetricOnTask(
872
+ projectRoot,
873
+ input.boardId,
874
+ input.taskId,
875
+ input.metricId,
876
+ {
877
+ ...input.metricName !== void 0 ? { name: input.metricName } : {},
878
+ ...input.metricStatus !== void 0 ? { status: input.metricStatus } : {},
879
+ ...input.metricTarget !== void 0 ? { target: input.metricTarget } : {},
880
+ ...input.metricCurrent !== void 0 ? { current: input.metricCurrent } : {},
881
+ ...input.metricUnit !== void 0 ? { unit: input.metricUnit } : {},
882
+ ...input.metricNotes !== void 0 ? { notes: input.metricNotes } : {}
883
+ }
884
+ );
885
+ return board ? okBoard(board, "Goal metric updated.") : fail("Metric not found.");
886
+ }
887
+ case "add_check": {
888
+ if (!input.boardId || !input.taskId || !input.checkDescription) {
889
+ return fail("add_check requires boardId, taskId, and checkDescription.");
850
890
  }
851
- );
852
- return board ? okBoard(board, "Check updated.") : fail("Check not found.");
853
- }
854
- case "add_note": {
855
- if (!input.boardId || !input.taskId || !input.note)
856
- return fail("add_note requires boardId, taskId, and note.");
857
- const board = await addNoteToTask(projectRoot, input.boardId, input.taskId, {
858
- author: input.author ?? "agent",
859
- content: input.note
860
- });
861
- return board ? okBoard(board, "Note added.") : fail("Task not found.");
862
- }
863
- case "add_link": {
864
- if (!input.boardId || !input.taskId || !input.url)
865
- return fail("add_link requires boardId, taskId, and url.");
866
- const board = await addLinkToTask(projectRoot, input.boardId, input.taskId, {
867
- url: input.url,
868
- type: input.linkType ?? "url",
869
- ...input.linkTitle !== void 0 ? { title: input.linkTitle } : {}
870
- });
871
- return board ? okBoard(board, "Link added.") : fail("Task not found.");
891
+ const board = await addCheckToTask(projectRoot, input.boardId, input.taskId, {
892
+ description: input.checkDescription,
893
+ type: "manual",
894
+ status: input.checkStatus
895
+ });
896
+ return board ? okBoard(board, "Check added.") : fail("Task not found.");
897
+ }
898
+ case "update_check": {
899
+ if (!input.boardId || !input.taskId || !input.checkId) {
900
+ return fail("update_check requires boardId, taskId, and checkId.");
901
+ }
902
+ const board = await updateCheckOnTask(
903
+ projectRoot,
904
+ input.boardId,
905
+ input.taskId,
906
+ input.checkId,
907
+ {
908
+ ...input.checkDescription !== void 0 ? { description: input.checkDescription } : {},
909
+ ...input.checkStatus !== void 0 ? { status: input.checkStatus } : {}
910
+ }
911
+ );
912
+ return board ? okBoard(board, "Check updated.") : fail("Check not found.");
913
+ }
914
+ case "add_note": {
915
+ if (!input.boardId || !input.taskId || !input.note)
916
+ return fail("add_note requires boardId, taskId, and note.");
917
+ const board = await addNoteToTask(projectRoot, input.boardId, input.taskId, {
918
+ author: input.author ?? "agent",
919
+ content: input.note
920
+ });
921
+ return board ? okBoard(board, "Note added.") : fail("Task not found.");
922
+ }
923
+ case "add_link": {
924
+ if (!input.boardId || !input.taskId || !input.url)
925
+ return fail("add_link requires boardId, taskId, and url.");
926
+ const board = await addLinkToTask(projectRoot, input.boardId, input.taskId, {
927
+ url: input.url,
928
+ type: input.linkType ?? "url",
929
+ ...input.linkTitle !== void 0 ? { title: input.linkTitle } : {}
930
+ });
931
+ return board ? okBoard(board, "Link added.") : fail("Task not found.");
932
+ }
933
+ default:
934
+ return fail(`Unknown kanban action: ${input.action}`);
872
935
  }
873
- default:
874
- return fail(`Unknown kanban action: ${input.action}`);
875
- }
936
+ })();
937
+ return withPresence(result);
876
938
  } catch (err) {
877
939
  return fail(err instanceof Error ? err.message : String(err));
878
940
  }
@@ -896,6 +958,7 @@ function taskInput(input) {
896
958
  title: input.title ?? "",
897
959
  columnId: input.columnId,
898
960
  description: input.description,
961
+ dueDate: input.dueDate,
899
962
  priority: input.priority,
900
963
  ...input.taskType !== void 0 ? { type: input.taskType } : {},
901
964
  status: input.status,
@@ -904,7 +967,67 @@ function taskInput(input) {
904
967
  ...input.assignee ?? assignment?.name ?? assignment?.agentId ? { assignee: input.assignee ?? assignment?.name ?? assignment?.agentId } : {},
905
968
  ...mergedDependsOn(input) ? { dependsOn: mergedDependsOn(input) } : {},
906
969
  ...input.estimatedHours !== void 0 ? { estimatedHours: input.estimatedHours } : {},
907
- ...assignment ? { assignment } : {}
970
+ ...input.actualHours !== void 0 ? { actualHours: input.actualHours } : {},
971
+ ...assignment ? { assignment } : {},
972
+ ...input.order !== void 0 ? { order: input.order } : {},
973
+ // Sprint 3 durable task-level fields
974
+ ...input.retryPolicy !== void 0 ? { retryPolicy: input.retryPolicy } : {},
975
+ ...input.costCeilingUsd !== void 0 ? { costCeilingUsd: input.costCeilingUsd } : {},
976
+ // Relationships — chainId alone cannot construct a KanbanTaskChainRef,
977
+ // so chain/previousTaskId/nextTaskId must be set via update_task.
978
+ ...input.childTitles !== void 0 ? { childTaskIds: input.childTitles } : {},
979
+ // Detail objects
980
+ ...input.checkDescription !== void 0 ? {
981
+ successCriteria: [
982
+ {
983
+ id: randomUUID(),
984
+ description: input.checkDescription,
985
+ type: "manual",
986
+ status: input.checkStatus ?? "pending"
987
+ }
988
+ ]
989
+ } : {},
990
+ ...input.metricName !== void 0 ? {
991
+ goalMetrics: [
992
+ {
993
+ id: randomUUID(),
994
+ name: input.metricName,
995
+ status: input.metricStatus ?? "pending",
996
+ ...input.metricTarget !== void 0 ? { target: input.metricTarget } : {},
997
+ ...input.metricCurrent !== void 0 ? { current: input.metricCurrent } : {},
998
+ ...input.metricUnit !== void 0 ? { unit: input.metricUnit } : {},
999
+ ...input.metricNotes !== void 0 ? { notes: input.metricNotes } : {}
1000
+ }
1001
+ ]
1002
+ } : {},
1003
+ ...input.url !== void 0 ? {
1004
+ links: [
1005
+ {
1006
+ url: input.url,
1007
+ type: input.linkType ?? "url",
1008
+ ...input.linkTitle !== void 0 ? { title: input.linkTitle } : {}
1009
+ }
1010
+ ]
1011
+ } : {},
1012
+ ...input.note !== void 0 ? {
1013
+ notes: [
1014
+ {
1015
+ id: randomUUID(),
1016
+ author: input.author ?? "agent",
1017
+ content: input.note,
1018
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
1019
+ }
1020
+ ]
1021
+ } : {},
1022
+ // Origin tracking
1023
+ ...input.graphId !== void 0 ? {
1024
+ origin: {
1025
+ system: input.sourceSystem ?? "kanban-tool",
1026
+ ...input.graphId !== void 0 ? { graphId: input.graphId } : {},
1027
+ ...input.specId !== void 0 ? { specId: input.specId } : {},
1028
+ ...input.phaseId !== void 0 ? { phaseId: input.phaseId } : {}
1029
+ }
1030
+ } : {}
908
1031
  };
909
1032
  }
910
1033
  function mergedDependsOn(input) {
@@ -918,6 +1041,7 @@ function taskPatch(input) {
918
1041
  return {
919
1042
  title: input.title,
920
1043
  description: input.description,
1044
+ dueDate: input.dueDate,
921
1045
  columnId: input.columnId,
922
1046
  order: input.order,
923
1047
  priority: input.priority,