@hasna/todos 0.11.33 → 0.11.34

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
@@ -2888,6 +2888,26 @@ var init_migrations = __esm(() => {
2888
2888
  ALTER TABLE tasks ADD COLUMN current_step TEXT;
2889
2889
  ALTER TABLE tasks ADD COLUMN total_steps INTEGER;
2890
2890
  INSERT OR IGNORE INTO _migrations (id) VALUES (48);
2891
+ `,
2892
+ `
2893
+ CREATE TABLE IF NOT EXISTS cycles (
2894
+ id TEXT PRIMARY KEY,
2895
+ project_id TEXT REFERENCES projects(id) ON DELETE CASCADE,
2896
+ number INTEGER NOT NULL,
2897
+ start_date TEXT NOT NULL,
2898
+ end_date TEXT NOT NULL,
2899
+ duration_weeks INTEGER NOT NULL DEFAULT 1,
2900
+ status TEXT NOT NULL DEFAULT 'active' CHECK(status IN ('active', 'completed', 'archived')),
2901
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
2902
+ updated_at TEXT NOT NULL DEFAULT (datetime('now'))
2903
+ );
2904
+ CREATE INDEX IF NOT EXISTS idx_cycles_project ON cycles(project_id);
2905
+ CREATE INDEX IF NOT EXISTS idx_cycles_number ON cycles(number);
2906
+ CREATE INDEX IF NOT EXISTS idx_cycles_status ON cycles(status);
2907
+ CREATE INDEX IF NOT EXISTS idx_cycles_dates ON cycles(start_date, end_date);
2908
+ ALTER TABLE tasks ADD COLUMN cycle_id TEXT REFERENCES cycles(id) ON DELETE SET NULL;
2909
+ CREATE INDEX IF NOT EXISTS idx_tasks_cycle ON tasks(cycle_id) WHERE cycle_id IS NOT NULL;
2910
+ INSERT OR IGNORE INTO _migrations (id) VALUES (49);
2891
2911
  `
2892
2912
  ];
2893
2913
  });
@@ -4675,8 +4695,8 @@ function createTask(input, db) {
4675
4695
  let id = uuid();
4676
4696
  for (let attempt = 0;attempt < 3; attempt++) {
4677
4697
  try {
4678
- d.run(`INSERT INTO tasks (id, short_id, project_id, parent_id, plan_id, task_list_id, cycle_id, title, description, status, priority, agent_id, assigned_to, session_id, working_dir, tags, metadata, version, created_at, updated_at, due_at, estimated_minutes, requires_approval, approved_by, approved_at, recurrence_rule, recurrence_parent_id, spawns_template_id, reason, spawned_from_session, assigned_by, assigned_from_project, task_type)
4679
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
4698
+ d.run(`INSERT INTO tasks (id, short_id, project_id, parent_id, plan_id, task_list_id, cycle_id, title, description, status, priority, agent_id, assigned_to, session_id, working_dir, tags, metadata, version, created_at, updated_at, due_at, estimated_minutes, confidence, retry_count, max_retries, retry_after, requires_approval, approved_by, approved_at, recurrence_rule, recurrence_parent_id, spawns_template_id, reason, spawned_from_session, assigned_by, assigned_from_project, task_type)
4699
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
4680
4700
  id,
4681
4701
  null,
4682
4702
  input.project_id || null,
@@ -4698,6 +4718,10 @@ function createTask(input, db) {
4698
4718
  timestamp,
4699
4719
  input.due_at || null,
4700
4720
  input.estimated_minutes || null,
4721
+ input.confidence ?? null,
4722
+ input.retry_count ?? 0,
4723
+ input.max_retries ?? 3,
4724
+ input.retry_after ?? null,
4701
4725
  input.requires_approval ? 1 : 0,
4702
4726
  null,
4703
4727
  null,
@@ -4722,11 +4746,11 @@ function createTask(input, db) {
4722
4746
  if (tags.length > 0) {
4723
4747
  insertTaskTags(id, tags, d);
4724
4748
  }
4725
- const task = getTask2(id, d);
4749
+ const task = getTask(id, d);
4726
4750
  dispatchWebhook("task.created", { id: task.id, short_id: task.short_id, title: task.title, status: task.status, priority: task.priority, project_id: task.project_id, assigned_to: task.assigned_to }, d).catch(() => {});
4727
4751
  return task;
4728
4752
  }
4729
- function getTask2(id, db) {
4753
+ function getTask(id, db) {
4730
4754
  const d = db || getDatabase();
4731
4755
  const row = d.query("SELECT * FROM tasks WHERE id = ?").get(id);
4732
4756
  if (!row)
@@ -4735,7 +4759,7 @@ function getTask2(id, db) {
4735
4759
  }
4736
4760
  function getTaskWithRelations(id, db) {
4737
4761
  const d = db || getDatabase();
4738
- const task = getTask2(id, d);
4762
+ const task = getTask(id, d);
4739
4763
  if (!task)
4740
4764
  return null;
4741
4765
  const subtaskRows = d.query("SELECT * FROM tasks WHERE parent_id = ? ORDER BY created_at").all(id);
@@ -4749,7 +4773,7 @@ function getTaskWithRelations(id, db) {
4749
4773
  WHERE td.depends_on = ?`).all(id);
4750
4774
  const blocked_by = blockedByRows.map(rowToTask);
4751
4775
  const comments = d.query("SELECT * FROM task_comments WHERE task_id = ? ORDER BY created_at").all(id);
4752
- const parent = task.parent_id ? getTask2(task.parent_id, d) : null;
4776
+ const parent = task.parent_id ? getTask(task.parent_id, d) : null;
4753
4777
  const checklist = getChecklist(id, d);
4754
4778
  return {
4755
4779
  ...task,
@@ -4936,14 +4960,16 @@ function countTasks(filter = {}, db) {
4936
4960
  }
4937
4961
  function updateTask(id, input, db) {
4938
4962
  const d = db || getDatabase();
4939
- const task = getTask2(id, d);
4963
+ const task = getTask(id, d);
4940
4964
  if (!task)
4941
4965
  throw new TaskNotFoundError(id);
4942
4966
  if (task.version !== input.version) {
4943
4967
  throw new VersionConflictError(id, input.version, task.version);
4944
4968
  }
4969
+ const timestamp = now();
4970
+ const completionTimestamp = input.completed_at ?? timestamp;
4945
4971
  const sets = ["version = version + 1", "updated_at = ?"];
4946
- const params = [now()];
4972
+ const params = [timestamp];
4947
4973
  if (input.title !== undefined) {
4948
4974
  sets.push("title = ?");
4949
4975
  params.push(input.title);
@@ -4960,13 +4986,17 @@ function updateTask(id, input, db) {
4960
4986
  params.push(input.status);
4961
4987
  if (input.status === "completed") {
4962
4988
  sets.push("completed_at = ?");
4963
- params.push(now());
4989
+ params.push(completionTimestamp);
4964
4990
  }
4965
4991
  }
4966
4992
  if (input.priority !== undefined) {
4967
4993
  sets.push("priority = ?");
4968
4994
  params.push(input.priority);
4969
4995
  }
4996
+ if (input.project_id !== undefined) {
4997
+ sets.push("project_id = ?");
4998
+ params.push(input.project_id);
4999
+ }
4970
5000
  if (input.assigned_to !== undefined) {
4971
5001
  sets.push("assigned_to = ?");
4972
5002
  params.push(input.assigned_to);
@@ -4995,6 +5025,30 @@ function updateTask(id, input, db) {
4995
5025
  sets.push("estimated_minutes = ?");
4996
5026
  params.push(input.estimated_minutes);
4997
5027
  }
5028
+ if (input.actual_minutes !== undefined) {
5029
+ sets.push("actual_minutes = ?");
5030
+ params.push(input.actual_minutes);
5031
+ }
5032
+ if (input.completed_at !== undefined && input.status !== "completed") {
5033
+ sets.push("completed_at = ?");
5034
+ params.push(input.completed_at);
5035
+ }
5036
+ if (input.confidence !== undefined) {
5037
+ sets.push("confidence = ?");
5038
+ params.push(input.confidence);
5039
+ }
5040
+ if (input.retry_count !== undefined) {
5041
+ sets.push("retry_count = ?");
5042
+ params.push(input.retry_count);
5043
+ }
5044
+ if (input.max_retries !== undefined) {
5045
+ sets.push("max_retries = ?");
5046
+ params.push(input.max_retries);
5047
+ }
5048
+ if (input.retry_after !== undefined) {
5049
+ sets.push("retry_after = ?");
5050
+ params.push(input.retry_after);
5051
+ }
4998
5052
  if (input.requires_approval !== undefined) {
4999
5053
  sets.push("requires_approval = ?");
5000
5054
  params.push(input.requires_approval ? 1 : 0);
@@ -5016,7 +5070,7 @@ function updateTask(id, input, db) {
5016
5070
  params.push(id, input.version);
5017
5071
  const result = d.run(`UPDATE tasks SET ${sets.join(", ")} WHERE id = ? AND version = ?`, params);
5018
5072
  if (result.changes === 0) {
5019
- const current = getTask2(id, d);
5073
+ const current = getTask(id, d);
5020
5074
  throw new VersionConflictError(id, input.version, current?.version ?? -1);
5021
5075
  }
5022
5076
  if (input.tags !== undefined) {
@@ -5045,11 +5099,16 @@ function updateTask(id, input, db) {
5045
5099
  tags: input.tags ?? task.tags,
5046
5100
  metadata: input.metadata ?? task.metadata,
5047
5101
  version: task.version + 1,
5048
- updated_at: now(),
5049
- completed_at: input.status === "completed" ? now() : task.completed_at,
5102
+ updated_at: timestamp,
5103
+ completed_at: input.status === "completed" ? completionTimestamp : input.completed_at !== undefined ? input.completed_at : task.completed_at,
5104
+ actual_minutes: input.actual_minutes ?? task.actual_minutes,
5105
+ confidence: input.confidence !== undefined ? input.confidence : task.confidence,
5106
+ retry_count: input.retry_count ?? task.retry_count,
5107
+ max_retries: input.max_retries ?? task.max_retries,
5108
+ retry_after: input.retry_after !== undefined ? input.retry_after : task.retry_after,
5050
5109
  requires_approval: input.requires_approval !== undefined ? input.requires_approval : task.requires_approval,
5051
5110
  approved_by: input.approved_by ?? task.approved_by,
5052
- approved_at: input.approved_by ? now() : task.approved_at
5111
+ approved_at: input.approved_by ? timestamp : task.approved_at
5053
5112
  };
5054
5113
  }
5055
5114
  function deleteTask(id, db) {
@@ -5622,9 +5681,9 @@ var init_templates = __esm(() => {
5622
5681
  // src/db/task-graph.ts
5623
5682
  function addDependency(taskId, dependsOn, db) {
5624
5683
  const d = db || getDatabase();
5625
- if (!getTask2(taskId, d))
5684
+ if (!getTask(taskId, d))
5626
5685
  throw new TaskNotFoundError(taskId);
5627
- if (!getTask2(dependsOn, d))
5686
+ if (!getTask(dependsOn, d))
5628
5687
  throw new TaskNotFoundError(dependsOn);
5629
5688
  if (wouldCreateCycle(taskId, dependsOn, d)) {
5630
5689
  throw new DependencyCycleError(taskId, dependsOn);
@@ -5646,7 +5705,7 @@ function getTaskDependents(taskId, db) {
5646
5705
  }
5647
5706
  function cloneTask(taskId, overrides, db) {
5648
5707
  const d = db || getDatabase();
5649
- const source = getTask2(taskId, d);
5708
+ const source = getTask(taskId, d);
5650
5709
  if (!source)
5651
5710
  throw new TaskNotFoundError(taskId);
5652
5711
  const input = {
@@ -5669,13 +5728,13 @@ function cloneTask(taskId, overrides, db) {
5669
5728
  }
5670
5729
  function getTaskGraph(taskId, direction = "both", db) {
5671
5730
  const d = db || getDatabase();
5672
- const task = getTask2(taskId, d);
5731
+ const task = getTask(taskId, d);
5673
5732
  if (!task)
5674
5733
  throw new TaskNotFoundError(taskId);
5675
5734
  function toNode(t) {
5676
5735
  const deps = getTaskDependencies(t.id, d);
5677
5736
  const hasUnfinishedDeps = deps.some((dep) => {
5678
- const depTask = getTask2(dep.depends_on, d);
5737
+ const depTask = getTask(dep.depends_on, d);
5679
5738
  return depTask && depTask.status !== "completed";
5680
5739
  });
5681
5740
  return { id: t.id, short_id: t.short_id, title: t.title, status: t.status, priority: t.priority, is_blocked: hasUnfinishedDeps };
@@ -5686,7 +5745,7 @@ function getTaskGraph(taskId, direction = "both", db) {
5686
5745
  visited.add(id);
5687
5746
  const deps = d.query("SELECT depends_on FROM task_dependencies WHERE task_id = ?").all(id);
5688
5747
  return deps.map((dep) => {
5689
- const depTask = getTask2(dep.depends_on, d);
5748
+ const depTask = getTask(dep.depends_on, d);
5690
5749
  if (!depTask)
5691
5750
  return null;
5692
5751
  return { task: toNode(depTask), depends_on: buildUp(dep.depends_on, visited), blocks: [] };
@@ -5698,7 +5757,7 @@ function getTaskGraph(taskId, direction = "both", db) {
5698
5757
  visited.add(id);
5699
5758
  const dependents = d.query("SELECT task_id FROM task_dependencies WHERE depends_on = ?").all(id);
5700
5759
  return dependents.map((dep) => {
5701
- const depTask = getTask2(dep.task_id, d);
5760
+ const depTask = getTask(dep.task_id, d);
5702
5761
  if (!depTask)
5703
5762
  return null;
5704
5763
  return { task: toNode(depTask), depends_on: [], blocks: buildDown(dep.task_id, visited) };
@@ -5711,7 +5770,7 @@ function getTaskGraph(taskId, direction = "both", db) {
5711
5770
  }
5712
5771
  function moveTask(taskId, target, db) {
5713
5772
  const d = db || getDatabase();
5714
- const task = getTask2(taskId, d);
5773
+ const task = getTask(taskId, d);
5715
5774
  if (!task)
5716
5775
  throw new TaskNotFoundError(taskId);
5717
5776
  const sets = ["updated_at = ?", "version = version + 1"];
@@ -5730,7 +5789,7 @@ function moveTask(taskId, target, db) {
5730
5789
  }
5731
5790
  params.push(taskId);
5732
5791
  d.run(`UPDATE tasks SET ${sets.join(", ")} WHERE id = ?`, params);
5733
- return getTask2(taskId, d);
5792
+ return getTask(taskId, d);
5734
5793
  }
5735
5794
  function wouldCreateCycle(taskId, dependsOn, db) {
5736
5795
  const visited = new Set;
@@ -5763,7 +5822,7 @@ function getBlockingDeps(id, db) {
5763
5822
  return [];
5764
5823
  const blocking = [];
5765
5824
  for (const dep of deps) {
5766
- const task = getTask2(dep.depends_on, d);
5825
+ const task = getTask(dep.depends_on, d);
5767
5826
  if (task && task.status !== "completed")
5768
5827
  blocking.push(task);
5769
5828
  }
@@ -5771,7 +5830,7 @@ function getBlockingDeps(id, db) {
5771
5830
  }
5772
5831
  function startTask(id, agentId, db) {
5773
5832
  const d = db || getDatabase();
5774
- const task = getTask2(id, d);
5833
+ const task = getTask(id, d);
5775
5834
  if (!task)
5776
5835
  throw new TaskNotFoundError(id);
5777
5836
  const blocking = getBlockingDeps(id, d);
@@ -5794,7 +5853,7 @@ function startTask(id, agentId, db) {
5794
5853
  }
5795
5854
  function completeTask(id, agentId, db, options) {
5796
5855
  const d = db || getDatabase();
5797
- const task = getTask2(id, d);
5856
+ const task = getTask(id, d);
5798
5857
  if (!task)
5799
5858
  throw new TaskNotFoundError(id);
5800
5859
  if (agentId && task.locked_by && task.locked_by !== agentId && !isLockExpired(task.locked_at)) {
@@ -5810,14 +5869,14 @@ function completeTask(id, agentId, db, options) {
5810
5869
  completionMeta._completion = { confidence: options.confidence };
5811
5870
  }
5812
5871
  const hasMeta = Object.keys(completionMeta).length > 0;
5813
- const timestamp = now();
5872
+ const timestamp = options?.completed_at || now();
5814
5873
  const confidence = options?.confidence !== undefined ? options.confidence : null;
5815
5874
  const tx = d.transaction(() => {
5816
5875
  if (hasMeta) {
5817
5876
  const meta2 = { ...task.metadata, ...completionMeta };
5818
5877
  const metaResult = d.run("UPDATE tasks SET metadata = ?, version = version + 1, updated_at = ? WHERE id = ? AND version = ?", [JSON.stringify(meta2), timestamp, id, task.version]);
5819
5878
  if (metaResult.changes === 0) {
5820
- const current = getTask2(id, d);
5879
+ const current = getTask(id, d);
5821
5880
  throw new VersionConflictError(id, task.version, current?.version ?? -1);
5822
5881
  }
5823
5882
  }
@@ -5874,7 +5933,7 @@ function completeTask(id, agentId, db, options) {
5874
5933
  }
5875
5934
  function lockTask(id, agentId, db) {
5876
5935
  const d = db || getDatabase();
5877
- const task = getTask2(id, d);
5936
+ const task = getTask(id, d);
5878
5937
  if (!task)
5879
5938
  throw new TaskNotFoundError(id);
5880
5939
  if (task.locked_by === agentId && !isLockExpired(task.locked_at)) {
@@ -5885,7 +5944,7 @@ function lockTask(id, agentId, db) {
5885
5944
  const result = d.run(`UPDATE tasks SET locked_by = ?, locked_at = ?, version = version + 1, updated_at = ?
5886
5945
  WHERE id = ? AND (locked_by IS NULL OR locked_by = ? OR locked_at < ?)`, [agentId, timestamp, timestamp, id, agentId, cutoff]);
5887
5946
  if (result.changes === 0) {
5888
- const current = getTask2(id, d);
5947
+ const current = getTask(id, d);
5889
5948
  if (!current)
5890
5949
  throw new TaskNotFoundError(id);
5891
5950
  if (current.locked_by && !isLockExpired(current.locked_at)) {
@@ -5901,7 +5960,7 @@ function lockTask(id, agentId, db) {
5901
5960
  }
5902
5961
  function unlockTask(id, agentId, db) {
5903
5962
  const d = db || getDatabase();
5904
- const task = getTask2(id, d);
5963
+ const task = getTask(id, d);
5905
5964
  if (!task)
5906
5965
  throw new TaskNotFoundError(id);
5907
5966
  if (agentId && task.locked_by && task.locked_by !== agentId) {
@@ -6002,7 +6061,7 @@ function getTasksChangedSince(since, filters, db) {
6002
6061
  }
6003
6062
  function failTask(id, agentId, reason, options, db) {
6004
6063
  const d = db || getDatabase();
6005
- const task = getTask2(id, d);
6064
+ const task = getTask(id, d);
6006
6065
  if (!task)
6007
6066
  throw new TaskNotFoundError(id);
6008
6067
  const meta = {
@@ -6201,7 +6260,7 @@ function getStatus(filters, agentId, options, db) {
6201
6260
  }
6202
6261
  function decomposeTasks(parentId, subtasks, options, db) {
6203
6262
  const d = db || getDatabase();
6204
- const parent = getTask2(parentId, d);
6263
+ const parent = getTask(parentId, d);
6205
6264
  if (!parent)
6206
6265
  throw new TaskNotFoundError(parentId);
6207
6266
  const created = [];
@@ -6232,7 +6291,7 @@ function decomposeTasks(parentId, subtasks, options, db) {
6232
6291
  function setTaskStatus(id, status, _agentId, db) {
6233
6292
  const d = db || getDatabase();
6234
6293
  for (let attempt = 0;attempt < 3; attempt++) {
6235
- const task = getTask2(id, d);
6294
+ const task = getTask(id, d);
6236
6295
  if (!task)
6237
6296
  throw new TaskNotFoundError(id);
6238
6297
  if (task.status === status)
@@ -6250,7 +6309,7 @@ function setTaskStatus(id, status, _agentId, db) {
6250
6309
  function setTaskPriority(id, priority, _agentId, db) {
6251
6310
  const d = db || getDatabase();
6252
6311
  for (let attempt = 0;attempt < 3; attempt++) {
6253
- const task = getTask2(id, d);
6312
+ const task = getTask(id, d);
6254
6313
  if (!task)
6255
6314
  throw new TaskNotFoundError(id);
6256
6315
  if (task.priority === priority)
@@ -6358,7 +6417,7 @@ function bulkUpdateTasks(taskIds, updates, db) {
6358
6417
  const tx = d.transaction(() => {
6359
6418
  for (const id of taskIds) {
6360
6419
  try {
6361
- const task = getTask2(id, d);
6420
+ const task = getTask(id, d);
6362
6421
  if (!task) {
6363
6422
  failed.push({ id, error: "Task not found" });
6364
6423
  continue;
@@ -6373,6 +6432,30 @@ function bulkUpdateTasks(taskIds, updates, db) {
6373
6432
  tx();
6374
6433
  return { updated, failed };
6375
6434
  }
6435
+ function bulkDeleteTasks(taskIds, force = false, db) {
6436
+ const d = db || getDatabase();
6437
+ let deleted = 0;
6438
+ let skipped = 0;
6439
+ const failed = [];
6440
+ const tx = d.transaction(() => {
6441
+ for (const id of taskIds) {
6442
+ try {
6443
+ const childCount = d.query("SELECT COUNT(*) as count FROM tasks WHERE parent_id = ?").get(id);
6444
+ if (!force && childCount.count > 0) {
6445
+ skipped++;
6446
+ continue;
6447
+ }
6448
+ const result = d.run("DELETE FROM tasks WHERE id = ?", [id]);
6449
+ if (result.changes > 0)
6450
+ deleted++;
6451
+ } catch (e) {
6452
+ failed.push({ id, error: e instanceof Error ? e.message : String(e) });
6453
+ }
6454
+ }
6455
+ });
6456
+ tx();
6457
+ return { deleted, skipped, failed };
6458
+ }
6376
6459
  function archiveTasks(options, db) {
6377
6460
  const d = db || getDatabase();
6378
6461
  const conditions = ["archived_at IS NULL"];
@@ -6397,10 +6480,33 @@ function archiveTasks(options, db) {
6397
6480
  const result = d.run(`UPDATE tasks SET archived_at = ? WHERE ${conditions.join(" AND ")}`, [ts, ...params]);
6398
6481
  return { archived: result.changes };
6399
6482
  }
6483
+ function archiveCompletedTasks(days = 7, projectId, db) {
6484
+ return archiveTasks({
6485
+ project_id: projectId,
6486
+ older_than_days: days,
6487
+ status: ["completed"]
6488
+ }, db).archived;
6489
+ }
6490
+ function getArchivedTasks(opts = {}, db) {
6491
+ const d = db || getDatabase();
6492
+ const conditions = ["archived_at IS NOT NULL"];
6493
+ const params = [];
6494
+ if (opts.project_id) {
6495
+ conditions.push("project_id = ?");
6496
+ params.push(opts.project_id);
6497
+ }
6498
+ let limitClause = "";
6499
+ if (opts.limit) {
6500
+ limitClause = " LIMIT ?";
6501
+ params.push(opts.limit);
6502
+ }
6503
+ const rows = d.query(`SELECT * FROM tasks WHERE ${conditions.join(" AND ")} ORDER BY archived_at DESC${limitClause}`).all(...params);
6504
+ return rows.map(rowToTask);
6505
+ }
6400
6506
  function unarchiveTask(id, db) {
6401
6507
  const d = db || getDatabase();
6402
6508
  d.run("UPDATE tasks SET archived_at = NULL WHERE id = ?", [id]);
6403
- return getTask2(id, d);
6509
+ return getTask(id, d);
6404
6510
  }
6405
6511
  function getOverdueTasks(projectId, db) {
6406
6512
  const d = db || getDatabase();
@@ -6415,6 +6521,80 @@ function getOverdueTasks(projectId, db) {
6415
6521
  const rows = d.query(query).all(...params);
6416
6522
  return rows.map(rowToTask);
6417
6523
  }
6524
+ function notifyUpcomingDeadlines(opts = {}, db) {
6525
+ const d = db || getDatabase();
6526
+ const hours = opts.hours ?? 24;
6527
+ const start = new Date().toISOString();
6528
+ const end = new Date(Date.now() + hours * 60 * 60 * 1000).toISOString();
6529
+ const conditions = [
6530
+ "archived_at IS NULL",
6531
+ "due_at IS NOT NULL",
6532
+ "due_at >= ?",
6533
+ "due_at <= ?",
6534
+ "status NOT IN ('completed', 'cancelled', 'failed')"
6535
+ ];
6536
+ const params = [start, end];
6537
+ if (opts.project_id) {
6538
+ conditions.push("project_id = ?");
6539
+ params.push(opts.project_id);
6540
+ }
6541
+ if (opts.agent_id) {
6542
+ conditions.push("assigned_to = ?");
6543
+ params.push(opts.agent_id);
6544
+ }
6545
+ const rows = d.query(`SELECT * FROM tasks WHERE ${conditions.join(" AND ")} ORDER BY due_at ASC`).all(...params);
6546
+ return rows.map(rowToTask);
6547
+ }
6548
+ function getBlockedTasks(projectId, db) {
6549
+ const d = db || getDatabase();
6550
+ const params = [];
6551
+ let projectClause = "";
6552
+ if (projectId) {
6553
+ projectClause = "AND t.project_id = ?";
6554
+ params.push(projectId);
6555
+ }
6556
+ const rows = d.query(`
6557
+ SELECT t.*, GROUP_CONCAT(dep.id) AS blocked_by_ids
6558
+ FROM tasks t
6559
+ JOIN task_dependencies td ON td.task_id = t.id
6560
+ JOIN tasks dep ON dep.id = td.depends_on
6561
+ WHERE t.archived_at IS NULL
6562
+ AND t.status NOT IN ('completed', 'cancelled', 'failed')
6563
+ AND dep.status NOT IN ('completed', 'cancelled')
6564
+ ${projectClause}
6565
+ GROUP BY t.id
6566
+ ORDER BY t.priority DESC, t.created_at DESC
6567
+ `).all(...params);
6568
+ return rows.map((row) => ({
6569
+ ...rowToTask(row),
6570
+ blocked_by: row.blocked_by_ids ? row.blocked_by_ids.split(",") : []
6571
+ }));
6572
+ }
6573
+ function getBlockingTasks(projectId, db) {
6574
+ const d = db || getDatabase();
6575
+ const params = [];
6576
+ let projectClause = "";
6577
+ if (projectId) {
6578
+ projectClause = "AND blocker.project_id = ?";
6579
+ params.push(projectId);
6580
+ }
6581
+ const rows = d.query(`
6582
+ SELECT blocker.*, COUNT(DISTINCT blocked.id) AS blocking_count
6583
+ FROM tasks blocker
6584
+ JOIN task_dependencies td ON td.depends_on = blocker.id
6585
+ JOIN tasks blocked ON blocked.id = td.task_id
6586
+ WHERE blocker.archived_at IS NULL
6587
+ AND blocker.status NOT IN ('completed', 'cancelled', 'failed')
6588
+ AND blocked.status NOT IN ('completed', 'cancelled', 'failed')
6589
+ ${projectClause}
6590
+ GROUP BY blocker.id
6591
+ ORDER BY blocking_count DESC, blocker.created_at DESC
6592
+ `).all(...params);
6593
+ return rows.map((row) => ({
6594
+ ...rowToTask(row),
6595
+ blocking_count: row.blocking_count
6596
+ }));
6597
+ }
6418
6598
  function logTime(input, db) {
6419
6599
  const d = db || getDatabase();
6420
6600
  const id = uuid();
@@ -6495,6 +6675,7 @@ __export(exports_tasks, {
6495
6675
  removeDependency: () => removeDependency,
6496
6676
  redistributeStaleTasks: () => redistributeStaleTasks,
6497
6677
  notifyWatchers: () => notifyWatchers,
6678
+ notifyUpcomingDeadlines: () => notifyUpcomingDeadlines,
6498
6679
  moveTask: () => moveTask,
6499
6680
  logTime: () => logTime,
6500
6681
  logCost: () => logCost,
@@ -6510,12 +6691,15 @@ __export(exports_tasks, {
6510
6691
  getTaskGraph: () => getTaskGraph,
6511
6692
  getTaskDependents: () => getTaskDependents,
6512
6693
  getTaskDependencies: () => getTaskDependencies,
6513
- getTask: () => getTask2,
6694
+ getTask: () => getTask,
6514
6695
  getStatus: () => getStatus,
6515
6696
  getStaleTasks: () => getStaleTasks,
6516
6697
  getOverdueTasks: () => getOverdueTasks,
6517
6698
  getNextTask: () => getNextTask,
6699
+ getBlockingTasks: () => getBlockingTasks,
6518
6700
  getBlockingDeps: () => getBlockingDeps,
6701
+ getBlockedTasks: () => getBlockedTasks,
6702
+ getArchivedTasks: () => getArchivedTasks,
6519
6703
  getActiveWork: () => getActiveWork,
6520
6704
  failTask: () => failTask,
6521
6705
  deleteTask: () => deleteTask,
@@ -6527,8 +6711,10 @@ __export(exports_tasks, {
6527
6711
  claimOrSteal: () => claimOrSteal,
6528
6712
  claimNextTask: () => claimNextTask,
6529
6713
  bulkUpdateTasks: () => bulkUpdateTasks,
6714
+ bulkDeleteTasks: () => bulkDeleteTasks,
6530
6715
  bulkCreateTasks: () => bulkCreateTasks,
6531
6716
  archiveTasks: () => archiveTasks,
6717
+ archiveCompletedTasks: () => archiveCompletedTasks,
6532
6718
  addDependency: () => addDependency
6533
6719
  });
6534
6720
  var init_tasks = __esm(() => {
@@ -6934,9 +7120,18 @@ var init_builtin_templates = __esm(() => {
6934
7120
  });
6935
7121
 
6936
7122
  // src/db/comments.ts
7123
+ var exports_comments = {};
7124
+ __export(exports_comments, {
7125
+ updateComment: () => updateComment,
7126
+ logProgress: () => logProgress,
7127
+ listComments: () => listComments,
7128
+ getComment: () => getComment,
7129
+ deleteComment: () => deleteComment,
7130
+ addComment: () => addComment
7131
+ });
6937
7132
  function addComment(input, db) {
6938
7133
  const d = db || getDatabase();
6939
- if (!getTask2(input.task_id, d)) {
7134
+ if (!getTask(input.task_id, d)) {
6940
7135
  throw new TaskNotFoundError(input.task_id);
6941
7136
  }
6942
7137
  const id = uuid();
@@ -6965,6 +7160,15 @@ function listComments(taskId, db) {
6965
7160
  const d = db || getDatabase();
6966
7161
  return d.query("SELECT * FROM task_comments WHERE task_id = ? ORDER BY created_at").all(taskId);
6967
7162
  }
7163
+ function updateComment(id, input, db) {
7164
+ const d = db || getDatabase();
7165
+ d.run("UPDATE task_comments SET content = ? WHERE id = ?", [input.content, id]);
7166
+ const comment = getComment(id, d);
7167
+ if (!comment) {
7168
+ throw new Error(`Comment not found: ${id}`);
7169
+ }
7170
+ return comment;
7171
+ }
6968
7172
  function deleteComment(id, db) {
6969
7173
  const d = db || getDatabase();
6970
7174
  const result = d.run("DELETE FROM task_comments WHERE id = ?", [id]);
@@ -6977,6 +7181,10 @@ var init_comments = __esm(() => {
6977
7181
  });
6978
7182
 
6979
7183
  // src/lib/search.ts
7184
+ var exports_search = {};
7185
+ __export(exports_search, {
7186
+ searchTasks: () => searchTasks
7187
+ });
6980
7188
  function rowToTask2(row) {
6981
7189
  return {
6982
7190
  ...row,
@@ -7994,7 +8202,7 @@ function handleTasksContext(_req, url, _ctx, json2, taskToSummary2) {
7994
8202
  return new Response(text, { headers: { "Content-Type": "text/plain" } });
7995
8203
  }
7996
8204
  function handleTaskAttachments(id, _ctx, json2) {
7997
- const task = getTask2(id);
8205
+ const task = getTask(id);
7998
8206
  if (!task)
7999
8207
  return json2({ error: "Task not found" }, 404);
8000
8208
  const evidence = task.metadata?._evidence || {};
@@ -8002,7 +8210,7 @@ function handleTaskAttachments(id, _ctx, json2) {
8002
8210
  return json2({ task_id: id, short_id: task.short_id, attachment_ids: attachmentIds, count: attachmentIds.length, files_changed: evidence.files_changed, commit_hash: evidence.commit_hash, notes: evidence.notes });
8003
8211
  }
8004
8212
  async function handleTaskProgress(id, req, method, _ctx, json2) {
8005
- const task = getTask2(id);
8213
+ const task = getTask(id);
8006
8214
  if (!task)
8007
8215
  return json2({ error: "Task not found" }, 404);
8008
8216
  if (method === "GET") {
@@ -8025,7 +8233,7 @@ async function handleTaskProgress(id, req, method, _ctx, json2) {
8025
8233
  return null;
8026
8234
  }
8027
8235
  function handleGetTask(id, _ctx, json2, taskToSummary2) {
8028
- const task = getTask2(id);
8236
+ const task = getTask(id);
8029
8237
  if (!task)
8030
8238
  return json2({ error: "Task not found" }, 404);
8031
8239
  return json2(taskToSummary2(task));
@@ -8033,10 +8241,10 @@ function handleGetTask(id, _ctx, json2, taskToSummary2) {
8033
8241
  async function handlePatchTask(id, req, _ctx, json2, taskToSummary2) {
8034
8242
  try {
8035
8243
  const body = await req.json();
8036
- const task = getTask2(id);
8244
+ const task = getTask(id);
8037
8245
  if (!task)
8038
8246
  return json2({ error: "Task not found" }, 404);
8039
- const ALLOWED = new Set(["title", "description", "status", "priority", "assigned_to", "plan_id", "task_list_id", "tags", "metadata", "due_at", "estimated_minutes", "task_type"]);
8247
+ const ALLOWED = new Set(["title", "description", "status", "priority", "assigned_to", "plan_id", "task_list_id", "tags", "metadata", "due_at", "estimated_minutes", "actual_minutes", "confidence", "retry_count", "max_retries", "retry_after", "task_type"]);
8040
8248
  const safeBody = {};
8041
8249
  for (const [key, value] of Object.entries(body)) {
8042
8250
  if (ALLOWED.has(key))
@@ -10199,6 +10407,77 @@ var init_Dashboard = __esm(() => {
10199
10407
  init_audit();
10200
10408
  });
10201
10409
 
10410
+ // src/db/handoffs.ts
10411
+ var exports_handoffs = {};
10412
+ __export(exports_handoffs, {
10413
+ listHandoffs: () => listHandoffs,
10414
+ getLatestHandoff: () => getLatestHandoff,
10415
+ createHandoff: () => createHandoff
10416
+ });
10417
+ function createHandoff(input, db) {
10418
+ const d = db || getDatabase();
10419
+ const id = uuid();
10420
+ const timestamp = now();
10421
+ d.run(`INSERT INTO handoffs (id, agent_id, project_id, summary, completed, in_progress, blockers, next_steps, created_at)
10422
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
10423
+ id,
10424
+ input.agent_id || null,
10425
+ input.project_id || null,
10426
+ input.summary,
10427
+ input.completed ? JSON.stringify(input.completed) : null,
10428
+ input.in_progress ? JSON.stringify(input.in_progress) : null,
10429
+ input.blockers ? JSON.stringify(input.blockers) : null,
10430
+ input.next_steps ? JSON.stringify(input.next_steps) : null,
10431
+ timestamp
10432
+ ]);
10433
+ return {
10434
+ id,
10435
+ agent_id: input.agent_id || null,
10436
+ project_id: input.project_id || null,
10437
+ summary: input.summary,
10438
+ completed: input.completed || null,
10439
+ in_progress: input.in_progress || null,
10440
+ blockers: input.blockers || null,
10441
+ next_steps: input.next_steps || null,
10442
+ created_at: timestamp
10443
+ };
10444
+ }
10445
+ function rowToHandoff(row) {
10446
+ return {
10447
+ ...row,
10448
+ completed: row.completed ? JSON.parse(row.completed) : null,
10449
+ in_progress: row.in_progress ? JSON.parse(row.in_progress) : null,
10450
+ blockers: row.blockers ? JSON.parse(row.blockers) : null,
10451
+ next_steps: row.next_steps ? JSON.parse(row.next_steps) : null
10452
+ };
10453
+ }
10454
+ function listHandoffs(projectId, limit = 10, db) {
10455
+ const d = db || getDatabase();
10456
+ if (projectId) {
10457
+ return d.query("SELECT * FROM handoffs WHERE project_id = ? ORDER BY rowid DESC LIMIT ?").all(projectId, limit).map(rowToHandoff);
10458
+ }
10459
+ return d.query("SELECT * FROM handoffs ORDER BY rowid DESC LIMIT ?").all(limit).map(rowToHandoff);
10460
+ }
10461
+ function getLatestHandoff(agentId, projectId, db) {
10462
+ const d = db || getDatabase();
10463
+ let query = "SELECT * FROM handoffs WHERE 1=1";
10464
+ const params = [];
10465
+ if (agentId) {
10466
+ query += " AND agent_id = ?";
10467
+ params.push(agentId);
10468
+ }
10469
+ if (projectId) {
10470
+ query += " AND project_id = ?";
10471
+ params.push(projectId);
10472
+ }
10473
+ query += " ORDER BY rowid DESC LIMIT 1";
10474
+ const row = d.query(query).get(...params);
10475
+ return row ? rowToHandoff(row) : null;
10476
+ }
10477
+ var init_handoffs = __esm(() => {
10478
+ init_database();
10479
+ });
10480
+
10202
10481
  // src/lib/auto-assign.ts
10203
10482
  var exports_auto_assign = {};
10204
10483
  __export(exports_auto_assign, {
@@ -10289,7 +10568,7 @@ async function callCerebras(prompt, apiKey) {
10289
10568
  }
10290
10569
  async function autoAssignTask(taskId, db) {
10291
10570
  const d = db || getDatabase();
10292
- const task = getTask2(taskId, d);
10571
+ const task = getTask(taskId, d);
10293
10572
  if (!task)
10294
10573
  throw new Error(`Task ${taskId} not found`);
10295
10574
  const agents = listAgents(d).filter((a) => a.status === "active");
@@ -26215,12 +26494,21 @@ var init_dispatch2 = __esm(() => {
26215
26494
  // src/mcp/tools/task-crud.ts
26216
26495
  function registerTaskCrudTools(server, ctx) {
26217
26496
  const { shouldRegisterTool, resolveId, formatError, formatTask } = ctx;
26497
+ function versionFor(taskId, version) {
26498
+ const current = getTask(taskId);
26499
+ if (!current)
26500
+ throw new TaskNotFoundError(taskId);
26501
+ if (version !== undefined && current.version !== version) {
26502
+ throw new VersionConflictError(taskId, version, current.version);
26503
+ }
26504
+ return current.version;
26505
+ }
26218
26506
  if (shouldRegisterTool("create_task")) {
26219
26507
  server.tool("create_task", "Create a new task in a project. Pass short_id=null to auto-generate.", {
26220
26508
  title: exports_external2.string().describe("Task title"),
26221
26509
  description: exports_external2.string().optional().describe("Task description (markdown)"),
26222
- status: exports_external2.enum(["pending", "in_progress", "completed", "cancelled"]).optional().describe("Initial status (default: pending)"),
26223
- priority: exports_external2.enum(["low", "medium", "high", "urgent"]).optional().describe("Priority (default: medium)"),
26510
+ status: exports_external2.enum(["pending", "in_progress", "completed", "failed", "cancelled"]).optional().describe("Initial status (default: pending)"),
26511
+ priority: exports_external2.enum(["low", "medium", "high", "critical"]).optional().describe("Priority (default: medium)"),
26224
26512
  project_id: exports_external2.string().optional().describe("Project ID"),
26225
26513
  task_list_id: exports_external2.string().optional().describe("Task list ID"),
26226
26514
  assigned_to: exports_external2.string().optional().describe("Agent ID or name to assign to"),
@@ -26250,9 +26538,9 @@ function registerTaskCrudTools(server, ctx) {
26250
26538
  if (confidence !== undefined)
26251
26539
  resolved.confidence = confidence;
26252
26540
  if (retry_count !== undefined)
26253
- resolved.retry_count = retry_count;
26541
+ resolved.max_retries = retry_count;
26254
26542
  if (deadline)
26255
- resolved.deadline = deadline;
26543
+ resolved.due_at = deadline;
26256
26544
  const task = createTask(resolved);
26257
26545
  return { content: [{ type: "text", text: formatTask(task) }] };
26258
26546
  } catch (e) {
@@ -26262,8 +26550,8 @@ function registerTaskCrudTools(server, ctx) {
26262
26550
  }
26263
26551
  if (shouldRegisterTool("list_tasks")) {
26264
26552
  server.tool("list_tasks", "List tasks with optional filters. Pass empty arrays for multi-value filters (e.g. status=[] shows all).", {
26265
- status: exports_external2.union([exports_external2.enum(["pending", "in_progress", "completed", "cancelled"]), exports_external2.array(exports_external2.enum(["pending", "in_progress", "completed", "cancelled"]))]).optional().describe("Filter by status"),
26266
- priority: exports_external2.union([exports_external2.enum(["low", "medium", "high", "urgent"]), exports_external2.array(exports_external2.enum(["low", "medium", "high", "urgent"]))]).optional().describe("Filter by priority"),
26553
+ status: exports_external2.union([exports_external2.enum(["pending", "in_progress", "completed", "failed", "cancelled"]), exports_external2.array(exports_external2.enum(["pending", "in_progress", "completed", "failed", "cancelled"]))]).optional().describe("Filter by status"),
26554
+ priority: exports_external2.union([exports_external2.enum(["low", "medium", "high", "critical"]), exports_external2.array(exports_external2.enum(["low", "medium", "high", "critical"]))]).optional().describe("Filter by priority"),
26267
26555
  project_id: exports_external2.string().optional().describe("Filter by project"),
26268
26556
  task_list_id: exports_external2.string().optional().describe("Filter by task list"),
26269
26557
  assigned_to: exports_external2.string().optional().describe("Filter by assignee (agent ID or name, empty string = unassigned)"),
@@ -26298,9 +26586,9 @@ function registerTaskCrudTools(server, ctx) {
26298
26586
  }, async ({ task_id }) => {
26299
26587
  try {
26300
26588
  const resolvedId = resolveId(task_id);
26301
- const task = getTask2(resolvedId);
26589
+ const task = getTask(resolvedId);
26302
26590
  if (!task)
26303
- throw new NotFoundError(`Task not found: ${task_id}`);
26591
+ throw new TaskNotFoundError(task_id);
26304
26592
  const focus = ctx.getAgentFocus(task.assigned_to || "");
26305
26593
  const lines = [
26306
26594
  `ID: ${task.id}`,
@@ -26314,7 +26602,7 @@ function registerTaskCrudTools(server, ctx) {
26314
26602
  task.estimated_minutes != null ? `Estimate: ${task.estimated_minutes} min` : null,
26315
26603
  task.actual_minutes != null ? `Actual: ${task.actual_minutes} min` : null,
26316
26604
  task.confidence != null ? `Confidence: ${task.confidence}` : null,
26317
- task.deadline ? `Deadline: ${task.deadline}` : null,
26605
+ task.due_at ? `Due: ${task.due_at}` : null,
26318
26606
  task.completed_at ? `Completed: ${task.completed_at}` : null,
26319
26607
  focus ? `Focus: agent=${focus.agent_id} project=${focus.project_id || "(global)"}` : null,
26320
26608
  task.created_at ? `Created: ${task.created_at}` : null,
@@ -26336,8 +26624,8 @@ ${task.description}` : null
26336
26624
  task_id: exports_external2.string().describe("Task ID"),
26337
26625
  title: exports_external2.string().optional(),
26338
26626
  description: exports_external2.string().optional(),
26339
- status: exports_external2.enum(["pending", "in_progress", "completed", "cancelled"]).optional(),
26340
- priority: exports_external2.enum(["low", "medium", "high", "urgent"]).optional(),
26627
+ status: exports_external2.enum(["pending", "in_progress", "completed", "failed", "cancelled"]).optional(),
26628
+ priority: exports_external2.enum(["low", "medium", "high", "critical"]).optional(),
26341
26629
  assigned_to: exports_external2.string().nullable().optional().describe("Agent ID or name, null to unassign"),
26342
26630
  project_id: exports_external2.string().nullable().optional(),
26343
26631
  task_list_id: exports_external2.string().nullable().optional(),
@@ -26366,9 +26654,15 @@ ${task.description}` : null
26366
26654
  resolved.task_list_id = resolveId(resolved.task_list_id, "task_lists");
26367
26655
  if (resolved.depends_on && Array.isArray(resolved.depends_on))
26368
26656
  resolved.depends_on = resolved.depends_on.map(resolveId);
26369
- if (resolved.estimate !== undefined)
26657
+ if (resolved.estimate !== undefined) {
26370
26658
  resolved.estimated_minutes = resolved.estimate;
26371
- const task = updateTask(resolvedId, resolved, version);
26659
+ delete resolved.estimate;
26660
+ }
26661
+ if (resolved.deadline !== undefined) {
26662
+ resolved.due_at = resolved.deadline;
26663
+ delete resolved.deadline;
26664
+ }
26665
+ const task = updateTask(resolvedId, { ...resolved, version: versionFor(resolvedId, version) });
26372
26666
  return { content: [{ type: "text", text: formatTask(task) }] };
26373
26667
  } catch (e) {
26374
26668
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
@@ -26393,11 +26687,24 @@ ${task.description}` : null
26393
26687
  var init_task_crud2 = __esm(() => {
26394
26688
  init_zod2();
26395
26689
  init_tasks();
26690
+ init_types();
26396
26691
  });
26397
26692
 
26398
26693
  // src/mcp/tools/task-project-tools.ts
26399
26694
  function registerTaskProjectTools(server, ctx) {
26400
26695
  const { shouldRegisterTool, resolveId, formatError, formatTask } = ctx;
26696
+ function versionFor(taskId, version) {
26697
+ const current = getTask(taskId);
26698
+ if (!current)
26699
+ throw new TaskNotFoundError(taskId);
26700
+ if (version !== undefined && current.version !== version) {
26701
+ throw new VersionConflictError(taskId, version, current.version);
26702
+ }
26703
+ return current.version;
26704
+ }
26705
+ function updateWithOptionalVersion(taskId, updates, version) {
26706
+ return updateTask(taskId, { ...updates, version: versionFor(taskId, version) });
26707
+ }
26401
26708
  if (shouldRegisterTool("start_task")) {
26402
26709
  server.tool("start_task", "Mark a task as in_progress. Uses optimistic locking via version if provided.", {
26403
26710
  task_id: exports_external2.string().describe("Task ID"),
@@ -26405,7 +26712,12 @@ function registerTaskProjectTools(server, ctx) {
26405
26712
  }, async ({ task_id, version }) => {
26406
26713
  try {
26407
26714
  const resolvedId = resolveId(task_id);
26408
- const task = updateTask(resolvedId, { status: "in_progress" }, version);
26715
+ if (version !== undefined)
26716
+ versionFor(resolvedId, version);
26717
+ const current = getTask(resolvedId);
26718
+ if (!current)
26719
+ throw new TaskNotFoundError(resolvedId);
26720
+ const task = startTask(resolvedId, current.assigned_to || current.agent_id || "mcp");
26409
26721
  return { content: [{ type: "text", text: formatTask(task) }] };
26410
26722
  } catch (e) {
26411
26723
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
@@ -26421,7 +26733,12 @@ function registerTaskProjectTools(server, ctx) {
26421
26733
  }, async ({ task_id, confidence, completed_at, version }) => {
26422
26734
  try {
26423
26735
  const resolvedId = resolveId(task_id);
26424
- const task = updateTask(resolvedId, { status: "completed", confidence, completed_at }, version);
26736
+ if (version !== undefined)
26737
+ versionFor(resolvedId, version);
26738
+ const current = getTask(resolvedId);
26739
+ if (!current)
26740
+ throw new TaskNotFoundError(resolvedId);
26741
+ const task = completeTask(resolvedId, current.assigned_to || current.agent_id || undefined, undefined, { confidence, completed_at });
26425
26742
  return { content: [{ type: "text", text: formatTask(task) }] };
26426
26743
  } catch (e) {
26427
26744
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
@@ -26435,7 +26752,7 @@ function registerTaskProjectTools(server, ctx) {
26435
26752
  }, async ({ task_id, version }) => {
26436
26753
  try {
26437
26754
  const resolvedId = resolveId(task_id);
26438
- const task = updateTask(resolvedId, { status: "cancelled" }, version);
26755
+ const task = version === undefined ? setTaskStatus(resolvedId, "cancelled") : updateWithOptionalVersion(resolvedId, { status: "cancelled" }, version);
26439
26756
  return { content: [{ type: "text", text: formatTask(task) }] };
26440
26757
  } catch (e) {
26441
26758
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
@@ -26451,7 +26768,7 @@ function registerTaskProjectTools(server, ctx) {
26451
26768
  try {
26452
26769
  const resolvedId = resolveId(task_id);
26453
26770
  const resolvedAssignee = resolveId(new_assignee, "agents");
26454
- const task = updateTask(resolvedId, { assigned_to: resolvedAssignee }, version);
26771
+ const task = updateWithOptionalVersion(resolvedId, { assigned_to: resolvedAssignee }, version);
26455
26772
  return { content: [{ type: "text", text: formatTask(task) }] };
26456
26773
  } catch (e) {
26457
26774
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
@@ -26466,7 +26783,7 @@ function registerTaskProjectTools(server, ctx) {
26466
26783
  }, async ({ task_id, deadline, version }) => {
26467
26784
  try {
26468
26785
  const resolvedId = resolveId(task_id);
26469
- const task = updateTask(resolvedId, { deadline }, version);
26786
+ const task = updateWithOptionalVersion(resolvedId, { due_at: deadline }, version);
26470
26787
  return { content: [{ type: "text", text: formatTask(task) }] };
26471
26788
  } catch (e) {
26472
26789
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
@@ -26476,12 +26793,12 @@ function registerTaskProjectTools(server, ctx) {
26476
26793
  if (shouldRegisterTool("prioritize_task")) {
26477
26794
  server.tool("prioritize_task", "Set a task's priority.", {
26478
26795
  task_id: exports_external2.string().describe("Task ID"),
26479
- priority: exports_external2.enum(["low", "medium", "high", "urgent"]).describe("New priority"),
26796
+ priority: exports_external2.enum(["low", "medium", "high", "critical"]).describe("New priority"),
26480
26797
  version: exports_external2.number().optional().describe("Expected version for optimistic locking")
26481
26798
  }, async ({ task_id, priority, version }) => {
26482
26799
  try {
26483
26800
  const resolvedId = resolveId(task_id);
26484
- const task = updateTask(resolvedId, { priority }, version);
26801
+ const task = version === undefined ? setTaskPriority(resolvedId, priority) : updateWithOptionalVersion(resolvedId, { priority }, version);
26485
26802
  return { content: [{ type: "text", text: formatTask(task) }] };
26486
26803
  } catch (e) {
26487
26804
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
@@ -26537,18 +26854,18 @@ function registerTaskProjectTools(server, ctx) {
26537
26854
  if (shouldRegisterTool("bulk_update_tasks")) {
26538
26855
  server.tool("bulk_update_tasks", "Update multiple tasks at once. All tasks must pass the dependency check.", {
26539
26856
  task_ids: exports_external2.array(exports_external2.string()).describe("Array of task IDs to update"),
26540
- status: exports_external2.enum(["pending", "in_progress", "completed", "cancelled"]).optional(),
26541
- priority: exports_external2.enum(["low", "medium", "high", "urgent"]).optional(),
26857
+ status: exports_external2.enum(["pending", "in_progress", "completed", "failed", "cancelled"]).optional(),
26858
+ priority: exports_external2.enum(["low", "medium", "high", "critical"]).optional(),
26542
26859
  assigned_to: exports_external2.string().nullable().optional().describe("Agent ID or name, null to unassign")
26543
26860
  }, async ({ task_ids, status, priority, assigned_to }) => {
26544
26861
  try {
26545
- const { bulkUpdateTasks: bulkUpdateTasks2 } = (()=>{throw new Error("Cannot require module "+"../db/tasks.js");})();
26862
+ const { bulkUpdateTasks: bulkUpdateTasks2 } = (init_tasks(), __toCommonJS(exports_tasks));
26546
26863
  const resolved = task_ids.map(resolveId);
26547
26864
  let resolvedAssignee = assigned_to;
26548
26865
  if (resolvedAssignee && typeof resolvedAssignee === "string")
26549
26866
  resolvedAssignee = resolveId(resolvedAssignee, "agents");
26550
26867
  const result = bulkUpdateTasks2(resolved, { status, priority, assigned_to: resolvedAssignee });
26551
- return { content: [{ type: "text", text: `${result.updated} task(s) updated, ${result.skipped} skipped (dependency check).` }] };
26868
+ return { content: [{ type: "text", text: `${result.updated} task(s) updated, ${result.failed.length} failed.` }] };
26552
26869
  } catch (e) {
26553
26870
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
26554
26871
  }
@@ -26559,8 +26876,8 @@ function registerTaskProjectTools(server, ctx) {
26559
26876
  tasks: exports_external2.array(exports_external2.object({
26560
26877
  title: exports_external2.string(),
26561
26878
  description: exports_external2.string().optional(),
26562
- status: exports_external2.enum(["pending", "in_progress", "completed", "cancelled"]).optional(),
26563
- priority: exports_external2.enum(["low", "medium", "high", "urgent"]).optional(),
26879
+ status: exports_external2.enum(["pending", "in_progress", "completed", "failed", "cancelled"]).optional(),
26880
+ priority: exports_external2.enum(["low", "medium", "high", "critical"]).optional(),
26564
26881
  project_id: exports_external2.string().optional(),
26565
26882
  task_list_id: exports_external2.string().optional(),
26566
26883
  assigned_to: exports_external2.string().optional(),
@@ -26571,7 +26888,7 @@ function registerTaskProjectTools(server, ctx) {
26571
26888
  })).describe("Array of task objects")
26572
26889
  }, async ({ tasks }) => {
26573
26890
  try {
26574
- const { bulkCreateTasks: bulkCreateTasks2 } = (()=>{throw new Error("Cannot require module "+"../db/tasks.js");})();
26891
+ const { bulkCreateTasks: bulkCreateTasks2 } = (init_tasks(), __toCommonJS(exports_tasks));
26575
26892
  const resolved = tasks.map((t) => {
26576
26893
  const r = { ...t };
26577
26894
  if (r.project_id)
@@ -26585,7 +26902,7 @@ function registerTaskProjectTools(server, ctx) {
26585
26902
  return r;
26586
26903
  });
26587
26904
  const result = bulkCreateTasks2(resolved);
26588
- return { content: [{ type: "text", text: `${result.created} task(s) created, ${result.skipped} skipped (duplicate short_id).` }] };
26905
+ return { content: [{ type: "text", text: `${result.created.length} task(s) created.` }] };
26589
26906
  } catch (e) {
26590
26907
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
26591
26908
  }
@@ -26597,9 +26914,9 @@ function registerTaskProjectTools(server, ctx) {
26597
26914
  force: exports_external2.boolean().optional().describe("Skip child check for all tasks (dangerous)")
26598
26915
  }, async ({ task_ids, force }) => {
26599
26916
  try {
26600
- const { bulkDeleteTasks } = (()=>{throw new Error("Cannot require module "+"../db/tasks.js");})();
26917
+ const { bulkDeleteTasks: bulkDeleteTasks2 } = (init_tasks(), __toCommonJS(exports_tasks));
26601
26918
  const resolved = task_ids.map(resolveId);
26602
- const result = bulkDeleteTasks(resolved, force);
26919
+ const result = bulkDeleteTasks2(resolved, force);
26603
26920
  return { content: [{ type: "text", text: `${result.deleted} task(s) deleted, ${result.skipped} skipped (has children).` }] };
26604
26921
  } catch (e) {
26605
26922
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
@@ -26648,7 +26965,7 @@ function registerTaskProjectTools(server, ctx) {
26648
26965
  const project = getProject(resolvedId);
26649
26966
  if (!project)
26650
26967
  throw new TaskNotFoundError(`Project not found: ${project_id}`);
26651
- const { listTasks: listTasks3 } = (()=>{throw new Error("Cannot require module "+"../db/tasks.js");})();
26968
+ const { listTasks: listTasks3 } = (init_tasks(), __toCommonJS(exports_tasks));
26652
26969
  const tasks = listTasks3({ project_id: resolvedId, limit: 100 }, undefined);
26653
26970
  const lines = [
26654
26971
  `ID: ${project.id}`,
@@ -26749,7 +27066,7 @@ function registerTaskProjectTools(server, ctx) {
26749
27066
  throw new TaskNotFoundError(`Task list not found: ${task_list_id}`);
26750
27067
  let tasks = [];
26751
27068
  if (include_tasks) {
26752
- const { listTasks: listTasks3 } = (()=>{throw new Error("Cannot require module "+"../db/tasks.js");})();
27069
+ const { listTasks: listTasks3 } = (init_tasks(), __toCommonJS(exports_tasks));
26753
27070
  tasks = listTasks3({ task_list_id: resolvedId, limit: 200 }, undefined);
26754
27071
  }
26755
27072
  const lines = [
@@ -26849,7 +27166,7 @@ Tasks:` : null,
26849
27166
  throw new TaskNotFoundError(`Plan not found: ${plan_id}`);
26850
27167
  let tasks = [];
26851
27168
  if (include_tasks) {
26852
- const { listTasks: listTasks3 } = (()=>{throw new Error("Cannot require module "+"../db/tasks.js");})();
27169
+ const { listTasks: listTasks3 } = (init_tasks(), __toCommonJS(exports_tasks));
26853
27170
  tasks = listTasks3({ plan_id: resolvedId, limit: 200 }, undefined);
26854
27171
  }
26855
27172
  const lines = [
@@ -26938,7 +27255,7 @@ Tasks:` : null,
26938
27255
  const tag = getTag(tag_id);
26939
27256
  if (!tag)
26940
27257
  throw new TaskNotFoundError(`Tag not found: ${tag_id}`);
26941
- const { listTasks: listTasks3 } = (()=>{throw new Error("Cannot require module "+"../db/tasks.js");})();
27258
+ const { listTasks: listTasks3 } = (init_tasks(), __toCommonJS(exports_tasks));
26942
27259
  const tasks = listTasks3({ tags: [tag.name], limit: 100 }, undefined);
26943
27260
  const lines = [
26944
27261
  `Tag: ${tag.name}${tag.color ? ` (${tag.color})` : ""}`,
@@ -27017,7 +27334,7 @@ Tasks:` : null,
27017
27334
  const label = getLabel(label_id);
27018
27335
  if (!label)
27019
27336
  throw new TaskNotFoundError(`Label not found: ${label_id}`);
27020
- const { listTasks: listTasks3 } = (()=>{throw new Error("Cannot require module "+"../db/tasks.js");})();
27337
+ const { listTasks: listTasks3 } = (init_tasks(), __toCommonJS(exports_tasks));
27021
27338
  const tasks = listTasks3({ tags: [label.name], limit: 100 }, undefined);
27022
27339
  const lines = [
27023
27340
  `Label: ${label.name}${label.color ? ` (${label.color})` : ""}`,
@@ -27068,7 +27385,7 @@ Tasks:` : null,
27068
27385
  try {
27069
27386
  const resolvedId = resolveId(task_id);
27070
27387
  const resolvedAuthor = author ? resolveId(author, "agents") : undefined;
27071
- const comment = addComment({ task_id: resolvedId, body, author: resolvedAuthor });
27388
+ const comment = addComment({ task_id: resolvedId, content: body, agent_id: resolvedAuthor });
27072
27389
  return { content: [{ type: "text", text: `Comment added to ${task_id.slice(0, 8)}: ${body.slice(0, 50)}${body.length > 50 ? "..." : ""}` }] };
27073
27390
  } catch (e) {
27074
27391
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
@@ -27084,8 +27401,8 @@ Tasks:` : null,
27084
27401
  const comments = listComments(resolvedId);
27085
27402
  if (comments.length === 0)
27086
27403
  return { content: [{ type: "text", text: "No comments." }] };
27087
- const lines = comments.map((c) => `[${c.author || "unknown"}] ${c.created_at?.slice(0, 16)}:
27088
- ${c.body}`);
27404
+ const lines = comments.map((c) => `[${c.agent_id || "unknown"}] ${c.created_at?.slice(0, 16)}:
27405
+ ${c.content}`);
27089
27406
  return { content: [{ type: "text", text: lines.join(`
27090
27407
 
27091
27408
  `) }] };
@@ -27100,7 +27417,7 @@ Tasks:` : null,
27100
27417
  body: exports_external2.string().describe("New comment body")
27101
27418
  }, async ({ comment_id, body }) => {
27102
27419
  try {
27103
- const comment = updateComment(comment_id, { body });
27420
+ const comment = updateComment(comment_id, { content: body });
27104
27421
  return { content: [{ type: "text", text: `Comment ${comment_id.slice(0, 8)} updated.` }] };
27105
27422
  } catch (e) {
27106
27423
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
@@ -27123,11 +27440,11 @@ Tasks:` : null,
27123
27440
  server.tool("search_tasks", "Full-text search across task titles and descriptions.", {
27124
27441
  query: exports_external2.string().describe("Search query"),
27125
27442
  project_id: exports_external2.string().optional().describe("Filter by project"),
27126
- status: exports_external2.enum(["pending", "in_progress", "completed", "cancelled"]).optional(),
27443
+ status: exports_external2.enum(["pending", "in_progress", "completed", "failed", "cancelled"]).optional(),
27127
27444
  limit: exports_external2.number().optional().describe("Max results (default: 20)")
27128
27445
  }, async ({ query, project_id, status, limit }) => {
27129
27446
  try {
27130
- const { searchTasks: searchTasks2 } = (()=>{throw new Error("Cannot require module "+"../db/tasks.js");})();
27447
+ const { searchTasks: searchTasks2 } = (init_search(), __toCommonJS(exports_search));
27131
27448
  const resolved = { query, limit };
27132
27449
  if (project_id)
27133
27450
  resolved.project_id = resolveId(project_id, "projects");
@@ -27145,29 +27462,6 @@ ${lines.join(`
27145
27462
  }
27146
27463
  });
27147
27464
  }
27148
- if (shouldRegisterTool("sync")) {
27149
- server.tool("sync", "Sync tasks from a GitHub PR or external source into the project.", {
27150
- source: exports_external2.enum(["github_pr", "linear", "asana"]).describe("Source type"),
27151
- source_id: exports_external2.string().describe("PR number, Linear issue ID, or Asana task ID"),
27152
- project_id: exports_external2.string().optional().describe("Project ID to import into"),
27153
- options: exports_external2.record(exports_external2.unknown()).optional().describe("Source-specific options")
27154
- }, async ({ source, source_id, project_id, options }) => {
27155
- try {
27156
- const { syncFromGithubPR, syncFromLinear, syncFromAsana } = (()=>{throw new Error("Cannot require module "+"../lib/sync.js");})();
27157
- let result;
27158
- if (source === "github_pr") {
27159
- result = await syncFromGithubPR({ prNumber: parseInt(source_id), project_id: project_id ? resolveId(project_id, "projects") : undefined, ...options });
27160
- } else if (source === "linear") {
27161
- result = await syncFromLinear({ issueId: source_id, project_id: project_id ? resolveId(project_id, "projects") : undefined, ...options });
27162
- } else {
27163
- result = await syncFromAsana({ taskId: source_id, project_id: project_id ? resolveId(project_id, "projects") : undefined, ...options });
27164
- }
27165
- return { content: [{ type: "text", text: `Synced from ${source}: ${result.task?.title || source_id}` }] };
27166
- } catch (e) {
27167
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
27168
- }
27169
- });
27170
- }
27171
27465
  }
27172
27466
  var init_task_project_tools = __esm(() => {
27173
27467
  init_zod2();
@@ -27182,6 +27476,16 @@ var init_task_project_tools = __esm(() => {
27182
27476
  // src/mcp/tools/task-workflow-tools.ts
27183
27477
  function registerTaskWorkflowTools(server, ctx) {
27184
27478
  const { shouldRegisterTool, resolveId, formatError, formatTask } = ctx;
27479
+ function versionFor(taskId, version) {
27480
+ const { getTask: getTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
27481
+ const current = getTask2(taskId);
27482
+ if (!current)
27483
+ throw new TaskNotFoundError(taskId);
27484
+ if (version !== undefined && current.version !== version) {
27485
+ throw new VersionConflictError(taskId, version, current.version);
27486
+ }
27487
+ return current.version;
27488
+ }
27185
27489
  if (shouldRegisterTool("approve_task")) {
27186
27490
  server.tool("approve_task", "Approve a task that requires_approval. Records who approved it.", {
27187
27491
  task_id: exports_external2.string().describe("Task ID"),
@@ -27190,13 +27494,14 @@ function registerTaskWorkflowTools(server, ctx) {
27190
27494
  version: exports_external2.number().optional().describe("Expected version for optimistic locking")
27191
27495
  }, async ({ task_id, approved_by, notes, version }) => {
27192
27496
  try {
27193
- const { updateTask: updateTask2 } = (()=>{throw new Error("Cannot require module "+"../db/tasks.js");})();
27497
+ const { updateTask: updateTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
27194
27498
  const resolvedId = resolveId(task_id);
27195
27499
  const resolvedApprover = approved_by ? resolveId(approved_by, "agents") : undefined;
27196
27500
  const task = updateTask2(resolvedId, {
27197
27501
  approved_by: resolvedApprover,
27198
- metadata: notes ? { approval_notes: notes } : {}
27199
- }, version);
27502
+ metadata: notes ? { approval_notes: notes } : {},
27503
+ version: versionFor(resolvedId, version)
27504
+ });
27200
27505
  return { content: [{ type: "text", text: `Task ${task_id.slice(0, 8)} approved by ${resolvedApprover || "unknown"}` }] };
27201
27506
  } catch (e) {
27202
27507
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
@@ -27211,15 +27516,11 @@ function registerTaskWorkflowTools(server, ctx) {
27211
27516
  version: exports_external2.number().optional().describe("Expected version for optimistic locking")
27212
27517
  }, async ({ task_id, reason, agent_id, version }) => {
27213
27518
  try {
27214
- const { updateTask: updateTask2 } = (()=>{throw new Error("Cannot require module "+"../db/tasks.js");})();
27519
+ const { failTask: failTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
27215
27520
  const resolvedId = resolveId(task_id);
27216
- const task = updateTask2(resolvedId, {
27217
- status: "pending",
27218
- retry_count: (getTask(resolvedId)?.retry_count ?? 0) + 1,
27219
- back_on_board: false,
27220
- metadata: reason ? { failure_reason: reason } : {}
27221
- }, version);
27222
- return { content: [{ type: "text", text: `Task ${task_id.slice(0, 8)} marked failed. Retry count: ${task.retry_count}` }] };
27521
+ versionFor(resolvedId, version);
27522
+ const result = failTask2(resolvedId, agent_id, reason);
27523
+ return { content: [{ type: "text", text: `Task ${task_id.slice(0, 8)} marked failed. Retry count: ${result.task.retry_count}` }] };
27223
27524
  } catch (e) {
27224
27525
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
27225
27526
  }
@@ -27228,12 +27529,12 @@ function registerTaskWorkflowTools(server, ctx) {
27228
27529
  if (shouldRegisterTool("get_my_tasks")) {
27229
27530
  server.tool("get_my_tasks", "Get tasks assigned to the calling agent. Supports focus mode scoping.", {
27230
27531
  agent_id: exports_external2.string().optional().describe("Agent ID (defaults to context agent)"),
27231
- status: exports_external2.enum(["pending", "in_progress", "completed", "cancelled"]).optional().describe("Filter by status"),
27532
+ status: exports_external2.enum(["pending", "in_progress", "completed", "failed", "cancelled"]).optional().describe("Filter by status"),
27232
27533
  project_id: exports_external2.string().optional().describe("Filter by project (respects focus mode)"),
27233
27534
  limit: exports_external2.number().optional().describe("Max results (default: 50)")
27234
27535
  }, async ({ agent_id, status, project_id, limit }) => {
27235
27536
  try {
27236
- const { listTasks: listTasks3 } = (()=>{throw new Error("Cannot require module "+"../db/tasks.js");})();
27537
+ const { listTasks: listTasks3 } = (init_tasks(), __toCommonJS(exports_tasks));
27237
27538
  const focus = ctx.getAgentFocus(agent_id || "");
27238
27539
  const effectiveAgentId = focus ? focus.agent_id : agent_id || "";
27239
27540
  const effectiveProjectId = focus?.project_id || project_id;
@@ -27253,6 +27554,122 @@ function registerTaskWorkflowTools(server, ctx) {
27253
27554
  }
27254
27555
  });
27255
27556
  }
27557
+ if (shouldRegisterTool("get_next_task")) {
27558
+ server.tool("get_next_task", "Get the best available pending task without claiming it.", {
27559
+ agent_id: exports_external2.string().optional().describe("Agent ID or name for assignment affinity"),
27560
+ project_id: exports_external2.string().optional().describe("Filter by project"),
27561
+ task_list_id: exports_external2.string().optional().describe("Filter by task list"),
27562
+ plan_id: exports_external2.string().optional().describe("Filter by plan"),
27563
+ tags: exports_external2.array(exports_external2.string()).optional().describe("Filter by tags")
27564
+ }, async ({ agent_id, project_id, task_list_id, plan_id, tags }) => {
27565
+ try {
27566
+ const { getNextTask: getNextTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
27567
+ const filters = {};
27568
+ if (project_id)
27569
+ filters.project_id = resolveId(project_id, "projects");
27570
+ if (task_list_id)
27571
+ filters.task_list_id = resolveId(task_list_id, "task_lists");
27572
+ if (plan_id)
27573
+ filters.plan_id = resolveId(plan_id, "plans");
27574
+ if (tags)
27575
+ filters.tags = tags;
27576
+ const task = getNextTask2(agent_id, filters);
27577
+ return { content: [{ type: "text", text: task ? formatTask(task) : "No available task." }] };
27578
+ } catch (e) {
27579
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
27580
+ }
27581
+ });
27582
+ }
27583
+ if (shouldRegisterTool("claim_next_task")) {
27584
+ server.tool("claim_next_task", "Atomically claim and start the best available pending task for an agent.", {
27585
+ agent_id: exports_external2.string().describe("Agent ID or name claiming the task"),
27586
+ project_id: exports_external2.string().optional().describe("Filter by project"),
27587
+ task_list_id: exports_external2.string().optional().describe("Filter by task list"),
27588
+ plan_id: exports_external2.string().optional().describe("Filter by plan"),
27589
+ tags: exports_external2.array(exports_external2.string()).optional().describe("Filter by tags")
27590
+ }, async ({ agent_id, project_id, task_list_id, plan_id, tags }) => {
27591
+ try {
27592
+ const { claimNextTask: claimNextTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
27593
+ const filters = {};
27594
+ if (project_id)
27595
+ filters.project_id = resolveId(project_id, "projects");
27596
+ if (task_list_id)
27597
+ filters.task_list_id = resolveId(task_list_id, "task_lists");
27598
+ if (plan_id)
27599
+ filters.plan_id = resolveId(plan_id, "plans");
27600
+ if (tags)
27601
+ filters.tags = tags;
27602
+ const task = claimNextTask2(agent_id, filters);
27603
+ return { content: [{ type: "text", text: task ? formatTask(task) : "No available task to claim." }] };
27604
+ } catch (e) {
27605
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
27606
+ }
27607
+ });
27608
+ }
27609
+ if (shouldRegisterTool("get_tasks_changed_since")) {
27610
+ server.tool("get_tasks_changed_since", "List tasks changed since an ISO timestamp.", {
27611
+ since: exports_external2.string().describe("ISO timestamp"),
27612
+ project_id: exports_external2.string().optional().describe("Filter by project"),
27613
+ task_list_id: exports_external2.string().optional().describe("Filter by task list"),
27614
+ limit: exports_external2.number().optional().describe("Maximum tasks to return")
27615
+ }, async ({ since, project_id, task_list_id, limit }) => {
27616
+ try {
27617
+ const { getTasksChangedSince: getTasksChangedSince2 } = (init_tasks(), __toCommonJS(exports_tasks));
27618
+ const filters = {};
27619
+ if (project_id)
27620
+ filters.project_id = resolveId(project_id, "projects");
27621
+ if (task_list_id)
27622
+ filters.task_list_id = resolveId(task_list_id, "task_lists");
27623
+ const tasks = getTasksChangedSince2(since, filters).slice(0, limit || 50);
27624
+ if (tasks.length === 0)
27625
+ return { content: [{ type: "text", text: "No changed tasks." }] };
27626
+ return { content: [{ type: "text", text: tasks.map(formatTask).join(`
27627
+ `) }] };
27628
+ } catch (e) {
27629
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
27630
+ }
27631
+ });
27632
+ }
27633
+ function registerContextTool(name, description) {
27634
+ if (!shouldRegisterTool(name))
27635
+ return;
27636
+ server.tool(name, description, {
27637
+ agent_id: exports_external2.string().optional().describe("Agent ID or name"),
27638
+ project_id: exports_external2.string().optional().describe("Filter by project"),
27639
+ task_list_id: exports_external2.string().optional().describe("Filter by task list"),
27640
+ explain_blocked: exports_external2.boolean().optional().describe("Include blocked task details")
27641
+ }, async ({ agent_id, project_id, task_list_id, explain_blocked }) => {
27642
+ try {
27643
+ const { getStatus: getStatus2, getNextTask: getNextTask2, getOverdueTasks: getOverdueTasks2 } = (init_tasks(), __toCommonJS(exports_tasks));
27644
+ const { getLatestHandoff: getLatestHandoff2 } = (init_handoffs(), __toCommonJS(exports_handoffs));
27645
+ const filters = {};
27646
+ if (project_id)
27647
+ filters.project_id = resolveId(project_id, "projects");
27648
+ if (task_list_id)
27649
+ filters.task_list_id = resolveId(task_list_id, "task_lists");
27650
+ const status = getStatus2(filters, agent_id, { explain_blocked });
27651
+ const next_task = getNextTask2(agent_id, filters);
27652
+ const overdue = getOverdueTasks2(filters.project_id);
27653
+ const latest_handoff = getLatestHandoff2(agent_id, filters.project_id);
27654
+ return {
27655
+ content: [{
27656
+ type: "text",
27657
+ text: JSON.stringify({
27658
+ status,
27659
+ next_task,
27660
+ overdue_count: overdue.length,
27661
+ latest_handoff,
27662
+ as_of: new Date().toISOString()
27663
+ }, null, 2)
27664
+ }]
27665
+ };
27666
+ } catch (e) {
27667
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
27668
+ }
27669
+ });
27670
+ }
27671
+ registerContextTool("get_context", "Get session start context: queue status, next task, overdue count, and latest handoff.");
27672
+ registerContextTool("bootstrap", "Bootstrap an agent session with queue context and the next available task.");
27256
27673
  if (shouldRegisterTool("get_org_chart")) {
27257
27674
  server.tool("get_org_chart", "Get the global org chart (agent hierarchy + titles).", {
27258
27675
  format: exports_external2.enum(["text", "json"]).optional().describe("Output format (default: text)")
@@ -27273,7 +27690,7 @@ function registerTaskWorkflowTools(server, ctx) {
27273
27690
  }).join(`
27274
27691
  `);
27275
27692
  };
27276
- const { getOrgChart: getOrgChart2 } = (()=>{throw new Error("Cannot require module "+"../db/agents.js");})();
27693
+ const { getOrgChart: getOrgChart2 } = (init_agents(), __toCommonJS(exports_agents));
27277
27694
  const tree = getOrgChart2();
27278
27695
  if (format === "json") {
27279
27696
  return { content: [{ type: "text", text: JSON.stringify(tree, null, 2) }] };
@@ -27293,10 +27710,10 @@ function registerTaskWorkflowTools(server, ctx) {
27293
27710
  reports_to: exports_external2.string().describe("Manager agent ID or name")
27294
27711
  }, async ({ agent_id, reports_to }) => {
27295
27712
  try {
27296
- const { setReportsTo } = (()=>{throw new Error("Cannot require module "+"../db/agents.js");})();
27713
+ const { updateAgent: updateAgent2 } = (init_agents(), __toCommonJS(exports_agents));
27297
27714
  const resolvedAgent = resolveId(agent_id, "agents");
27298
27715
  const resolvedManager = resolveId(reports_to, "agents");
27299
- setReportsTo(resolvedAgent, resolvedManager);
27716
+ updateAgent2(resolvedAgent, { reports_to: resolvedManager });
27300
27717
  return { content: [{ type: "text", text: `${agent_id} now reports to ${reports_to}` }] };
27301
27718
  } catch (e) {
27302
27719
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
@@ -27306,6 +27723,7 @@ function registerTaskWorkflowTools(server, ctx) {
27306
27723
  }
27307
27724
  var init_task_workflow_tools = __esm(() => {
27308
27725
  init_zod2();
27726
+ init_types();
27309
27727
  });
27310
27728
 
27311
27729
  // src/mcp/tools/task-auto-tools.ts
@@ -27317,9 +27735,9 @@ function registerTaskAutoTools(server, ctx) {
27317
27735
  project_id: exports_external2.string().optional().describe("Scope to a project")
27318
27736
  }, async ({ days = 7, project_id }) => {
27319
27737
  try {
27320
- const { archiveCompletedTasks } = (init_tasks(), __toCommonJS(exports_tasks));
27738
+ const { archiveCompletedTasks: archiveCompletedTasks2 } = (init_tasks(), __toCommonJS(exports_tasks));
27321
27739
  const resolvedProjectId = project_id ? resolveId(project_id, "projects") : undefined;
27322
- const count = archiveCompletedTasks(days, resolvedProjectId);
27740
+ const count = archiveCompletedTasks2(days, resolvedProjectId);
27323
27741
  return { content: [{ type: "text", text: `Archived ${count} completed task(s) older than ${days} days.` }] };
27324
27742
  } catch (e) {
27325
27743
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
@@ -27345,9 +27763,9 @@ function registerTaskAutoTools(server, ctx) {
27345
27763
  limit: exports_external2.number().optional().describe("Max results (default: 50)")
27346
27764
  }, async ({ project_id, limit }) => {
27347
27765
  try {
27348
- const { getArchivedTasks } = (init_tasks(), __toCommonJS(exports_tasks));
27766
+ const { getArchivedTasks: getArchivedTasks2 } = (init_tasks(), __toCommonJS(exports_tasks));
27349
27767
  const resolvedProjectId = project_id ? resolveId(project_id, "projects") : undefined;
27350
- const tasks = getArchivedTasks({ project_id: resolvedProjectId, limit: limit || 50 });
27768
+ const tasks = getArchivedTasks2({ project_id: resolvedProjectId, limit: limit || 50 });
27351
27769
  if (tasks.length === 0)
27352
27770
  return { content: [{ type: "text", text: "No archived tasks." }] };
27353
27771
  const lines = tasks.map((t) => `${t.short_id || t.id.slice(0, 8)} ${t.title} archived ${t.archived_at}`);
@@ -27363,14 +27781,13 @@ function registerTaskAutoTools(server, ctx) {
27363
27781
  task_id: exports_external2.string().describe("Task ID")
27364
27782
  }, async ({ task_id }) => {
27365
27783
  try {
27366
- const { autoAssign } = (init_agents(), __toCommonJS(exports_agents));
27367
27784
  const resolvedId = resolveId(task_id);
27368
- const assignment = autoAssign(resolvedId);
27369
- if (!assignment)
27785
+ const { autoAssignTask: autoAssignTask2 } = (init_auto_assign(), __toCommonJS(exports_auto_assign));
27786
+ const assignment = await autoAssignTask2(resolvedId);
27787
+ if (!assignment.assigned_to)
27370
27788
  return { content: [{ type: "text", text: "No suitable agent found for this task." }] };
27371
- const { updateTask: updateTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
27372
- updateTask2(resolvedId, { assigned_to: assignment.agent_id });
27373
- return { content: [{ type: "text", text: `Task ${task_id.slice(0, 8)} assigned to ${assignment.agent_name} (score: ${assignment.score.toFixed(2)})` }] };
27789
+ const reason = assignment.reason ? ` \u2014 ${assignment.reason}` : "";
27790
+ return { content: [{ type: "text", text: `Task ${task_id.slice(0, 8)} assigned to ${assignment.agent_name || assignment.assigned_to} via ${assignment.method}${reason}` }] };
27374
27791
  } catch (e) {
27375
27792
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
27376
27793
  }
@@ -27381,10 +27798,23 @@ function registerTaskAutoTools(server, ctx) {
27381
27798
  agent_id: exports_external2.string().optional().describe("Agent ID (defaults to context agent)")
27382
27799
  }, async ({ agent_id }) => {
27383
27800
  try {
27384
- const { getAgentWorkload } = (init_agents(), __toCommonJS(exports_agents));
27801
+ const { listTasks: listTasks3, getBlockedTasks: getBlockedTasks2 } = (init_tasks(), __toCommonJS(exports_tasks));
27385
27802
  const focus = ctx.getAgentFocus(agent_id || "");
27386
27803
  const effectiveAgentId = focus ? focus.agent_id : agent_id || "";
27387
- const workload = getAgentWorkload(effectiveAgentId);
27804
+ if (!effectiveAgentId) {
27805
+ return { content: [{ type: "text", text: "No agent_id provided and no agent focus is active." }], isError: true };
27806
+ }
27807
+ const assigned = listTasks3({ assigned_to: effectiveAgentId, limit: 500 }, undefined);
27808
+ const now2 = Date.now();
27809
+ const dueSoonCutoff = now2 + 24 * 60 * 60 * 1000;
27810
+ const blocked = getBlockedTasks2().filter((t) => t.assigned_to === effectiveAgentId);
27811
+ const workload = {
27812
+ in_progress: assigned.filter((t) => t.status === "in_progress").length,
27813
+ pending: assigned.filter((t) => t.status === "pending").length,
27814
+ completed_recent: assigned.filter((t) => t.status === "completed" && t.completed_at && now2 - new Date(t.completed_at).getTime() <= 7 * 24 * 60 * 60 * 1000).length,
27815
+ due_soon: assigned.filter((t) => t.due_at && new Date(t.due_at).getTime() <= dueSoonCutoff && new Date(t.due_at).getTime() >= now2 && !["completed", "cancelled", "failed"].includes(t.status)).length,
27816
+ blocked: blocked.length
27817
+ };
27388
27818
  const lines = [
27389
27819
  `Agent: ${effectiveAgentId}`,
27390
27820
  `In Progress: ${workload.in_progress}`,
@@ -27406,10 +27836,29 @@ function registerTaskAutoTools(server, ctx) {
27406
27836
  max_per_agent: exports_external2.number().optional().describe("Max tasks per agent (default: 5)")
27407
27837
  }, async ({ project_id, max_per_agent }) => {
27408
27838
  try {
27409
- const { rebalanceWorkload } = (init_agents(), __toCommonJS(exports_agents));
27839
+ const { listAgents: listAgents2 } = (init_agents(), __toCommonJS(exports_agents));
27840
+ const { listTasks: listTasks3, updateTask: updateTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
27410
27841
  const resolvedProjectId = project_id ? resolveId(project_id, "projects") : undefined;
27411
- const result = rebalanceWorkload({ project_id: resolvedProjectId, max_per_agent: max_per_agent || 5 });
27412
- return { content: [{ type: "text", text: `Rebalanced: moved ${result.moved} task(s), ${result.skipped} skipped.` }] };
27842
+ const limit = max_per_agent || 5;
27843
+ const agents = listAgents2().filter((agent) => agent.status === "active");
27844
+ if (agents.length === 0)
27845
+ return { content: [{ type: "text", text: "No active agents available for rebalancing." }] };
27846
+ const activeTasks = listTasks3({ project_id: resolvedProjectId, status: ["pending", "in_progress"], limit: 1000 }, undefined);
27847
+ const load = new Map(agents.map((agent) => [agent.id, activeTasks.filter((t) => t.assigned_to === agent.id).length]));
27848
+ let moved = 0;
27849
+ let skipped = 0;
27850
+ for (const task of activeTasks.filter((t) => t.status === "pending" && t.assigned_to && (load.get(t.assigned_to) ?? 0) > limit)) {
27851
+ const target = agents.filter((agent) => agent.id !== task.assigned_to).sort((a, b) => (load.get(a.id) ?? 0) - (load.get(b.id) ?? 0))[0];
27852
+ if (!target || (load.get(target.id) ?? 0) >= limit) {
27853
+ skipped++;
27854
+ continue;
27855
+ }
27856
+ updateTask2(task.id, { assigned_to: target.id, version: task.version });
27857
+ load.set(task.assigned_to, (load.get(task.assigned_to) ?? 1) - 1);
27858
+ load.set(target.id, (load.get(target.id) ?? 0) + 1);
27859
+ moved++;
27860
+ }
27861
+ return { content: [{ type: "text", text: `Rebalanced: moved ${moved} task(s), ${skipped} skipped.` }] };
27413
27862
  } catch (e) {
27414
27863
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
27415
27864
  }
@@ -27422,13 +27871,13 @@ function registerTaskAutoTools(server, ctx) {
27422
27871
  agent_id: exports_external2.string().optional().describe("Filter by assignee")
27423
27872
  }, async ({ hours = 24, project_id, agent_id }) => {
27424
27873
  try {
27425
- const { notifyUpcomingDeadlines } = (init_tasks(), __toCommonJS(exports_tasks));
27874
+ const { notifyUpcomingDeadlines: notifyUpcomingDeadlines2 } = (init_tasks(), __toCommonJS(exports_tasks));
27426
27875
  const resolvedProjectId = project_id ? resolveId(project_id, "projects") : undefined;
27427
27876
  const resolvedAgentId = agent_id ? resolveId(agent_id, "agents") : undefined;
27428
- const tasks = notifyUpcomingDeadlines({ hours, project_id: resolvedProjectId, agent_id: resolvedAgentId });
27877
+ const tasks = notifyUpcomingDeadlines2({ hours, project_id: resolvedProjectId, agent_id: resolvedAgentId });
27429
27878
  if (tasks.length === 0)
27430
27879
  return { content: [{ type: "text", text: "No deadlines approaching." }] };
27431
- const lines = tasks.map((t) => `${t.short_id || t.id.slice(0, 8)} ${t.title} due ${t.deadline}`);
27880
+ const lines = tasks.map((t) => `${t.short_id || t.id.slice(0, 8)} ${t.title} due ${t.due_at}`);
27432
27881
  return { content: [{ type: "text", text: `${tasks.length} task(s) due within ${hours}h:
27433
27882
  ${lines.join(`
27434
27883
  `)}` }] };
@@ -27462,9 +27911,9 @@ ${lines.join(`
27462
27911
  project_id: exports_external2.string().optional().describe("Filter by project")
27463
27912
  }, async ({ project_id }) => {
27464
27913
  try {
27465
- const { getBlockedTasks } = (init_tasks(), __toCommonJS(exports_tasks));
27914
+ const { getBlockedTasks: getBlockedTasks2 } = (init_tasks(), __toCommonJS(exports_tasks));
27466
27915
  const resolvedProjectId = project_id ? resolveId(project_id, "projects") : undefined;
27467
- const tasks = getBlockedTasks(resolvedProjectId);
27916
+ const tasks = getBlockedTasks2(resolvedProjectId);
27468
27917
  if (tasks.length === 0)
27469
27918
  return { content: [{ type: "text", text: "No blocked tasks." }] };
27470
27919
  const lines = tasks.map((t) => `${t.short_id || t.id.slice(0, 8)} [${t.status}] ${t.title} \u2014 blocked by ${(t.blocked_by || []).map((b) => b.slice(0, 8)).join(", ")}`);
@@ -27481,9 +27930,9 @@ ${lines.join(`
27481
27930
  project_id: exports_external2.string().optional().describe("Filter by project")
27482
27931
  }, async ({ project_id }) => {
27483
27932
  try {
27484
- const { getBlockingTasks } = (init_tasks(), __toCommonJS(exports_tasks));
27933
+ const { getBlockingTasks: getBlockingTasks2 } = (init_tasks(), __toCommonJS(exports_tasks));
27485
27934
  const resolvedProjectId = project_id ? resolveId(project_id, "projects") : undefined;
27486
- const tasks = getBlockingTasks(resolvedProjectId);
27935
+ const tasks = getBlockingTasks2(resolvedProjectId);
27487
27936
  if (tasks.length === 0)
27488
27937
  return { content: [{ type: "text", text: "No tasks blocking others." }] };
27489
27938
  const lines = tasks.map((t) => `${t.short_id || t.id.slice(0, 8)} [${t.status}] ${t.title} \u2014 blocking ${t.blocking_count} task(s)`);
@@ -27498,20 +27947,18 @@ ${lines.join(`
27498
27947
  if (shouldRegisterTool("get_health")) {
27499
27948
  server.tool("get_health", "Get system health: task counts by status, active agents, project summary.", async () => {
27500
27949
  try {
27501
- const { listTasks: listTasks3 } = (init_tasks(), __toCommonJS(exports_tasks));
27502
- const { listProjects: listProjects2 } = (init_tasks(), __toCommonJS(exports_tasks));
27950
+ const { countTasks: countTasks2 } = (init_tasks(), __toCommonJS(exports_tasks));
27951
+ const { listProjects: listProjects2 } = (init_projects(), __toCommonJS(exports_projects));
27503
27952
  const { listAgents: listAgents2 } = (init_agents(), __toCommonJS(exports_agents));
27504
- const [pending, inProgress, completed, cancelled] = await Promise.all([
27505
- Promise.resolve(listTasks3({ status: "pending", limit: 1 }, undefined)),
27506
- Promise.resolve(listTasks3({ status: "in_progress", limit: 1 }, undefined)),
27507
- Promise.resolve(listTasks3({ status: "completed", limit: 1 }, undefined)),
27508
- Promise.resolve(listTasks3({ status: "cancelled", limit: 1 }, undefined))
27509
- ]);
27510
- const projects = listProjects2({ limit: 100 });
27511
- const agents = listAgents2({ limit: 100 });
27953
+ const pending = countTasks2({ status: "pending" });
27954
+ const inProgress = countTasks2({ status: "in_progress" });
27955
+ const completed = countTasks2({ status: "completed" });
27956
+ const cancelled = countTasks2({ status: "cancelled" });
27957
+ const projects = listProjects2();
27958
+ const agents = listAgents2();
27512
27959
  const lines = [
27513
27960
  `=== System Health ===`,
27514
- `Tasks: ${pending.total} pending | ${inProgress.total} in progress | ${completed.total} completed | ${cancelled.total} cancelled`,
27961
+ `Tasks: ${pending} pending | ${inProgress} in progress | ${completed} completed | ${cancelled} cancelled`,
27515
27962
  `Projects: ${projects.length} total`,
27516
27963
  `Agents: ${agents.length} registered`
27517
27964
  ];
@@ -27657,17 +28104,31 @@ var init_task_relationships = __esm(() => {
27657
28104
  function registerTaskAdvTools(server, ctx) {
27658
28105
  const { shouldRegisterTool, resolveId, formatError, formatTask, formatTaskDetail } = ctx;
27659
28106
  if (shouldRegisterTool("get_status")) {
27660
- server.tool("get_status", "Get a task's current status including blockers, dependencies, comments, and assignee.", {
27661
- task_id: exports_external2.string().describe("Task ID")
27662
- }, async ({ task_id }) => {
28107
+ server.tool("get_status", "Get queue status summary, or pass task_id for a task's detailed status.", {
28108
+ task_id: exports_external2.string().optional().describe("Task ID for task-specific status"),
28109
+ project_id: exports_external2.string().optional().describe("Filter summary by project"),
28110
+ task_list_id: exports_external2.string().optional().describe("Filter summary by task list"),
28111
+ agent_id: exports_external2.string().optional().describe("Agent for next-task affinity"),
28112
+ explain_blocked: exports_external2.boolean().optional().describe("Include blocked task explanations in summary")
28113
+ }, async ({ task_id, project_id, task_list_id, agent_id, explain_blocked }) => {
27663
28114
  try {
28115
+ if (!task_id) {
28116
+ const { getStatus: getStatus2 } = (init_tasks(), __toCommonJS(exports_tasks));
28117
+ const filters = {};
28118
+ if (project_id)
28119
+ filters.project_id = resolveId(project_id, "projects");
28120
+ if (task_list_id)
28121
+ filters.task_list_id = resolveId(task_list_id, "task_lists");
28122
+ const status = getStatus2(filters, agent_id, { explain_blocked });
28123
+ return { content: [{ type: "text", text: JSON.stringify(status, null, 2) }] };
28124
+ }
27664
28125
  const resolvedId = resolveId(task_id);
27665
- const { getTask: getTask4 } = (init_tasks(), __toCommonJS(exports_tasks));
27666
- const task = getTask4(resolvedId);
28126
+ const { getTask: getTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
28127
+ const task = getTask2(resolvedId);
27667
28128
  if (!task)
27668
28129
  throw new Error(`Task not found: ${task_id}`);
27669
28130
  const { getTaskDependencies: getTaskDependencies3 } = (init_tasks(), __toCommonJS(exports_tasks));
27670
- const { listComments: listComments2 } = (init_tasks(), __toCommonJS(exports_tasks));
28131
+ const { listComments: listComments2 } = (init_comments(), __toCommonJS(exports_comments));
27671
28132
  const { listTaskFiles: listTaskFiles2 } = (init_task_files(), __toCommonJS(exports_task_files));
27672
28133
  const [deps, comments, files] = await Promise.all([
27673
28134
  Promise.resolve(getTaskDependencies3(resolvedId, "both")),
@@ -27677,14 +28138,14 @@ function registerTaskAdvTools(server, ctx) {
27677
28138
  const lines = [
27678
28139
  `Status: ${task.status} | Priority: ${task.priority}`,
27679
28140
  task.assigned_to ? `Assigned: ${task.assigned_to}` : "Unassigned",
27680
- task.deadline ? `Deadline: ${task.deadline}` : null,
28141
+ task.due_at ? `Due: ${task.due_at}` : null,
27681
28142
  task.confidence != null ? `Confidence: ${task.confidence}` : null,
27682
28143
  deps.length > 0 ? `
27683
28144
  Dependencies (${deps.length}):` : null,
27684
28145
  ...deps.map((d) => ` [${d.direction}] ${d.task_id.slice(0, 8)} (${d.status})`),
27685
28146
  comments.length > 0 ? `
27686
28147
  Comments (${comments.length}):` : null,
27687
- ...comments.map((c) => ` [${c.author || "?"}] ${c.created_at?.slice(0, 16)}: ${c.body.slice(0, 80)}`),
28148
+ ...comments.map((c) => ` [${c.agent_id || "?"}] ${c.created_at?.slice(0, 16)}: ${c.content.slice(0, 80)}`),
27688
28149
  files.length > 0 ? `
27689
28150
  Files (${files.length}):` : null,
27690
28151
  ...files.map((f) => ` ${f.status} ${f.path}`)
@@ -27702,15 +28163,15 @@ Files (${files.length}):` : null,
27702
28163
  }, async ({ task_id }) => {
27703
28164
  try {
27704
28165
  const resolvedId = resolveId(task_id);
27705
- const { getTask: getTask4 } = (init_tasks(), __toCommonJS(exports_tasks));
27706
- const task = getTask4(resolvedId);
28166
+ const { getTask: getTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
28167
+ const task = getTask2(resolvedId);
27707
28168
  if (!task)
27708
28169
  throw new Error(`Task not found: ${task_id}`);
27709
28170
  const { getTaskDependencies: getTaskDependencies3 } = (init_tasks(), __toCommonJS(exports_tasks));
27710
28171
  const { getTaskRelationships: getTaskRelationships2 } = (init_task_relationships(), __toCommonJS(exports_task_relationships));
27711
- const { listComments: listComments2 } = (init_tasks(), __toCommonJS(exports_tasks));
28172
+ const { listComments: listComments2 } = (init_comments(), __toCommonJS(exports_comments));
27712
28173
  const { listTaskFiles: listTaskFiles2 } = (init_task_files(), __toCommonJS(exports_task_files));
27713
- const { getTaskCommits: getTaskCommits2 } = (init_tasks(), __toCommonJS(exports_tasks));
28174
+ const { getTaskCommits: getTaskCommits2 } = (init_task_commits(), __toCommonJS(exports_task_commits));
27714
28175
  const { getTaskWatchers: getTaskWatchers2 } = (init_tasks(), __toCommonJS(exports_tasks));
27715
28176
  const [deps, rels, comments, files, commits, watchers] = await Promise.all([
27716
28177
  Promise.resolve(getTaskDependencies3(resolvedId, "both")),
@@ -27731,7 +28192,7 @@ Files (${files.length}):` : null,
27731
28192
  task.tags?.length ? `Tags: ${task.tags.join(", ")}` : null,
27732
28193
  task.created_at ? `Created: ${task.created_at}` : null,
27733
28194
  task.updated_at ? `Updated: ${task.updated_at}` : null,
27734
- task.deadline ? `Deadline: ${task.deadline}` : null,
28195
+ task.due_at ? `Due: ${task.due_at}` : null,
27735
28196
  task.completed_at ? `Completed: ${task.completed_at}` : null,
27736
28197
  deps.length > 0 ? `
27737
28198
  --- Dependencies (${deps.length}) ---` : null,
@@ -27741,7 +28202,7 @@ Files (${files.length}):` : null,
27741
28202
  ...rels.map((r) => ` ${r.source_task_id.slice(0, 8)} --[${r.relationship_type}]--> ${r.target_task_id.slice(0, 8)}`),
27742
28203
  comments.length > 0 ? `
27743
28204
  --- Comments (${comments.length}) ---` : null,
27744
- ...comments.map((c) => ` [${c.author || "?"}] ${c.created_at?.slice(0, 16)}: ${c.body.slice(0, 120)}`),
28205
+ ...comments.map((c) => ` [${c.agent_id || "?"}] ${c.created_at?.slice(0, 16)}: ${c.content.slice(0, 120)}`),
27745
28206
  files.length > 0 ? `
27746
28207
  --- Files (${files.length}) ---` : null,
27747
28208
  ...files.map((f) => ` [${f.status}] ${f.path}`),
@@ -27790,8 +28251,8 @@ ${JSON.stringify(task.metadata, null, 2)}` : null
27790
28251
  limit: 20
27791
28252
  }, undefined);
27792
28253
  const completedYesterday = completed.filter((t) => t.completed_at && t.completed_at.startsWith(yesterdayStr));
27793
- const { getBlockedTasks } = (init_tasks(), __toCommonJS(exports_tasks));
27794
- const blocked = getBlockedTasks(effectiveProjectId ? resolveId(effectiveProjectId, "projects") : undefined).filter((t) => t.assigned_to === effectiveAgentId);
28254
+ const { getBlockedTasks: getBlockedTasks2 } = (init_tasks(), __toCommonJS(exports_tasks));
28255
+ const blocked = getBlockedTasks2(effectiveProjectId ? resolveId(effectiveProjectId, "projects") : undefined).filter((t) => t.assigned_to === effectiveAgentId);
27795
28256
  const lines = [
27796
28257
  `Standup for ${effectiveAgentId} (${effectiveProjectId ? `project: ${effectiveProjectId.slice(0, 8)}` : "all projects"})`,
27797
28258
  inProgress.length > 0 ? `
@@ -27820,11 +28281,11 @@ No blocked tasks.`,
27820
28281
  agent_id: exports_external2.string().optional().describe("Agent claiming (defaults to context)")
27821
28282
  }, async ({ task_id, agent_id }) => {
27822
28283
  try {
27823
- const { updateTask: updateTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
28284
+ const { startTask: startTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
27824
28285
  const resolvedId = resolveId(task_id);
27825
28286
  const focus = ctx.getAgentFocus(agent_id || "");
27826
- const effectiveAgent = focus ? focus.agent_id : agent_id || "";
27827
- const task = updateTask2(resolvedId, { status: "in_progress", assigned_to: effectiveAgent });
28287
+ const effectiveAgent = focus ? focus.agent_id : agent_id || "mcp";
28288
+ const task = startTask2(resolvedId, effectiveAgent);
27828
28289
  return { content: [{ type: "text", text: formatTask(task) }] };
27829
28290
  } catch (e) {
27830
28291
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
@@ -27836,8 +28297,12 @@ No blocked tasks.`,
27836
28297
  task_id: exports_external2.string().describe("Task ID")
27837
28298
  }, async ({ task_id }) => {
27838
28299
  try {
27839
- const { updateTask: updateTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
27840
- const task = updateTask2(resolveId(task_id), { status: "pending", assigned_to: null });
28300
+ const { getTask: getTask2, updateTask: updateTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
28301
+ const resolvedId = resolveId(task_id);
28302
+ const current = getTask2(resolvedId);
28303
+ if (!current)
28304
+ throw new Error(`Task not found: ${task_id}`);
28305
+ const task = updateTask2(resolvedId, { status: "pending", assigned_to: null, version: current.version });
27841
28306
  return { content: [{ type: "text", text: formatTask(task) }] };
27842
28307
  } catch (e) {
27843
28308
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
@@ -27850,13 +28315,13 @@ No blocked tasks.`,
27850
28315
  minutes: exports_external2.number().describe("Additional minutes to add to estimate")
27851
28316
  }, async ({ task_id, minutes }) => {
27852
28317
  try {
27853
- const { getTask: getTask4, updateTask: updateTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
28318
+ const { getTask: getTask2, updateTask: updateTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
27854
28319
  const resolvedId = resolveId(task_id);
27855
- const task = getTask4(resolvedId);
28320
+ const task = getTask2(resolvedId);
27856
28321
  if (!task)
27857
28322
  throw new Error(`Task not found: ${task_id}`);
27858
28323
  const currentEstimate = task.estimated_minutes || 0;
27859
- const updated = updateTask2(resolvedId, { estimated_minutes: currentEstimate + minutes });
28324
+ const updated = updateTask2(resolvedId, { estimated_minutes: currentEstimate + minutes, version: task.version });
27860
28325
  return { content: [{ type: "text", text: `Estimate updated: ${currentEstimate} \u2192 ${updated.estimated_minutes} min (+${minutes})` }] };
27861
28326
  } catch (e) {
27862
28327
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
@@ -27870,10 +28335,10 @@ No blocked tasks.`,
27870
28335
  author: exports_external2.string().optional().describe("Author agent ID or name")
27871
28336
  }, async ({ task_id, body, author }) => {
27872
28337
  try {
27873
- const { createComment } = (init_tasks(), __toCommonJS(exports_tasks));
28338
+ const { addComment: addComment2 } = (init_comments(), __toCommonJS(exports_comments));
27874
28339
  const resolvedId = resolveId(task_id);
27875
28340
  const resolvedAuthor = author ? resolveId(author, "agents") : undefined;
27876
- createComment({ task_id: resolvedId, body, author: resolvedAuthor });
28341
+ addComment2({ task_id: resolvedId, content: body, agent_id: resolvedAuthor });
27877
28342
  return { content: [{ type: "text", text: `Comment added to ${task_id.slice(0, 8)}` }] };
27878
28343
  } catch (e) {
27879
28344
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
@@ -27885,11 +28350,11 @@ No blocked tasks.`,
27885
28350
  task_id: exports_external2.string().describe("Task ID")
27886
28351
  }, async ({ task_id }) => {
27887
28352
  try {
27888
- const { listComments: listComments2 } = (init_tasks(), __toCommonJS(exports_tasks));
28353
+ const { listComments: listComments2 } = (init_comments(), __toCommonJS(exports_comments));
27889
28354
  const comments = listComments2(resolveId(task_id));
27890
28355
  if (comments.length === 0)
27891
28356
  return { content: [{ type: "text", text: "No comments." }] };
27892
- const lines = comments.map((c) => `[${c.author || "?"}] ${c.created_at?.slice(0, 16)}: ${c.body}`);
28357
+ const lines = comments.map((c) => `[${c.agent_id || "?"}] ${c.created_at?.slice(0, 16)}: ${c.content}`);
27893
28358
  return { content: [{ type: "text", text: lines.join(`
27894
28359
 
27895
28360
  `) }] };
@@ -27901,7 +28366,7 @@ No blocked tasks.`,
27901
28366
  if (shouldRegisterTool("list_my_tasks")) {
27902
28367
  server.tool("list_my_tasks", "Alias for get_my_tasks.", {
27903
28368
  agent_id: exports_external2.string().optional().describe("Agent ID (defaults to context agent)"),
27904
- status: exports_external2.enum(["pending", "in_progress", "completed", "cancelled"]).optional(),
28369
+ status: exports_external2.enum(["pending", "in_progress", "completed", "failed", "cancelled"]).optional(),
27905
28370
  project_id: exports_external2.string().optional().describe("Filter by project"),
27906
28371
  limit: exports_external2.number().optional()
27907
28372
  }, async ({ agent_id, status, project_id, limit }) => {
@@ -27926,126 +28391,6 @@ No blocked tasks.`,
27926
28391
  }
27927
28392
  });
27928
28393
  }
27929
- if (shouldRegisterTool("list_agents")) {
27930
- server.tool("list_agents", "List all registered agents.", {
27931
- project_id: exports_external2.string().optional().describe("Filter by project"),
27932
- role: exports_external2.string().optional().describe("Filter by global role"),
27933
- capabilities: exports_external2.array(exports_external2.string()).optional().describe("Filter by capabilities")
27934
- }, async ({ project_id, role, capabilities }) => {
27935
- try {
27936
- const { listAgents: listAgents2 } = (init_agents(), __toCommonJS(exports_agents));
27937
- const resolved = {};
27938
- if (project_id)
27939
- resolved.project_id = resolveId(project_id, "projects");
27940
- if (role)
27941
- resolved.role = role;
27942
- if (capabilities)
27943
- resolved.capabilities = capabilities;
27944
- const agents = listAgents2(resolved);
27945
- if (agents.length === 0)
27946
- return { content: [{ type: "text", text: "No agents found." }] };
27947
- const lines = agents.map((a) => `\u25CF ${a.name} [${a.role || "no role"}]${a.capabilities?.length ? ` caps:[${a.capabilities.join(",")}]` : ""}`);
27948
- return { content: [{ type: "text", text: lines.join(`
27949
- `) }] };
27950
- } catch (e) {
27951
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
27952
- }
27953
- });
27954
- }
27955
- if (shouldRegisterTool("get_agent")) {
27956
- server.tool("get_agent", "Get full details for an agent.", {
27957
- agent_id: exports_external2.string().describe("Agent ID or name")
27958
- }, async ({ agent_id }) => {
27959
- try {
27960
- const { getAgentByName: getAgentByName2, getAgent: getAgent2 } = (init_agents(), __toCommonJS(exports_agents));
27961
- const resolvedId = resolveId(agent_id, "agents");
27962
- const agent = agent_id.includes("@") || !resolvedId.startsWith("agent_") ? getAgentByName2(agent_id) : getAgent2(resolvedId);
27963
- if (!agent)
27964
- return { content: [{ type: "text", text: `Agent not found: ${agent_id}` }], isError: true };
27965
- const lines = [
27966
- `ID: ${agent.id}`,
27967
- `Name: ${agent.name}`,
27968
- agent.email ? `Email: ${agent.email}` : null,
27969
- agent.title ? `Title: ${agent.title}` : null,
27970
- agent.role ? `Role: ${agent.role}` : null,
27971
- agent.capabilities?.length ? `Capabilities: ${agent.capabilities.join(", ")}` : null,
27972
- agent.last_seen_at ? `Last Seen: ${agent.last_seen_at}` : null
27973
- ].filter(Boolean);
27974
- return { content: [{ type: "text", text: lines.join(`
27975
- `) }] };
27976
- } catch (e) {
27977
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
27978
- }
27979
- });
27980
- }
27981
- if (shouldRegisterTool("update_agent")) {
27982
- server.tool("update_agent", "Update an agent's fields.", {
27983
- agent_id: exports_external2.string().describe("Agent ID or name"),
27984
- name: exports_external2.string().optional(),
27985
- email: exports_external2.string().optional(),
27986
- title: exports_external2.string().optional(),
27987
- role: exports_external2.string().optional(),
27988
- capabilities: exports_external2.array(exports_external2.string()).optional()
27989
- }, async ({ agent_id, ...updates }) => {
27990
- try {
27991
- const { getAgentByName: getAgentByName2, updateAgent: updateAgent2 } = (init_agents(), __toCommonJS(exports_agents));
27992
- const resolvedId = resolveId(agent_id, "agents");
27993
- const agent = agent_id.includes("@") || !resolvedId.startsWith("agent_") ? getAgentByName2(agent_id) : { id: resolvedId };
27994
- updateAgent2(agent.id, updates);
27995
- return { content: [{ type: "text", text: `Agent ${agent.id.slice(0, 8)} updated.` }] };
27996
- } catch (e) {
27997
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
27998
- }
27999
- });
28000
- }
28001
- if (shouldRegisterTool("delete_agent")) {
28002
- server.tool("delete_agent", "Deregister an agent.", {
28003
- agent_id: exports_external2.string().describe("Agent ID or name")
28004
- }, async ({ agent_id }) => {
28005
- try {
28006
- const { getAgentByName: getAgentByName2, deleteAgent: deleteAgent2 } = (init_agents(), __toCommonJS(exports_agents));
28007
- const resolvedId = resolveId(agent_id, "agents");
28008
- const agent = agent_id.includes("@") || !resolvedId.startsWith("agent_") ? getAgentByName2(agent_id) : { id: resolvedId };
28009
- if (!agent)
28010
- return { content: [{ type: "text", text: `Agent not found: ${agent_id}` }], isError: true };
28011
- deleteAgent2(agent.id);
28012
- return { content: [{ type: "text", text: `Agent ${agent_id} deleted.` }] };
28013
- } catch (e) {
28014
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
28015
- }
28016
- });
28017
- }
28018
- if (shouldRegisterTool("register_agent")) {
28019
- server.tool("register_agent", "Register a new agent.", {
28020
- name: exports_external2.string().describe("Agent name"),
28021
- email: exports_external2.string().optional(),
28022
- title: exports_external2.string().optional(),
28023
- role: exports_external2.string().optional(),
28024
- capabilities: exports_external2.array(exports_external2.string()).optional()
28025
- }, async ({ name, email, title, role, capabilities }) => {
28026
- try {
28027
- const { registerAgent: registerAgent2 } = (init_agents(), __toCommonJS(exports_agents));
28028
- const agent = registerAgent2({ name, email, title, role, capabilities });
28029
- return { content: [{ type: "text", text: `Agent registered: ${agent.name} (${agent.id})` }] };
28030
- } catch (e) {
28031
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
28032
- }
28033
- });
28034
- }
28035
- if (shouldRegisterTool("heartbeat")) {
28036
- server.tool("heartbeat", "Agent heartbeat \u2014 updates last_seen_at timestamp.", {
28037
- agent_id: exports_external2.string().describe("Agent ID"),
28038
- status: exports_external2.string().optional().describe("Current status message")
28039
- }, async ({ agent_id, status }) => {
28040
- try {
28041
- const { heartbeat } = (init_agents(), __toCommonJS(exports_agents));
28042
- heartbeat(resolveId(agent_id, "agents"), status);
28043
- return { content: [{ type: "text", text: "Heartbeat received." }] };
28044
- } catch (e) {
28045
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
28046
- }
28047
- });
28048
- }
28049
28394
  }
28050
28395
  var init_task_adv_tools = __esm(() => {
28051
28396
  init_zod2();
@@ -28090,6 +28435,11 @@ function registerTaskMetaTools(server, ctx) {
28090
28435
  prioritize_task: "prioritize_task \u2014 Set priority. Params: task_id, priority, version",
28091
28436
  search_tasks: "search_tasks \u2014 Full-text search. Params: query, project_id, status, limit",
28092
28437
  get_my_tasks: "get_my_tasks \u2014 Get tasks for calling agent. Params: agent_id, status, project_id, limit",
28438
+ get_next_task: "get_next_task \u2014 Get the next available task without claiming it. Params: agent_id, project_id, task_list_id, plan_id, tags",
28439
+ claim_next_task: "claim_next_task \u2014 Atomically claim and start the next available task. Params: agent_id, project_id, task_list_id, plan_id, tags",
28440
+ get_tasks_changed_since: "get_tasks_changed_since \u2014 List tasks changed since an ISO timestamp. Params: since, project_id, task_list_id, limit",
28441
+ get_context: "get_context \u2014 Get session start context. Params: agent_id, project_id, task_list_id, explain_blocked",
28442
+ bootstrap: "bootstrap \u2014 Bootstrap an agent session with queue context. Params: agent_id, project_id, task_list_id, explain_blocked",
28093
28443
  standup: "standup \u2014 Get standup report. Params: agent_id, project_id",
28094
28444
  patrol_tasks: "patrol_tasks \u2014 Scan for task issues. Params: stuck_minutes, confidence_threshold, project_id",
28095
28445
  get_review_queue: "get_review_queue \u2014 Get tasks needing review. Params: project_id, limit",
@@ -28147,9 +28497,20 @@ function registerTaskMetaTools(server, ctx) {
28147
28497
  get_health: "get_health \u2014 Get system health stats",
28148
28498
  approve_task: "approve_task \u2014 Approve a task. Params: task_id, approved_by, notes, version",
28149
28499
  fail_task: "fail_task \u2014 Mark task failed. Params: task_id, reason, agent_id, version",
28500
+ register_agent: "register_agent \u2014 Register an agent. Params: name, description, role, title, capabilities, session_id, working_dir, force",
28501
+ list_agents: "list_agents \u2014 List registered agents. Params: include_archived",
28502
+ get_agent: "get_agent \u2014 Get agent details. Params: agent_id, id, name",
28503
+ update_agent: "update_agent \u2014 Update an agent. Params: agent_id, id, name, description, role, title, level, capabilities, permissions, metadata",
28504
+ delete_agent: "delete_agent \u2014 Archive an agent. Params: agent_id, id, name",
28505
+ unarchive_agent: "unarchive_agent \u2014 Restore an archived agent. Params: agent_id, id, name",
28506
+ heartbeat: "heartbeat \u2014 Update an agent heartbeat. Params: agent_id",
28507
+ release_agent: "release_agent \u2014 Release an agent session/name. Params: agent_id, session_id",
28508
+ set_focus: "set_focus \u2014 Focus an agent on a project. Params: agent_id, project_id, task_list_id",
28509
+ get_focus: "get_focus \u2014 Get current agent focus. Params: agent_id",
28510
+ unfocus: "unfocus \u2014 Clear agent focus. Params: agent_id",
28511
+ suggest_agent_name: "suggest_agent_name \u2014 Suggest available agent names. Params: working_dir",
28150
28512
  get_org_chart: "get_org_chart \u2014 Get global org chart. Params: format",
28151
28513
  set_reports_to: "set_reports_to \u2014 Set org hierarchy. Params: agent_id, reports_to",
28152
- sync: "sync \u2014 Sync from external source. Params: source, source_id, project_id, options",
28153
28514
  extract_todos: "extract_todos \u2014 Scan code for TODO comments. Params: path, project_id, task_list_id, patterns, tags, assigned_to, agent_id, dry_run, extensions",
28154
28515
  migrate_pg: "migrate_pg \u2014 Apply PostgreSQL migrations. Params: connection_string"
28155
28516
  };
@@ -28167,6 +28528,74 @@ var init_task_meta_tools = __esm(() => {
28167
28528
  init_zod2();
28168
28529
  });
28169
28530
 
28531
+ // src/db/file-locks.ts
28532
+ var exports_file_locks = {};
28533
+ __export(exports_file_locks, {
28534
+ unlockFile: () => unlockFile,
28535
+ lockFile: () => lockFile,
28536
+ listFileLocks: () => listFileLocks,
28537
+ forceUnlockFile: () => forceUnlockFile,
28538
+ cleanExpiredFileLocks: () => cleanExpiredFileLocks,
28539
+ checkFileLock: () => checkFileLock,
28540
+ FILE_LOCK_DEFAULT_TTL_SECONDS: () => FILE_LOCK_DEFAULT_TTL_SECONDS
28541
+ });
28542
+ function expiresAt(ttlSeconds) {
28543
+ return new Date(Date.now() + ttlSeconds * 1000).toISOString();
28544
+ }
28545
+ function cleanExpiredFileLocks(db) {
28546
+ const d = db || getDatabase();
28547
+ const result = d.run("DELETE FROM file_locks WHERE expires_at <= ?", [now()]);
28548
+ return result.changes;
28549
+ }
28550
+ function lockFile(input, db) {
28551
+ const d = db || getDatabase();
28552
+ const ttl = input.ttl_seconds ?? FILE_LOCK_DEFAULT_TTL_SECONDS;
28553
+ const expiry = expiresAt(ttl);
28554
+ const timestamp = now();
28555
+ cleanExpiredFileLocks(d);
28556
+ const existing = d.query("SELECT * FROM file_locks WHERE path = ?").get(input.path);
28557
+ if (existing) {
28558
+ if (existing.agent_id === input.agent_id) {
28559
+ d.run("UPDATE file_locks SET expires_at = ?, task_id = COALESCE(?, task_id) WHERE id = ?", [expiry, input.task_id ?? null, existing.id]);
28560
+ return d.query("SELECT * FROM file_locks WHERE id = ?").get(existing.id);
28561
+ }
28562
+ throw new LockError(input.path, existing.agent_id);
28563
+ }
28564
+ const id = uuid();
28565
+ d.run("INSERT INTO file_locks (id, path, agent_id, task_id, expires_at, created_at) VALUES (?, ?, ?, ?, ?, ?)", [id, input.path, input.agent_id, input.task_id ?? null, expiry, timestamp]);
28566
+ return d.query("SELECT * FROM file_locks WHERE id = ?").get(id);
28567
+ }
28568
+ function unlockFile(path, agentId, db) {
28569
+ const d = db || getDatabase();
28570
+ cleanExpiredFileLocks(d);
28571
+ const result = d.run("DELETE FROM file_locks WHERE path = ? AND agent_id = ?", [path, agentId]);
28572
+ return result.changes > 0;
28573
+ }
28574
+ function checkFileLock(path, db) {
28575
+ const d = db || getDatabase();
28576
+ cleanExpiredFileLocks(d);
28577
+ return d.query("SELECT * FROM file_locks WHERE path = ?").get(path);
28578
+ }
28579
+ function listFileLocks(agentId, db) {
28580
+ const d = db || getDatabase();
28581
+ cleanExpiredFileLocks(d);
28582
+ if (agentId) {
28583
+ return d.query("SELECT * FROM file_locks WHERE agent_id = ? ORDER BY created_at DESC").all(agentId);
28584
+ }
28585
+ return d.query("SELECT * FROM file_locks ORDER BY created_at DESC").all();
28586
+ }
28587
+ function forceUnlockFile(path, db) {
28588
+ const d = db || getDatabase();
28589
+ const result = d.run("DELETE FROM file_locks WHERE path = ?", [path]);
28590
+ return result.changes > 0;
28591
+ }
28592
+ var FILE_LOCK_DEFAULT_TTL_SECONDS;
28593
+ var init_file_locks = __esm(() => {
28594
+ init_database();
28595
+ init_types();
28596
+ FILE_LOCK_DEFAULT_TTL_SECONDS = 30 * 60;
28597
+ });
28598
+
28170
28599
  // src/mcp/tools/task-resources.ts
28171
28600
  function registerTaskResources(server, ctx) {
28172
28601
  const { shouldRegisterTool, resolveId, formatError } = ctx;
@@ -28192,7 +28621,7 @@ function registerTaskResources(server, ctx) {
28192
28621
  note: exports_external2.string().optional().describe("Note about why this file is linked")
28193
28622
  }, async ({ task_id, path, paths: multiplePaths, status, agent_id, note }) => {
28194
28623
  try {
28195
- const { addTaskFile: addTaskFile2, bulkAddTaskFiles: bulkAddTaskFiles2, detectFileConflicts: detectFileConflicts2 } = (()=>{throw new Error("Cannot require module "+"../db/task-files.js");})();
28624
+ const { addTaskFile: addTaskFile2, bulkAddTaskFiles: bulkAddTaskFiles2, detectFileConflicts: detectFileConflicts2 } = (init_task_files(), __toCommonJS(exports_task_files));
28196
28625
  const resolvedId = resolveId(task_id);
28197
28626
  let addedFiles;
28198
28627
  if (multiplePaths && multiplePaths.length > 0) {
@@ -28236,7 +28665,7 @@ function registerTaskResources(server, ctx) {
28236
28665
  if (shouldRegisterTool("list_task_files")) {
28237
28666
  server.tool("list_task_files", "List all files linked to a task.", { task_id: exports_external2.string().describe("Task ID") }, async ({ task_id }) => {
28238
28667
  try {
28239
- const { listTaskFiles: listTaskFiles2 } = (()=>{throw new Error("Cannot require module "+"../db/task-files.js");})();
28668
+ const { listTaskFiles: listTaskFiles2 } = (init_task_files(), __toCommonJS(exports_task_files));
28240
28669
  const resolvedId = resolveId(task_id);
28241
28670
  const files = listTaskFiles2(resolvedId);
28242
28671
  if (files.length === 0)
@@ -28253,7 +28682,7 @@ ${lines.join(`
28253
28682
  if (shouldRegisterTool("find_tasks_by_file")) {
28254
28683
  server.tool("find_tasks_by_file", "Find which tasks are linked to a specific file path. Shows who's working on what files.", { path: exports_external2.string().describe("File path to search for") }, async ({ path }) => {
28255
28684
  try {
28256
- const { findTasksByFile: findTasksByFile2 } = (()=>{throw new Error("Cannot require module "+"../db/task-files.js");})();
28685
+ const { findTasksByFile: findTasksByFile2 } = (init_task_files(), __toCommonJS(exports_task_files));
28257
28686
  const files = findTasksByFile2(path);
28258
28687
  if (files.length === 0)
28259
28688
  return { content: [{ type: "text", text: `No tasks linked to ${path}` }] };
@@ -28273,7 +28702,7 @@ ${lines.join(`
28273
28702
  min_edits: exports_external2.number().optional().describe("Minimum edit count to include (default: 1)")
28274
28703
  }, async ({ limit, project_id, min_edits }) => {
28275
28704
  try {
28276
- const { getFileHeatMap: getFileHeatMap2 } = (()=>{throw new Error("Cannot require module "+"../db/task-files.js");})();
28705
+ const { getFileHeatMap: getFileHeatMap2 } = (init_task_files(), __toCommonJS(exports_task_files));
28277
28706
  const resolvedProjectId = project_id ? resolveId(project_id, "projects") : undefined;
28278
28707
  const results = getFileHeatMap2({ limit, project_id: resolvedProjectId, min_edits });
28279
28708
  return { content: [{ type: "text", text: JSON.stringify(results, null, 2) }] };
@@ -28287,7 +28716,7 @@ ${lines.join(`
28287
28716
  paths: exports_external2.array(exports_external2.string()).describe("Array of file paths to check")
28288
28717
  }, async ({ paths }) => {
28289
28718
  try {
28290
- const { bulkFindTasksByFiles: bulkFindTasksByFiles2 } = (()=>{throw new Error("Cannot require module "+"../db/task-files.js");})();
28719
+ const { bulkFindTasksByFiles: bulkFindTasksByFiles2 } = (init_task_files(), __toCommonJS(exports_task_files));
28291
28720
  const results = bulkFindTasksByFiles2(paths);
28292
28721
  return { content: [{ type: "text", text: JSON.stringify(results, null, 2) }] };
28293
28722
  } catch (e) {
@@ -28300,11 +28729,11 @@ ${lines.join(`
28300
28729
  project_id: exports_external2.string().optional().describe("Filter by project")
28301
28730
  }, async ({ project_id }) => {
28302
28731
  try {
28303
- const { listActiveFiles: listActiveFiles2 } = (()=>{throw new Error("Cannot require module "+"../db/task-files.js");})();
28732
+ const { listActiveFiles: listActiveFiles2 } = (init_task_files(), __toCommonJS(exports_task_files));
28304
28733
  let files = listActiveFiles2();
28305
28734
  if (project_id) {
28306
28735
  const pid = resolveId(project_id, "projects");
28307
- const db = (()=>{throw new Error("Cannot require module "+"../db/database.js");})().getDatabase();
28736
+ const db = (init_database(), __toCommonJS(exports_database)).getDatabase();
28308
28737
  files = db.query(`
28309
28738
  SELECT
28310
28739
  tf.path,
@@ -28386,8 +28815,8 @@ ${lines.join(`
28386
28815
  ttl_seconds: exports_external2.number().optional().describe("Lock TTL in seconds (default: 1800 = 30 min)")
28387
28816
  }, async ({ path, agent_id, task_id, ttl_seconds }) => {
28388
28817
  try {
28389
- const { lockFile } = (()=>{throw new Error("Cannot require module "+"../db/file-locks.js");})();
28390
- const lock = lockFile({ path, agent_id, task_id, ttl_seconds });
28818
+ const { lockFile: lockFile2 } = (init_file_locks(), __toCommonJS(exports_file_locks));
28819
+ const lock = lockFile2({ path, agent_id, task_id, ttl_seconds });
28391
28820
  return { content: [{ type: "text", text: JSON.stringify(lock, null, 2) }] };
28392
28821
  } catch (e) {
28393
28822
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
@@ -28400,8 +28829,8 @@ ${lines.join(`
28400
28829
  agent_id: exports_external2.string().describe("Agent releasing the lock (must be the lock holder)")
28401
28830
  }, async ({ path, agent_id }) => {
28402
28831
  try {
28403
- const { unlockFile } = (()=>{throw new Error("Cannot require module "+"../db/file-locks.js");})();
28404
- const released = unlockFile(path, agent_id);
28832
+ const { unlockFile: unlockFile2 } = (init_file_locks(), __toCommonJS(exports_file_locks));
28833
+ const released = unlockFile2(path, agent_id);
28405
28834
  return { content: [{ type: "text", text: JSON.stringify({ released, path }) }] };
28406
28835
  } catch (e) {
28407
28836
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
@@ -28413,8 +28842,8 @@ ${lines.join(`
28413
28842
  path: exports_external2.string().describe("File path to check")
28414
28843
  }, async ({ path }) => {
28415
28844
  try {
28416
- const { checkFileLock } = (()=>{throw new Error("Cannot require module "+"../db/file-locks.js");})();
28417
- const lock = checkFileLock(path);
28845
+ const { checkFileLock: checkFileLock2 } = (init_file_locks(), __toCommonJS(exports_file_locks));
28846
+ const lock = checkFileLock2(path);
28418
28847
  if (!lock)
28419
28848
  return { content: [{ type: "text", text: JSON.stringify({ path, locked: false }) }] };
28420
28849
  return { content: [{ type: "text", text: JSON.stringify({ path, locked: true, ...lock }) }] };
@@ -28428,8 +28857,8 @@ ${lines.join(`
28428
28857
  agent_id: exports_external2.string().optional().describe("Filter locks by agent")
28429
28858
  }, async ({ agent_id }) => {
28430
28859
  try {
28431
- const { listFileLocks } = (()=>{throw new Error("Cannot require module "+"../db/file-locks.js");})();
28432
- const locks = listFileLocks(agent_id);
28860
+ const { listFileLocks: listFileLocks2 } = (init_file_locks(), __toCommonJS(exports_file_locks));
28861
+ const locks = listFileLocks2(agent_id);
28433
28862
  return { content: [{ type: "text", text: JSON.stringify(locks, null, 2) }] };
28434
28863
  } catch (e) {
28435
28864
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
@@ -28445,6 +28874,533 @@ var init_task_resources = __esm(() => {
28445
28874
  init_task_commits();
28446
28875
  });
28447
28876
 
28877
+ // src/db/kg.ts
28878
+ var exports_kg = {};
28879
+ __export(exports_kg, {
28880
+ syncKgEdges: () => syncKgEdges,
28881
+ removeKgEdges: () => removeKgEdges,
28882
+ getRelated: () => getRelated,
28883
+ getImpactAnalysis: () => getImpactAnalysis,
28884
+ getCriticalPath: () => getCriticalPath,
28885
+ findPath: () => findPath,
28886
+ addKgEdge: () => addKgEdge
28887
+ });
28888
+ function rowToEdge(row) {
28889
+ return {
28890
+ ...row,
28891
+ metadata: JSON.parse(row.metadata || "{}")
28892
+ };
28893
+ }
28894
+ function syncKgEdges(db) {
28895
+ const d = db || getDatabase();
28896
+ let synced = 0;
28897
+ const tx = d.transaction(() => {
28898
+ const deps = d.query("SELECT task_id, depends_on FROM task_dependencies").all();
28899
+ for (const dep of deps) {
28900
+ synced += upsertEdge(d, dep.task_id, "task", dep.depends_on, "task", "depends_on");
28901
+ }
28902
+ const assignments = d.query("SELECT id, assigned_to FROM tasks WHERE assigned_to IS NOT NULL").all();
28903
+ for (const a of assignments) {
28904
+ synced += upsertEdge(d, a.id, "task", a.assigned_to, "agent", "assigned_to");
28905
+ }
28906
+ const agents = d.query("SELECT id, reports_to FROM agents WHERE reports_to IS NOT NULL").all();
28907
+ for (const a of agents) {
28908
+ synced += upsertEdge(d, a.id, "agent", a.reports_to, "agent", "reports_to");
28909
+ }
28910
+ const files = d.query("SELECT task_id, path FROM task_files WHERE status != 'removed'").all();
28911
+ for (const f of files) {
28912
+ synced += upsertEdge(d, f.task_id, "task", f.path, "file", "references_file");
28913
+ }
28914
+ const taskProjects = d.query("SELECT id, project_id FROM tasks WHERE project_id IS NOT NULL").all();
28915
+ for (const tp of taskProjects) {
28916
+ synced += upsertEdge(d, tp.id, "task", tp.project_id, "project", "in_project");
28917
+ }
28918
+ const taskPlans = d.query("SELECT id, plan_id FROM tasks WHERE plan_id IS NOT NULL").all();
28919
+ for (const tp of taskPlans) {
28920
+ synced += upsertEdge(d, tp.id, "task", tp.plan_id, "plan", "in_plan");
28921
+ }
28922
+ try {
28923
+ const rels = d.query("SELECT source_task_id, target_task_id, relationship_type FROM task_relationships").all();
28924
+ for (const r of rels) {
28925
+ synced += upsertEdge(d, r.source_task_id, "task", r.target_task_id, "task", r.relationship_type);
28926
+ }
28927
+ } catch {}
28928
+ });
28929
+ tx();
28930
+ return { synced };
28931
+ }
28932
+ function upsertEdge(d, sourceId, sourceType, targetId, targetType, relationType, weight = 1) {
28933
+ try {
28934
+ d.run(`INSERT OR IGNORE INTO kg_edges (id, source_id, source_type, target_id, target_type, relation_type, weight, metadata, created_at)
28935
+ VALUES (?, ?, ?, ?, ?, ?, ?, '{}', ?)`, [uuid(), sourceId, sourceType, targetId, targetType, relationType, weight, now()]);
28936
+ return 1;
28937
+ } catch {
28938
+ return 0;
28939
+ }
28940
+ }
28941
+ function getRelated(entityId, opts, db) {
28942
+ const d = db || getDatabase();
28943
+ const direction = opts?.direction || "both";
28944
+ const conditions = [];
28945
+ const params = [];
28946
+ if (direction === "outgoing" || direction === "both") {
28947
+ conditions.push("source_id = ?");
28948
+ params.push(entityId);
28949
+ }
28950
+ if (direction === "incoming" || direction === "both") {
28951
+ conditions.push("target_id = ?");
28952
+ params.push(entityId);
28953
+ }
28954
+ let sql = `SELECT * FROM kg_edges WHERE (${conditions.join(" OR ")})`;
28955
+ if (opts?.relation_type) {
28956
+ sql += " AND relation_type = ?";
28957
+ params.push(opts.relation_type);
28958
+ }
28959
+ if (opts?.entity_type) {
28960
+ sql += " AND (source_type = ? OR target_type = ?)";
28961
+ params.push(opts.entity_type, opts.entity_type);
28962
+ }
28963
+ sql += " ORDER BY weight DESC, created_at DESC";
28964
+ if (opts?.limit) {
28965
+ sql += " LIMIT ?";
28966
+ params.push(opts.limit);
28967
+ }
28968
+ return d.query(sql).all(...params).map(rowToEdge);
28969
+ }
28970
+ function findPath(sourceId, targetId, opts, db) {
28971
+ const d = db || getDatabase();
28972
+ const maxDepth = opts?.max_depth || 5;
28973
+ const visited = new Set;
28974
+ const queue = [{ id: sourceId, path: [] }];
28975
+ const results = [];
28976
+ visited.add(sourceId);
28977
+ while (queue.length > 0) {
28978
+ const current = queue.shift();
28979
+ if (current.path.length >= maxDepth)
28980
+ continue;
28981
+ let sql = "SELECT * FROM kg_edges WHERE source_id = ?";
28982
+ const params = [current.id];
28983
+ if (opts?.relation_types && opts.relation_types.length > 0) {
28984
+ const placeholders = opts.relation_types.map(() => "?").join(",");
28985
+ sql += ` AND relation_type IN (${placeholders})`;
28986
+ params.push(...opts.relation_types);
28987
+ }
28988
+ const edges = d.query(sql).all(...params).map(rowToEdge);
28989
+ for (const edge of edges) {
28990
+ const nextId = edge.target_id;
28991
+ const newPath = [...current.path, edge];
28992
+ if (nextId === targetId) {
28993
+ results.push(newPath);
28994
+ if (results.length >= 3)
28995
+ return results;
28996
+ continue;
28997
+ }
28998
+ if (!visited.has(nextId)) {
28999
+ visited.add(nextId);
29000
+ queue.push({ id: nextId, path: newPath });
29001
+ }
29002
+ }
29003
+ }
29004
+ return results;
29005
+ }
29006
+ function getImpactAnalysis(entityId, opts, db) {
29007
+ const d = db || getDatabase();
29008
+ const maxDepth = opts?.max_depth || 3;
29009
+ const results = [];
29010
+ const visited = new Set;
29011
+ visited.add(entityId);
29012
+ const queue = [{ id: entityId, depth: 0 }];
29013
+ while (queue.length > 0) {
29014
+ const current = queue.shift();
29015
+ if (current.depth >= maxDepth)
29016
+ continue;
29017
+ let sql = "SELECT * FROM kg_edges WHERE source_id = ?";
29018
+ const params = [current.id];
29019
+ if (opts?.relation_types && opts.relation_types.length > 0) {
29020
+ const placeholders = opts.relation_types.map(() => "?").join(",");
29021
+ sql += ` AND relation_type IN (${placeholders})`;
29022
+ params.push(...opts.relation_types);
29023
+ }
29024
+ const edges = d.query(sql).all(...params).map(rowToEdge);
29025
+ for (const edge of edges) {
29026
+ if (!visited.has(edge.target_id)) {
29027
+ visited.add(edge.target_id);
29028
+ results.push({
29029
+ entity_id: edge.target_id,
29030
+ entity_type: edge.target_type,
29031
+ depth: current.depth + 1,
29032
+ relation: edge.relation_type
29033
+ });
29034
+ queue.push({ id: edge.target_id, depth: current.depth + 1 });
29035
+ }
29036
+ }
29037
+ }
29038
+ return results;
29039
+ }
29040
+ function getCriticalPath(opts, db) {
29041
+ const d = db || getDatabase();
29042
+ let sql = `SELECT source_id, target_id FROM kg_edges WHERE relation_type = 'depends_on'`;
29043
+ const params = [];
29044
+ if (opts?.project_id) {
29045
+ sql += ` AND source_id IN (SELECT id FROM tasks WHERE project_id = ?)`;
29046
+ params.push(opts.project_id);
29047
+ }
29048
+ const edges = d.query(sql).all(...params);
29049
+ const blocks = new Map;
29050
+ for (const e of edges) {
29051
+ if (!blocks.has(e.target_id))
29052
+ blocks.set(e.target_id, new Set);
29053
+ blocks.get(e.target_id).add(e.source_id);
29054
+ }
29055
+ const results = [];
29056
+ for (const [taskId] of blocks) {
29057
+ const visited = new Set;
29058
+ const q = [taskId];
29059
+ let maxDepth = 0;
29060
+ let depth = 0;
29061
+ let levelSize = q.length;
29062
+ while (q.length > 0) {
29063
+ const node = q.shift();
29064
+ levelSize--;
29065
+ const downstream = blocks.get(node);
29066
+ if (downstream) {
29067
+ for (const d2 of downstream) {
29068
+ if (!visited.has(d2)) {
29069
+ visited.add(d2);
29070
+ q.push(d2);
29071
+ }
29072
+ }
29073
+ }
29074
+ if (levelSize === 0) {
29075
+ depth++;
29076
+ maxDepth = Math.max(maxDepth, depth);
29077
+ levelSize = q.length;
29078
+ }
29079
+ }
29080
+ if (visited.size > 0) {
29081
+ results.push({ task_id: taskId, blocking_count: visited.size, depth: maxDepth });
29082
+ }
29083
+ }
29084
+ results.sort((a, b) => b.blocking_count - a.blocking_count);
29085
+ return results.slice(0, opts?.limit || 20);
29086
+ }
29087
+ function addKgEdge(sourceId, sourceType, targetId, targetType, relationType, weight = 1, metadata, db) {
29088
+ const d = db || getDatabase();
29089
+ const id = uuid();
29090
+ const timestamp = now();
29091
+ d.run(`INSERT OR IGNORE INTO kg_edges (id, source_id, source_type, target_id, target_type, relation_type, weight, metadata, created_at)
29092
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`, [id, sourceId, sourceType, targetId, targetType, relationType, weight, JSON.stringify(metadata || {}), timestamp]);
29093
+ return { id, source_id: sourceId, source_type: sourceType, target_id: targetId, target_type: targetType, relation_type: relationType, weight, metadata: metadata || {}, created_at: timestamp };
29094
+ }
29095
+ function removeKgEdges(sourceId, targetId, relationType, db) {
29096
+ const d = db || getDatabase();
29097
+ if (relationType) {
29098
+ return d.run("DELETE FROM kg_edges WHERE source_id = ? AND target_id = ? AND relation_type = ?", [sourceId, targetId, relationType]).changes;
29099
+ }
29100
+ return d.run("DELETE FROM kg_edges WHERE source_id = ? AND target_id = ?", [sourceId, targetId]).changes;
29101
+ }
29102
+ var init_kg = __esm(() => {
29103
+ init_database();
29104
+ });
29105
+
29106
+ // src/db/project-agent-roles.ts
29107
+ var exports_project_agent_roles = {};
29108
+ __export(exports_project_agent_roles, {
29109
+ setProjectAgentRole: () => setProjectAgentRole,
29110
+ removeProjectAgentRole: () => removeProjectAgentRole,
29111
+ listProjectAgentRoles: () => listProjectAgentRoles,
29112
+ getProjectOrgChart: () => getProjectOrgChart,
29113
+ getAgentProjectRoles: () => getAgentProjectRoles
29114
+ });
29115
+ function rowToRole(row) {
29116
+ return { ...row, is_lead: row.is_lead === 1 };
29117
+ }
29118
+ function setProjectAgentRole(projectId, agentId, role, isLead = false, db) {
29119
+ const d = db || getDatabase();
29120
+ const existing = d.query("SELECT * FROM project_agent_roles WHERE project_id = ? AND agent_id = ? AND role = ?").get(projectId, agentId, role);
29121
+ if (existing) {
29122
+ d.run("UPDATE project_agent_roles SET is_lead = ? WHERE id = ?", [isLead ? 1 : 0, existing.id]);
29123
+ return rowToRole(d.query("SELECT * FROM project_agent_roles WHERE id = ?").get(existing.id));
29124
+ }
29125
+ const id = uuid();
29126
+ d.run("INSERT INTO project_agent_roles (id, project_id, agent_id, role, is_lead, created_at) VALUES (?, ?, ?, ?, ?, ?)", [id, projectId, agentId, role, isLead ? 1 : 0, now()]);
29127
+ return rowToRole(d.query("SELECT * FROM project_agent_roles WHERE id = ?").get(id));
29128
+ }
29129
+ function removeProjectAgentRole(projectId, agentId, role, db) {
29130
+ const d = db || getDatabase();
29131
+ if (role) {
29132
+ return d.run("DELETE FROM project_agent_roles WHERE project_id = ? AND agent_id = ? AND role = ?", [projectId, agentId, role]).changes;
29133
+ }
29134
+ return d.run("DELETE FROM project_agent_roles WHERE project_id = ? AND agent_id = ?", [projectId, agentId]).changes;
29135
+ }
29136
+ function listProjectAgentRoles(projectId, db) {
29137
+ const d = db || getDatabase();
29138
+ return d.query("SELECT * FROM project_agent_roles WHERE project_id = ? ORDER BY role, created_at").all(projectId).map(rowToRole);
29139
+ }
29140
+ function getAgentProjectRoles(agentId, db) {
29141
+ const d = db || getDatabase();
29142
+ return d.query("SELECT * FROM project_agent_roles WHERE agent_id = ? ORDER BY project_id, role").all(agentId).map(rowToRole);
29143
+ }
29144
+ function getProjectOrgChart(projectId, opts, db) {
29145
+ const d = db || getDatabase();
29146
+ const globalTree = getOrgChart(d);
29147
+ const projectRoles = listProjectAgentRoles(projectId, d);
29148
+ const rolesByAgent = new Map;
29149
+ for (const pr of projectRoles) {
29150
+ if (!rolesByAgent.has(pr.agent_id))
29151
+ rolesByAgent.set(pr.agent_id, { roles: [], isLead: false });
29152
+ const entry = rolesByAgent.get(pr.agent_id);
29153
+ entry.roles.push(pr.role);
29154
+ if (pr.is_lead)
29155
+ entry.isLead = true;
29156
+ }
29157
+ function augmentTree(nodes) {
29158
+ return nodes.map((n) => {
29159
+ const override = rolesByAgent.get(n.agent.id);
29160
+ return {
29161
+ ...n,
29162
+ reports: augmentTree(n.reports),
29163
+ project_roles: override?.roles ?? [],
29164
+ is_project_lead: override?.isLead ?? false
29165
+ };
29166
+ }).filter((n) => {
29167
+ if (!opts?.filter_to_project)
29168
+ return true;
29169
+ const hasRole = n.project_roles.length > 0;
29170
+ const hasDescendant = n.reports.length > 0;
29171
+ return hasRole || hasDescendant;
29172
+ });
29173
+ }
29174
+ return augmentTree(globalTree);
29175
+ }
29176
+ var init_project_agent_roles = __esm(() => {
29177
+ init_database();
29178
+ init_agents();
29179
+ });
29180
+
29181
+ // src/db/patrol.ts
29182
+ var exports_patrol = {};
29183
+ __export(exports_patrol, {
29184
+ patrolTasks: () => patrolTasks,
29185
+ getReviewQueue: () => getReviewQueue
29186
+ });
29187
+ function rowToTask3(row) {
29188
+ return {
29189
+ ...row,
29190
+ status: row.status,
29191
+ priority: row.priority,
29192
+ tags: JSON.parse(row.tags || "[]"),
29193
+ metadata: JSON.parse(row.metadata || "{}"),
29194
+ requires_approval: Boolean(row.requires_approval)
29195
+ };
29196
+ }
29197
+ function patrolTasks(opts, db) {
29198
+ const d = db || getDatabase();
29199
+ const stuckMinutes = opts?.stuck_minutes || 60;
29200
+ const confidenceThreshold = opts?.confidence_threshold || 0.5;
29201
+ const issues = [];
29202
+ let projectFilter = "";
29203
+ const projectParams = [];
29204
+ if (opts?.project_id) {
29205
+ projectFilter = " AND project_id = ?";
29206
+ projectParams.push(opts.project_id);
29207
+ }
29208
+ const stuckCutoff = new Date(Date.now() - stuckMinutes * 60 * 1000).toISOString();
29209
+ const stuckRows = d.query(`SELECT * FROM tasks WHERE status = 'in_progress' AND updated_at < ?${projectFilter} ORDER BY updated_at ASC`).all(stuckCutoff, ...projectParams);
29210
+ for (const row of stuckRows) {
29211
+ const task = rowToTask3(row);
29212
+ const minutesStuck = Math.round((Date.now() - new Date(task.updated_at).getTime()) / 60000);
29213
+ issues.push({
29214
+ type: "stuck",
29215
+ task_id: task.id,
29216
+ task_title: task.title,
29217
+ severity: minutesStuck > 480 ? "critical" : minutesStuck > 120 ? "high" : "medium",
29218
+ detail: `In progress for ${minutesStuck} minutes without update`
29219
+ });
29220
+ }
29221
+ const lowConfRows = d.query(`SELECT * FROM tasks WHERE status = 'completed' AND confidence IS NOT NULL AND confidence < ?${projectFilter} ORDER BY confidence ASC`).all(confidenceThreshold, ...projectParams);
29222
+ for (const row of lowConfRows) {
29223
+ const task = rowToTask3(row);
29224
+ issues.push({
29225
+ type: "low_confidence",
29226
+ task_id: task.id,
29227
+ task_title: task.title,
29228
+ severity: (task.confidence ?? 0) < 0.3 ? "high" : "medium",
29229
+ detail: `Completed with confidence ${task.confidence} (threshold: ${confidenceThreshold})`
29230
+ });
29231
+ }
29232
+ const orphanedRows = d.query(`SELECT * FROM tasks WHERE status = 'pending' AND project_id IS NULL AND agent_id IS NULL AND assigned_to IS NULL ORDER BY created_at ASC`).all();
29233
+ for (const row of orphanedRows) {
29234
+ const task = rowToTask3(row);
29235
+ issues.push({
29236
+ type: "orphaned",
29237
+ task_id: task.id,
29238
+ task_title: task.title,
29239
+ severity: "low",
29240
+ detail: "Pending task with no project, no agent, and no assignee"
29241
+ });
29242
+ }
29243
+ const needsReviewRows = d.query(`SELECT * FROM tasks WHERE status = 'completed' AND requires_approval = 1 AND approved_by IS NULL${projectFilter} ORDER BY completed_at DESC`).all(...projectParams);
29244
+ for (const row of needsReviewRows) {
29245
+ const task = rowToTask3(row);
29246
+ issues.push({
29247
+ type: "needs_review",
29248
+ task_id: task.id,
29249
+ task_title: task.title,
29250
+ severity: "medium",
29251
+ detail: "Completed but requires approval \u2014 not yet reviewed"
29252
+ });
29253
+ }
29254
+ const pendingWithDeps = d.query(`SELECT t.* FROM tasks t
29255
+ WHERE t.status = 'pending'${projectFilter}
29256
+ AND t.id IN (SELECT task_id FROM task_dependencies)`).all(...projectParams);
29257
+ for (const row of pendingWithDeps) {
29258
+ const deps = d.query(`SELECT d.depends_on, t.status FROM task_dependencies d
29259
+ JOIN tasks t ON t.id = d.depends_on
29260
+ WHERE d.task_id = ?`).all(row.id);
29261
+ if (deps.length > 0 && deps.every((dep) => dep.status === "failed" || dep.status === "cancelled")) {
29262
+ const task = rowToTask3(row);
29263
+ issues.push({
29264
+ type: "zombie_blocked",
29265
+ task_id: task.id,
29266
+ task_title: task.title,
29267
+ severity: "high",
29268
+ detail: `Blocked by ${deps.length} task(s) that are all failed/cancelled \u2014 will never unblock`
29269
+ });
29270
+ }
29271
+ }
29272
+ const severityOrder = { critical: 0, high: 1, medium: 2, low: 3 };
29273
+ issues.sort((a, b) => severityOrder[a.severity] - severityOrder[b.severity]);
29274
+ return {
29275
+ issues,
29276
+ total_issues: issues.length,
29277
+ scanned_at: new Date().toISOString()
29278
+ };
29279
+ }
29280
+ function getReviewQueue(opts, db) {
29281
+ const d = db || getDatabase();
29282
+ let sql = `SELECT * FROM tasks WHERE status = 'completed' AND (
29283
+ (requires_approval = 1 AND approved_by IS NULL)
29284
+ OR (confidence IS NOT NULL AND confidence < 0.5)
29285
+ )`;
29286
+ const params = [];
29287
+ if (opts?.project_id) {
29288
+ sql += " AND project_id = ?";
29289
+ params.push(opts.project_id);
29290
+ }
29291
+ sql += " ORDER BY CASE WHEN requires_approval = 1 AND approved_by IS NULL THEN 0 ELSE 1 END, confidence ASC";
29292
+ if (opts?.limit) {
29293
+ sql += " LIMIT ?";
29294
+ params.push(opts.limit);
29295
+ }
29296
+ return d.query(sql).all(...params).map(rowToTask3);
29297
+ }
29298
+ var init_patrol = __esm(() => {
29299
+ init_database();
29300
+ });
29301
+
29302
+ // src/db/agent-metrics.ts
29303
+ var exports_agent_metrics = {};
29304
+ __export(exports_agent_metrics, {
29305
+ scoreTask: () => scoreTask,
29306
+ getLeaderboard: () => getLeaderboard,
29307
+ getAgentMetrics: () => getAgentMetrics
29308
+ });
29309
+ function getAgentMetrics(agentId, opts, db) {
29310
+ const d = db || getDatabase();
29311
+ const agent = d.query("SELECT id, name FROM agents WHERE id = ? OR LOWER(name) = LOWER(?)").get(agentId, agentId);
29312
+ if (!agent)
29313
+ return null;
29314
+ let projectFilter = "";
29315
+ const params = [agent.id, agent.id];
29316
+ if (opts?.project_id) {
29317
+ projectFilter = " AND project_id = ?";
29318
+ params.push(opts.project_id);
29319
+ }
29320
+ const completed = d.query(`SELECT COUNT(*) as count FROM tasks WHERE (agent_id = ? OR assigned_to = ?) AND status = 'completed'${projectFilter}`).get(...params).count;
29321
+ const failed = d.query(`SELECT COUNT(*) as count FROM tasks WHERE (agent_id = ? OR assigned_to = ?) AND status = 'failed'${projectFilter}`).get(...params).count;
29322
+ const inProgress = d.query(`SELECT COUNT(*) as count FROM tasks WHERE (agent_id = ? OR assigned_to = ?) AND status = 'in_progress'${projectFilter}`).get(...params).count;
29323
+ const total = completed + failed;
29324
+ const completionRate = total > 0 ? completed / total : 0;
29325
+ const avgTime = d.query(`SELECT AVG(
29326
+ (julianday(completed_at) - julianday(created_at)) * 24 * 60
29327
+ ) as avg_minutes
29328
+ FROM tasks
29329
+ WHERE (agent_id = ? OR assigned_to = ?) AND status = 'completed' AND completed_at IS NOT NULL${projectFilter}`).get(...params);
29330
+ const avgConf = d.query(`SELECT AVG(confidence) as avg_confidence
29331
+ FROM tasks
29332
+ WHERE (agent_id = ? OR assigned_to = ?) AND status = 'completed' AND confidence IS NOT NULL${projectFilter}`).get(...params);
29333
+ const reviewTasks = d.query(`SELECT metadata FROM tasks
29334
+ WHERE (agent_id = ? OR assigned_to = ?) AND status = 'completed'${projectFilter}
29335
+ AND metadata LIKE '%_review_score%'`).all(...params);
29336
+ let reviewScoreAvg = null;
29337
+ if (reviewTasks.length > 0) {
29338
+ let total2 = 0;
29339
+ let count = 0;
29340
+ for (const row of reviewTasks) {
29341
+ try {
29342
+ const meta = JSON.parse(row.metadata);
29343
+ if (typeof meta._review_score === "number") {
29344
+ total2 += meta._review_score;
29345
+ count++;
29346
+ }
29347
+ } catch {}
29348
+ }
29349
+ if (count > 0)
29350
+ reviewScoreAvg = total2 / count;
29351
+ }
29352
+ const speedScore = avgTime?.avg_minutes != null ? Math.max(0, 1 - avgTime.avg_minutes / (60 * 24)) : 0.5;
29353
+ const confidenceScore = avgConf?.avg_confidence ?? 0.5;
29354
+ const volumeScore = Math.min(1, completed / 50);
29355
+ const compositeScore = completionRate * 0.3 + speedScore * 0.2 + confidenceScore * 0.3 + volumeScore * 0.2;
29356
+ return {
29357
+ agent_id: agent.id,
29358
+ agent_name: agent.name,
29359
+ tasks_completed: completed,
29360
+ tasks_failed: failed,
29361
+ tasks_in_progress: inProgress,
29362
+ completion_rate: Math.round(completionRate * 1000) / 1000,
29363
+ avg_completion_minutes: avgTime?.avg_minutes != null ? Math.round(avgTime.avg_minutes * 10) / 10 : null,
29364
+ avg_confidence: avgConf?.avg_confidence != null ? Math.round(avgConf.avg_confidence * 1000) / 1000 : null,
29365
+ review_score_avg: reviewScoreAvg != null ? Math.round(reviewScoreAvg * 1000) / 1000 : null,
29366
+ composite_score: Math.round(compositeScore * 1000) / 1000
29367
+ };
29368
+ }
29369
+ function getLeaderboard(opts, db) {
29370
+ const d = db || getDatabase();
29371
+ const agents = d.query("SELECT id FROM agents ORDER BY name").all();
29372
+ const entries = [];
29373
+ for (const agent of agents) {
29374
+ const metrics = getAgentMetrics(agent.id, { project_id: opts?.project_id }, d);
29375
+ if (metrics && (metrics.tasks_completed > 0 || metrics.tasks_failed > 0 || metrics.tasks_in_progress > 0)) {
29376
+ entries.push(metrics);
29377
+ }
29378
+ }
29379
+ entries.sort((a, b) => b.composite_score - a.composite_score);
29380
+ const limit = opts?.limit || 20;
29381
+ return entries.slice(0, limit).map((entry, idx) => ({
29382
+ ...entry,
29383
+ rank: idx + 1
29384
+ }));
29385
+ }
29386
+ function scoreTask(taskId, score, reviewerId, db) {
29387
+ const d = db || getDatabase();
29388
+ if (score < 0 || score > 1)
29389
+ throw new Error("Score must be between 0 and 1");
29390
+ const task = d.query("SELECT metadata FROM tasks WHERE id = ?").get(taskId);
29391
+ if (!task)
29392
+ throw new Error(`Task not found: ${taskId}`);
29393
+ const metadata = JSON.parse(task.metadata || "{}");
29394
+ metadata._review_score = score;
29395
+ if (reviewerId)
29396
+ metadata._reviewed_by = reviewerId;
29397
+ metadata._reviewed_at = now();
29398
+ d.run("UPDATE tasks SET metadata = ?, updated_at = ? WHERE id = ?", [JSON.stringify(metadata), now(), taskId]);
29399
+ }
29400
+ var init_agent_metrics = __esm(() => {
29401
+ init_database();
29402
+ });
29403
+
28448
29404
  // src/mcp/tools/task-rel-tools.ts
28449
29405
  function registerTaskRelTools(server, ctx) {
28450
29406
  const { shouldRegisterTool, resolveId, formatError, formatTask } = ctx;
@@ -28459,7 +29415,7 @@ function registerTaskRelTools(server, ctx) {
28459
29415
  next_steps: exports_external2.array(exports_external2.string()).optional().describe("Recommended next actions")
28460
29416
  }, async ({ agent_id, project_id, summary, completed, in_progress, blockers, next_steps }) => {
28461
29417
  try {
28462
- const { createHandoff: createHandoff2 } = (()=>{throw new Error("Cannot require module "+"../db/handoffs.js");})();
29418
+ const { createHandoff: createHandoff2 } = (init_handoffs(), __toCommonJS(exports_handoffs));
28463
29419
  const handoff = createHandoff2({
28464
29420
  agent_id,
28465
29421
  project_id: project_id ? resolveId(project_id, "projects") : undefined,
@@ -28481,7 +29437,7 @@ function registerTaskRelTools(server, ctx) {
28481
29437
  project_id: exports_external2.string().optional().describe("Filter by project")
28482
29438
  }, async ({ agent_id, project_id }) => {
28483
29439
  try {
28484
- const { getLatestHandoff: getLatestHandoff2 } = (()=>{throw new Error("Cannot require module "+"../db/handoffs.js");})();
29440
+ const { getLatestHandoff: getLatestHandoff2 } = (init_handoffs(), __toCommonJS(exports_handoffs));
28485
29441
  const handoff = getLatestHandoff2(agent_id, project_id ? resolveId(project_id, "projects") : undefined);
28486
29442
  if (!handoff)
28487
29443
  return { content: [{ type: "text", text: "No handoffs found." }] };
@@ -28512,7 +29468,7 @@ function registerTaskRelTools(server, ctx) {
28512
29468
  created_by: exports_external2.string().optional().describe("Agent ID who created this relationship")
28513
29469
  }, async ({ source_task_id, target_task_id, relationship_type, created_by }) => {
28514
29470
  try {
28515
- const { addTaskRelationship: addTaskRelationship2 } = (()=>{throw new Error("Cannot require module "+"../db/task-relationships.js");})();
29471
+ const { addTaskRelationship: addTaskRelationship2 } = (init_task_relationships(), __toCommonJS(exports_task_relationships));
28516
29472
  const rel = addTaskRelationship2({
28517
29473
  source_task_id: resolveId(source_task_id),
28518
29474
  target_task_id: resolveId(target_task_id),
@@ -28533,7 +29489,7 @@ function registerTaskRelTools(server, ctx) {
28533
29489
  relationship_type: exports_external2.enum(["related_to", "conflicts_with", "similar_to", "duplicates", "supersedes", "modifies_same_file"]).optional()
28534
29490
  }, async ({ id, source_task_id, target_task_id, relationship_type }) => {
28535
29491
  try {
28536
- const { removeTaskRelationship: removeTaskRelationship2, removeTaskRelationshipByPair: removeTaskRelationshipByPair2 } = (()=>{throw new Error("Cannot require module "+"../db/task-relationships.js");})();
29492
+ const { removeTaskRelationship: removeTaskRelationship2, removeTaskRelationshipByPair: removeTaskRelationshipByPair2 } = (init_task_relationships(), __toCommonJS(exports_task_relationships));
28537
29493
  let removed = false;
28538
29494
  if (id) {
28539
29495
  removed = removeTaskRelationship2(id);
@@ -28554,7 +29510,7 @@ function registerTaskRelTools(server, ctx) {
28554
29510
  relationship_type: exports_external2.enum(["related_to", "conflicts_with", "similar_to", "duplicates", "supersedes", "modifies_same_file"]).optional()
28555
29511
  }, async ({ task_id, relationship_type }) => {
28556
29512
  try {
28557
- const { getTaskRelationships: getTaskRelationships2 } = (()=>{throw new Error("Cannot require module "+"../db/task-relationships.js");})();
29513
+ const { getTaskRelationships: getTaskRelationships2 } = (init_task_relationships(), __toCommonJS(exports_task_relationships));
28558
29514
  const rels = getTaskRelationships2(resolveId(task_id), relationship_type);
28559
29515
  if (rels.length === 0)
28560
29516
  return { content: [{ type: "text", text: "No relationships found." }] };
@@ -28571,7 +29527,7 @@ function registerTaskRelTools(server, ctx) {
28571
29527
  task_id: exports_external2.string().describe("Task ID to detect file relationships for")
28572
29528
  }, async ({ task_id }) => {
28573
29529
  try {
28574
- const { autoDetectFileRelationships: autoDetectFileRelationships2 } = (()=>{throw new Error("Cannot require module "+"../db/task-relationships.js");})();
29530
+ const { autoDetectFileRelationships: autoDetectFileRelationships2 } = (init_task_relationships(), __toCommonJS(exports_task_relationships));
28575
29531
  const created = autoDetectFileRelationships2(resolveId(task_id));
28576
29532
  return { content: [{ type: "text", text: created.length > 0 ? `Created ${created.length} file relationship(s).` : "No file overlaps detected." }] };
28577
29533
  } catch (e) {
@@ -28582,8 +29538,8 @@ function registerTaskRelTools(server, ctx) {
28582
29538
  if (shouldRegisterTool("sync_kg")) {
28583
29539
  server.tool("sync_kg", "Sync all existing relationships into the knowledge graph edges table. Idempotent.", {}, async () => {
28584
29540
  try {
28585
- const { syncKgEdges } = (()=>{throw new Error("Cannot require module "+"../db/kg.js");})();
28586
- const result = syncKgEdges();
29541
+ const { syncKgEdges: syncKgEdges2 } = (init_kg(), __toCommonJS(exports_kg));
29542
+ const result = syncKgEdges2();
28587
29543
  return { content: [{ type: "text", text: `Knowledge graph synced: ${result.synced} edge(s) processed.` }] };
28588
29544
  } catch (e) {
28589
29545
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
@@ -28599,8 +29555,8 @@ function registerTaskRelTools(server, ctx) {
28599
29555
  limit: exports_external2.number().optional().describe("Max results")
28600
29556
  }, async ({ entity_id, relation_type, entity_type, direction, limit }) => {
28601
29557
  try {
28602
- const { getRelated } = (()=>{throw new Error("Cannot require module "+"../db/kg.js");})();
28603
- const edges = getRelated(entity_id, { relation_type, entity_type, direction, limit });
29558
+ const { getRelated: getRelated2 } = (init_kg(), __toCommonJS(exports_kg));
29559
+ const edges = getRelated2(entity_id, { relation_type, entity_type, direction, limit });
28604
29560
  if (edges.length === 0)
28605
29561
  return { content: [{ type: "text", text: "No related entities found." }] };
28606
29562
  const lines = edges.map((e) => `${e.source_id.slice(0, 12)}(${e.source_type}) --[${e.relation_type}]--> ${e.target_id.slice(0, 12)}(${e.target_type})`);
@@ -28619,8 +29575,8 @@ function registerTaskRelTools(server, ctx) {
28619
29575
  relation_types: exports_external2.array(exports_external2.string()).optional().describe("Filter by relation types")
28620
29576
  }, async ({ source_id, target_id, max_depth, relation_types }) => {
28621
29577
  try {
28622
- const { findPath } = (()=>{throw new Error("Cannot require module "+"../db/kg.js");})();
28623
- const paths = findPath(source_id, target_id, { max_depth, relation_types });
29578
+ const { findPath: findPath2 } = (init_kg(), __toCommonJS(exports_kg));
29579
+ const paths = findPath2(source_id, target_id, { max_depth, relation_types });
28624
29580
  if (paths.length === 0)
28625
29581
  return { content: [{ type: "text", text: "No path found." }] };
28626
29582
  const lines = paths.map((path, i) => {
@@ -28644,8 +29600,8 @@ function registerTaskRelTools(server, ctx) {
28644
29600
  relation_types: exports_external2.array(exports_external2.string()).optional().describe("Filter by relation types")
28645
29601
  }, async ({ entity_id, max_depth, relation_types }) => {
28646
29602
  try {
28647
- const { getImpactAnalysis } = (()=>{throw new Error("Cannot require module "+"../db/kg.js");})();
28648
- const impact = getImpactAnalysis(entity_id, { max_depth, relation_types });
29603
+ const { getImpactAnalysis: getImpactAnalysis2 } = (init_kg(), __toCommonJS(exports_kg));
29604
+ const impact = getImpactAnalysis2(entity_id, { max_depth, relation_types });
28649
29605
  if (impact.length === 0)
28650
29606
  return { content: [{ type: "text", text: "No downstream impact detected." }] };
28651
29607
  const byDepth = new Map;
@@ -28675,8 +29631,8 @@ Depth ${depth}:`);
28675
29631
  limit: exports_external2.number().optional().describe("Max results (default: 20)")
28676
29632
  }, async ({ project_id, limit }) => {
28677
29633
  try {
28678
- const { getCriticalPath } = (()=>{throw new Error("Cannot require module "+"../db/kg.js");})();
28679
- const result = getCriticalPath({ project_id: project_id ? resolveId(project_id, "projects") : undefined, limit });
29634
+ const { getCriticalPath: getCriticalPath2 } = (init_kg(), __toCommonJS(exports_kg));
29635
+ const result = getCriticalPath2({ project_id: project_id ? resolveId(project_id, "projects") : undefined, limit });
28680
29636
  if (result.length === 0)
28681
29637
  return { content: [{ type: "text", text: "No critical path data. Run sync_kg first to populate the knowledge graph." }] };
28682
29638
  const lines = result.map((r, i) => `${i + 1}. ${r.task_id.slice(0, 8)} blocks ${r.blocking_count} task(s), max depth ${r.depth}`);
@@ -28696,13 +29652,13 @@ ${lines.join(`
28696
29652
  is_lead: exports_external2.coerce.boolean().optional().describe("Whether this agent is the project lead for this role")
28697
29653
  }, async ({ project_id, agent_name, role, is_lead }) => {
28698
29654
  try {
28699
- const { setProjectAgentRole } = (()=>{throw new Error("Cannot require module "+"../db/project-agent-roles.js");})();
28700
- const { getAgentByName: getAgentByName2 } = (()=>{throw new Error("Cannot require module "+"../db/agents.js");})();
29655
+ const { setProjectAgentRole: setProjectAgentRole2 } = (init_project_agent_roles(), __toCommonJS(exports_project_agent_roles));
29656
+ const { getAgentByName: getAgentByName2 } = (init_agents(), __toCommonJS(exports_agents));
28701
29657
  const agent = getAgentByName2(agent_name);
28702
29658
  if (!agent)
28703
29659
  return { content: [{ type: "text", text: `Agent not found: ${agent_name}` }], isError: true };
28704
29660
  const pid = resolveId(project_id, "projects");
28705
- const result = setProjectAgentRole(pid, agent.id, role, is_lead ?? false);
29661
+ const result = setProjectAgentRole2(pid, agent.id, role, is_lead ?? false);
28706
29662
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
28707
29663
  } catch (e) {
28708
29664
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
@@ -28732,9 +29688,9 @@ ${lines.join(`
28732
29688
  }).join(`
28733
29689
  `);
28734
29690
  };
28735
- const { getProjectOrgChart } = (()=>{throw new Error("Cannot require module "+"../db/project-agent-roles.js");})();
29691
+ const { getProjectOrgChart: getProjectOrgChart2 } = (init_project_agent_roles(), __toCommonJS(exports_project_agent_roles));
28736
29692
  const pid = resolveId(project_id, "projects");
28737
- const tree = getProjectOrgChart(pid, { filter_to_project });
29693
+ const tree = getProjectOrgChart2(pid, { filter_to_project });
28738
29694
  if (format === "json") {
28739
29695
  return { content: [{ type: "text", text: JSON.stringify(tree, null, 2) }] };
28740
29696
  }
@@ -28752,9 +29708,9 @@ ${lines.join(`
28752
29708
  project_id: exports_external2.string().describe("Project ID")
28753
29709
  }, async ({ project_id }) => {
28754
29710
  try {
28755
- const { listProjectAgentRoles } = (()=>{throw new Error("Cannot require module "+"../db/project-agent-roles.js");})();
29711
+ const { listProjectAgentRoles: listProjectAgentRoles2 } = (init_project_agent_roles(), __toCommonJS(exports_project_agent_roles));
28756
29712
  const pid = resolveId(project_id, "projects");
28757
- const roles = listProjectAgentRoles(pid);
29713
+ const roles = listProjectAgentRoles2(pid);
28758
29714
  return { content: [{ type: "text", text: JSON.stringify(roles, null, 2) }] };
28759
29715
  } catch (e) {
28760
29716
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
@@ -28768,7 +29724,7 @@ ${lines.join(`
28768
29724
  limit: exports_external2.number().optional().describe("Max results")
28769
29725
  }, async ({ capabilities, min_score, limit }) => {
28770
29726
  try {
28771
- const { getCapableAgents: getCapableAgents2 } = (()=>{throw new Error("Cannot require module "+"../db/agents.js");})();
29727
+ const { getCapableAgents: getCapableAgents2 } = (init_agents(), __toCommonJS(exports_agents));
28772
29728
  const results = getCapableAgents2(capabilities, { min_score, limit });
28773
29729
  if (results.length === 0)
28774
29730
  return { content: [{ type: "text", text: "No agents match the given capabilities." }] };
@@ -28787,8 +29743,8 @@ ${lines.join(`
28787
29743
  project_id: exports_external2.string().optional().describe("Filter by project")
28788
29744
  }, async ({ stuck_minutes, confidence_threshold, project_id }) => {
28789
29745
  try {
28790
- const { patrolTasks } = (()=>{throw new Error("Cannot require module "+"../db/patrol.js");})();
28791
- const result = patrolTasks({
29746
+ const { patrolTasks: patrolTasks2 } = (init_patrol(), __toCommonJS(exports_patrol));
29747
+ const result = patrolTasks2({
28792
29748
  stuck_minutes,
28793
29749
  confidence_threshold,
28794
29750
  project_id: project_id ? resolveId(project_id, "projects") : undefined
@@ -28814,8 +29770,8 @@ ${lines.join(`
28814
29770
  limit: exports_external2.number().optional().describe("Max results (default: all)")
28815
29771
  }, async ({ project_id, limit }) => {
28816
29772
  try {
28817
- const { getReviewQueue } = (()=>{throw new Error("Cannot require module "+"../db/patrol.js");})();
28818
- const tasks = getReviewQueue({
29773
+ const { getReviewQueue: getReviewQueue2 } = (init_patrol(), __toCommonJS(exports_patrol));
29774
+ const tasks = getReviewQueue2({
28819
29775
  project_id: project_id ? resolveId(project_id, "projects") : undefined,
28820
29776
  limit
28821
29777
  });
@@ -28841,8 +29797,8 @@ ${lines.join(`
28841
29797
  reviewer_id: exports_external2.string().optional().describe("Agent ID of reviewer")
28842
29798
  }, async ({ task_id, score, reviewer_id }) => {
28843
29799
  try {
28844
- const { scoreTask } = (()=>{throw new Error("Cannot require module "+"../db/agent-metrics.js");})();
28845
- scoreTask(resolveId(task_id), score, reviewer_id);
29800
+ const { scoreTask: scoreTask2 } = (init_agent_metrics(), __toCommonJS(exports_agent_metrics));
29801
+ scoreTask2(resolveId(task_id), score, reviewer_id);
28846
29802
  return { content: [{ type: "text", text: `Task ${task_id.slice(0, 8)} scored: ${score}` }] };
28847
29803
  } catch (e) {
28848
29804
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
@@ -28859,7 +29815,7 @@ ${lines.join(`
28859
29815
  notes: exports_external2.string().optional().describe("Notes about what was done")
28860
29816
  }, async ({ task_id, minutes, agent_id, started_at, ended_at, notes }) => {
28861
29817
  try {
28862
- const { logTime: logTime2 } = (()=>{throw new Error("Cannot require module "+"../db/tasks.js");})();
29818
+ const { logTime: logTime2 } = (init_tasks(), __toCommonJS(exports_tasks));
28863
29819
  logTime2({ task_id: resolveId(task_id), minutes, agent_id, started_at, ended_at, notes });
28864
29820
  return { content: [{ type: "text", text: `Logged ${minutes} min on task ${task_id.slice(0, 8)}` }] };
28865
29821
  } catch (e) {
@@ -28874,7 +29830,7 @@ ${lines.join(`
28874
29830
  since: exports_external2.string().optional().describe("ISO date \u2014 only tasks completed after this date")
28875
29831
  }, async ({ project_id, agent_id, since }) => {
28876
29832
  try {
28877
- const { getTimeReport: getTimeReport2 } = (()=>{throw new Error("Cannot require module "+"../db/tasks.js");})();
29833
+ const { getTimeReport: getTimeReport2 } = (init_tasks(), __toCommonJS(exports_tasks));
28878
29834
  const report = getTimeReport2({ project_id: project_id ? resolveId(project_id, "projects") : undefined, agent_id, since });
28879
29835
  if (report.length === 0)
28880
29836
  return { content: [{ type: "text", text: "No completed tasks found." }] };
@@ -28898,7 +29854,7 @@ ${lines.join(`
28898
29854
  agent_id: exports_external2.string().optional().describe("Agent subscribing (defaults to context agent)")
28899
29855
  }, async ({ task_id, agent_id }) => {
28900
29856
  try {
28901
- const { watchTask: watchTask2 } = (()=>{throw new Error("Cannot require module "+"../db/tasks.js");})();
29857
+ const { watchTask: watchTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
28902
29858
  watchTask2(resolveId(task_id), agent_id || "");
28903
29859
  return { content: [{ type: "text", text: `Now watching task ${task_id.slice(0, 8)}` }] };
28904
29860
  } catch (e) {
@@ -28912,7 +29868,7 @@ ${lines.join(`
28912
29868
  agent_id: exports_external2.string().optional().describe("Agent unsubscribing (defaults to context agent)")
28913
29869
  }, async ({ task_id, agent_id }) => {
28914
29870
  try {
28915
- const { unwatchTask: unwatchTask2 } = (()=>{throw new Error("Cannot require module "+"../db/tasks.js");})();
29871
+ const { unwatchTask: unwatchTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
28916
29872
  unwatchTask2(resolveId(task_id), agent_id || "");
28917
29873
  return { content: [{ type: "text", text: `Stopped watching task ${task_id.slice(0, 8)}` }] };
28918
29874
  } catch (e) {
@@ -28925,7 +29881,7 @@ ${lines.join(`
28925
29881
  task_id: exports_external2.string().describe("Task ID")
28926
29882
  }, async ({ task_id }) => {
28927
29883
  try {
28928
- const { getTaskWatchers: getTaskWatchers2 } = (()=>{throw new Error("Cannot require module "+"../db/tasks.js");})();
29884
+ const { getTaskWatchers: getTaskWatchers2 } = (init_tasks(), __toCommonJS(exports_tasks));
28929
29885
  const watchers = getTaskWatchers2(resolveId(task_id));
28930
29886
  if (watchers.length === 0)
28931
29887
  return { content: [{ type: "text", text: "No watchers." }] };
@@ -28944,15 +29900,15 @@ ${lines.join(`
28944
29900
  agent_id: exports_external2.string().optional().describe("Filter by assignee")
28945
29901
  }, async ({ project_id, plan_id, task_list_id, since, agent_id }) => {
28946
29902
  try {
28947
- const { listTasks: listTasks3 } = (()=>{throw new Error("Cannot require module "+"../db/tasks.js");})();
28948
- const { patrolTasks } = (()=>{throw new Error("Cannot require module "+"../db/patrol.js");})();
29903
+ const { listTasks: listTasks3 } = (init_tasks(), __toCommonJS(exports_tasks));
29904
+ const { patrolTasks: patrolTasks2 } = (init_patrol(), __toCommonJS(exports_patrol));
28949
29905
  const completed = listTasks3({ status: "completed", project_id, plan_id, task_list_id, assigned_to: agent_id, limit: 500 }, undefined);
28950
29906
  const filtered = since ? completed.filter((t) => t.completed_at && t.completed_at >= since) : completed;
28951
29907
  const total = filtered.length;
28952
29908
  const lowConf = filtered.filter((t) => t.confidence != null && t.confidence < 0.7).length;
28953
29909
  const withEstimate = filtered.filter((t) => t.estimated_minutes != null && t.actual_minutes != null);
28954
29910
  const avgDiff = withEstimate.length > 0 ? withEstimate.reduce((acc, t) => acc + (t.actual_minutes - t.estimated_minutes), 0) / withEstimate.length : 0;
28955
- const patrolResult = patrolTasks({ project_id: project_id ? resolveId(project_id, "projects") : undefined });
29911
+ const patrolResult = patrolTasks2({ project_id: project_id ? resolveId(project_id, "projects") : undefined });
28956
29912
  const stuck = patrolResult.issues.filter((i) => i.type === "stuck").length;
28957
29913
  const lines = [
28958
29914
  `Retro (${total} completed tasks${since ? ` since ${since}` : ""})`,
@@ -28973,7 +29929,7 @@ ${lines.join(`
28973
29929
  limit: exports_external2.number().optional().describe("Max results (default: 20)")
28974
29930
  }, async ({ project_id, limit }) => {
28975
29931
  try {
28976
- const { listTasks: listTasks3 } = (()=>{throw new Error("Cannot require module "+"../db/tasks.js");})();
29932
+ const { listTasks: listTasks3 } = (init_tasks(), __toCommonJS(exports_tasks));
28977
29933
  const tasks = listTasks3({ status: "pending", project_id, assigned_to: "", limit: limit || 20 }, undefined);
28978
29934
  if (tasks.length === 0)
28979
29935
  return { content: [{ type: "text", text: "Inbox is empty." }] };
@@ -28992,8 +29948,8 @@ ${lines.join(`
28992
29948
  project_id: exports_external2.string().optional().describe("Filter by project")
28993
29949
  }, async ({ agent_id, project_id }) => {
28994
29950
  try {
28995
- const { getAgentMetrics } = (()=>{throw new Error("Cannot require module "+"../db/agent-metrics.js");})();
28996
- const metrics = getAgentMetrics(agent_id, {
29951
+ const { getAgentMetrics: getAgentMetrics2 } = (init_agent_metrics(), __toCommonJS(exports_agent_metrics));
29952
+ const metrics = getAgentMetrics2(agent_id, {
28997
29953
  project_id: project_id ? resolveId(project_id, "projects") : undefined
28998
29954
  });
28999
29955
  if (!metrics)
@@ -29020,8 +29976,8 @@ ${lines.join(`
29020
29976
  limit: exports_external2.number().optional().describe("Max entries (default: 20)")
29021
29977
  }, async ({ project_id, limit }) => {
29022
29978
  try {
29023
- const { getLeaderboard } = (()=>{throw new Error("Cannot require module "+"../db/agent-metrics.js");})();
29024
- const entries = getLeaderboard({
29979
+ const { getLeaderboard: getLeaderboard2 } = (init_agent_metrics(), __toCommonJS(exports_agent_metrics));
29980
+ const entries = getLeaderboard2({
29025
29981
  project_id: project_id ? resolveId(project_id, "projects") : undefined,
29026
29982
  limit
29027
29983
  });
@@ -29981,6 +30937,361 @@ var init_machines2 = __esm(() => {
29981
30937
  init_database();
29982
30938
  });
29983
30939
 
30940
+ // src/mcp/tools/agents.ts
30941
+ function registerAgentTools(server, { shouldRegisterTool, resolveId, formatError, agentFocusMap, getAgentFocus }) {
30942
+ if (shouldRegisterTool("set_focus")) {
30943
+ server.tool("set_focus", "Focus this agent on a project. All list/search/status tools will default to this project.", {
30944
+ agent_id: exports_external2.string().describe("Agent ID or name"),
30945
+ project_id: exports_external2.string().optional().describe("Project to focus on. Omit to clear."),
30946
+ task_list_id: exports_external2.string().optional().describe("Task list to focus on")
30947
+ }, async ({ agent_id, project_id, task_list_id }) => {
30948
+ try {
30949
+ const resolvedProject = project_id ? resolveId(project_id, "projects") : undefined;
30950
+ const focus = { agent_id, project_id: resolvedProject, task_list_id };
30951
+ agentFocusMap.set(agent_id, focus);
30952
+ try {
30953
+ const agent = getAgentByName(agent_id) || getAgent(agent_id);
30954
+ if (agent) {
30955
+ const db = getDatabase();
30956
+ db.run("UPDATE agents SET active_project_id = ? WHERE id = ?", [resolvedProject || null, agent.id]);
30957
+ }
30958
+ } catch {}
30959
+ const projectName = resolvedProject ? ` (${resolvedProject.slice(0, 8)})` : "";
30960
+ return { content: [{ type: "text", text: `Focused on project${projectName}. Read tools will default to this scope. Pass explicit project_id to override.` }] };
30961
+ } catch (e) {
30962
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
30963
+ }
30964
+ });
30965
+ }
30966
+ if (shouldRegisterTool("get_focus")) {
30967
+ server.tool("get_focus", "Get the current focus for an agent.", { agent_id: exports_external2.string().describe("Agent ID or name") }, async ({ agent_id }) => {
30968
+ const focus = getAgentFocus(agent_id);
30969
+ if (!focus?.project_id) {
30970
+ return { content: [{ type: "text", text: "No focus set. Showing all projects." }] };
30971
+ }
30972
+ return { content: [{ type: "text", text: `Focused on project: ${focus.project_id}${focus.task_list_id ? `, task list: ${focus.task_list_id}` : ""}` }] };
30973
+ });
30974
+ }
30975
+ if (shouldRegisterTool("unfocus")) {
30976
+ server.tool("unfocus", "Clear focus \u2014 show all projects and tasks.", { agent_id: exports_external2.string().describe("Agent ID or name") }, async ({ agent_id }) => {
30977
+ agentFocusMap.delete(agent_id);
30978
+ try {
30979
+ const agent = getAgentByName(agent_id) || getAgent(agent_id);
30980
+ if (agent) {
30981
+ const db = getDatabase();
30982
+ db.run("UPDATE agents SET active_project_id = NULL WHERE id = ?", [agent.id]);
30983
+ }
30984
+ } catch {}
30985
+ return { content: [{ type: "text", text: "Focus cleared. Showing all projects." }] };
30986
+ });
30987
+ }
30988
+ if (shouldRegisterTool("register_agent")) {
30989
+ server.tool("register_agent", "Register an agent. Any name is allowed \u2014 the configured pool is advisory, not enforced. Returns a conflict error if the name is held by a recently-active agent.", {
30990
+ name: exports_external2.string().describe("Agent name \u2014 any name is allowed. Use suggest_agent_name to see pool suggestions and avoid conflicts."),
30991
+ description: exports_external2.string().optional(),
30992
+ role: exports_external2.string().optional(),
30993
+ title: exports_external2.string().optional(),
30994
+ level: exports_external2.string().optional(),
30995
+ permissions: exports_external2.array(exports_external2.string()).optional(),
30996
+ capabilities: exports_external2.array(exports_external2.string()).optional().describe("Agent capabilities/skills for task routing (e.g. ['typescript', 'testing', 'devops'])"),
30997
+ session_id: exports_external2.string().optional().describe("Unique ID for this coding session (e.g. process PID + timestamp, or env var). Used to detect name collisions across sessions. Store it and pass on every register_agent call."),
30998
+ working_dir: exports_external2.string().optional().describe("Working directory of this session \u2014 used to look up the project's agent pool and identify who holds the name in a conflict"),
30999
+ force: exports_external2.boolean().optional().describe("Force takeover of an active agent's name. Use with caution \u2014 only when you know the previous session is dead.")
31000
+ }, async ({ name, description, role, title, level, permissions, capabilities, session_id, working_dir, force }) => {
31001
+ try {
31002
+ const pool = getAgentPoolForProject(working_dir);
31003
+ const result = registerAgent({ name, description, role, title, level, permissions, capabilities, session_id, working_dir, force, pool: pool || undefined });
31004
+ if (isAgentConflict(result)) {
31005
+ const suggestLine = result.suggestions && result.suggestions.length > 0 ? `
31006
+ Available names: ${result.suggestions.join(", ")}` : "";
31007
+ const hint = `CONFLICT: ${result.message}${suggestLine}`;
31008
+ return {
31009
+ content: [{ type: "text", text: hint }],
31010
+ isError: true
31011
+ };
31012
+ }
31013
+ const agent = result;
31014
+ const poolLine = pool ? `
31015
+ Pool: [${pool.join(", ")}]` : "";
31016
+ return {
31017
+ content: [{
31018
+ type: "text",
31019
+ text: `Agent registered:
31020
+ ID: ${agent.id}
31021
+ Name: ${agent.name}${agent.description ? `
31022
+ Description: ${agent.description}` : ""}
31023
+ Session: ${agent.session_id ?? "unbound"}${poolLine}
31024
+ Created: ${agent.created_at}
31025
+ Last seen: ${agent.last_seen_at}`
31026
+ }]
31027
+ };
31028
+ } catch (e) {
31029
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
31030
+ }
31031
+ });
31032
+ }
31033
+ if (shouldRegisterTool("suggest_agent_name")) {
31034
+ server.tool("suggest_agent_name", "Get available agent names for a project. Shows configured pool, active agents, and suggestions. If no pool is configured, any name is allowed.", {
31035
+ working_dir: exports_external2.string().optional().describe("Your working directory \u2014 used to look up the project's allowed name pool from config")
31036
+ }, async ({ working_dir }) => {
31037
+ try {
31038
+ const pool = getAgentPoolForProject(working_dir);
31039
+ const cutoff = new Date(Date.now() - 30 * 60 * 1000).toISOString();
31040
+ const allActive = listAgents().filter((a) => a.last_seen_at > cutoff);
31041
+ if (!pool) {
31042
+ const lines2 = [
31043
+ "No agent pool configured \u2014 any name is allowed.",
31044
+ allActive.length > 0 ? `Active agents (avoid these names): ${allActive.map((a) => `${a.name} (seen ${Math.round((Date.now() - new Date(a.last_seen_at).getTime()) / 60000)}m ago)`).join(", ")}` : "No active agents.",
31045
+ `
31046
+ To restrict names, configure agent_pool or project_pools in ~/.hasna/todos/config.json`
31047
+ ];
31048
+ return { content: [{ type: "text", text: lines2.join(`
31049
+ `) }] };
31050
+ }
31051
+ const available = getAvailableNamesFromPool(pool, getDatabase());
31052
+ const activeInPool = allActive.filter((a) => pool.map((n) => n.toLowerCase()).includes(a.name));
31053
+ const lines = [
31054
+ `Project pool: ${pool.join(", ")}`,
31055
+ `Available now (${available.length}): ${available.length > 0 ? available.join(", ") : "none \u2014 all names in use"}`,
31056
+ activeInPool.length > 0 ? `Active agents: ${activeInPool.map((a) => `${a.name} (seen ${Math.round((Date.now() - new Date(a.last_seen_at).getTime()) / 60000)}m ago)`).join(", ")}` : "Active agents: none",
31057
+ available.length > 0 ? `
31058
+ Suggested: ${available[0]}` : `
31059
+ No names available. Wait for an active agent to go stale (30min timeout).`
31060
+ ];
31061
+ return { content: [{ type: "text", text: lines.join(`
31062
+ `) }] };
31063
+ } catch (e) {
31064
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
31065
+ }
31066
+ });
31067
+ }
31068
+ if (shouldRegisterTool("list_agents")) {
31069
+ server.tool("list_agents", "List all registered agents. By default shows only active agents \u2014 set include_archived to see archived ones too.", {
31070
+ include_archived: exports_external2.boolean().optional().describe("Include archived agents in the list (default: false)")
31071
+ }, async ({ include_archived }) => {
31072
+ try {
31073
+ const agents = listAgents({ include_archived: include_archived ?? false });
31074
+ if (agents.length === 0) {
31075
+ return { content: [{ type: "text", text: "No agents registered." }] };
31076
+ }
31077
+ const text = agents.map((a) => {
31078
+ const statusTag = a.status === "archived" ? " [archived]" : "";
31079
+ return `${a.id} | ${a.name}${statusTag}${a.description ? ` - ${a.description}` : ""} (last seen: ${a.last_seen_at})`;
31080
+ }).join(`
31081
+ `);
31082
+ return { content: [{ type: "text", text: `${agents.length} agent(s):
31083
+ ${text}` }] };
31084
+ } catch (e) {
31085
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
31086
+ }
31087
+ });
31088
+ }
31089
+ if (shouldRegisterTool("get_agent")) {
31090
+ server.tool("get_agent", "Get agent details by ID or name. Provide one of id or name.", {
31091
+ agent_id: exports_external2.string().optional(),
31092
+ id: exports_external2.string().optional(),
31093
+ name: exports_external2.string().optional()
31094
+ }, async ({ agent_id, id, name }) => {
31095
+ try {
31096
+ const identifier = agent_id || id || name;
31097
+ if (!identifier) {
31098
+ return { content: [{ type: "text", text: "Provide agent_id, id, or name." }], isError: true };
31099
+ }
31100
+ const agent = getAgent(identifier) || getAgentByName(identifier);
31101
+ if (!agent) {
31102
+ return { content: [{ type: "text", text: `Agent not found: ${identifier}` }], isError: true };
31103
+ }
31104
+ const parts = [
31105
+ `ID: ${agent.id}`,
31106
+ `Name: ${agent.name}`
31107
+ ];
31108
+ if (agent.description)
31109
+ parts.push(`Description: ${agent.description}`);
31110
+ if (Object.keys(agent.metadata).length > 0)
31111
+ parts.push(`Metadata: ${JSON.stringify(agent.metadata)}`);
31112
+ parts.push(`Created: ${agent.created_at}`);
31113
+ parts.push(`Last seen: ${agent.last_seen_at}`);
31114
+ return { content: [{ type: "text", text: parts.join(`
31115
+ `) }] };
31116
+ } catch (e) {
31117
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
31118
+ }
31119
+ });
31120
+ }
31121
+ if (shouldRegisterTool("rename_agent")) {
31122
+ server.tool("rename_agent", "Rename an agent. Resolve by id or current name.", {
31123
+ id: exports_external2.string().optional(),
31124
+ name: exports_external2.string().optional(),
31125
+ new_name: exports_external2.string()
31126
+ }, async ({ id, name, new_name }) => {
31127
+ try {
31128
+ if (!id && !name) {
31129
+ return { content: [{ type: "text", text: "Provide either id or name." }], isError: true };
31130
+ }
31131
+ const agent = id ? getAgent(id) : getAgentByName(name);
31132
+ if (!agent) {
31133
+ return { content: [{ type: "text", text: `Agent not found: ${id || name}` }], isError: true };
31134
+ }
31135
+ const oldName = agent.name;
31136
+ const updated = updateAgent(agent.id, { name: new_name });
31137
+ const db = getDatabase();
31138
+ const tasksResult = db.run("UPDATE tasks SET assigned_to = ? WHERE assigned_to = ?", [new_name, oldName]);
31139
+ const taskNote = tasksResult.changes > 0 ? `
31140
+ Updated assigned_to on ${tasksResult.changes} task(s).` : "";
31141
+ return {
31142
+ content: [{
31143
+ type: "text",
31144
+ text: `Agent renamed: ${oldName} -> ${updated.name}
31145
+ ID: ${updated.id}${taskNote}`
31146
+ }]
31147
+ };
31148
+ } catch (e) {
31149
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
31150
+ }
31151
+ });
31152
+ }
31153
+ if (shouldRegisterTool("update_agent")) {
31154
+ server.tool("update_agent", "Update an agent's description, role, title, or other metadata. Resolve by id or name.", {
31155
+ agent_id: exports_external2.string().optional(),
31156
+ id: exports_external2.string().optional(),
31157
+ name: exports_external2.string().optional(),
31158
+ description: exports_external2.string().optional(),
31159
+ role: exports_external2.string().optional(),
31160
+ title: exports_external2.string().optional(),
31161
+ level: exports_external2.string().optional(),
31162
+ capabilities: exports_external2.array(exports_external2.string()).optional(),
31163
+ permissions: exports_external2.array(exports_external2.string()).optional(),
31164
+ metadata: exports_external2.record(exports_external2.unknown()).optional()
31165
+ }, async ({ agent_id, id, name, ...updates }) => {
31166
+ try {
31167
+ const identifier = agent_id || id || name;
31168
+ if (!identifier) {
31169
+ return { content: [{ type: "text", text: "Provide agent_id, id, or name." }], isError: true };
31170
+ }
31171
+ const agent = getAgent(identifier) || getAgentByName(identifier);
31172
+ if (!agent) {
31173
+ return { content: [{ type: "text", text: `Agent not found: ${identifier}` }], isError: true };
31174
+ }
31175
+ const updated = updateAgent(agent.id, updates);
31176
+ return { content: [{ type: "text", text: `Agent updated: ${updated.name} (${updated.id.slice(0, 8)})` }] };
31177
+ } catch (e) {
31178
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
31179
+ }
31180
+ });
31181
+ }
31182
+ if (shouldRegisterTool("delete_agent")) {
31183
+ server.tool("delete_agent", "Archive an agent (soft delete). The agent is hidden from list_agents but preserved for task history. Use unarchive_agent to restore. Resolve by id or name.", {
31184
+ agent_id: exports_external2.string().optional(),
31185
+ id: exports_external2.string().optional(),
31186
+ name: exports_external2.string().optional()
31187
+ }, async ({ agent_id, id, name }) => {
31188
+ try {
31189
+ const identifier = agent_id || id || name;
31190
+ if (!identifier) {
31191
+ return { content: [{ type: "text", text: "Provide agent_id, id, or name." }], isError: true };
31192
+ }
31193
+ const agent = getAgent(identifier) || getAgentByName(identifier);
31194
+ if (!agent) {
31195
+ return { content: [{ type: "text", text: `Agent not found: ${identifier}` }], isError: true };
31196
+ }
31197
+ const archived = archiveAgent(agent.id);
31198
+ return {
31199
+ content: [{
31200
+ type: "text",
31201
+ text: archived ? `Agent archived: ${agent.name} (${agent.id}). Use unarchive_agent to restore.` : `Failed to archive agent: ${agent.name}`
31202
+ }],
31203
+ isError: !archived
31204
+ };
31205
+ } catch (e) {
31206
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
31207
+ }
31208
+ });
31209
+ }
31210
+ if (shouldRegisterTool("unarchive_agent")) {
31211
+ server.tool("unarchive_agent", "Restore an archived agent back to active status. Resolve by id or name.", {
31212
+ agent_id: exports_external2.string().optional(),
31213
+ id: exports_external2.string().optional(),
31214
+ name: exports_external2.string().optional()
31215
+ }, async ({ agent_id, id, name }) => {
31216
+ try {
31217
+ const identifier = agent_id || id || name;
31218
+ if (!identifier) {
31219
+ return { content: [{ type: "text", text: "Provide agent_id, id, or name." }], isError: true };
31220
+ }
31221
+ const agent = getAgent(identifier) || getAgentByName(identifier);
31222
+ if (!agent) {
31223
+ return { content: [{ type: "text", text: `Agent not found: ${identifier}` }], isError: true };
31224
+ }
31225
+ if (agent.status === "active") {
31226
+ return { content: [{ type: "text", text: `Agent ${agent.name} is already active.` }] };
31227
+ }
31228
+ const restored = unarchiveAgent(agent.id);
31229
+ return {
31230
+ content: [{
31231
+ type: "text",
31232
+ text: restored ? `Agent restored: ${agent.name} (${agent.id}) is now active.` : `Failed to restore agent: ${agent.name}`
31233
+ }],
31234
+ isError: !restored
31235
+ };
31236
+ } catch (e) {
31237
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
31238
+ }
31239
+ });
31240
+ }
31241
+ if (shouldRegisterTool("heartbeat")) {
31242
+ server.tool("heartbeat", "Update your last_seen_at timestamp to signal you're still active. Call periodically during long tasks to prevent being marked stale.", {
31243
+ agent_id: exports_external2.string().describe("Your agent ID or name.")
31244
+ }, async ({ agent_id }) => {
31245
+ try {
31246
+ const agent = getAgent(agent_id) || getAgentByName(agent_id);
31247
+ if (!agent) {
31248
+ return { content: [{ type: "text", text: `Agent not found: ${agent_id}` }], isError: true };
31249
+ }
31250
+ updateAgentActivity(agent.id);
31251
+ return {
31252
+ content: [{
31253
+ type: "text",
31254
+ text: `Heartbeat: ${agent.name} (${agent.id}) \u2014 last_seen_at updated to ${new Date().toISOString()}`
31255
+ }]
31256
+ };
31257
+ } catch (e) {
31258
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
31259
+ }
31260
+ });
31261
+ }
31262
+ if (shouldRegisterTool("release_agent")) {
31263
+ server.tool("release_agent", "Explicitly release/logout an agent \u2014 clears session binding and makes the name immediately available. Call this when your session ends instead of waiting for the 30-minute stale timeout.", {
31264
+ agent_id: exports_external2.string().describe("Your agent ID or name."),
31265
+ session_id: exports_external2.string().optional().describe("Your session ID \u2014 if provided, release only succeeds if it matches (prevents other sessions from releasing your agent).")
31266
+ }, async ({ agent_id, session_id }) => {
31267
+ try {
31268
+ const agent = getAgent(agent_id) || getAgentByName(agent_id);
31269
+ if (!agent) {
31270
+ return { content: [{ type: "text", text: `Agent not found: ${agent_id}` }], isError: true };
31271
+ }
31272
+ const released = releaseAgent(agent.id, session_id);
31273
+ if (!released) {
31274
+ return { content: [{ type: "text", text: `Release denied: session_id does not match agent's current session.` }], isError: true };
31275
+ }
31276
+ return {
31277
+ content: [{
31278
+ type: "text",
31279
+ text: `Agent released: ${agent.name} (${agent.id}) \u2014 session cleared, name is now available.`
31280
+ }]
31281
+ };
31282
+ } catch (e) {
31283
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
31284
+ }
31285
+ });
31286
+ }
31287
+ }
31288
+ var init_agents2 = __esm(() => {
31289
+ init_zod2();
31290
+ init_agents();
31291
+ init_config();
31292
+ init_database();
31293
+ });
31294
+
29984
31295
  // src/mcp/index.ts
29985
31296
  var exports_mcp = {};
29986
31297
  __export(exports_mcp, {
@@ -29988,17 +31299,23 @@ __export(exports_mcp, {
29988
31299
  });
29989
31300
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
29990
31301
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
29991
- import { readFileSync as readFileSync7 } from "fs";
31302
+ import { existsSync as existsSync9, readFileSync as readFileSync7 } from "fs";
29992
31303
  import { join as join12, dirname as dirname6 } from "path";
29993
31304
  import { fileURLToPath as fileURLToPath3 } from "url";
29994
31305
  function getMcpVersion() {
29995
31306
  try {
29996
- const __dir = dirname6(fileURLToPath3(import.meta.url));
29997
- const pkgPath = join12(__dir, "..", "package.json");
29998
- return JSON.parse(readFileSync7(pkgPath, "utf-8")).version || "0.0.0";
31307
+ let dir = dirname6(fileURLToPath3(import.meta.url));
31308
+ for (let i = 0;i < 4; i++) {
31309
+ const pkgPath = join12(dir, "package.json");
31310
+ if (existsSync9(pkgPath)) {
31311
+ return JSON.parse(readFileSync7(pkgPath, "utf-8")).version || "0.0.0";
31312
+ }
31313
+ dir = dirname6(dir);
31314
+ }
29999
31315
  } catch {
30000
31316
  return "0.0.0";
30001
31317
  }
31318
+ return "0.0.0";
30002
31319
  }
30003
31320
  function shouldRegisterTool(name) {
30004
31321
  if (TODOS_PROFILE === "minimal")
@@ -30159,6 +31476,7 @@ var init_mcp = __esm(() => {
30159
31476
  init_task_rel_tools();
30160
31477
  init_code_tools();
30161
31478
  init_machines2();
31479
+ init_agents2();
30162
31480
  server = new McpServer({
30163
31481
  name: "todos",
30164
31482
  version: getMcpVersion()
@@ -30176,6 +31494,7 @@ var init_mcp = __esm(() => {
30176
31494
  "get_next_task",
30177
31495
  "bootstrap",
30178
31496
  "get_tasks_changed_since",
31497
+ "get_health",
30179
31498
  "heartbeat",
30180
31499
  "release_agent"
30181
31500
  ]);
@@ -30216,6 +31535,7 @@ var init_mcp = __esm(() => {
30216
31535
  registerTaskResources(server, toolContext);
30217
31536
  registerTaskRelTools(server, toolContext);
30218
31537
  registerCodeTools(server, toolContext);
31538
+ registerAgentTools(server, { ...toolContext, agentFocusMap });
30219
31539
  registerMachineTools(server, { shouldRegisterTool, formatError });
30220
31540
  registerDispatchTools(server, { shouldRegisterTool, resolveId, formatError });
30221
31541
  registerCloudSyncTools(server, { shouldRegisterTool, formatError });
@@ -30766,7 +32086,7 @@ ${chalk2.cyan(sid)} ${statusColor(task.status)} ${prioColor(task.priority)} ${ta
30766
32086
  opts.tags = opts.tags || opts.tag;
30767
32087
  opts.list = opts.list || opts.taskList;
30768
32088
  const resolvedId = resolveTaskId(id);
30769
- const current = getTask2(resolvedId);
32089
+ const current = getTask(resolvedId);
30770
32090
  if (!current) {
30771
32091
  console.error(chalk2.red(`Task not found: ${id}`));
30772
32092
  process.exit(1);
@@ -30827,7 +32147,7 @@ ${chalk2.cyan(sid)} ${statusColor(task.status)} ${prioColor(task.priority)} ${ta
30827
32147
  program2.command("approve <id>").description("Approve a task that requires approval").action((id) => {
30828
32148
  const globalOpts = program2.opts();
30829
32149
  const resolvedId = resolveTaskId(id);
30830
- const task = getTask2(resolvedId);
32150
+ const task = getTask(resolvedId);
30831
32151
  if (!task) {
30832
32152
  console.error(chalk2.red(`Task not found: ${id}`));
30833
32153
  process.exit(1);
@@ -31429,7 +32749,7 @@ function pushToClaudeTaskList(taskListId, projectId, options = {}) {
31429
32749
  updated.activeForm = existing.task.activeForm;
31430
32750
  writeClaudeTask(dir, updated);
31431
32751
  if (recordConflict) {
31432
- const latest = getTask2(task.id);
32752
+ const latest = getTask(task.id);
31433
32753
  if (latest) {
31434
32754
  const conflict = {
31435
32755
  agent: "claude",
@@ -31452,7 +32772,7 @@ function pushToClaudeTaskList(taskListId, projectId, options = {}) {
31452
32772
  ct.subject = formatPrefixedSubject(task.title, prefixConfig.prefix, prefixCounter);
31453
32773
  }
31454
32774
  writeClaudeTask(dir, ct);
31455
- const current = getTask2(task.id);
32775
+ const current = getTask(task.id);
31456
32776
  if (current) {
31457
32777
  const newMeta = { ...current.metadata, claude_task_id: claudeId };
31458
32778
  updateTask(task.id, { version: current.version, metadata: newMeta });
@@ -31654,7 +32974,7 @@ function pushToAgentTaskList(agent, taskListId, projectId, options = {}) {
31654
32974
  const updated = taskToAgentTask(task, existing.task.id, existing.task.metadata);
31655
32975
  writeAgentTask(dir, updated);
31656
32976
  if (recordConflict) {
31657
- const latest = getTask2(task.id);
32977
+ const latest = getTask(task.id);
31658
32978
  if (latest) {
31659
32979
  const conflict = {
31660
32980
  agent,
@@ -31673,7 +32993,7 @@ function pushToAgentTaskList(agent, taskListId, projectId, options = {}) {
31673
32993
  hwm++;
31674
32994
  const at = taskToAgentTask(task, externalId);
31675
32995
  writeAgentTask(dir, at);
31676
- const current = getTask2(task.id);
32996
+ const current = getTask(task.id);
31677
32997
  if (current) {
31678
32998
  const newMeta = { ...current.metadata, [metaKey]: externalId };
31679
32999
  updateTask(task.id, { version: current.version, metadata: newMeta });
@@ -32756,7 +34076,7 @@ ${tasks.length} task(s) shown`));
32756
34076
  program2.command("blame <file>").description("Show which tasks/agents touched a file and why \u2014 combines task_files + task_commits").action(async (filePath) => {
32757
34077
  const globalOpts = program2.opts();
32758
34078
  const { findTasksByFile: findTasksByFile2 } = await Promise.resolve().then(() => (init_task_files(), exports_task_files));
32759
- const { getTask: getTask3 } = await Promise.resolve().then(() => (init_tasks(), exports_tasks));
34079
+ const { getTask: getTask2 } = await Promise.resolve().then(() => (init_tasks(), exports_tasks));
32760
34080
  const db = getDatabase();
32761
34081
  const taskFiles = findTasksByFile2(filePath, db);
32762
34082
  const commitRows = db.query("SELECT tc.*, t.title, t.short_id FROM task_commits tc JOIN tasks t ON t.id = tc.task_id WHERE tc.files_changed LIKE ? ORDER BY tc.committed_at DESC").all(`%${filePath}%`);
@@ -32770,7 +34090,7 @@ Blame: ${filePath}
32770
34090
  if (taskFiles.length > 0) {
32771
34091
  console.log(chalk6.bold("Task File Links:"));
32772
34092
  for (const tf of taskFiles) {
32773
- const task = getTask3(tf.task_id, db);
34093
+ const task = getTask2(tf.task_id, db);
32774
34094
  const title = task ? task.title : "unknown";
32775
34095
  const sid = task?.short_id || tf.task_id.slice(0, 8);
32776
34096
  console.log(` ${chalk6.cyan(sid)} ${title} \u2014 ${chalk6.dim(tf.role || "file")} ${chalk6.dim(tf.updated_at)}`);
@@ -32804,74 +34124,10 @@ Commit Links (${commitRows.length}):`));
32804
34124
  init_database();
32805
34125
  init_tasks();
32806
34126
  init_audit();
32807
- import chalk7 from "chalk";
32808
-
32809
- // src/db/handoffs.ts
32810
- init_database();
32811
- function createHandoff(input, db) {
32812
- const d = db || getDatabase();
32813
- const id = uuid();
32814
- const timestamp = now();
32815
- d.run(`INSERT INTO handoffs (id, agent_id, project_id, summary, completed, in_progress, blockers, next_steps, created_at)
32816
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
32817
- id,
32818
- input.agent_id || null,
32819
- input.project_id || null,
32820
- input.summary,
32821
- input.completed ? JSON.stringify(input.completed) : null,
32822
- input.in_progress ? JSON.stringify(input.in_progress) : null,
32823
- input.blockers ? JSON.stringify(input.blockers) : null,
32824
- input.next_steps ? JSON.stringify(input.next_steps) : null,
32825
- timestamp
32826
- ]);
32827
- return {
32828
- id,
32829
- agent_id: input.agent_id || null,
32830
- project_id: input.project_id || null,
32831
- summary: input.summary,
32832
- completed: input.completed || null,
32833
- in_progress: input.in_progress || null,
32834
- blockers: input.blockers || null,
32835
- next_steps: input.next_steps || null,
32836
- created_at: timestamp
32837
- };
32838
- }
32839
- function rowToHandoff(row) {
32840
- return {
32841
- ...row,
32842
- completed: row.completed ? JSON.parse(row.completed) : null,
32843
- in_progress: row.in_progress ? JSON.parse(row.in_progress) : null,
32844
- blockers: row.blockers ? JSON.parse(row.blockers) : null,
32845
- next_steps: row.next_steps ? JSON.parse(row.next_steps) : null
32846
- };
32847
- }
32848
- function listHandoffs(projectId, limit = 10, db) {
32849
- const d = db || getDatabase();
32850
- if (projectId) {
32851
- return d.query("SELECT * FROM handoffs WHERE project_id = ? ORDER BY rowid DESC LIMIT ?").all(projectId, limit).map(rowToHandoff);
32852
- }
32853
- return d.query("SELECT * FROM handoffs ORDER BY rowid DESC LIMIT ?").all(limit).map(rowToHandoff);
32854
- }
32855
- function getLatestHandoff(agentId, projectId, db) {
32856
- const d = db || getDatabase();
32857
- let query = "SELECT * FROM handoffs WHERE 1=1";
32858
- const params = [];
32859
- if (agentId) {
32860
- query += " AND agent_id = ?";
32861
- params.push(agentId);
32862
- }
32863
- if (projectId) {
32864
- query += " AND project_id = ?";
32865
- params.push(projectId);
32866
- }
32867
- query += " ORDER BY rowid DESC LIMIT 1";
32868
- const row = d.query(query).get(...params);
32869
- return row ? rowToHandoff(row) : null;
32870
- }
32871
-
32872
- // src/cli/commands/query-commands.ts
34127
+ init_handoffs();
32873
34128
  init_recurrence();
32874
34129
  init_helpers();
34130
+ import chalk7 from "chalk";
32875
34131
  function registerQueryCommands(program2) {
32876
34132
  program2.command("next").description("Show the best pending task to work on next").option("--agent <id>", "Prefer tasks assigned to this agent").option("--project <id>", "Filter to project").option("-j, --json", "Output as JSON").action(async (opts) => {
32877
34133
  const globalOpts = program2.opts();
@@ -33160,7 +34416,7 @@ No task claimed (nothing available).`));
33160
34416
  const globalOpts = program2.opts();
33161
34417
  const resolvedId = resolveTaskId(id);
33162
34418
  const db = getDatabase();
33163
- const task = getTask2(resolvedId, db);
34419
+ const task = getTask(resolvedId, db);
33164
34420
  if (!task) {
33165
34421
  console.error(chalk7.red(`Task not found: ${id}`));
33166
34422
  process.exit(1);
@@ -33180,7 +34436,7 @@ No task claimed (nothing available).`));
33180
34436
  const globalOpts = program2.opts();
33181
34437
  const resolvedId = resolveTaskId(id);
33182
34438
  const db = getDatabase();
33183
- const task = getTask2(resolvedId, db);
34439
+ const task = getTask(resolvedId, db);
33184
34440
  if (!task) {
33185
34441
  console.error(chalk7.red(`Task not found: ${id}`));
33186
34442
  process.exit(1);
@@ -33200,7 +34456,7 @@ No task claimed (nothing available).`));
33200
34456
  const globalOpts = program2.opts();
33201
34457
  const resolvedId = resolveTaskId(id);
33202
34458
  const db = getDatabase();
33203
- const task = getTask2(resolvedId, db);
34459
+ const task = getTask(resolvedId, db);
33204
34460
  if (!task) {
33205
34461
  console.error(chalk7.red(`Task not found: ${id}`));
33206
34462
  process.exit(1);
@@ -33221,7 +34477,7 @@ No task claimed (nothing available).`));
33221
34477
  const globalOpts = program2.opts();
33222
34478
  const resolvedId = resolveTaskId(id);
33223
34479
  const db = getDatabase();
33224
- const task = getTask2(resolvedId, db);
34480
+ const task = getTask(resolvedId, db);
33225
34481
  if (!task) {
33226
34482
  console.error(chalk7.red(`Task not found: ${id}`));
33227
34483
  process.exit(1);
@@ -34384,7 +35640,7 @@ init_tasks();
34384
35640
  init_helpers();
34385
35641
  import chalk9 from "chalk";
34386
35642
  import { execSync as execSync3 } from "child_process";
34387
- import { existsSync as existsSync9, readFileSync as readFileSync8, writeFileSync as writeFileSync5, mkdirSync as mkdirSync5, chmodSync } from "fs";
35643
+ import { existsSync as existsSync10, readFileSync as readFileSync8, writeFileSync as writeFileSync5, mkdirSync as mkdirSync5, chmodSync } from "fs";
34388
35644
  import { dirname as dirname7, join as join13 } from "path";
34389
35645
  var HOME2 = process.env["HOME"] || process.env["USERPROFILE"] || "~";
34390
35646
  function getMcpBinaryPath() {
@@ -34394,12 +35650,12 @@ function getMcpBinaryPath() {
34394
35650
  return p;
34395
35651
  } catch {}
34396
35652
  const bunBin = join13(HOME2, ".bun", "bin", "todos-mcp");
34397
- if (existsSync9(bunBin))
35653
+ if (existsSync10(bunBin))
34398
35654
  return bunBin;
34399
35655
  return "todos-mcp";
34400
35656
  }
34401
35657
  function readJsonFile2(path) {
34402
- if (!existsSync9(path))
35658
+ if (!existsSync10(path))
34403
35659
  return {};
34404
35660
  try {
34405
35661
  return JSON.parse(readFileSync8(path, "utf-8"));
@@ -34409,19 +35665,19 @@ function readJsonFile2(path) {
34409
35665
  }
34410
35666
  function writeJsonFile2(path, data) {
34411
35667
  const dir = dirname7(path);
34412
- if (!existsSync9(dir))
35668
+ if (!existsSync10(dir))
34413
35669
  mkdirSync5(dir, { recursive: true });
34414
35670
  writeFileSync5(path, JSON.stringify(data, null, 2) + `
34415
35671
  `);
34416
35672
  }
34417
35673
  function readTomlFile(path) {
34418
- if (!existsSync9(path))
35674
+ if (!existsSync10(path))
34419
35675
  return "";
34420
35676
  return readFileSync8(path, "utf-8");
34421
35677
  }
34422
35678
  function writeTomlFile(path, content) {
34423
35679
  const dir = dirname7(path);
34424
- if (!existsSync9(dir))
35680
+ if (!existsSync10(dir))
34425
35681
  mkdirSync5(dir, { recursive: true });
34426
35682
  writeFileSync5(path, content);
34427
35683
  }
@@ -34565,7 +35821,7 @@ function registerMcpHooksCommands(program2) {
34565
35821
  todosBin = p;
34566
35822
  } catch {}
34567
35823
  const hooksDir = join13(process.cwd(), ".claude", "hooks");
34568
- if (!existsSync9(hooksDir))
35824
+ if (!existsSync10(hooksDir))
34569
35825
  mkdirSync5(hooksDir, { recursive: true });
34570
35826
  const hookScript = `#!/usr/bin/env bash
34571
35827
  # Auto-generated by: todos hooks install
@@ -34687,7 +35943,7 @@ exit 0
34687
35943
  const gitDir = execSync3("git rev-parse --git-dir", { encoding: "utf-8" }).trim();
34688
35944
  const hookPath = `${gitDir}/hooks/post-commit`;
34689
35945
  const marker = "# todos-auto-link";
34690
- if (existsSync9(hookPath)) {
35946
+ if (existsSync10(hookPath)) {
34691
35947
  const existing = readFileSync8(hookPath, "utf-8");
34692
35948
  if (existing.includes(marker)) {
34693
35949
  console.log(chalk9.yellow("Hook already installed."));
@@ -34715,7 +35971,7 @@ $(dirname "$0")/../../scripts/post-commit-hook.sh
34715
35971
  const gitDir = execSync3("git rev-parse --git-dir", { encoding: "utf-8" }).trim();
34716
35972
  const hookPath = `${gitDir}/hooks/post-commit`;
34717
35973
  const marker = "# todos-auto-link";
34718
- if (!existsSync9(hookPath)) {
35974
+ if (!existsSync10(hookPath)) {
34719
35975
  console.log(chalk9.dim("No post-commit hook found."));
34720
35976
  return;
34721
35977
  }