@hasna/todos 0.9.28 → 0.9.29

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/cli/index.js CHANGED
@@ -3198,7 +3198,18 @@ function updateTask(id, input, db) {
3198
3198
  logTaskChange(id, "update", "assigned_to", task.assigned_to, input.assigned_to, agentId, d);
3199
3199
  if (input.approved_by !== undefined)
3200
3200
  logTaskChange(id, "approve", "approved_by", null, input.approved_by, agentId, d);
3201
- return getTask(id, d);
3201
+ return {
3202
+ ...task,
3203
+ ...Object.fromEntries(Object.entries(input).filter(([, v]) => v !== undefined)),
3204
+ tags: input.tags ?? task.tags,
3205
+ metadata: input.metadata ?? task.metadata,
3206
+ version: task.version + 1,
3207
+ updated_at: now(),
3208
+ completed_at: input.status === "completed" ? now() : task.completed_at,
3209
+ requires_approval: input.requires_approval !== undefined ? input.requires_approval : task.requires_approval,
3210
+ approved_by: input.approved_by ?? task.approved_by,
3211
+ approved_at: input.approved_by ? now() : task.approved_at
3212
+ };
3202
3213
  }
3203
3214
  function deleteTask(id, db) {
3204
3215
  const d = db || getDatabase();
@@ -3220,6 +3231,9 @@ function getBlockingDeps(id, db) {
3220
3231
  }
3221
3232
  function startTask(id, agentId, db) {
3222
3233
  const d = db || getDatabase();
3234
+ const task = getTask(id, d);
3235
+ if (!task)
3236
+ throw new TaskNotFoundError(id);
3223
3237
  const blocking = getBlockingDeps(id, d);
3224
3238
  if (blocking.length > 0) {
3225
3239
  const blockerIds = blocking.map((b) => b.id.slice(0, 8)).join(", ");
@@ -3230,15 +3244,12 @@ function startTask(id, agentId, db) {
3230
3244
  const result = d.run(`UPDATE tasks SET status = 'in_progress', assigned_to = ?, locked_by = ?, locked_at = ?, version = version + 1, updated_at = ?
3231
3245
  WHERE id = ? AND (locked_by IS NULL OR locked_by = ? OR locked_at < ?)`, [agentId, agentId, timestamp, timestamp, id, agentId, cutoff]);
3232
3246
  if (result.changes === 0) {
3233
- const current = getTask(id, d);
3234
- if (!current)
3235
- throw new TaskNotFoundError(id);
3236
- if (current.locked_by && current.locked_by !== agentId && !isLockExpired(current.locked_at)) {
3237
- throw new LockError(id, current.locked_by);
3247
+ if (task.locked_by && task.locked_by !== agentId && !isLockExpired(task.locked_at)) {
3248
+ throw new LockError(id, task.locked_by);
3238
3249
  }
3239
3250
  }
3240
3251
  logTaskChange(id, "start", "status", "pending", "in_progress", agentId, d);
3241
- return getTask(id, d);
3252
+ return { ...task, status: "in_progress", assigned_to: agentId, locked_by: agentId, locked_at: timestamp, version: task.version + 1, updated_at: timestamp };
3242
3253
  }
3243
3254
  function completeTask(id, agentId, db, evidence) {
3244
3255
  const d = db || getDatabase();
@@ -3250,14 +3261,15 @@ function completeTask(id, agentId, db, evidence) {
3250
3261
  }
3251
3262
  checkCompletionGuard(task, agentId || null, d);
3252
3263
  if (evidence) {
3253
- const meta = { ...task.metadata, _evidence: evidence };
3254
- d.run("UPDATE tasks SET metadata = ? WHERE id = ?", [JSON.stringify(meta), id]);
3264
+ const meta2 = { ...task.metadata, _evidence: evidence };
3265
+ d.run("UPDATE tasks SET metadata = ? WHERE id = ?", [JSON.stringify(meta2), id]);
3255
3266
  }
3256
3267
  const timestamp = now();
3257
3268
  d.run(`UPDATE tasks SET status = 'completed', locked_by = NULL, locked_at = NULL, completed_at = ?, version = version + 1, updated_at = ?
3258
3269
  WHERE id = ?`, [timestamp, timestamp, id]);
3259
3270
  logTaskChange(id, "complete", "status", task.status, "completed", agentId || null, d);
3260
- return getTask(id, d);
3271
+ const meta = evidence ? { ...task.metadata, _evidence: evidence } : task.metadata;
3272
+ return { ...task, status: "completed", locked_by: null, locked_at: null, completed_at: timestamp, version: task.version + 1, updated_at: timestamp, metadata: meta };
3261
3273
  }
3262
3274
  function lockTask(id, agentId, db) {
3263
3275
  const d = db || getDatabase();
@@ -8363,22 +8375,22 @@ var init_mcp = __esm(() => {
8363
8375
  version: "0.9.15"
8364
8376
  });
8365
8377
  server.tool("create_task", "Create a new task", {
8366
- title: exports_external.string().describe("Task title"),
8367
- description: exports_external.string().optional().describe("Task description"),
8368
- project_id: exports_external.string().optional().describe("Project ID"),
8369
- parent_id: exports_external.string().optional().describe("Parent task ID (for subtasks)"),
8370
- priority: exports_external.enum(["low", "medium", "high", "critical"]).optional().describe("Task priority"),
8371
- status: exports_external.enum(["pending", "in_progress", "completed", "failed", "cancelled"]).optional().describe("Initial status"),
8372
- agent_id: exports_external.string().optional().describe("Creator agent ID"),
8373
- assigned_to: exports_external.string().optional().describe("Assigned agent ID"),
8374
- session_id: exports_external.string().optional().describe("Session ID"),
8375
- working_dir: exports_external.string().optional().describe("Working directory context"),
8376
- plan_id: exports_external.string().optional().describe("Plan ID to assign task to"),
8377
- task_list_id: exports_external.string().optional().describe("Task list ID to assign task to"),
8378
- tags: exports_external.array(exports_external.string()).optional().describe("Task tags"),
8379
- metadata: exports_external.record(exports_external.unknown()).optional().describe("Arbitrary metadata"),
8380
- estimated_minutes: exports_external.number().optional().describe("Estimated time in minutes"),
8381
- requires_approval: exports_external.boolean().optional().describe("Require approval before completion")
8378
+ title: exports_external.string(),
8379
+ description: exports_external.string().optional(),
8380
+ project_id: exports_external.string().optional(),
8381
+ parent_id: exports_external.string().optional(),
8382
+ priority: exports_external.enum(["low", "medium", "high", "critical"]).optional(),
8383
+ status: exports_external.enum(["pending", "in_progress", "completed", "failed", "cancelled"]).optional(),
8384
+ agent_id: exports_external.string().optional(),
8385
+ assigned_to: exports_external.string().optional(),
8386
+ session_id: exports_external.string().optional(),
8387
+ working_dir: exports_external.string().optional(),
8388
+ plan_id: exports_external.string().optional(),
8389
+ task_list_id: exports_external.string().optional(),
8390
+ tags: exports_external.array(exports_external.string()).optional(),
8391
+ metadata: exports_external.record(exports_external.unknown()).optional(),
8392
+ estimated_minutes: exports_external.number().optional(),
8393
+ requires_approval: exports_external.boolean().optional()
8382
8394
  }, async (params) => {
8383
8395
  try {
8384
8396
  const resolved = { ...params };
@@ -8397,19 +8409,19 @@ var init_mcp = __esm(() => {
8397
8409
  }
8398
8410
  });
8399
8411
  server.tool("list_tasks", "List tasks with optional filters", {
8400
- project_id: exports_external.string().optional().describe("Filter by project"),
8412
+ project_id: exports_external.string().optional(),
8401
8413
  status: exports_external.union([
8402
8414
  exports_external.enum(["pending", "in_progress", "completed", "failed", "cancelled"]),
8403
8415
  exports_external.array(exports_external.enum(["pending", "in_progress", "completed", "failed", "cancelled"]))
8404
- ]).optional().describe("Filter by status"),
8416
+ ]).optional(),
8405
8417
  priority: exports_external.union([
8406
8418
  exports_external.enum(["low", "medium", "high", "critical"]),
8407
8419
  exports_external.array(exports_external.enum(["low", "medium", "high", "critical"]))
8408
- ]).optional().describe("Filter by priority"),
8409
- assigned_to: exports_external.string().optional().describe("Filter by assigned agent"),
8410
- tags: exports_external.array(exports_external.string()).optional().describe("Filter by tags (any match)"),
8411
- plan_id: exports_external.string().optional().describe("Filter by plan"),
8412
- task_list_id: exports_external.string().optional().describe("Filter by task list")
8420
+ ]).optional(),
8421
+ assigned_to: exports_external.string().optional(),
8422
+ tags: exports_external.array(exports_external.string()).optional(),
8423
+ plan_id: exports_external.string().optional(),
8424
+ task_list_id: exports_external.string().optional()
8413
8425
  }, async (params) => {
8414
8426
  try {
8415
8427
  const resolved = { ...params };
@@ -8436,7 +8448,7 @@ ${text}` }] };
8436
8448
  }
8437
8449
  });
8438
8450
  server.tool("get_task", "Get full task details with relations", {
8439
- id: exports_external.string().describe("Task ID (full or partial)")
8451
+ id: exports_external.string()
8440
8452
  }, async ({ id }) => {
8441
8453
  try {
8442
8454
  const resolvedId = resolveId(id);
@@ -8484,17 +8496,17 @@ Parent: ${task.parent.id.slice(0, 8)} | ${task.parent.title}`);
8484
8496
  }
8485
8497
  });
8486
8498
  server.tool("update_task", "Update task fields. Version required for optimistic locking.", {
8487
- id: exports_external.string().describe("Task ID (full or partial)"),
8488
- version: exports_external.number().describe("Current version (for optimistic locking)"),
8489
- title: exports_external.string().optional().describe("New title"),
8490
- description: exports_external.string().optional().describe("New description"),
8491
- status: exports_external.enum(["pending", "in_progress", "completed", "failed", "cancelled"]).optional().describe("New status"),
8492
- priority: exports_external.enum(["low", "medium", "high", "critical"]).optional().describe("New priority"),
8493
- assigned_to: exports_external.string().optional().describe("Assign to agent"),
8494
- tags: exports_external.array(exports_external.string()).optional().describe("New tags"),
8495
- metadata: exports_external.record(exports_external.unknown()).optional().describe("New metadata"),
8496
- plan_id: exports_external.string().optional().describe("Plan ID to assign task to"),
8497
- task_list_id: exports_external.string().optional().describe("Task list ID")
8499
+ id: exports_external.string(),
8500
+ version: exports_external.number(),
8501
+ title: exports_external.string().optional(),
8502
+ description: exports_external.string().optional(),
8503
+ status: exports_external.enum(["pending", "in_progress", "completed", "failed", "cancelled"]).optional(),
8504
+ priority: exports_external.enum(["low", "medium", "high", "critical"]).optional(),
8505
+ assigned_to: exports_external.string().optional(),
8506
+ tags: exports_external.array(exports_external.string()).optional(),
8507
+ metadata: exports_external.record(exports_external.unknown()).optional(),
8508
+ plan_id: exports_external.string().optional(),
8509
+ task_list_id: exports_external.string().optional()
8498
8510
  }, async ({ id, ...rest }) => {
8499
8511
  try {
8500
8512
  const resolvedId = resolveId(id);
@@ -8505,7 +8517,7 @@ Parent: ${task.parent.id.slice(0, 8)} | ${task.parent.title}`);
8505
8517
  }
8506
8518
  });
8507
8519
  server.tool("delete_task", "Delete a task permanently", {
8508
- id: exports_external.string().describe("Task ID (full or partial)")
8520
+ id: exports_external.string()
8509
8521
  }, async ({ id }) => {
8510
8522
  try {
8511
8523
  const resolvedId = resolveId(id);
@@ -8521,8 +8533,8 @@ Parent: ${task.parent.id.slice(0, 8)} | ${task.parent.title}`);
8521
8533
  }
8522
8534
  });
8523
8535
  server.tool("start_task", "Claim, lock, and set task status to in_progress.", {
8524
- id: exports_external.string().describe("Task ID (full or partial)"),
8525
- agent_id: exports_external.string().describe("Agent claiming the task")
8536
+ id: exports_external.string(),
8537
+ agent_id: exports_external.string()
8526
8538
  }, async ({ id, agent_id }) => {
8527
8539
  try {
8528
8540
  const resolvedId = resolveId(id);
@@ -8533,8 +8545,8 @@ Parent: ${task.parent.id.slice(0, 8)} | ${task.parent.title}`);
8533
8545
  }
8534
8546
  });
8535
8547
  server.tool("complete_task", "Mark task completed and release lock.", {
8536
- id: exports_external.string().describe("Task ID (full or partial)"),
8537
- agent_id: exports_external.string().optional().describe("Agent completing the task")
8548
+ id: exports_external.string(),
8549
+ agent_id: exports_external.string().optional()
8538
8550
  }, async ({ id, agent_id }) => {
8539
8551
  try {
8540
8552
  const resolvedId = resolveId(id);
@@ -8545,8 +8557,8 @@ Parent: ${task.parent.id.slice(0, 8)} | ${task.parent.title}`);
8545
8557
  }
8546
8558
  });
8547
8559
  server.tool("lock_task", "Acquire exclusive lock on a task", {
8548
- id: exports_external.string().describe("Task ID (full or partial)"),
8549
- agent_id: exports_external.string().describe("Agent acquiring lock")
8560
+ id: exports_external.string(),
8561
+ agent_id: exports_external.string()
8550
8562
  }, async ({ id, agent_id }) => {
8551
8563
  try {
8552
8564
  const resolvedId = resolveId(id);
@@ -8560,8 +8572,8 @@ Parent: ${task.parent.id.slice(0, 8)} | ${task.parent.title}`);
8560
8572
  }
8561
8573
  });
8562
8574
  server.tool("unlock_task", "Release exclusive lock on a task", {
8563
- id: exports_external.string().describe("Task ID (full or partial)"),
8564
- agent_id: exports_external.string().optional().describe("Agent releasing lock (omit to force)")
8575
+ id: exports_external.string(),
8576
+ agent_id: exports_external.string().optional()
8565
8577
  }, async ({ id, agent_id }) => {
8566
8578
  try {
8567
8579
  const resolvedId = resolveId(id);
@@ -8572,8 +8584,8 @@ Parent: ${task.parent.id.slice(0, 8)} | ${task.parent.title}`);
8572
8584
  }
8573
8585
  });
8574
8586
  server.tool("add_dependency", "Add a dependency: task_id depends on depends_on.", {
8575
- task_id: exports_external.string().describe("Task that depends on another"),
8576
- depends_on: exports_external.string().describe("Task that must complete first")
8587
+ task_id: exports_external.string(),
8588
+ depends_on: exports_external.string()
8577
8589
  }, async ({ task_id, depends_on }) => {
8578
8590
  try {
8579
8591
  const resolvedTaskId = resolveId(task_id);
@@ -8585,8 +8597,8 @@ Parent: ${task.parent.id.slice(0, 8)} | ${task.parent.title}`);
8585
8597
  }
8586
8598
  });
8587
8599
  server.tool("remove_dependency", "Remove a dependency between tasks", {
8588
- task_id: exports_external.string().describe("Task ID"),
8589
- depends_on: exports_external.string().describe("Dependency to remove")
8600
+ task_id: exports_external.string(),
8601
+ depends_on: exports_external.string()
8590
8602
  }, async ({ task_id, depends_on }) => {
8591
8603
  try {
8592
8604
  const resolvedTaskId = resolveId(task_id);
@@ -8603,10 +8615,10 @@ Parent: ${task.parent.id.slice(0, 8)} | ${task.parent.title}`);
8603
8615
  }
8604
8616
  });
8605
8617
  server.tool("add_comment", "Add a comment/note to a task", {
8606
- task_id: exports_external.string().describe("Task ID (full or partial)"),
8607
- content: exports_external.string().describe("Comment content"),
8608
- agent_id: exports_external.string().optional().describe("Agent adding comment"),
8609
- session_id: exports_external.string().optional().describe("Session ID")
8618
+ task_id: exports_external.string(),
8619
+ content: exports_external.string(),
8620
+ agent_id: exports_external.string().optional(),
8621
+ session_id: exports_external.string().optional()
8610
8622
  }, async ({ task_id, ...rest }) => {
8611
8623
  try {
8612
8624
  const resolvedId = resolveId(task_id);
@@ -8634,10 +8646,10 @@ ${text}` }] };
8634
8646
  }
8635
8647
  });
8636
8648
  server.tool("create_project", "Register a new project", {
8637
- name: exports_external.string().describe("Project name"),
8638
- path: exports_external.string().describe("Absolute path to project"),
8639
- description: exports_external.string().optional().describe("Project description"),
8640
- task_list_id: exports_external.string().optional().describe("Custom task list ID for Claude Code sync (defaults to todos-<slugified-name>)")
8649
+ name: exports_external.string(),
8650
+ path: exports_external.string(),
8651
+ description: exports_external.string().optional(),
8652
+ task_list_id: exports_external.string().optional()
8641
8653
  }, async (params) => {
8642
8654
  try {
8643
8655
  const project = createProject(params);
@@ -8653,12 +8665,12 @@ ${text}` }] };
8653
8665
  }
8654
8666
  });
8655
8667
  server.tool("create_plan", "Create a new plan", {
8656
- name: exports_external.string().describe("Plan name"),
8657
- project_id: exports_external.string().optional().describe("Project ID"),
8658
- description: exports_external.string().optional().describe("Plan description"),
8659
- status: exports_external.enum(["active", "completed", "archived"]).optional().describe("Plan status"),
8660
- task_list_id: exports_external.string().optional().describe("Task list ID"),
8661
- agent_id: exports_external.string().optional().describe("Owner agent ID")
8668
+ name: exports_external.string(),
8669
+ project_id: exports_external.string().optional(),
8670
+ description: exports_external.string().optional(),
8671
+ status: exports_external.enum(["active", "completed", "archived"]).optional(),
8672
+ task_list_id: exports_external.string().optional(),
8673
+ agent_id: exports_external.string().optional()
8662
8674
  }, async (params) => {
8663
8675
  try {
8664
8676
  const resolved = { ...params };
@@ -8678,7 +8690,7 @@ ${text}` }] };
8678
8690
  }
8679
8691
  });
8680
8692
  server.tool("list_plans", "List plans with optional project filter", {
8681
- project_id: exports_external.string().optional().describe("Filter by project")
8693
+ project_id: exports_external.string().optional()
8682
8694
  }, async ({ project_id }) => {
8683
8695
  try {
8684
8696
  const resolvedProjectId = project_id ? resolveId(project_id, "projects") : undefined;
@@ -8698,7 +8710,7 @@ ${text}` }] };
8698
8710
  }
8699
8711
  });
8700
8712
  server.tool("get_plan", "Get plan details", {
8701
- id: exports_external.string().describe("Plan ID (full or partial)")
8713
+ id: exports_external.string()
8702
8714
  }, async ({ id }) => {
8703
8715
  try {
8704
8716
  const resolvedId = resolveId(id, "plans");
@@ -8723,12 +8735,12 @@ ${text}` }] };
8723
8735
  }
8724
8736
  });
8725
8737
  server.tool("update_plan", "Update a plan", {
8726
- id: exports_external.string().describe("Plan ID (full or partial)"),
8727
- name: exports_external.string().optional().describe("New name"),
8728
- description: exports_external.string().optional().describe("New description"),
8729
- status: exports_external.enum(["active", "completed", "archived"]).optional().describe("New status"),
8730
- task_list_id: exports_external.string().optional().describe("Task list ID"),
8731
- agent_id: exports_external.string().optional().describe("Owner agent ID")
8738
+ id: exports_external.string(),
8739
+ name: exports_external.string().optional(),
8740
+ description: exports_external.string().optional(),
8741
+ status: exports_external.enum(["active", "completed", "archived"]).optional(),
8742
+ task_list_id: exports_external.string().optional(),
8743
+ agent_id: exports_external.string().optional()
8732
8744
  }, async ({ id, ...rest }) => {
8733
8745
  try {
8734
8746
  const resolvedId = resolveId(id, "plans");
@@ -8747,7 +8759,7 @@ ${text}` }] };
8747
8759
  }
8748
8760
  });
8749
8761
  server.tool("delete_plan", "Delete a plan", {
8750
- id: exports_external.string().describe("Plan ID (full or partial)")
8762
+ id: exports_external.string()
8751
8763
  }, async ({ id }) => {
8752
8764
  try {
8753
8765
  const resolvedId = resolveId(id, "plans");
@@ -8763,9 +8775,9 @@ ${text}` }] };
8763
8775
  }
8764
8776
  });
8765
8777
  server.tool("search_tasks", "Full-text search across task titles, descriptions, tags.", {
8766
- query: exports_external.string().describe("Search query"),
8767
- project_id: exports_external.string().optional().describe("Limit to project"),
8768
- task_list_id: exports_external.string().optional().describe("Filter by task list")
8778
+ query: exports_external.string(),
8779
+ project_id: exports_external.string().optional(),
8780
+ task_list_id: exports_external.string().optional()
8769
8781
  }, async ({ query, project_id, task_list_id }) => {
8770
8782
  try {
8771
8783
  const resolvedProjectId = project_id ? resolveId(project_id, "projects") : undefined;
@@ -8783,12 +8795,12 @@ ${text}` }] };
8783
8795
  }
8784
8796
  });
8785
8797
  server.tool("sync", "Sync tasks with an agent task list.", {
8786
- task_list_id: exports_external.string().optional().describe("Task list ID (required for Claude)"),
8787
- agent: exports_external.string().optional().describe("Agent/provider name (default: claude)"),
8788
- all_agents: exports_external.boolean().optional().describe("Sync across all configured agents"),
8789
- project_id: exports_external.string().optional().describe("Project ID \u2014 its task_list_id will be used for Claude if task_list_id is not provided"),
8790
- direction: exports_external.enum(["push", "pull", "both"]).optional().describe("Sync direction: push (SQLite->agent), pull (agent->SQLite), or both (default)"),
8791
- prefer: exports_external.enum(["local", "remote"]).optional().describe("Conflict strategy")
8798
+ task_list_id: exports_external.string().optional(),
8799
+ agent: exports_external.string().optional(),
8800
+ all_agents: exports_external.boolean().optional(),
8801
+ project_id: exports_external.string().optional(),
8802
+ direction: exports_external.enum(["push", "pull", "both"]).optional(),
8803
+ prefer: exports_external.enum(["local", "remote"]).optional()
8792
8804
  }, async ({ task_list_id, agent, all_agents, project_id, direction, prefer }) => {
8793
8805
  try {
8794
8806
  const resolvedProjectId = project_id ? resolveId(project_id, "projects") : undefined;
@@ -8831,8 +8843,8 @@ ${text}` }] };
8831
8843
  }
8832
8844
  });
8833
8845
  server.tool("register_agent", "Register an agent (idempotent by name).", {
8834
- name: exports_external.string().describe("Agent name"),
8835
- description: exports_external.string().optional().describe("Agent description")
8846
+ name: exports_external.string(),
8847
+ description: exports_external.string().optional()
8836
8848
  }, async ({ name, description }) => {
8837
8849
  try {
8838
8850
  const agent = registerAgent({ name, description });
@@ -8868,8 +8880,8 @@ ${text}` }] };
8868
8880
  }
8869
8881
  });
8870
8882
  server.tool("get_agent", "Get agent details by ID or name", {
8871
- id: exports_external.string().optional().describe("Agent ID"),
8872
- name: exports_external.string().optional().describe("Agent name")
8883
+ id: exports_external.string().optional(),
8884
+ name: exports_external.string().optional()
8873
8885
  }, async ({ id, name }) => {
8874
8886
  try {
8875
8887
  if (!id && !name) {
@@ -8896,10 +8908,10 @@ ${text}` }] };
8896
8908
  }
8897
8909
  });
8898
8910
  server.tool("create_task_list", "Create a new task list", {
8899
- name: exports_external.string().describe("Task list name"),
8900
- slug: exports_external.string().optional().describe("URL-friendly slug (auto-generated from name if omitted)"),
8901
- project_id: exports_external.string().optional().describe("Project ID to associate with"),
8902
- description: exports_external.string().optional().describe("Task list description")
8911
+ name: exports_external.string(),
8912
+ slug: exports_external.string().optional(),
8913
+ project_id: exports_external.string().optional(),
8914
+ description: exports_external.string().optional()
8903
8915
  }, async (params) => {
8904
8916
  try {
8905
8917
  const resolved = { ...params };
@@ -8922,7 +8934,7 @@ Description: ${list.description}` : ""}`
8922
8934
  }
8923
8935
  });
8924
8936
  server.tool("list_task_lists", "List task lists, optionally filtered by project", {
8925
- project_id: exports_external.string().optional().describe("Filter by project")
8937
+ project_id: exports_external.string().optional()
8926
8938
  }, async ({ project_id }) => {
8927
8939
  try {
8928
8940
  const resolvedProjectId = project_id ? resolveId(project_id, "projects") : undefined;
@@ -8942,7 +8954,7 @@ ${text}` }] };
8942
8954
  }
8943
8955
  });
8944
8956
  server.tool("get_task_list", "Get task list details", {
8945
- id: exports_external.string().describe("Task list ID (full or partial)")
8957
+ id: exports_external.string()
8946
8958
  }, async ({ id }) => {
8947
8959
  try {
8948
8960
  const resolvedId = resolveId(id, "task_lists");
@@ -8970,9 +8982,9 @@ ${text}` }] };
8970
8982
  }
8971
8983
  });
8972
8984
  server.tool("update_task_list", "Update a task list", {
8973
- id: exports_external.string().describe("Task list ID (full or partial)"),
8974
- name: exports_external.string().optional().describe("New name"),
8975
- description: exports_external.string().optional().describe("New description")
8985
+ id: exports_external.string(),
8986
+ name: exports_external.string().optional(),
8987
+ description: exports_external.string().optional()
8976
8988
  }, async ({ id, ...rest }) => {
8977
8989
  try {
8978
8990
  const resolvedId = resolveId(id, "task_lists");
@@ -8991,7 +9003,7 @@ Slug: ${list.slug}`
8991
9003
  }
8992
9004
  });
8993
9005
  server.tool("delete_task_list", "Delete a task list. Tasks lose association but keep data.", {
8994
- id: exports_external.string().describe("Task list ID (full or partial)")
9006
+ id: exports_external.string()
8995
9007
  }, async ({ id }) => {
8996
9008
  try {
8997
9009
  const resolvedId = resolveId(id, "task_lists");
@@ -9007,7 +9019,7 @@ Slug: ${list.slug}`
9007
9019
  }
9008
9020
  });
9009
9021
  server.tool("get_task_history", "Get audit log for a task.", {
9010
- task_id: exports_external.string().describe("Task ID (full or partial)")
9022
+ task_id: exports_external.string()
9011
9023
  }, async ({ task_id }) => {
9012
9024
  try {
9013
9025
  const resolvedId = resolveId(task_id);
@@ -9024,7 +9036,7 @@ ${text}` }] };
9024
9036
  }
9025
9037
  });
9026
9038
  server.tool("get_recent_activity", "Get recent task changes across all tasks.", {
9027
- limit: exports_external.number().optional().describe("Max entries (default 50)")
9039
+ limit: exports_external.number().optional()
9028
9040
  }, async ({ limit }) => {
9029
9041
  try {
9030
9042
  const { getRecentActivity: getRecentActivity2 } = await Promise.resolve().then(() => (init_audit(), exports_audit));
@@ -9040,9 +9052,9 @@ ${text}` }] };
9040
9052
  }
9041
9053
  });
9042
9054
  server.tool("create_webhook", "Register a webhook to receive task change events.", {
9043
- url: exports_external.string().describe("Webhook URL"),
9044
- events: exports_external.array(exports_external.string()).optional().describe("Event types to subscribe to (empty = all)"),
9045
- secret: exports_external.string().optional().describe("HMAC secret for signature verification")
9055
+ url: exports_external.string(),
9056
+ events: exports_external.array(exports_external.string()).optional(),
9057
+ secret: exports_external.string().optional()
9046
9058
  }, async (params) => {
9047
9059
  try {
9048
9060
  const { createWebhook: createWebhook2 } = await Promise.resolve().then(() => (init_webhooks(), exports_webhooks));
@@ -9067,7 +9079,7 @@ ${text}` }] };
9067
9079
  }
9068
9080
  });
9069
9081
  server.tool("delete_webhook", "Delete a webhook", {
9070
- id: exports_external.string().describe("Webhook ID")
9082
+ id: exports_external.string()
9071
9083
  }, async ({ id }) => {
9072
9084
  try {
9073
9085
  const { deleteWebhook: deleteWebhook2 } = await Promise.resolve().then(() => (init_webhooks(), exports_webhooks));
@@ -9078,13 +9090,13 @@ ${text}` }] };
9078
9090
  }
9079
9091
  });
9080
9092
  server.tool("create_template", "Create a reusable task template.", {
9081
- name: exports_external.string().describe("Template name"),
9082
- title_pattern: exports_external.string().describe("Title pattern for tasks created from this template"),
9083
- description: exports_external.string().optional().describe("Default description"),
9084
- priority: exports_external.enum(["low", "medium", "high", "critical"]).optional().describe("Default priority"),
9085
- tags: exports_external.array(exports_external.string()).optional().describe("Default tags"),
9086
- project_id: exports_external.string().optional().describe("Default project"),
9087
- plan_id: exports_external.string().optional().describe("Default plan")
9093
+ name: exports_external.string(),
9094
+ title_pattern: exports_external.string(),
9095
+ description: exports_external.string().optional(),
9096
+ priority: exports_external.enum(["low", "medium", "high", "critical"]).optional(),
9097
+ tags: exports_external.array(exports_external.string()).optional(),
9098
+ project_id: exports_external.string().optional(),
9099
+ plan_id: exports_external.string().optional()
9088
9100
  }, async (params) => {
9089
9101
  try {
9090
9102
  const { createTemplate: createTemplate2 } = await Promise.resolve().then(() => (init_templates(), exports_templates));
@@ -9109,12 +9121,12 @@ ${text}` }] };
9109
9121
  }
9110
9122
  });
9111
9123
  server.tool("create_task_from_template", "Create a task from a template with optional overrides.", {
9112
- template_id: exports_external.string().describe("Template ID"),
9113
- title: exports_external.string().optional().describe("Override title"),
9114
- description: exports_external.string().optional().describe("Override description"),
9115
- priority: exports_external.enum(["low", "medium", "high", "critical"]).optional().describe("Override priority"),
9116
- assigned_to: exports_external.string().optional().describe("Assign to agent"),
9117
- project_id: exports_external.string().optional().describe("Override project")
9124
+ template_id: exports_external.string(),
9125
+ title: exports_external.string().optional(),
9126
+ description: exports_external.string().optional(),
9127
+ priority: exports_external.enum(["low", "medium", "high", "critical"]).optional(),
9128
+ assigned_to: exports_external.string().optional(),
9129
+ project_id: exports_external.string().optional()
9118
9130
  }, async (params) => {
9119
9131
  try {
9120
9132
  const { taskFromTemplate: taskFromTemplate2 } = await Promise.resolve().then(() => (init_templates(), exports_templates));
@@ -9132,7 +9144,7 @@ ${task.id.slice(0, 8)} | ${task.priority} | ${task.title}` }] };
9132
9144
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
9133
9145
  }
9134
9146
  });
9135
- server.tool("delete_template", "Delete a task template", { id: exports_external.string().describe("Template ID") }, async ({ id }) => {
9147
+ server.tool("delete_template", "Delete a task template", { id: exports_external.string() }, async ({ id }) => {
9136
9148
  try {
9137
9149
  const { deleteTemplate: deleteTemplate2 } = await Promise.resolve().then(() => (init_templates(), exports_templates));
9138
9150
  const deleted = deleteTemplate2(id);
@@ -9142,8 +9154,8 @@ ${task.id.slice(0, 8)} | ${task.priority} | ${task.title}` }] };
9142
9154
  }
9143
9155
  });
9144
9156
  server.tool("approve_task", "Approve a task that requires approval.", {
9145
- id: exports_external.string().describe("Task ID (full or partial)"),
9146
- agent_id: exports_external.string().optional().describe("Agent approving the task")
9157
+ id: exports_external.string(),
9158
+ agent_id: exports_external.string().optional()
9147
9159
  }, async ({ id, agent_id }) => {
9148
9160
  try {
9149
9161
  const resolvedId = resolveId(id);
@@ -9161,7 +9173,7 @@ ${task.id.slice(0, 8)} | ${task.priority} | ${task.title}` }] };
9161
9173
  }
9162
9174
  });
9163
9175
  server.tool("get_my_tasks", "Get assigned tasks and stats for an agent.", {
9164
- agent_name: exports_external.string().describe("Your agent name")
9176
+ agent_name: exports_external.string()
9165
9177
  }, async ({ agent_name }) => {
9166
9178
  try {
9167
9179
  const agent = registerAgent({ name: agent_name });
@@ -9193,7 +9205,7 @@ In Progress:`);
9193
9205
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
9194
9206
  }
9195
9207
  });
9196
- server.tool("search_tools", "List tool names matching a query.", { query: exports_external.string().optional().describe("Keyword to filter tools") }, async ({ query }) => {
9208
+ server.tool("search_tools", "List tool names matching a query.", { query: exports_external.string().optional() }, async ({ query }) => {
9197
9209
  const all = [
9198
9210
  "create_task",
9199
9211
  "list_tasks",
@@ -9242,7 +9254,7 @@ In Progress:`);
9242
9254
  const matches = q ? all.filter((n) => n.includes(q)) : all;
9243
9255
  return { content: [{ type: "text", text: matches.join(", ") }] };
9244
9256
  });
9245
- server.tool("describe_tools", "Get descriptions for specific tools by name.", { names: exports_external.array(exports_external.string()).describe("Tool names from search_tools") }, async ({ names }) => {
9257
+ server.tool("describe_tools", "Get descriptions for specific tools by name.", { names: exports_external.array(exports_external.string()) }, async ({ names }) => {
9246
9258
  const descriptions = {
9247
9259
  create_task: "Create a task. Params: title(req), description, priority, project_id, plan_id, tags, assigned_to, estimated_minutes, requires_approval",
9248
9260
  list_tasks: "List tasks. Params: status, priority, project_id, plan_id, assigned_to, tags, limit",
package/dist/index.js CHANGED
@@ -1115,7 +1115,18 @@ function updateTask(id, input, db) {
1115
1115
  logTaskChange(id, "update", "assigned_to", task.assigned_to, input.assigned_to, agentId, d);
1116
1116
  if (input.approved_by !== undefined)
1117
1117
  logTaskChange(id, "approve", "approved_by", null, input.approved_by, agentId, d);
1118
- return getTask(id, d);
1118
+ return {
1119
+ ...task,
1120
+ ...Object.fromEntries(Object.entries(input).filter(([, v]) => v !== undefined)),
1121
+ tags: input.tags ?? task.tags,
1122
+ metadata: input.metadata ?? task.metadata,
1123
+ version: task.version + 1,
1124
+ updated_at: now(),
1125
+ completed_at: input.status === "completed" ? now() : task.completed_at,
1126
+ requires_approval: input.requires_approval !== undefined ? input.requires_approval : task.requires_approval,
1127
+ approved_by: input.approved_by ?? task.approved_by,
1128
+ approved_at: input.approved_by ? now() : task.approved_at
1129
+ };
1119
1130
  }
1120
1131
  function deleteTask(id, db) {
1121
1132
  const d = db || getDatabase();
@@ -1137,6 +1148,9 @@ function getBlockingDeps(id, db) {
1137
1148
  }
1138
1149
  function startTask(id, agentId, db) {
1139
1150
  const d = db || getDatabase();
1151
+ const task = getTask(id, d);
1152
+ if (!task)
1153
+ throw new TaskNotFoundError(id);
1140
1154
  const blocking = getBlockingDeps(id, d);
1141
1155
  if (blocking.length > 0) {
1142
1156
  const blockerIds = blocking.map((b) => b.id.slice(0, 8)).join(", ");
@@ -1147,15 +1161,12 @@ function startTask(id, agentId, db) {
1147
1161
  const result = d.run(`UPDATE tasks SET status = 'in_progress', assigned_to = ?, locked_by = ?, locked_at = ?, version = version + 1, updated_at = ?
1148
1162
  WHERE id = ? AND (locked_by IS NULL OR locked_by = ? OR locked_at < ?)`, [agentId, agentId, timestamp, timestamp, id, agentId, cutoff]);
1149
1163
  if (result.changes === 0) {
1150
- const current = getTask(id, d);
1151
- if (!current)
1152
- throw new TaskNotFoundError(id);
1153
- if (current.locked_by && current.locked_by !== agentId && !isLockExpired(current.locked_at)) {
1154
- throw new LockError(id, current.locked_by);
1164
+ if (task.locked_by && task.locked_by !== agentId && !isLockExpired(task.locked_at)) {
1165
+ throw new LockError(id, task.locked_by);
1155
1166
  }
1156
1167
  }
1157
1168
  logTaskChange(id, "start", "status", "pending", "in_progress", agentId, d);
1158
- return getTask(id, d);
1169
+ return { ...task, status: "in_progress", assigned_to: agentId, locked_by: agentId, locked_at: timestamp, version: task.version + 1, updated_at: timestamp };
1159
1170
  }
1160
1171
  function completeTask(id, agentId, db, evidence) {
1161
1172
  const d = db || getDatabase();
@@ -1167,14 +1178,15 @@ function completeTask(id, agentId, db, evidence) {
1167
1178
  }
1168
1179
  checkCompletionGuard(task, agentId || null, d);
1169
1180
  if (evidence) {
1170
- const meta = { ...task.metadata, _evidence: evidence };
1171
- d.run("UPDATE tasks SET metadata = ? WHERE id = ?", [JSON.stringify(meta), id]);
1181
+ const meta2 = { ...task.metadata, _evidence: evidence };
1182
+ d.run("UPDATE tasks SET metadata = ? WHERE id = ?", [JSON.stringify(meta2), id]);
1172
1183
  }
1173
1184
  const timestamp = now();
1174
1185
  d.run(`UPDATE tasks SET status = 'completed', locked_by = NULL, locked_at = NULL, completed_at = ?, version = version + 1, updated_at = ?
1175
1186
  WHERE id = ?`, [timestamp, timestamp, id]);
1176
1187
  logTaskChange(id, "complete", "status", task.status, "completed", agentId || null, d);
1177
- return getTask(id, d);
1188
+ const meta = evidence ? { ...task.metadata, _evidence: evidence } : task.metadata;
1189
+ return { ...task, status: "completed", locked_by: null, locked_at: null, completed_at: timestamp, version: task.version + 1, updated_at: timestamp, metadata: meta };
1178
1190
  }
1179
1191
  function lockTask(id, agentId, db) {
1180
1192
  const d = db || getDatabase();
package/dist/mcp/index.js CHANGED
@@ -5154,7 +5154,18 @@ function updateTask(id, input, db) {
5154
5154
  logTaskChange(id, "update", "assigned_to", task.assigned_to, input.assigned_to, agentId, d);
5155
5155
  if (input.approved_by !== undefined)
5156
5156
  logTaskChange(id, "approve", "approved_by", null, input.approved_by, agentId, d);
5157
- return getTask(id, d);
5157
+ return {
5158
+ ...task,
5159
+ ...Object.fromEntries(Object.entries(input).filter(([, v]) => v !== undefined)),
5160
+ tags: input.tags ?? task.tags,
5161
+ metadata: input.metadata ?? task.metadata,
5162
+ version: task.version + 1,
5163
+ updated_at: now(),
5164
+ completed_at: input.status === "completed" ? now() : task.completed_at,
5165
+ requires_approval: input.requires_approval !== undefined ? input.requires_approval : task.requires_approval,
5166
+ approved_by: input.approved_by ?? task.approved_by,
5167
+ approved_at: input.approved_by ? now() : task.approved_at
5168
+ };
5158
5169
  }
5159
5170
  function deleteTask(id, db) {
5160
5171
  const d = db || getDatabase();
@@ -5176,6 +5187,9 @@ function getBlockingDeps(id, db) {
5176
5187
  }
5177
5188
  function startTask(id, agentId, db) {
5178
5189
  const d = db || getDatabase();
5190
+ const task = getTask(id, d);
5191
+ if (!task)
5192
+ throw new TaskNotFoundError(id);
5179
5193
  const blocking = getBlockingDeps(id, d);
5180
5194
  if (blocking.length > 0) {
5181
5195
  const blockerIds = blocking.map((b) => b.id.slice(0, 8)).join(", ");
@@ -5186,15 +5200,12 @@ function startTask(id, agentId, db) {
5186
5200
  const result = d.run(`UPDATE tasks SET status = 'in_progress', assigned_to = ?, locked_by = ?, locked_at = ?, version = version + 1, updated_at = ?
5187
5201
  WHERE id = ? AND (locked_by IS NULL OR locked_by = ? OR locked_at < ?)`, [agentId, agentId, timestamp, timestamp, id, agentId, cutoff]);
5188
5202
  if (result.changes === 0) {
5189
- const current = getTask(id, d);
5190
- if (!current)
5191
- throw new TaskNotFoundError(id);
5192
- if (current.locked_by && current.locked_by !== agentId && !isLockExpired(current.locked_at)) {
5193
- throw new LockError(id, current.locked_by);
5203
+ if (task.locked_by && task.locked_by !== agentId && !isLockExpired(task.locked_at)) {
5204
+ throw new LockError(id, task.locked_by);
5194
5205
  }
5195
5206
  }
5196
5207
  logTaskChange(id, "start", "status", "pending", "in_progress", agentId, d);
5197
- return getTask(id, d);
5208
+ return { ...task, status: "in_progress", assigned_to: agentId, locked_by: agentId, locked_at: timestamp, version: task.version + 1, updated_at: timestamp };
5198
5209
  }
5199
5210
  function completeTask(id, agentId, db, evidence) {
5200
5211
  const d = db || getDatabase();
@@ -5206,14 +5217,15 @@ function completeTask(id, agentId, db, evidence) {
5206
5217
  }
5207
5218
  checkCompletionGuard(task, agentId || null, d);
5208
5219
  if (evidence) {
5209
- const meta = { ...task.metadata, _evidence: evidence };
5210
- d.run("UPDATE tasks SET metadata = ? WHERE id = ?", [JSON.stringify(meta), id]);
5220
+ const meta2 = { ...task.metadata, _evidence: evidence };
5221
+ d.run("UPDATE tasks SET metadata = ? WHERE id = ?", [JSON.stringify(meta2), id]);
5211
5222
  }
5212
5223
  const timestamp = now();
5213
5224
  d.run(`UPDATE tasks SET status = 'completed', locked_by = NULL, locked_at = NULL, completed_at = ?, version = version + 1, updated_at = ?
5214
5225
  WHERE id = ?`, [timestamp, timestamp, id]);
5215
5226
  logTaskChange(id, "complete", "status", task.status, "completed", agentId || null, d);
5216
- return getTask(id, d);
5227
+ const meta = evidence ? { ...task.metadata, _evidence: evidence } : task.metadata;
5228
+ return { ...task, status: "completed", locked_by: null, locked_at: null, completed_at: timestamp, version: task.version + 1, updated_at: timestamp, metadata: meta };
5217
5229
  }
5218
5230
  function lockTask(id, agentId, db) {
5219
5231
  const d = db || getDatabase();
@@ -1233,7 +1233,18 @@ function updateTask(id, input, db) {
1233
1233
  logTaskChange(id, "update", "assigned_to", task.assigned_to, input.assigned_to, agentId, d);
1234
1234
  if (input.approved_by !== undefined)
1235
1235
  logTaskChange(id, "approve", "approved_by", null, input.approved_by, agentId, d);
1236
- return getTask(id, d);
1236
+ return {
1237
+ ...task,
1238
+ ...Object.fromEntries(Object.entries(input).filter(([, v]) => v !== undefined)),
1239
+ tags: input.tags ?? task.tags,
1240
+ metadata: input.metadata ?? task.metadata,
1241
+ version: task.version + 1,
1242
+ updated_at: now(),
1243
+ completed_at: input.status === "completed" ? now() : task.completed_at,
1244
+ requires_approval: input.requires_approval !== undefined ? input.requires_approval : task.requires_approval,
1245
+ approved_by: input.approved_by ?? task.approved_by,
1246
+ approved_at: input.approved_by ? now() : task.approved_at
1247
+ };
1237
1248
  }
1238
1249
  function deleteTask(id, db) {
1239
1250
  const d = db || getDatabase();
@@ -1255,6 +1266,9 @@ function getBlockingDeps(id, db) {
1255
1266
  }
1256
1267
  function startTask(id, agentId, db) {
1257
1268
  const d = db || getDatabase();
1269
+ const task = getTask(id, d);
1270
+ if (!task)
1271
+ throw new TaskNotFoundError(id);
1258
1272
  const blocking = getBlockingDeps(id, d);
1259
1273
  if (blocking.length > 0) {
1260
1274
  const blockerIds = blocking.map((b) => b.id.slice(0, 8)).join(", ");
@@ -1265,15 +1279,12 @@ function startTask(id, agentId, db) {
1265
1279
  const result = d.run(`UPDATE tasks SET status = 'in_progress', assigned_to = ?, locked_by = ?, locked_at = ?, version = version + 1, updated_at = ?
1266
1280
  WHERE id = ? AND (locked_by IS NULL OR locked_by = ? OR locked_at < ?)`, [agentId, agentId, timestamp, timestamp, id, agentId, cutoff]);
1267
1281
  if (result.changes === 0) {
1268
- const current = getTask(id, d);
1269
- if (!current)
1270
- throw new TaskNotFoundError(id);
1271
- if (current.locked_by && current.locked_by !== agentId && !isLockExpired(current.locked_at)) {
1272
- throw new LockError(id, current.locked_by);
1282
+ if (task.locked_by && task.locked_by !== agentId && !isLockExpired(task.locked_at)) {
1283
+ throw new LockError(id, task.locked_by);
1273
1284
  }
1274
1285
  }
1275
1286
  logTaskChange(id, "start", "status", "pending", "in_progress", agentId, d);
1276
- return getTask(id, d);
1287
+ return { ...task, status: "in_progress", assigned_to: agentId, locked_by: agentId, locked_at: timestamp, version: task.version + 1, updated_at: timestamp };
1277
1288
  }
1278
1289
  function completeTask(id, agentId, db, evidence) {
1279
1290
  const d = db || getDatabase();
@@ -1285,14 +1296,15 @@ function completeTask(id, agentId, db, evidence) {
1285
1296
  }
1286
1297
  checkCompletionGuard(task, agentId || null, d);
1287
1298
  if (evidence) {
1288
- const meta = { ...task.metadata, _evidence: evidence };
1289
- d.run("UPDATE tasks SET metadata = ? WHERE id = ?", [JSON.stringify(meta), id]);
1299
+ const meta2 = { ...task.metadata, _evidence: evidence };
1300
+ d.run("UPDATE tasks SET metadata = ? WHERE id = ?", [JSON.stringify(meta2), id]);
1290
1301
  }
1291
1302
  const timestamp = now();
1292
1303
  d.run(`UPDATE tasks SET status = 'completed', locked_by = NULL, locked_at = NULL, completed_at = ?, version = version + 1, updated_at = ?
1293
1304
  WHERE id = ?`, [timestamp, timestamp, id]);
1294
1305
  logTaskChange(id, "complete", "status", task.status, "completed", agentId || null, d);
1295
- return getTask(id, d);
1306
+ const meta = evidence ? { ...task.metadata, _evidence: evidence } : task.metadata;
1307
+ return { ...task, status: "completed", locked_by: null, locked_at: null, completed_at: timestamp, version: task.version + 1, updated_at: timestamp, metadata: meta };
1296
1308
  }
1297
1309
  function getTaskDependencies(taskId, db) {
1298
1310
  const d = db || getDatabase();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/todos",
3
- "version": "0.9.28",
3
+ "version": "0.9.29",
4
4
  "description": "Universal task management for AI coding agents - CLI + MCP server + interactive TUI",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",