@hasna/todos 0.11.33 → 0.11.35

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/README.md +12 -0
  2. package/dist/cli/commands/agent-commands.d.ts.map +1 -1
  3. package/dist/cli/commands/api-key-commands.d.ts +3 -0
  4. package/dist/cli/commands/api-key-commands.d.ts.map +1 -0
  5. package/dist/cli/commands/config-serve-commands.d.ts.map +1 -1
  6. package/dist/cli/index.js +2343 -607
  7. package/dist/db/agent-names.d.ts +23 -0
  8. package/dist/db/agent-names.d.ts.map +1 -0
  9. package/dist/db/agents.d.ts +2 -0
  10. package/dist/db/agents.d.ts.map +1 -1
  11. package/dist/db/api-keys.d.ts +28 -0
  12. package/dist/db/api-keys.d.ts.map +1 -0
  13. package/dist/db/comments.d.ts +3 -0
  14. package/dist/db/comments.d.ts.map +1 -1
  15. package/dist/db/migrations.d.ts.map +1 -1
  16. package/dist/db/schema.d.ts.map +1 -1
  17. package/dist/db/task-crud.d.ts.map +1 -1
  18. package/dist/db/task-lifecycle.d.ts +1 -0
  19. package/dist/db/task-lifecycle.d.ts.map +1 -1
  20. package/dist/db/task-relations.d.ts +24 -0
  21. package/dist/db/task-relations.d.ts.map +1 -1
  22. package/dist/db/tasks.d.ts +1 -1
  23. package/dist/db/tasks.d.ts.map +1 -1
  24. package/dist/index.d.ts +3 -1
  25. package/dist/index.d.ts.map +1 -1
  26. package/dist/index.js +400 -13
  27. package/dist/mcp/index.d.ts.map +1 -1
  28. package/dist/mcp/index.js +2585 -536
  29. package/dist/mcp/tools/agents.d.ts.map +1 -1
  30. package/dist/mcp/tools/task-adv-tools.d.ts.map +1 -1
  31. package/dist/mcp/tools/task-auto-tools.d.ts.map +1 -1
  32. package/dist/mcp/tools/task-crud.d.ts.map +1 -1
  33. package/dist/mcp/tools/task-meta-tools.d.ts.map +1 -1
  34. package/dist/mcp/tools/task-project-tools.d.ts.map +1 -1
  35. package/dist/mcp/tools/task-workflow-tools.d.ts.map +1 -1
  36. package/dist/server/index.js +352 -42
  37. package/dist/server/routes.d.ts.map +1 -1
  38. package/dist/server/serve.d.ts.map +1 -1
  39. package/dist/types/index.d.ts +21 -0
  40. package/dist/types/index.d.ts.map +1 -1
  41. package/package.json +1 -1
package/dist/cli/index.js CHANGED
@@ -2888,6 +2888,42 @@ 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);
2911
+ `,
2912
+ `
2913
+ CREATE TABLE IF NOT EXISTS api_keys (
2914
+ id TEXT PRIMARY KEY,
2915
+ name TEXT NOT NULL,
2916
+ key_hash TEXT NOT NULL UNIQUE,
2917
+ prefix TEXT NOT NULL UNIQUE,
2918
+ permissions TEXT NOT NULL DEFAULT '["*"]',
2919
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
2920
+ last_used_at TEXT,
2921
+ expires_at TEXT,
2922
+ revoked_at TEXT
2923
+ );
2924
+ CREATE INDEX IF NOT EXISTS idx_api_keys_prefix ON api_keys(prefix);
2925
+ CREATE INDEX IF NOT EXISTS idx_api_keys_active ON api_keys(revoked_at, expires_at);
2926
+ INSERT OR IGNORE INTO _migrations (id) VALUES (50);
2891
2927
  `
2892
2928
  ];
2893
2929
  });
@@ -3286,6 +3322,20 @@ function ensureSchema(db) {
3286
3322
  ensureIndex("CREATE INDEX IF NOT EXISTS idx_cycles_dates ON cycles(start_date, end_date)");
3287
3323
  ensureColumn("tasks", "cycle_id", "TEXT REFERENCES cycles(id) ON DELETE SET NULL");
3288
3324
  ensureIndex("CREATE INDEX IF NOT EXISTS idx_tasks_cycle ON tasks(cycle_id) WHERE cycle_id IS NOT NULL");
3325
+ ensureTable("api_keys", `
3326
+ CREATE TABLE api_keys (
3327
+ id TEXT PRIMARY KEY,
3328
+ name TEXT NOT NULL,
3329
+ key_hash TEXT NOT NULL UNIQUE,
3330
+ prefix TEXT NOT NULL UNIQUE,
3331
+ permissions TEXT NOT NULL DEFAULT '["*"]',
3332
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
3333
+ last_used_at TEXT,
3334
+ expires_at TEXT,
3335
+ revoked_at TEXT
3336
+ )`);
3337
+ ensureIndex("CREATE INDEX IF NOT EXISTS idx_api_keys_prefix ON api_keys(prefix)");
3338
+ ensureIndex("CREATE INDEX IF NOT EXISTS idx_api_keys_active ON api_keys(revoked_at, expires_at)");
3289
3339
  }
3290
3340
  function backfillTaskTags(db) {
3291
3341
  try {
@@ -4675,8 +4725,8 @@ function createTask(input, db) {
4675
4725
  let id = uuid();
4676
4726
  for (let attempt = 0;attempt < 3; attempt++) {
4677
4727
  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, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
4728
+ 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)
4729
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
4680
4730
  id,
4681
4731
  null,
4682
4732
  input.project_id || null,
@@ -4698,6 +4748,10 @@ function createTask(input, db) {
4698
4748
  timestamp,
4699
4749
  input.due_at || null,
4700
4750
  input.estimated_minutes || null,
4751
+ input.confidence ?? null,
4752
+ input.retry_count ?? 0,
4753
+ input.max_retries ?? 3,
4754
+ input.retry_after ?? null,
4701
4755
  input.requires_approval ? 1 : 0,
4702
4756
  null,
4703
4757
  null,
@@ -4722,11 +4776,11 @@ function createTask(input, db) {
4722
4776
  if (tags.length > 0) {
4723
4777
  insertTaskTags(id, tags, d);
4724
4778
  }
4725
- const task = getTask2(id, d);
4779
+ const task = getTask(id, d);
4726
4780
  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
4781
  return task;
4728
4782
  }
4729
- function getTask2(id, db) {
4783
+ function getTask(id, db) {
4730
4784
  const d = db || getDatabase();
4731
4785
  const row = d.query("SELECT * FROM tasks WHERE id = ?").get(id);
4732
4786
  if (!row)
@@ -4735,7 +4789,7 @@ function getTask2(id, db) {
4735
4789
  }
4736
4790
  function getTaskWithRelations(id, db) {
4737
4791
  const d = db || getDatabase();
4738
- const task = getTask2(id, d);
4792
+ const task = getTask(id, d);
4739
4793
  if (!task)
4740
4794
  return null;
4741
4795
  const subtaskRows = d.query("SELECT * FROM tasks WHERE parent_id = ? ORDER BY created_at").all(id);
@@ -4749,7 +4803,7 @@ function getTaskWithRelations(id, db) {
4749
4803
  WHERE td.depends_on = ?`).all(id);
4750
4804
  const blocked_by = blockedByRows.map(rowToTask);
4751
4805
  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;
4806
+ const parent = task.parent_id ? getTask(task.parent_id, d) : null;
4753
4807
  const checklist = getChecklist(id, d);
4754
4808
  return {
4755
4809
  ...task,
@@ -4936,14 +4990,16 @@ function countTasks(filter = {}, db) {
4936
4990
  }
4937
4991
  function updateTask(id, input, db) {
4938
4992
  const d = db || getDatabase();
4939
- const task = getTask2(id, d);
4993
+ const task = getTask(id, d);
4940
4994
  if (!task)
4941
4995
  throw new TaskNotFoundError(id);
4942
4996
  if (task.version !== input.version) {
4943
4997
  throw new VersionConflictError(id, input.version, task.version);
4944
4998
  }
4999
+ const timestamp = now();
5000
+ const completionTimestamp = input.completed_at ?? timestamp;
4945
5001
  const sets = ["version = version + 1", "updated_at = ?"];
4946
- const params = [now()];
5002
+ const params = [timestamp];
4947
5003
  if (input.title !== undefined) {
4948
5004
  sets.push("title = ?");
4949
5005
  params.push(input.title);
@@ -4960,13 +5016,17 @@ function updateTask(id, input, db) {
4960
5016
  params.push(input.status);
4961
5017
  if (input.status === "completed") {
4962
5018
  sets.push("completed_at = ?");
4963
- params.push(now());
5019
+ params.push(completionTimestamp);
4964
5020
  }
4965
5021
  }
4966
5022
  if (input.priority !== undefined) {
4967
5023
  sets.push("priority = ?");
4968
5024
  params.push(input.priority);
4969
5025
  }
5026
+ if (input.project_id !== undefined) {
5027
+ sets.push("project_id = ?");
5028
+ params.push(input.project_id);
5029
+ }
4970
5030
  if (input.assigned_to !== undefined) {
4971
5031
  sets.push("assigned_to = ?");
4972
5032
  params.push(input.assigned_to);
@@ -4995,6 +5055,30 @@ function updateTask(id, input, db) {
4995
5055
  sets.push("estimated_minutes = ?");
4996
5056
  params.push(input.estimated_minutes);
4997
5057
  }
5058
+ if (input.actual_minutes !== undefined) {
5059
+ sets.push("actual_minutes = ?");
5060
+ params.push(input.actual_minutes);
5061
+ }
5062
+ if (input.completed_at !== undefined && input.status !== "completed") {
5063
+ sets.push("completed_at = ?");
5064
+ params.push(input.completed_at);
5065
+ }
5066
+ if (input.confidence !== undefined) {
5067
+ sets.push("confidence = ?");
5068
+ params.push(input.confidence);
5069
+ }
5070
+ if (input.retry_count !== undefined) {
5071
+ sets.push("retry_count = ?");
5072
+ params.push(input.retry_count);
5073
+ }
5074
+ if (input.max_retries !== undefined) {
5075
+ sets.push("max_retries = ?");
5076
+ params.push(input.max_retries);
5077
+ }
5078
+ if (input.retry_after !== undefined) {
5079
+ sets.push("retry_after = ?");
5080
+ params.push(input.retry_after);
5081
+ }
4998
5082
  if (input.requires_approval !== undefined) {
4999
5083
  sets.push("requires_approval = ?");
5000
5084
  params.push(input.requires_approval ? 1 : 0);
@@ -5016,7 +5100,7 @@ function updateTask(id, input, db) {
5016
5100
  params.push(id, input.version);
5017
5101
  const result = d.run(`UPDATE tasks SET ${sets.join(", ")} WHERE id = ? AND version = ?`, params);
5018
5102
  if (result.changes === 0) {
5019
- const current = getTask2(id, d);
5103
+ const current = getTask(id, d);
5020
5104
  throw new VersionConflictError(id, input.version, current?.version ?? -1);
5021
5105
  }
5022
5106
  if (input.tags !== undefined) {
@@ -5045,11 +5129,16 @@ function updateTask(id, input, db) {
5045
5129
  tags: input.tags ?? task.tags,
5046
5130
  metadata: input.metadata ?? task.metadata,
5047
5131
  version: task.version + 1,
5048
- updated_at: now(),
5049
- completed_at: input.status === "completed" ? now() : task.completed_at,
5132
+ updated_at: timestamp,
5133
+ completed_at: input.status === "completed" ? completionTimestamp : input.completed_at !== undefined ? input.completed_at : task.completed_at,
5134
+ actual_minutes: input.actual_minutes ?? task.actual_minutes,
5135
+ confidence: input.confidence !== undefined ? input.confidence : task.confidence,
5136
+ retry_count: input.retry_count ?? task.retry_count,
5137
+ max_retries: input.max_retries ?? task.max_retries,
5138
+ retry_after: input.retry_after !== undefined ? input.retry_after : task.retry_after,
5050
5139
  requires_approval: input.requires_approval !== undefined ? input.requires_approval : task.requires_approval,
5051
5140
  approved_by: input.approved_by ?? task.approved_by,
5052
- approved_at: input.approved_by ? now() : task.approved_at
5141
+ approved_at: input.approved_by ? timestamp : task.approved_at
5053
5142
  };
5054
5143
  }
5055
5144
  function deleteTask(id, db) {
@@ -5622,9 +5711,9 @@ var init_templates = __esm(() => {
5622
5711
  // src/db/task-graph.ts
5623
5712
  function addDependency(taskId, dependsOn, db) {
5624
5713
  const d = db || getDatabase();
5625
- if (!getTask2(taskId, d))
5714
+ if (!getTask(taskId, d))
5626
5715
  throw new TaskNotFoundError(taskId);
5627
- if (!getTask2(dependsOn, d))
5716
+ if (!getTask(dependsOn, d))
5628
5717
  throw new TaskNotFoundError(dependsOn);
5629
5718
  if (wouldCreateCycle(taskId, dependsOn, d)) {
5630
5719
  throw new DependencyCycleError(taskId, dependsOn);
@@ -5646,7 +5735,7 @@ function getTaskDependents(taskId, db) {
5646
5735
  }
5647
5736
  function cloneTask(taskId, overrides, db) {
5648
5737
  const d = db || getDatabase();
5649
- const source = getTask2(taskId, d);
5738
+ const source = getTask(taskId, d);
5650
5739
  if (!source)
5651
5740
  throw new TaskNotFoundError(taskId);
5652
5741
  const input = {
@@ -5669,13 +5758,13 @@ function cloneTask(taskId, overrides, db) {
5669
5758
  }
5670
5759
  function getTaskGraph(taskId, direction = "both", db) {
5671
5760
  const d = db || getDatabase();
5672
- const task = getTask2(taskId, d);
5761
+ const task = getTask(taskId, d);
5673
5762
  if (!task)
5674
5763
  throw new TaskNotFoundError(taskId);
5675
5764
  function toNode(t) {
5676
5765
  const deps = getTaskDependencies(t.id, d);
5677
5766
  const hasUnfinishedDeps = deps.some((dep) => {
5678
- const depTask = getTask2(dep.depends_on, d);
5767
+ const depTask = getTask(dep.depends_on, d);
5679
5768
  return depTask && depTask.status !== "completed";
5680
5769
  });
5681
5770
  return { id: t.id, short_id: t.short_id, title: t.title, status: t.status, priority: t.priority, is_blocked: hasUnfinishedDeps };
@@ -5686,7 +5775,7 @@ function getTaskGraph(taskId, direction = "both", db) {
5686
5775
  visited.add(id);
5687
5776
  const deps = d.query("SELECT depends_on FROM task_dependencies WHERE task_id = ?").all(id);
5688
5777
  return deps.map((dep) => {
5689
- const depTask = getTask2(dep.depends_on, d);
5778
+ const depTask = getTask(dep.depends_on, d);
5690
5779
  if (!depTask)
5691
5780
  return null;
5692
5781
  return { task: toNode(depTask), depends_on: buildUp(dep.depends_on, visited), blocks: [] };
@@ -5698,7 +5787,7 @@ function getTaskGraph(taskId, direction = "both", db) {
5698
5787
  visited.add(id);
5699
5788
  const dependents = d.query("SELECT task_id FROM task_dependencies WHERE depends_on = ?").all(id);
5700
5789
  return dependents.map((dep) => {
5701
- const depTask = getTask2(dep.task_id, d);
5790
+ const depTask = getTask(dep.task_id, d);
5702
5791
  if (!depTask)
5703
5792
  return null;
5704
5793
  return { task: toNode(depTask), depends_on: [], blocks: buildDown(dep.task_id, visited) };
@@ -5711,7 +5800,7 @@ function getTaskGraph(taskId, direction = "both", db) {
5711
5800
  }
5712
5801
  function moveTask(taskId, target, db) {
5713
5802
  const d = db || getDatabase();
5714
- const task = getTask2(taskId, d);
5803
+ const task = getTask(taskId, d);
5715
5804
  if (!task)
5716
5805
  throw new TaskNotFoundError(taskId);
5717
5806
  const sets = ["updated_at = ?", "version = version + 1"];
@@ -5730,7 +5819,7 @@ function moveTask(taskId, target, db) {
5730
5819
  }
5731
5820
  params.push(taskId);
5732
5821
  d.run(`UPDATE tasks SET ${sets.join(", ")} WHERE id = ?`, params);
5733
- return getTask2(taskId, d);
5822
+ return getTask(taskId, d);
5734
5823
  }
5735
5824
  function wouldCreateCycle(taskId, dependsOn, db) {
5736
5825
  const visited = new Set;
@@ -5763,7 +5852,7 @@ function getBlockingDeps(id, db) {
5763
5852
  return [];
5764
5853
  const blocking = [];
5765
5854
  for (const dep of deps) {
5766
- const task = getTask2(dep.depends_on, d);
5855
+ const task = getTask(dep.depends_on, d);
5767
5856
  if (task && task.status !== "completed")
5768
5857
  blocking.push(task);
5769
5858
  }
@@ -5771,7 +5860,7 @@ function getBlockingDeps(id, db) {
5771
5860
  }
5772
5861
  function startTask(id, agentId, db) {
5773
5862
  const d = db || getDatabase();
5774
- const task = getTask2(id, d);
5863
+ const task = getTask(id, d);
5775
5864
  if (!task)
5776
5865
  throw new TaskNotFoundError(id);
5777
5866
  const blocking = getBlockingDeps(id, d);
@@ -5794,7 +5883,7 @@ function startTask(id, agentId, db) {
5794
5883
  }
5795
5884
  function completeTask(id, agentId, db, options) {
5796
5885
  const d = db || getDatabase();
5797
- const task = getTask2(id, d);
5886
+ const task = getTask(id, d);
5798
5887
  if (!task)
5799
5888
  throw new TaskNotFoundError(id);
5800
5889
  if (agentId && task.locked_by && task.locked_by !== agentId && !isLockExpired(task.locked_at)) {
@@ -5810,14 +5899,14 @@ function completeTask(id, agentId, db, options) {
5810
5899
  completionMeta._completion = { confidence: options.confidence };
5811
5900
  }
5812
5901
  const hasMeta = Object.keys(completionMeta).length > 0;
5813
- const timestamp = now();
5902
+ const timestamp = options?.completed_at || now();
5814
5903
  const confidence = options?.confidence !== undefined ? options.confidence : null;
5815
5904
  const tx = d.transaction(() => {
5816
5905
  if (hasMeta) {
5817
5906
  const meta2 = { ...task.metadata, ...completionMeta };
5818
5907
  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
5908
  if (metaResult.changes === 0) {
5820
- const current = getTask2(id, d);
5909
+ const current = getTask(id, d);
5821
5910
  throw new VersionConflictError(id, task.version, current?.version ?? -1);
5822
5911
  }
5823
5912
  }
@@ -5874,7 +5963,7 @@ function completeTask(id, agentId, db, options) {
5874
5963
  }
5875
5964
  function lockTask(id, agentId, db) {
5876
5965
  const d = db || getDatabase();
5877
- const task = getTask2(id, d);
5966
+ const task = getTask(id, d);
5878
5967
  if (!task)
5879
5968
  throw new TaskNotFoundError(id);
5880
5969
  if (task.locked_by === agentId && !isLockExpired(task.locked_at)) {
@@ -5885,7 +5974,7 @@ function lockTask(id, agentId, db) {
5885
5974
  const result = d.run(`UPDATE tasks SET locked_by = ?, locked_at = ?, version = version + 1, updated_at = ?
5886
5975
  WHERE id = ? AND (locked_by IS NULL OR locked_by = ? OR locked_at < ?)`, [agentId, timestamp, timestamp, id, agentId, cutoff]);
5887
5976
  if (result.changes === 0) {
5888
- const current = getTask2(id, d);
5977
+ const current = getTask(id, d);
5889
5978
  if (!current)
5890
5979
  throw new TaskNotFoundError(id);
5891
5980
  if (current.locked_by && !isLockExpired(current.locked_at)) {
@@ -5901,7 +5990,7 @@ function lockTask(id, agentId, db) {
5901
5990
  }
5902
5991
  function unlockTask(id, agentId, db) {
5903
5992
  const d = db || getDatabase();
5904
- const task = getTask2(id, d);
5993
+ const task = getTask(id, d);
5905
5994
  if (!task)
5906
5995
  throw new TaskNotFoundError(id);
5907
5996
  if (agentId && task.locked_by && task.locked_by !== agentId) {
@@ -6002,7 +6091,7 @@ function getTasksChangedSince(since, filters, db) {
6002
6091
  }
6003
6092
  function failTask(id, agentId, reason, options, db) {
6004
6093
  const d = db || getDatabase();
6005
- const task = getTask2(id, d);
6094
+ const task = getTask(id, d);
6006
6095
  if (!task)
6007
6096
  throw new TaskNotFoundError(id);
6008
6097
  const meta = {
@@ -6201,7 +6290,7 @@ function getStatus(filters, agentId, options, db) {
6201
6290
  }
6202
6291
  function decomposeTasks(parentId, subtasks, options, db) {
6203
6292
  const d = db || getDatabase();
6204
- const parent = getTask2(parentId, d);
6293
+ const parent = getTask(parentId, d);
6205
6294
  if (!parent)
6206
6295
  throw new TaskNotFoundError(parentId);
6207
6296
  const created = [];
@@ -6232,7 +6321,7 @@ function decomposeTasks(parentId, subtasks, options, db) {
6232
6321
  function setTaskStatus(id, status, _agentId, db) {
6233
6322
  const d = db || getDatabase();
6234
6323
  for (let attempt = 0;attempt < 3; attempt++) {
6235
- const task = getTask2(id, d);
6324
+ const task = getTask(id, d);
6236
6325
  if (!task)
6237
6326
  throw new TaskNotFoundError(id);
6238
6327
  if (task.status === status)
@@ -6250,7 +6339,7 @@ function setTaskStatus(id, status, _agentId, db) {
6250
6339
  function setTaskPriority(id, priority, _agentId, db) {
6251
6340
  const d = db || getDatabase();
6252
6341
  for (let attempt = 0;attempt < 3; attempt++) {
6253
- const task = getTask2(id, d);
6342
+ const task = getTask(id, d);
6254
6343
  if (!task)
6255
6344
  throw new TaskNotFoundError(id);
6256
6345
  if (task.priority === priority)
@@ -6358,7 +6447,7 @@ function bulkUpdateTasks(taskIds, updates, db) {
6358
6447
  const tx = d.transaction(() => {
6359
6448
  for (const id of taskIds) {
6360
6449
  try {
6361
- const task = getTask2(id, d);
6450
+ const task = getTask(id, d);
6362
6451
  if (!task) {
6363
6452
  failed.push({ id, error: "Task not found" });
6364
6453
  continue;
@@ -6373,6 +6462,30 @@ function bulkUpdateTasks(taskIds, updates, db) {
6373
6462
  tx();
6374
6463
  return { updated, failed };
6375
6464
  }
6465
+ function bulkDeleteTasks(taskIds, force = false, db) {
6466
+ const d = db || getDatabase();
6467
+ let deleted = 0;
6468
+ let skipped = 0;
6469
+ const failed = [];
6470
+ const tx = d.transaction(() => {
6471
+ for (const id of taskIds) {
6472
+ try {
6473
+ const childCount = d.query("SELECT COUNT(*) as count FROM tasks WHERE parent_id = ?").get(id);
6474
+ if (!force && childCount.count > 0) {
6475
+ skipped++;
6476
+ continue;
6477
+ }
6478
+ const result = d.run("DELETE FROM tasks WHERE id = ?", [id]);
6479
+ if (result.changes > 0)
6480
+ deleted++;
6481
+ } catch (e) {
6482
+ failed.push({ id, error: e instanceof Error ? e.message : String(e) });
6483
+ }
6484
+ }
6485
+ });
6486
+ tx();
6487
+ return { deleted, skipped, failed };
6488
+ }
6376
6489
  function archiveTasks(options, db) {
6377
6490
  const d = db || getDatabase();
6378
6491
  const conditions = ["archived_at IS NULL"];
@@ -6397,10 +6510,33 @@ function archiveTasks(options, db) {
6397
6510
  const result = d.run(`UPDATE tasks SET archived_at = ? WHERE ${conditions.join(" AND ")}`, [ts, ...params]);
6398
6511
  return { archived: result.changes };
6399
6512
  }
6513
+ function archiveCompletedTasks(days = 7, projectId, db) {
6514
+ return archiveTasks({
6515
+ project_id: projectId,
6516
+ older_than_days: days,
6517
+ status: ["completed"]
6518
+ }, db).archived;
6519
+ }
6520
+ function getArchivedTasks(opts = {}, db) {
6521
+ const d = db || getDatabase();
6522
+ const conditions = ["archived_at IS NOT NULL"];
6523
+ const params = [];
6524
+ if (opts.project_id) {
6525
+ conditions.push("project_id = ?");
6526
+ params.push(opts.project_id);
6527
+ }
6528
+ let limitClause = "";
6529
+ if (opts.limit) {
6530
+ limitClause = " LIMIT ?";
6531
+ params.push(opts.limit);
6532
+ }
6533
+ const rows = d.query(`SELECT * FROM tasks WHERE ${conditions.join(" AND ")} ORDER BY archived_at DESC${limitClause}`).all(...params);
6534
+ return rows.map(rowToTask);
6535
+ }
6400
6536
  function unarchiveTask(id, db) {
6401
6537
  const d = db || getDatabase();
6402
6538
  d.run("UPDATE tasks SET archived_at = NULL WHERE id = ?", [id]);
6403
- return getTask2(id, d);
6539
+ return getTask(id, d);
6404
6540
  }
6405
6541
  function getOverdueTasks(projectId, db) {
6406
6542
  const d = db || getDatabase();
@@ -6415,6 +6551,80 @@ function getOverdueTasks(projectId, db) {
6415
6551
  const rows = d.query(query).all(...params);
6416
6552
  return rows.map(rowToTask);
6417
6553
  }
6554
+ function notifyUpcomingDeadlines(opts = {}, db) {
6555
+ const d = db || getDatabase();
6556
+ const hours = opts.hours ?? 24;
6557
+ const start = new Date().toISOString();
6558
+ const end = new Date(Date.now() + hours * 60 * 60 * 1000).toISOString();
6559
+ const conditions = [
6560
+ "archived_at IS NULL",
6561
+ "due_at IS NOT NULL",
6562
+ "due_at >= ?",
6563
+ "due_at <= ?",
6564
+ "status NOT IN ('completed', 'cancelled', 'failed')"
6565
+ ];
6566
+ const params = [start, end];
6567
+ if (opts.project_id) {
6568
+ conditions.push("project_id = ?");
6569
+ params.push(opts.project_id);
6570
+ }
6571
+ if (opts.agent_id) {
6572
+ conditions.push("assigned_to = ?");
6573
+ params.push(opts.agent_id);
6574
+ }
6575
+ const rows = d.query(`SELECT * FROM tasks WHERE ${conditions.join(" AND ")} ORDER BY due_at ASC`).all(...params);
6576
+ return rows.map(rowToTask);
6577
+ }
6578
+ function getBlockedTasks(projectId, db) {
6579
+ const d = db || getDatabase();
6580
+ const params = [];
6581
+ let projectClause = "";
6582
+ if (projectId) {
6583
+ projectClause = "AND t.project_id = ?";
6584
+ params.push(projectId);
6585
+ }
6586
+ const rows = d.query(`
6587
+ SELECT t.*, GROUP_CONCAT(dep.id) AS blocked_by_ids
6588
+ FROM tasks t
6589
+ JOIN task_dependencies td ON td.task_id = t.id
6590
+ JOIN tasks dep ON dep.id = td.depends_on
6591
+ WHERE t.archived_at IS NULL
6592
+ AND t.status NOT IN ('completed', 'cancelled', 'failed')
6593
+ AND dep.status NOT IN ('completed', 'cancelled')
6594
+ ${projectClause}
6595
+ GROUP BY t.id
6596
+ ORDER BY t.priority DESC, t.created_at DESC
6597
+ `).all(...params);
6598
+ return rows.map((row) => ({
6599
+ ...rowToTask(row),
6600
+ blocked_by: row.blocked_by_ids ? row.blocked_by_ids.split(",") : []
6601
+ }));
6602
+ }
6603
+ function getBlockingTasks(projectId, db) {
6604
+ const d = db || getDatabase();
6605
+ const params = [];
6606
+ let projectClause = "";
6607
+ if (projectId) {
6608
+ projectClause = "AND blocker.project_id = ?";
6609
+ params.push(projectId);
6610
+ }
6611
+ const rows = d.query(`
6612
+ SELECT blocker.*, COUNT(DISTINCT blocked.id) AS blocking_count
6613
+ FROM tasks blocker
6614
+ JOIN task_dependencies td ON td.depends_on = blocker.id
6615
+ JOIN tasks blocked ON blocked.id = td.task_id
6616
+ WHERE blocker.archived_at IS NULL
6617
+ AND blocker.status NOT IN ('completed', 'cancelled', 'failed')
6618
+ AND blocked.status NOT IN ('completed', 'cancelled', 'failed')
6619
+ ${projectClause}
6620
+ GROUP BY blocker.id
6621
+ ORDER BY blocking_count DESC, blocker.created_at DESC
6622
+ `).all(...params);
6623
+ return rows.map((row) => ({
6624
+ ...rowToTask(row),
6625
+ blocking_count: row.blocking_count
6626
+ }));
6627
+ }
6418
6628
  function logTime(input, db) {
6419
6629
  const d = db || getDatabase();
6420
6630
  const id = uuid();
@@ -6495,6 +6705,7 @@ __export(exports_tasks, {
6495
6705
  removeDependency: () => removeDependency,
6496
6706
  redistributeStaleTasks: () => redistributeStaleTasks,
6497
6707
  notifyWatchers: () => notifyWatchers,
6708
+ notifyUpcomingDeadlines: () => notifyUpcomingDeadlines,
6498
6709
  moveTask: () => moveTask,
6499
6710
  logTime: () => logTime,
6500
6711
  logCost: () => logCost,
@@ -6510,12 +6721,15 @@ __export(exports_tasks, {
6510
6721
  getTaskGraph: () => getTaskGraph,
6511
6722
  getTaskDependents: () => getTaskDependents,
6512
6723
  getTaskDependencies: () => getTaskDependencies,
6513
- getTask: () => getTask2,
6724
+ getTask: () => getTask,
6514
6725
  getStatus: () => getStatus,
6515
6726
  getStaleTasks: () => getStaleTasks,
6516
6727
  getOverdueTasks: () => getOverdueTasks,
6517
6728
  getNextTask: () => getNextTask,
6729
+ getBlockingTasks: () => getBlockingTasks,
6518
6730
  getBlockingDeps: () => getBlockingDeps,
6731
+ getBlockedTasks: () => getBlockedTasks,
6732
+ getArchivedTasks: () => getArchivedTasks,
6519
6733
  getActiveWork: () => getActiveWork,
6520
6734
  failTask: () => failTask,
6521
6735
  deleteTask: () => deleteTask,
@@ -6527,8 +6741,10 @@ __export(exports_tasks, {
6527
6741
  claimOrSteal: () => claimOrSteal,
6528
6742
  claimNextTask: () => claimNextTask,
6529
6743
  bulkUpdateTasks: () => bulkUpdateTasks,
6744
+ bulkDeleteTasks: () => bulkDeleteTasks,
6530
6745
  bulkCreateTasks: () => bulkCreateTasks,
6531
6746
  archiveTasks: () => archiveTasks,
6747
+ archiveCompletedTasks: () => archiveCompletedTasks,
6532
6748
  addDependency: () => addDependency
6533
6749
  });
6534
6750
  var init_tasks = __esm(() => {
@@ -6934,9 +7150,18 @@ var init_builtin_templates = __esm(() => {
6934
7150
  });
6935
7151
 
6936
7152
  // src/db/comments.ts
7153
+ var exports_comments = {};
7154
+ __export(exports_comments, {
7155
+ updateComment: () => updateComment,
7156
+ logProgress: () => logProgress,
7157
+ listComments: () => listComments,
7158
+ getComment: () => getComment,
7159
+ deleteComment: () => deleteComment,
7160
+ addComment: () => addComment
7161
+ });
6937
7162
  function addComment(input, db) {
6938
7163
  const d = db || getDatabase();
6939
- if (!getTask2(input.task_id, d)) {
7164
+ if (!getTask(input.task_id, d)) {
6940
7165
  throw new TaskNotFoundError(input.task_id);
6941
7166
  }
6942
7167
  const id = uuid();
@@ -6965,6 +7190,15 @@ function listComments(taskId, db) {
6965
7190
  const d = db || getDatabase();
6966
7191
  return d.query("SELECT * FROM task_comments WHERE task_id = ? ORDER BY created_at").all(taskId);
6967
7192
  }
7193
+ function updateComment(id, input, db) {
7194
+ const d = db || getDatabase();
7195
+ d.run("UPDATE task_comments SET content = ? WHERE id = ?", [input.content, id]);
7196
+ const comment = getComment(id, d);
7197
+ if (!comment) {
7198
+ throw new Error(`Comment not found: ${id}`);
7199
+ }
7200
+ return comment;
7201
+ }
6968
7202
  function deleteComment(id, db) {
6969
7203
  const d = db || getDatabase();
6970
7204
  const result = d.run("DELETE FROM task_comments WHERE id = ?", [id]);
@@ -6977,6 +7211,10 @@ var init_comments = __esm(() => {
6977
7211
  });
6978
7212
 
6979
7213
  // src/lib/search.ts
7214
+ var exports_search = {};
7215
+ __export(exports_search, {
7216
+ searchTasks: () => searchTasks
7217
+ });
6980
7218
  function rowToTask2(row) {
6981
7219
  return {
6982
7220
  ...row,
@@ -7293,14 +7531,224 @@ var init_extract = __esm(() => {
7293
7531
  ]);
7294
7532
  });
7295
7533
 
7534
+ // src/db/agent-names.ts
7535
+ function normalizeAgentNameInput(name) {
7536
+ return name.trim().toLowerCase();
7537
+ }
7538
+ function hasGeneratedNumericSuffix(name) {
7539
+ return NUMERIC_SUFFIX_RE.test(normalizeAgentNameInput(name));
7540
+ }
7541
+ function isGenericAgentName(name) {
7542
+ const normalized = normalizeAgentNameInput(name);
7543
+ if (RESERVED_GENERIC_NAMES.has(normalized))
7544
+ return true;
7545
+ for (const generic of RESERVED_GENERIC_NAMES) {
7546
+ if (normalized === `${generic}s`)
7547
+ return true;
7548
+ if (normalized.match(new RegExp(`^${generic}\\d+$`)))
7549
+ return true;
7550
+ if (normalized.match(new RegExp(`^${generic}[-_]\\d+$`)))
7551
+ return true;
7552
+ }
7553
+ return false;
7554
+ }
7555
+ function isBlockedAgentName(name) {
7556
+ const normalized = normalizeAgentNameInput(name);
7557
+ return isGenericAgentName(normalized) || hasGeneratedNumericSuffix(normalized) || !ONE_WORD_NAME_RE.test(normalized);
7558
+ }
7559
+ function suggestAgentNames(existingNames = []) {
7560
+ const existing = new Set([...existingNames].map(normalizeAgentNameInput));
7561
+ return PREFERRED_AGENT_NAMES.filter((name) => !existing.has(name));
7562
+ }
7563
+ function validateAgentName(name, existingNames = []) {
7564
+ const normalized = normalizeAgentNameInput(name);
7565
+ const suggestions = suggestAgentNames(existingNames).slice(0, 5);
7566
+ if (!normalized) {
7567
+ throw new InvalidAgentNameError(name, "choose a real one-word name instead of an empty value", suggestions);
7568
+ }
7569
+ if (/\s/.test(normalized)) {
7570
+ throw new InvalidAgentNameError(name, "use a single word, preferably a Roman or Greek name", suggestions);
7571
+ }
7572
+ if (normalized.length < 3) {
7573
+ throw new InvalidAgentNameError(name, "use a more distinctive name with at least three characters", suggestions);
7574
+ }
7575
+ if (isGenericAgentName(normalized)) {
7576
+ throw new InvalidAgentNameError(name, "generic names like agent, agent-1, assistant, or worker-2 are reserved", suggestions);
7577
+ }
7578
+ if (hasGeneratedNumericSuffix(normalized)) {
7579
+ throw new InvalidAgentNameError(name, "numbered suffix names are not allowed; pick a distinct human-readable name", suggestions);
7580
+ }
7581
+ if (!ONE_WORD_NAME_RE.test(normalized)) {
7582
+ throw new InvalidAgentNameError(name, "use one word made of letters only, preferably a Roman or Greek name", suggestions);
7583
+ }
7584
+ return normalized;
7585
+ }
7586
+ function tableHasColumn(db, table, column) {
7587
+ try {
7588
+ return db.query(`PRAGMA table_info(${table})`).all().some((row) => row.name === column);
7589
+ } catch {
7590
+ return false;
7591
+ }
7592
+ }
7593
+ function updateReferences(db, oldName, newName) {
7594
+ const refs = [
7595
+ ["tasks", "assigned_to"],
7596
+ ["tasks", "agent_id"],
7597
+ ["tasks", "locked_by"],
7598
+ ["tasks", "assigned_by"],
7599
+ ["plans", "agent_id"],
7600
+ ["sessions", "agent_id"],
7601
+ ["task_comments", "agent_id"],
7602
+ ["task_history", "agent_id"],
7603
+ ["webhooks", "agent_id"],
7604
+ ["task_files", "agent_id"],
7605
+ ["task_time_logs", "agent_id"],
7606
+ ["task_watchers", "agent_id"],
7607
+ ["task_checkpoints", "agent_id"],
7608
+ ["task_heartbeats", "agent_id"],
7609
+ ["project_agent_roles", "agent_id"]
7610
+ ];
7611
+ let changed = 0;
7612
+ for (const [table, column] of refs) {
7613
+ if (!tableHasColumn(db, table, column))
7614
+ continue;
7615
+ try {
7616
+ changed += db.run(`UPDATE ${table} SET ${column} = ? WHERE LOWER(${column}) = ?`, [newName, oldName]).changes;
7617
+ } catch {}
7618
+ }
7619
+ return changed;
7620
+ }
7621
+ function normalizeGeneratedAgentNames(db) {
7622
+ const rows = db.query("SELECT * FROM agents ORDER BY created_at, id").all();
7623
+ const existing = new Set(rows.map((agent) => normalizeAgentNameInput(agent.name)));
7624
+ const renamed = [];
7625
+ for (const agent of rows) {
7626
+ const oldName = normalizeAgentNameInput(agent.name);
7627
+ if (!isBlockedAgentName(oldName))
7628
+ continue;
7629
+ const candidates = suggestAgentNames(existing);
7630
+ const replacement = candidates[0];
7631
+ if (!replacement) {
7632
+ throw new Error("No safe agent names are available for normalization");
7633
+ }
7634
+ existing.delete(oldName);
7635
+ existing.add(replacement);
7636
+ db.run("UPDATE agents SET name = ?, last_seen_at = ? WHERE id = ?", [replacement, now(), agent.id]);
7637
+ const referenceUpdates = updateReferences(db, oldName, replacement);
7638
+ renamed.push({
7639
+ id: agent.id,
7640
+ old_name: oldName,
7641
+ new_name: replacement,
7642
+ reference_updates: referenceUpdates
7643
+ });
7644
+ }
7645
+ return renamed;
7646
+ }
7647
+ var InvalidAgentNameError, ROMAN_AGENT_NAMES, GREEK_AGENT_NAMES, NICE_AGENT_NAMES, PREFERRED_AGENT_NAMES, RESERVED_GENERIC_NAMES, NUMERIC_SUFFIX_RE, ONE_WORD_NAME_RE;
7648
+ var init_agent_names = __esm(() => {
7649
+ init_database();
7650
+ InvalidAgentNameError = class InvalidAgentNameError extends Error {
7651
+ suggestions;
7652
+ constructor(name, reason, suggestions = []) {
7653
+ super(`Invalid agent name "${name}": ${reason}${suggestions.length > 0 ? `. Try: ${suggestions.join(", ")}` : ""}`);
7654
+ this.name = "InvalidAgentNameError";
7655
+ this.suggestions = suggestions;
7656
+ }
7657
+ };
7658
+ ROMAN_AGENT_NAMES = [
7659
+ "caesar",
7660
+ "augustus",
7661
+ "marcus",
7662
+ "brutus",
7663
+ "cicero",
7664
+ "cato",
7665
+ "nero",
7666
+ "claudius",
7667
+ "tiberius",
7668
+ "hadrian",
7669
+ "trajan",
7670
+ "vespasian",
7671
+ "domitian",
7672
+ "caligula",
7673
+ "commodus",
7674
+ "livia",
7675
+ "julia",
7676
+ "octavia",
7677
+ "claudia",
7678
+ "agrippina",
7679
+ "cornelia",
7680
+ "valeria",
7681
+ "fulvia",
7682
+ "hortensia",
7683
+ "fabia"
7684
+ ];
7685
+ GREEK_AGENT_NAMES = [
7686
+ "athena",
7687
+ "apollo",
7688
+ "artemis",
7689
+ "hera",
7690
+ "iris",
7691
+ "hector",
7692
+ "achilles",
7693
+ "odysseus",
7694
+ "theseus",
7695
+ "pericles",
7696
+ "solon",
7697
+ "sophia",
7698
+ "thalia",
7699
+ "calliope",
7700
+ "clio",
7701
+ "phoebe",
7702
+ "daphne",
7703
+ "leonidas",
7704
+ "andromeda",
7705
+ "cassander"
7706
+ ];
7707
+ NICE_AGENT_NAMES = [
7708
+ "atlas",
7709
+ "aurora",
7710
+ "ember",
7711
+ "nova",
7712
+ "orion",
7713
+ "rhea",
7714
+ "selene",
7715
+ "sirius",
7716
+ "vesper",
7717
+ "zephyr"
7718
+ ];
7719
+ PREFERRED_AGENT_NAMES = [
7720
+ ...ROMAN_AGENT_NAMES,
7721
+ ...GREEK_AGENT_NAMES,
7722
+ ...NICE_AGENT_NAMES
7723
+ ];
7724
+ RESERVED_GENERIC_NAMES = new Set([
7725
+ "agent",
7726
+ "agents",
7727
+ "ai",
7728
+ "assistant",
7729
+ "bot",
7730
+ "coder",
7731
+ "default",
7732
+ "helper",
7733
+ "model",
7734
+ "system",
7735
+ "user",
7736
+ "worker"
7737
+ ]);
7738
+ NUMERIC_SUFFIX_RE = /[-_]\d+$/;
7739
+ ONE_WORD_NAME_RE = /^[a-z]+$/;
7740
+ });
7741
+
7296
7742
  // src/db/agents.ts
7297
7743
  var exports_agents = {};
7298
7744
  __export(exports_agents, {
7299
7745
  updateAgentActivity: () => updateAgentActivity,
7300
7746
  updateAgent: () => updateAgent,
7301
7747
  unarchiveAgent: () => unarchiveAgent,
7748
+ suggestAgentNames: () => suggestAgentNames,
7302
7749
  releaseAgent: () => releaseAgent,
7303
7750
  registerAgent: () => registerAgent,
7751
+ normalizeGeneratedAgentNames: () => normalizeGeneratedAgentNames,
7304
7752
  matchCapabilities: () => matchCapabilities,
7305
7753
  listAgents: () => listAgents,
7306
7754
  isAgentConflict: () => isAgentConflict,
@@ -7312,7 +7760,8 @@ __export(exports_agents, {
7312
7760
  getAgent: () => getAgent,
7313
7761
  deleteAgent: () => deleteAgent,
7314
7762
  autoReleaseStaleAgents: () => autoReleaseStaleAgents,
7315
- archiveAgent: () => archiveAgent
7763
+ archiveAgent: () => archiveAgent,
7764
+ InvalidAgentNameError: () => InvalidAgentNameError
7316
7765
  });
7317
7766
  function getActiveWindowMs() {
7318
7767
  const env = process.env["TODOS_AGENT_TIMEOUT_MS"];
@@ -7335,7 +7784,7 @@ function getAvailableNamesFromPool(pool, db) {
7335
7784
  autoReleaseStaleAgents(db);
7336
7785
  const cutoff = new Date(Date.now() - getActiveWindowMs()).toISOString();
7337
7786
  const activeNames = new Set(db.query("SELECT name FROM agents WHERE status = 'active' AND last_seen_at > ?").all(cutoff).map((r) => r.name.toLowerCase()));
7338
- return pool.filter((name) => !activeNames.has(name.toLowerCase()));
7787
+ return pool.map(normalizeAgentNameInput).filter((name) => !activeNames.has(name));
7339
7788
  }
7340
7789
  function shortUuid() {
7341
7790
  return crypto.randomUUID().slice(0, 8);
@@ -7351,7 +7800,8 @@ function rowToAgent(row) {
7351
7800
  }
7352
7801
  function registerAgent(input, db) {
7353
7802
  const d = db || getDatabase();
7354
- const normalizedName = input.name.trim().toLowerCase();
7803
+ const existingNames = d.query("SELECT name FROM agents").all().map((row) => row.name);
7804
+ const normalizedName = validateAgentName(input.name, existingNames);
7355
7805
  const existing = getAgentByName(normalizedName, d);
7356
7806
  if (existing) {
7357
7807
  const lastSeenMs = new Date(existing.last_seen_at).getTime();
@@ -7484,7 +7934,8 @@ function updateAgent(id, input, db) {
7484
7934
  const sets = ["last_seen_at = ?"];
7485
7935
  const params = [now()];
7486
7936
  if (input.name !== undefined) {
7487
- const newName = input.name.trim().toLowerCase();
7937
+ const existingNames = d.query("SELECT name FROM agents WHERE id != ?").all(id).map((row) => row.name);
7938
+ const newName = validateAgentName(input.name, existingNames);
7488
7939
  const holder = getAgentByName(newName, d);
7489
7940
  if (holder && holder.id !== id) {
7490
7941
  const lastSeenMs = new Date(holder.last_seen_at).getTime();
@@ -7595,6 +8046,7 @@ function getCapableAgents(capabilities, opts, db) {
7595
8046
  }
7596
8047
  var init_agents = __esm(() => {
7597
8048
  init_database();
8049
+ init_agent_names();
7598
8050
  });
7599
8051
 
7600
8052
  // src/db/task-lists.ts
@@ -7664,6 +8116,90 @@ var init_task_lists = __esm(() => {
7664
8116
  init_projects();
7665
8117
  });
7666
8118
 
8119
+ // src/db/api-keys.ts
8120
+ import { createHash, randomBytes, timingSafeEqual } from "crypto";
8121
+ function rowToRecord(row) {
8122
+ return {
8123
+ id: row.id,
8124
+ name: row.name,
8125
+ prefix: row.prefix,
8126
+ permissions: JSON.parse(row.permissions || '["*"]'),
8127
+ created_at: row.created_at,
8128
+ last_used_at: row.last_used_at,
8129
+ expires_at: row.expires_at,
8130
+ revoked_at: row.revoked_at
8131
+ };
8132
+ }
8133
+ function hashApiKey(key) {
8134
+ return createHash("sha256").update(key).digest("hex");
8135
+ }
8136
+ function safeEqualHex(a, b) {
8137
+ if (a.length !== b.length)
8138
+ return false;
8139
+ return timingSafeEqual(Buffer.from(a, "hex"), Buffer.from(b, "hex"));
8140
+ }
8141
+ function generatePlaintextKey() {
8142
+ return `tdos_${randomBytes(32).toString("base64url")}`;
8143
+ }
8144
+ function createApiKey(input, db) {
8145
+ const d = db || getDatabase();
8146
+ const name = input.name.trim();
8147
+ if (!name)
8148
+ throw new Error("API key name is required");
8149
+ const key = generatePlaintextKey();
8150
+ const timestamp = now();
8151
+ const id = uuid();
8152
+ const prefix = key.slice(0, 12);
8153
+ d.run(`INSERT INTO api_keys (id, name, key_hash, prefix, permissions, created_at, expires_at)
8154
+ VALUES (?, ?, ?, ?, ?, ?, ?)`, [
8155
+ id,
8156
+ name,
8157
+ hashApiKey(key),
8158
+ prefix,
8159
+ JSON.stringify(input.permissions?.length ? input.permissions : ["*"]),
8160
+ timestamp,
8161
+ input.expires_at || null
8162
+ ]);
8163
+ const row = d.query("SELECT * FROM api_keys WHERE id = ?").get(id);
8164
+ return { key, record: rowToRecord(row) };
8165
+ }
8166
+ function listApiKeys(opts, db) {
8167
+ const d = db || getDatabase();
8168
+ const includeRevoked = opts?.include_revoked ?? false;
8169
+ const sql = includeRevoked ? "SELECT * FROM api_keys ORDER BY created_at DESC" : "SELECT * FROM api_keys WHERE revoked_at IS NULL ORDER BY created_at DESC";
8170
+ return d.query(sql).all().map(rowToRecord);
8171
+ }
8172
+ function hasActiveApiKeys(db) {
8173
+ const d = db || getDatabase();
8174
+ const row = d.query("SELECT COUNT(*) AS count FROM api_keys WHERE revoked_at IS NULL AND (expires_at IS NULL OR expires_at > ?)").get(now());
8175
+ return (row?.count ?? 0) > 0;
8176
+ }
8177
+ function verifyApiKey(key, db) {
8178
+ const d = db || getDatabase();
8179
+ const candidateHash = hashApiKey(key);
8180
+ const rows = d.query("SELECT * FROM api_keys WHERE revoked_at IS NULL AND (expires_at IS NULL OR expires_at > ?)").all(now());
8181
+ for (const row of rows) {
8182
+ if (!safeEqualHex(candidateHash, row.key_hash))
8183
+ continue;
8184
+ d.run("UPDATE api_keys SET last_used_at = ? WHERE id = ?", [now(), row.id]);
8185
+ return rowToRecord({ ...row, last_used_at: now() });
8186
+ }
8187
+ return null;
8188
+ }
8189
+ function revokeApiKey(idOrPrefix, db) {
8190
+ const d = db || getDatabase();
8191
+ const identifier = idOrPrefix.trim();
8192
+ const row = d.query("SELECT * FROM api_keys WHERE id = ? OR prefix = ?").get(identifier, identifier);
8193
+ if (!row)
8194
+ return null;
8195
+ d.run("UPDATE api_keys SET revoked_at = ? WHERE id = ?", [now(), row.id]);
8196
+ const updated = d.query("SELECT * FROM api_keys WHERE id = ?").get(row.id);
8197
+ return rowToRecord(updated);
8198
+ }
8199
+ var init_api_keys = __esm(() => {
8200
+ init_database();
8201
+ });
8202
+
7667
8203
  // src/db/orgs.ts
7668
8204
  function rowToOrg(row) {
7669
8205
  return { ...row, metadata: JSON.parse(row.metadata || "{}") };
@@ -7994,7 +8530,7 @@ function handleTasksContext(_req, url, _ctx, json2, taskToSummary2) {
7994
8530
  return new Response(text, { headers: { "Content-Type": "text/plain" } });
7995
8531
  }
7996
8532
  function handleTaskAttachments(id, _ctx, json2) {
7997
- const task = getTask2(id);
8533
+ const task = getTask(id);
7998
8534
  if (!task)
7999
8535
  return json2({ error: "Task not found" }, 404);
8000
8536
  const evidence = task.metadata?._evidence || {};
@@ -8002,7 +8538,7 @@ function handleTaskAttachments(id, _ctx, json2) {
8002
8538
  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
8539
  }
8004
8540
  async function handleTaskProgress(id, req, method, _ctx, json2) {
8005
- const task = getTask2(id);
8541
+ const task = getTask(id);
8006
8542
  if (!task)
8007
8543
  return json2({ error: "Task not found" }, 404);
8008
8544
  if (method === "GET") {
@@ -8025,7 +8561,7 @@ async function handleTaskProgress(id, req, method, _ctx, json2) {
8025
8561
  return null;
8026
8562
  }
8027
8563
  function handleGetTask(id, _ctx, json2, taskToSummary2) {
8028
- const task = getTask2(id);
8564
+ const task = getTask(id);
8029
8565
  if (!task)
8030
8566
  return json2({ error: "Task not found" }, 404);
8031
8567
  return json2(taskToSummary2(task));
@@ -8033,10 +8569,10 @@ function handleGetTask(id, _ctx, json2, taskToSummary2) {
8033
8569
  async function handlePatchTask(id, req, _ctx, json2, taskToSummary2) {
8034
8570
  try {
8035
8571
  const body = await req.json();
8036
- const task = getTask2(id);
8572
+ const task = getTask(id);
8037
8573
  if (!task)
8038
8574
  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"]);
8575
+ 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
8576
  const safeBody = {};
8041
8577
  for (const [key, value] of Object.entries(body)) {
8042
8578
  if (ALLOWED.has(key))
@@ -8109,31 +8645,37 @@ function handleDeleteProject(id, _ctx, json2) {
8109
8645
  return json2({ success: true });
8110
8646
  }
8111
8647
  async function handleAgentMe(_req, url, _ctx, json2, taskToSummary2) {
8112
- const name = url.searchParams.get("name");
8113
- if (!name)
8114
- return json2({ error: "Missing name param" }, 400);
8115
- const agentResult = registerAgent({ name });
8116
- if (isAgentConflict(agentResult))
8117
- return json2({ error: agentResult.message, conflict: true }, 409);
8118
- const agent = agentResult;
8119
- const tasks = listTasks({ assigned_to: name });
8120
- const agentIdTasks = listTasks({ agent_id: agent.id });
8121
- const allTasks = [...tasks, ...agentIdTasks.filter((t) => !tasks.some((tt) => tt.id === t.id))];
8122
- const pending = allTasks.filter((t) => t.status === "pending");
8123
- const inProgress = allTasks.filter((t) => t.status === "in_progress");
8124
- const completed = allTasks.filter((t) => t.status === "completed");
8125
- return json2({
8126
- agent,
8127
- pending_tasks: pending.map((t) => taskToSummary2(t)),
8128
- in_progress_tasks: inProgress.map((t) => taskToSummary2(t)),
8129
- stats: {
8130
- total: allTasks.length,
8131
- pending: pending.length,
8132
- in_progress: inProgress.length,
8133
- completed: completed.length,
8134
- completion_rate: allTasks.length > 0 ? Math.round(completed.length / allTasks.length * 100) : 0
8135
- }
8136
- });
8648
+ try {
8649
+ const name = url.searchParams.get("name");
8650
+ if (!name)
8651
+ return json2({ error: "Missing name param" }, 400);
8652
+ const agentResult = registerAgent({ name });
8653
+ if (isAgentConflict(agentResult))
8654
+ return json2({ error: agentResult.message, conflict: true }, 409);
8655
+ const agent = agentResult;
8656
+ const tasks = listTasks({ assigned_to: agent.name });
8657
+ const agentIdTasks = listTasks({ agent_id: agent.id });
8658
+ const allTasks = [...tasks, ...agentIdTasks.filter((t) => !tasks.some((tt) => tt.id === t.id))];
8659
+ const pending = allTasks.filter((t) => t.status === "pending");
8660
+ const inProgress = allTasks.filter((t) => t.status === "in_progress");
8661
+ const completed = allTasks.filter((t) => t.status === "completed");
8662
+ return json2({
8663
+ agent,
8664
+ pending_tasks: pending.map((t) => taskToSummary2(t)),
8665
+ in_progress_tasks: inProgress.map((t) => taskToSummary2(t)),
8666
+ stats: {
8667
+ total: allTasks.length,
8668
+ pending: pending.length,
8669
+ in_progress: inProgress.length,
8670
+ completed: completed.length,
8671
+ completion_rate: allTasks.length > 0 ? Math.round(completed.length / allTasks.length * 100) : 0
8672
+ }
8673
+ });
8674
+ } catch (e) {
8675
+ if (e instanceof InvalidAgentNameError)
8676
+ return json2({ error: e.message, suggestions: e.suggestions }, 400);
8677
+ return json2({ error: e instanceof Error ? e.message : "Failed to get agent profile" }, 500);
8678
+ }
8137
8679
  }
8138
8680
  function handleAgentQueue(agentId, _ctx, json2, taskToSummary2) {
8139
8681
  const pending = listTasks({ status: "pending" });
@@ -8199,6 +8741,8 @@ async function handleRegisterAgent(req, _ctx, json2) {
8199
8741
  return json2({ error: result.message, conflict: true }, 409);
8200
8742
  return json2(result, 201);
8201
8743
  } catch (e) {
8744
+ if (e instanceof InvalidAgentNameError)
8745
+ return json2({ error: e.message, suggestions: e.suggestions }, 400);
8202
8746
  return json2({ error: e instanceof Error ? e.message : "Failed to register agent" }, 500);
8203
8747
  }
8204
8748
  }
@@ -8208,6 +8752,8 @@ async function handleUpdateAgent(id, req, _ctx, json2) {
8208
8752
  const agent = updateAgent(id, body);
8209
8753
  return json2(agent);
8210
8754
  } catch (e) {
8755
+ if (e instanceof InvalidAgentNameError)
8756
+ return json2({ error: e.message, suggestions: e.suggestions }, 400);
8211
8757
  return json2({ error: e instanceof Error ? e.message : "Failed to update agent" }, 500);
8212
8758
  }
8213
8759
  }
@@ -8444,14 +8990,26 @@ function resolveDashboardDir() {
8444
8990
  }
8445
8991
  return join9(process.cwd(), "dashboard", "dist");
8446
8992
  }
8993
+ function getProvidedApiKey(req) {
8994
+ const headerKey = req.headers.get("x-api-key");
8995
+ if (headerKey)
8996
+ return headerKey.trim();
8997
+ const auth = req.headers.get("authorization");
8998
+ if (!auth)
8999
+ return null;
9000
+ return auth.replace(/^Bearer\s+/i, "").trim() || null;
9001
+ }
8447
9002
  function checkAuth(req, apiKey) {
8448
- if (!apiKey)
9003
+ const generatedKeysEnabled = hasActiveApiKeys();
9004
+ if (!apiKey && !generatedKeysEnabled)
8449
9005
  return null;
8450
- const provided = req.headers.get("x-api-key") || req.headers.get("authorization")?.replace("Bearer ", "");
8451
- if (!provided || provided !== apiKey) {
9006
+ const provided = getProvidedApiKey(req);
9007
+ const matchesEnvKey = Boolean(apiKey && provided && provided === apiKey);
9008
+ const matchesGeneratedKey = Boolean(provided && verifyApiKey(provided));
9009
+ if (!matchesEnvKey && !matchesGeneratedKey) {
8452
9010
  return new Response(JSON.stringify({ error: "Unauthorized" }), {
8453
9011
  status: 401,
8454
- headers: { "Content-Type": "application/json", ...SECURITY_HEADERS }
9012
+ headers: { "Content-Type": "application/json", "WWW-Authenticate": "Bearer", ...SECURITY_HEADERS }
8455
9013
  });
8456
9014
  }
8457
9015
  return null;
@@ -8833,6 +9391,7 @@ Dashboard not found at: ${dashboardDir}`);
8833
9391
  var MIME_TYPES, SECURITY_HEADERS, rateLimitMap, RATE_LIMIT_WINDOW_MS = 60000, RATE_LIMIT_MAX = 120;
8834
9392
  var init_serve = __esm(() => {
8835
9393
  init_database();
9394
+ init_api_keys();
8836
9395
  init_routes();
8837
9396
  MIME_TYPES = {
8838
9397
  ".html": "text/html; charset=utf-8",
@@ -10199,6 +10758,77 @@ var init_Dashboard = __esm(() => {
10199
10758
  init_audit();
10200
10759
  });
10201
10760
 
10761
+ // src/db/handoffs.ts
10762
+ var exports_handoffs = {};
10763
+ __export(exports_handoffs, {
10764
+ listHandoffs: () => listHandoffs,
10765
+ getLatestHandoff: () => getLatestHandoff,
10766
+ createHandoff: () => createHandoff
10767
+ });
10768
+ function createHandoff(input, db) {
10769
+ const d = db || getDatabase();
10770
+ const id = uuid();
10771
+ const timestamp = now();
10772
+ d.run(`INSERT INTO handoffs (id, agent_id, project_id, summary, completed, in_progress, blockers, next_steps, created_at)
10773
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
10774
+ id,
10775
+ input.agent_id || null,
10776
+ input.project_id || null,
10777
+ input.summary,
10778
+ input.completed ? JSON.stringify(input.completed) : null,
10779
+ input.in_progress ? JSON.stringify(input.in_progress) : null,
10780
+ input.blockers ? JSON.stringify(input.blockers) : null,
10781
+ input.next_steps ? JSON.stringify(input.next_steps) : null,
10782
+ timestamp
10783
+ ]);
10784
+ return {
10785
+ id,
10786
+ agent_id: input.agent_id || null,
10787
+ project_id: input.project_id || null,
10788
+ summary: input.summary,
10789
+ completed: input.completed || null,
10790
+ in_progress: input.in_progress || null,
10791
+ blockers: input.blockers || null,
10792
+ next_steps: input.next_steps || null,
10793
+ created_at: timestamp
10794
+ };
10795
+ }
10796
+ function rowToHandoff(row) {
10797
+ return {
10798
+ ...row,
10799
+ completed: row.completed ? JSON.parse(row.completed) : null,
10800
+ in_progress: row.in_progress ? JSON.parse(row.in_progress) : null,
10801
+ blockers: row.blockers ? JSON.parse(row.blockers) : null,
10802
+ next_steps: row.next_steps ? JSON.parse(row.next_steps) : null
10803
+ };
10804
+ }
10805
+ function listHandoffs(projectId, limit = 10, db) {
10806
+ const d = db || getDatabase();
10807
+ if (projectId) {
10808
+ return d.query("SELECT * FROM handoffs WHERE project_id = ? ORDER BY rowid DESC LIMIT ?").all(projectId, limit).map(rowToHandoff);
10809
+ }
10810
+ return d.query("SELECT * FROM handoffs ORDER BY rowid DESC LIMIT ?").all(limit).map(rowToHandoff);
10811
+ }
10812
+ function getLatestHandoff(agentId, projectId, db) {
10813
+ const d = db || getDatabase();
10814
+ let query = "SELECT * FROM handoffs WHERE 1=1";
10815
+ const params = [];
10816
+ if (agentId) {
10817
+ query += " AND agent_id = ?";
10818
+ params.push(agentId);
10819
+ }
10820
+ if (projectId) {
10821
+ query += " AND project_id = ?";
10822
+ params.push(projectId);
10823
+ }
10824
+ query += " ORDER BY rowid DESC LIMIT 1";
10825
+ const row = d.query(query).get(...params);
10826
+ return row ? rowToHandoff(row) : null;
10827
+ }
10828
+ var init_handoffs = __esm(() => {
10829
+ init_database();
10830
+ });
10831
+
10202
10832
  // src/lib/auto-assign.ts
10203
10833
  var exports_auto_assign = {};
10204
10834
  __export(exports_auto_assign, {
@@ -10289,7 +10919,7 @@ async function callCerebras(prompt, apiKey) {
10289
10919
  }
10290
10920
  async function autoAssignTask(taskId, db) {
10291
10921
  const d = db || getDatabase();
10292
- const task = getTask2(taskId, d);
10922
+ const task = getTask(taskId, d);
10293
10923
  if (!task)
10294
10924
  throw new Error(`Task ${taskId} not found`);
10295
10925
  const agents = listAgents(d).filter((a) => a.status === "active");
@@ -14489,7 +15119,7 @@ var init_dist = __esm(() => {
14489
15119
  var nodeCrypto = __require2("crypto");
14490
15120
  module.exports = {
14491
15121
  postgresMd5PasswordHash,
14492
- randomBytes,
15122
+ randomBytes: randomBytes2,
14493
15123
  deriveKey,
14494
15124
  sha256,
14495
15125
  hashByName,
@@ -14499,7 +15129,7 @@ var init_dist = __esm(() => {
14499
15129
  var webCrypto = nodeCrypto.webcrypto || globalThis.crypto;
14500
15130
  var subtleCrypto = webCrypto.subtle;
14501
15131
  var textEncoder = new TextEncoder;
14502
- function randomBytes(length) {
15132
+ function randomBytes2(length) {
14503
15133
  return webCrypto.getRandomValues(Buffer.alloc(length));
14504
15134
  }
14505
15135
  async function md5(string) {
@@ -26215,12 +26845,21 @@ var init_dispatch2 = __esm(() => {
26215
26845
  // src/mcp/tools/task-crud.ts
26216
26846
  function registerTaskCrudTools(server, ctx) {
26217
26847
  const { shouldRegisterTool, resolveId, formatError, formatTask } = ctx;
26848
+ function versionFor(taskId, version) {
26849
+ const current = getTask(taskId);
26850
+ if (!current)
26851
+ throw new TaskNotFoundError(taskId);
26852
+ if (version !== undefined && current.version !== version) {
26853
+ throw new VersionConflictError(taskId, version, current.version);
26854
+ }
26855
+ return current.version;
26856
+ }
26218
26857
  if (shouldRegisterTool("create_task")) {
26219
26858
  server.tool("create_task", "Create a new task in a project. Pass short_id=null to auto-generate.", {
26220
26859
  title: exports_external2.string().describe("Task title"),
26221
26860
  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)"),
26861
+ status: exports_external2.enum(["pending", "in_progress", "completed", "failed", "cancelled"]).optional().describe("Initial status (default: pending)"),
26862
+ priority: exports_external2.enum(["low", "medium", "high", "critical"]).optional().describe("Priority (default: medium)"),
26224
26863
  project_id: exports_external2.string().optional().describe("Project ID"),
26225
26864
  task_list_id: exports_external2.string().optional().describe("Task list ID"),
26226
26865
  assigned_to: exports_external2.string().optional().describe("Agent ID or name to assign to"),
@@ -26250,9 +26889,9 @@ function registerTaskCrudTools(server, ctx) {
26250
26889
  if (confidence !== undefined)
26251
26890
  resolved.confidence = confidence;
26252
26891
  if (retry_count !== undefined)
26253
- resolved.retry_count = retry_count;
26892
+ resolved.max_retries = retry_count;
26254
26893
  if (deadline)
26255
- resolved.deadline = deadline;
26894
+ resolved.due_at = deadline;
26256
26895
  const task = createTask(resolved);
26257
26896
  return { content: [{ type: "text", text: formatTask(task) }] };
26258
26897
  } catch (e) {
@@ -26262,8 +26901,8 @@ function registerTaskCrudTools(server, ctx) {
26262
26901
  }
26263
26902
  if (shouldRegisterTool("list_tasks")) {
26264
26903
  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"),
26904
+ 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"),
26905
+ 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
26906
  project_id: exports_external2.string().optional().describe("Filter by project"),
26268
26907
  task_list_id: exports_external2.string().optional().describe("Filter by task list"),
26269
26908
  assigned_to: exports_external2.string().optional().describe("Filter by assignee (agent ID or name, empty string = unassigned)"),
@@ -26298,9 +26937,9 @@ function registerTaskCrudTools(server, ctx) {
26298
26937
  }, async ({ task_id }) => {
26299
26938
  try {
26300
26939
  const resolvedId = resolveId(task_id);
26301
- const task = getTask2(resolvedId);
26940
+ const task = getTask(resolvedId);
26302
26941
  if (!task)
26303
- throw new NotFoundError(`Task not found: ${task_id}`);
26942
+ throw new TaskNotFoundError(task_id);
26304
26943
  const focus = ctx.getAgentFocus(task.assigned_to || "");
26305
26944
  const lines = [
26306
26945
  `ID: ${task.id}`,
@@ -26314,7 +26953,7 @@ function registerTaskCrudTools(server, ctx) {
26314
26953
  task.estimated_minutes != null ? `Estimate: ${task.estimated_minutes} min` : null,
26315
26954
  task.actual_minutes != null ? `Actual: ${task.actual_minutes} min` : null,
26316
26955
  task.confidence != null ? `Confidence: ${task.confidence}` : null,
26317
- task.deadline ? `Deadline: ${task.deadline}` : null,
26956
+ task.due_at ? `Due: ${task.due_at}` : null,
26318
26957
  task.completed_at ? `Completed: ${task.completed_at}` : null,
26319
26958
  focus ? `Focus: agent=${focus.agent_id} project=${focus.project_id || "(global)"}` : null,
26320
26959
  task.created_at ? `Created: ${task.created_at}` : null,
@@ -26336,8 +26975,8 @@ ${task.description}` : null
26336
26975
  task_id: exports_external2.string().describe("Task ID"),
26337
26976
  title: exports_external2.string().optional(),
26338
26977
  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(),
26978
+ status: exports_external2.enum(["pending", "in_progress", "completed", "failed", "cancelled"]).optional(),
26979
+ priority: exports_external2.enum(["low", "medium", "high", "critical"]).optional(),
26341
26980
  assigned_to: exports_external2.string().nullable().optional().describe("Agent ID or name, null to unassign"),
26342
26981
  project_id: exports_external2.string().nullable().optional(),
26343
26982
  task_list_id: exports_external2.string().nullable().optional(),
@@ -26366,9 +27005,15 @@ ${task.description}` : null
26366
27005
  resolved.task_list_id = resolveId(resolved.task_list_id, "task_lists");
26367
27006
  if (resolved.depends_on && Array.isArray(resolved.depends_on))
26368
27007
  resolved.depends_on = resolved.depends_on.map(resolveId);
26369
- if (resolved.estimate !== undefined)
27008
+ if (resolved.estimate !== undefined) {
26370
27009
  resolved.estimated_minutes = resolved.estimate;
26371
- const task = updateTask(resolvedId, resolved, version);
27010
+ delete resolved.estimate;
27011
+ }
27012
+ if (resolved.deadline !== undefined) {
27013
+ resolved.due_at = resolved.deadline;
27014
+ delete resolved.deadline;
27015
+ }
27016
+ const task = updateTask(resolvedId, { ...resolved, version: versionFor(resolvedId, version) });
26372
27017
  return { content: [{ type: "text", text: formatTask(task) }] };
26373
27018
  } catch (e) {
26374
27019
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
@@ -26393,11 +27038,24 @@ ${task.description}` : null
26393
27038
  var init_task_crud2 = __esm(() => {
26394
27039
  init_zod2();
26395
27040
  init_tasks();
27041
+ init_types();
26396
27042
  });
26397
27043
 
26398
27044
  // src/mcp/tools/task-project-tools.ts
26399
27045
  function registerTaskProjectTools(server, ctx) {
26400
27046
  const { shouldRegisterTool, resolveId, formatError, formatTask } = ctx;
27047
+ function versionFor(taskId, version) {
27048
+ const current = getTask(taskId);
27049
+ if (!current)
27050
+ throw new TaskNotFoundError(taskId);
27051
+ if (version !== undefined && current.version !== version) {
27052
+ throw new VersionConflictError(taskId, version, current.version);
27053
+ }
27054
+ return current.version;
27055
+ }
27056
+ function updateWithOptionalVersion(taskId, updates, version) {
27057
+ return updateTask(taskId, { ...updates, version: versionFor(taskId, version) });
27058
+ }
26401
27059
  if (shouldRegisterTool("start_task")) {
26402
27060
  server.tool("start_task", "Mark a task as in_progress. Uses optimistic locking via version if provided.", {
26403
27061
  task_id: exports_external2.string().describe("Task ID"),
@@ -26405,7 +27063,12 @@ function registerTaskProjectTools(server, ctx) {
26405
27063
  }, async ({ task_id, version }) => {
26406
27064
  try {
26407
27065
  const resolvedId = resolveId(task_id);
26408
- const task = updateTask(resolvedId, { status: "in_progress" }, version);
27066
+ if (version !== undefined)
27067
+ versionFor(resolvedId, version);
27068
+ const current = getTask(resolvedId);
27069
+ if (!current)
27070
+ throw new TaskNotFoundError(resolvedId);
27071
+ const task = startTask(resolvedId, current.assigned_to || current.agent_id || "mcp");
26409
27072
  return { content: [{ type: "text", text: formatTask(task) }] };
26410
27073
  } catch (e) {
26411
27074
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
@@ -26421,7 +27084,12 @@ function registerTaskProjectTools(server, ctx) {
26421
27084
  }, async ({ task_id, confidence, completed_at, version }) => {
26422
27085
  try {
26423
27086
  const resolvedId = resolveId(task_id);
26424
- const task = updateTask(resolvedId, { status: "completed", confidence, completed_at }, version);
27087
+ if (version !== undefined)
27088
+ versionFor(resolvedId, version);
27089
+ const current = getTask(resolvedId);
27090
+ if (!current)
27091
+ throw new TaskNotFoundError(resolvedId);
27092
+ const task = completeTask(resolvedId, current.assigned_to || current.agent_id || undefined, undefined, { confidence, completed_at });
26425
27093
  return { content: [{ type: "text", text: formatTask(task) }] };
26426
27094
  } catch (e) {
26427
27095
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
@@ -26435,7 +27103,7 @@ function registerTaskProjectTools(server, ctx) {
26435
27103
  }, async ({ task_id, version }) => {
26436
27104
  try {
26437
27105
  const resolvedId = resolveId(task_id);
26438
- const task = updateTask(resolvedId, { status: "cancelled" }, version);
27106
+ const task = version === undefined ? setTaskStatus(resolvedId, "cancelled") : updateWithOptionalVersion(resolvedId, { status: "cancelled" }, version);
26439
27107
  return { content: [{ type: "text", text: formatTask(task) }] };
26440
27108
  } catch (e) {
26441
27109
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
@@ -26451,7 +27119,7 @@ function registerTaskProjectTools(server, ctx) {
26451
27119
  try {
26452
27120
  const resolvedId = resolveId(task_id);
26453
27121
  const resolvedAssignee = resolveId(new_assignee, "agents");
26454
- const task = updateTask(resolvedId, { assigned_to: resolvedAssignee }, version);
27122
+ const task = updateWithOptionalVersion(resolvedId, { assigned_to: resolvedAssignee }, version);
26455
27123
  return { content: [{ type: "text", text: formatTask(task) }] };
26456
27124
  } catch (e) {
26457
27125
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
@@ -26466,7 +27134,7 @@ function registerTaskProjectTools(server, ctx) {
26466
27134
  }, async ({ task_id, deadline, version }) => {
26467
27135
  try {
26468
27136
  const resolvedId = resolveId(task_id);
26469
- const task = updateTask(resolvedId, { deadline }, version);
27137
+ const task = updateWithOptionalVersion(resolvedId, { due_at: deadline }, version);
26470
27138
  return { content: [{ type: "text", text: formatTask(task) }] };
26471
27139
  } catch (e) {
26472
27140
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
@@ -26476,12 +27144,12 @@ function registerTaskProjectTools(server, ctx) {
26476
27144
  if (shouldRegisterTool("prioritize_task")) {
26477
27145
  server.tool("prioritize_task", "Set a task's priority.", {
26478
27146
  task_id: exports_external2.string().describe("Task ID"),
26479
- priority: exports_external2.enum(["low", "medium", "high", "urgent"]).describe("New priority"),
27147
+ priority: exports_external2.enum(["low", "medium", "high", "critical"]).describe("New priority"),
26480
27148
  version: exports_external2.number().optional().describe("Expected version for optimistic locking")
26481
27149
  }, async ({ task_id, priority, version }) => {
26482
27150
  try {
26483
27151
  const resolvedId = resolveId(task_id);
26484
- const task = updateTask(resolvedId, { priority }, version);
27152
+ const task = version === undefined ? setTaskPriority(resolvedId, priority) : updateWithOptionalVersion(resolvedId, { priority }, version);
26485
27153
  return { content: [{ type: "text", text: formatTask(task) }] };
26486
27154
  } catch (e) {
26487
27155
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
@@ -26537,18 +27205,18 @@ function registerTaskProjectTools(server, ctx) {
26537
27205
  if (shouldRegisterTool("bulk_update_tasks")) {
26538
27206
  server.tool("bulk_update_tasks", "Update multiple tasks at once. All tasks must pass the dependency check.", {
26539
27207
  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(),
27208
+ status: exports_external2.enum(["pending", "in_progress", "completed", "failed", "cancelled"]).optional(),
27209
+ priority: exports_external2.enum(["low", "medium", "high", "critical"]).optional(),
26542
27210
  assigned_to: exports_external2.string().nullable().optional().describe("Agent ID or name, null to unassign")
26543
27211
  }, async ({ task_ids, status, priority, assigned_to }) => {
26544
27212
  try {
26545
- const { bulkUpdateTasks: bulkUpdateTasks2 } = (()=>{throw new Error("Cannot require module "+"../db/tasks.js");})();
27213
+ const { bulkUpdateTasks: bulkUpdateTasks2 } = (init_tasks(), __toCommonJS(exports_tasks));
26546
27214
  const resolved = task_ids.map(resolveId);
26547
27215
  let resolvedAssignee = assigned_to;
26548
27216
  if (resolvedAssignee && typeof resolvedAssignee === "string")
26549
27217
  resolvedAssignee = resolveId(resolvedAssignee, "agents");
26550
27218
  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).` }] };
27219
+ return { content: [{ type: "text", text: `${result.updated} task(s) updated, ${result.failed.length} failed.` }] };
26552
27220
  } catch (e) {
26553
27221
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
26554
27222
  }
@@ -26559,8 +27227,8 @@ function registerTaskProjectTools(server, ctx) {
26559
27227
  tasks: exports_external2.array(exports_external2.object({
26560
27228
  title: exports_external2.string(),
26561
27229
  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(),
27230
+ status: exports_external2.enum(["pending", "in_progress", "completed", "failed", "cancelled"]).optional(),
27231
+ priority: exports_external2.enum(["low", "medium", "high", "critical"]).optional(),
26564
27232
  project_id: exports_external2.string().optional(),
26565
27233
  task_list_id: exports_external2.string().optional(),
26566
27234
  assigned_to: exports_external2.string().optional(),
@@ -26571,7 +27239,7 @@ function registerTaskProjectTools(server, ctx) {
26571
27239
  })).describe("Array of task objects")
26572
27240
  }, async ({ tasks }) => {
26573
27241
  try {
26574
- const { bulkCreateTasks: bulkCreateTasks2 } = (()=>{throw new Error("Cannot require module "+"../db/tasks.js");})();
27242
+ const { bulkCreateTasks: bulkCreateTasks2 } = (init_tasks(), __toCommonJS(exports_tasks));
26575
27243
  const resolved = tasks.map((t) => {
26576
27244
  const r = { ...t };
26577
27245
  if (r.project_id)
@@ -26585,7 +27253,7 @@ function registerTaskProjectTools(server, ctx) {
26585
27253
  return r;
26586
27254
  });
26587
27255
  const result = bulkCreateTasks2(resolved);
26588
- return { content: [{ type: "text", text: `${result.created} task(s) created, ${result.skipped} skipped (duplicate short_id).` }] };
27256
+ return { content: [{ type: "text", text: `${result.created.length} task(s) created.` }] };
26589
27257
  } catch (e) {
26590
27258
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
26591
27259
  }
@@ -26597,9 +27265,9 @@ function registerTaskProjectTools(server, ctx) {
26597
27265
  force: exports_external2.boolean().optional().describe("Skip child check for all tasks (dangerous)")
26598
27266
  }, async ({ task_ids, force }) => {
26599
27267
  try {
26600
- const { bulkDeleteTasks } = (()=>{throw new Error("Cannot require module "+"../db/tasks.js");})();
27268
+ const { bulkDeleteTasks: bulkDeleteTasks2 } = (init_tasks(), __toCommonJS(exports_tasks));
26601
27269
  const resolved = task_ids.map(resolveId);
26602
- const result = bulkDeleteTasks(resolved, force);
27270
+ const result = bulkDeleteTasks2(resolved, force);
26603
27271
  return { content: [{ type: "text", text: `${result.deleted} task(s) deleted, ${result.skipped} skipped (has children).` }] };
26604
27272
  } catch (e) {
26605
27273
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
@@ -26648,7 +27316,7 @@ function registerTaskProjectTools(server, ctx) {
26648
27316
  const project = getProject(resolvedId);
26649
27317
  if (!project)
26650
27318
  throw new TaskNotFoundError(`Project not found: ${project_id}`);
26651
- const { listTasks: listTasks3 } = (()=>{throw new Error("Cannot require module "+"../db/tasks.js");})();
27319
+ const { listTasks: listTasks3 } = (init_tasks(), __toCommonJS(exports_tasks));
26652
27320
  const tasks = listTasks3({ project_id: resolvedId, limit: 100 }, undefined);
26653
27321
  const lines = [
26654
27322
  `ID: ${project.id}`,
@@ -26749,7 +27417,7 @@ function registerTaskProjectTools(server, ctx) {
26749
27417
  throw new TaskNotFoundError(`Task list not found: ${task_list_id}`);
26750
27418
  let tasks = [];
26751
27419
  if (include_tasks) {
26752
- const { listTasks: listTasks3 } = (()=>{throw new Error("Cannot require module "+"../db/tasks.js");})();
27420
+ const { listTasks: listTasks3 } = (init_tasks(), __toCommonJS(exports_tasks));
26753
27421
  tasks = listTasks3({ task_list_id: resolvedId, limit: 200 }, undefined);
26754
27422
  }
26755
27423
  const lines = [
@@ -26849,7 +27517,7 @@ Tasks:` : null,
26849
27517
  throw new TaskNotFoundError(`Plan not found: ${plan_id}`);
26850
27518
  let tasks = [];
26851
27519
  if (include_tasks) {
26852
- const { listTasks: listTasks3 } = (()=>{throw new Error("Cannot require module "+"../db/tasks.js");})();
27520
+ const { listTasks: listTasks3 } = (init_tasks(), __toCommonJS(exports_tasks));
26853
27521
  tasks = listTasks3({ plan_id: resolvedId, limit: 200 }, undefined);
26854
27522
  }
26855
27523
  const lines = [
@@ -26938,7 +27606,7 @@ Tasks:` : null,
26938
27606
  const tag = getTag(tag_id);
26939
27607
  if (!tag)
26940
27608
  throw new TaskNotFoundError(`Tag not found: ${tag_id}`);
26941
- const { listTasks: listTasks3 } = (()=>{throw new Error("Cannot require module "+"../db/tasks.js");})();
27609
+ const { listTasks: listTasks3 } = (init_tasks(), __toCommonJS(exports_tasks));
26942
27610
  const tasks = listTasks3({ tags: [tag.name], limit: 100 }, undefined);
26943
27611
  const lines = [
26944
27612
  `Tag: ${tag.name}${tag.color ? ` (${tag.color})` : ""}`,
@@ -27017,7 +27685,7 @@ Tasks:` : null,
27017
27685
  const label = getLabel(label_id);
27018
27686
  if (!label)
27019
27687
  throw new TaskNotFoundError(`Label not found: ${label_id}`);
27020
- const { listTasks: listTasks3 } = (()=>{throw new Error("Cannot require module "+"../db/tasks.js");})();
27688
+ const { listTasks: listTasks3 } = (init_tasks(), __toCommonJS(exports_tasks));
27021
27689
  const tasks = listTasks3({ tags: [label.name], limit: 100 }, undefined);
27022
27690
  const lines = [
27023
27691
  `Label: ${label.name}${label.color ? ` (${label.color})` : ""}`,
@@ -27068,7 +27736,7 @@ Tasks:` : null,
27068
27736
  try {
27069
27737
  const resolvedId = resolveId(task_id);
27070
27738
  const resolvedAuthor = author ? resolveId(author, "agents") : undefined;
27071
- const comment = addComment({ task_id: resolvedId, body, author: resolvedAuthor });
27739
+ const comment = addComment({ task_id: resolvedId, content: body, agent_id: resolvedAuthor });
27072
27740
  return { content: [{ type: "text", text: `Comment added to ${task_id.slice(0, 8)}: ${body.slice(0, 50)}${body.length > 50 ? "..." : ""}` }] };
27073
27741
  } catch (e) {
27074
27742
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
@@ -27084,8 +27752,8 @@ Tasks:` : null,
27084
27752
  const comments = listComments(resolvedId);
27085
27753
  if (comments.length === 0)
27086
27754
  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}`);
27755
+ const lines = comments.map((c) => `[${c.agent_id || "unknown"}] ${c.created_at?.slice(0, 16)}:
27756
+ ${c.content}`);
27089
27757
  return { content: [{ type: "text", text: lines.join(`
27090
27758
 
27091
27759
  `) }] };
@@ -27100,7 +27768,7 @@ Tasks:` : null,
27100
27768
  body: exports_external2.string().describe("New comment body")
27101
27769
  }, async ({ comment_id, body }) => {
27102
27770
  try {
27103
- const comment = updateComment(comment_id, { body });
27771
+ const comment = updateComment(comment_id, { content: body });
27104
27772
  return { content: [{ type: "text", text: `Comment ${comment_id.slice(0, 8)} updated.` }] };
27105
27773
  } catch (e) {
27106
27774
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
@@ -27123,11 +27791,11 @@ Tasks:` : null,
27123
27791
  server.tool("search_tasks", "Full-text search across task titles and descriptions.", {
27124
27792
  query: exports_external2.string().describe("Search query"),
27125
27793
  project_id: exports_external2.string().optional().describe("Filter by project"),
27126
- status: exports_external2.enum(["pending", "in_progress", "completed", "cancelled"]).optional(),
27794
+ status: exports_external2.enum(["pending", "in_progress", "completed", "failed", "cancelled"]).optional(),
27127
27795
  limit: exports_external2.number().optional().describe("Max results (default: 20)")
27128
27796
  }, async ({ query, project_id, status, limit }) => {
27129
27797
  try {
27130
- const { searchTasks: searchTasks2 } = (()=>{throw new Error("Cannot require module "+"../db/tasks.js");})();
27798
+ const { searchTasks: searchTasks2 } = (init_search(), __toCommonJS(exports_search));
27131
27799
  const resolved = { query, limit };
27132
27800
  if (project_id)
27133
27801
  resolved.project_id = resolveId(project_id, "projects");
@@ -27145,29 +27813,6 @@ ${lines.join(`
27145
27813
  }
27146
27814
  });
27147
27815
  }
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
27816
  }
27172
27817
  var init_task_project_tools = __esm(() => {
27173
27818
  init_zod2();
@@ -27182,6 +27827,16 @@ var init_task_project_tools = __esm(() => {
27182
27827
  // src/mcp/tools/task-workflow-tools.ts
27183
27828
  function registerTaskWorkflowTools(server, ctx) {
27184
27829
  const { shouldRegisterTool, resolveId, formatError, formatTask } = ctx;
27830
+ function versionFor(taskId, version) {
27831
+ const { getTask: getTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
27832
+ const current = getTask2(taskId);
27833
+ if (!current)
27834
+ throw new TaskNotFoundError(taskId);
27835
+ if (version !== undefined && current.version !== version) {
27836
+ throw new VersionConflictError(taskId, version, current.version);
27837
+ }
27838
+ return current.version;
27839
+ }
27185
27840
  if (shouldRegisterTool("approve_task")) {
27186
27841
  server.tool("approve_task", "Approve a task that requires_approval. Records who approved it.", {
27187
27842
  task_id: exports_external2.string().describe("Task ID"),
@@ -27190,13 +27845,14 @@ function registerTaskWorkflowTools(server, ctx) {
27190
27845
  version: exports_external2.number().optional().describe("Expected version for optimistic locking")
27191
27846
  }, async ({ task_id, approved_by, notes, version }) => {
27192
27847
  try {
27193
- const { updateTask: updateTask2 } = (()=>{throw new Error("Cannot require module "+"../db/tasks.js");})();
27848
+ const { updateTask: updateTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
27194
27849
  const resolvedId = resolveId(task_id);
27195
27850
  const resolvedApprover = approved_by ? resolveId(approved_by, "agents") : undefined;
27196
27851
  const task = updateTask2(resolvedId, {
27197
27852
  approved_by: resolvedApprover,
27198
- metadata: notes ? { approval_notes: notes } : {}
27199
- }, version);
27853
+ metadata: notes ? { approval_notes: notes } : {},
27854
+ version: versionFor(resolvedId, version)
27855
+ });
27200
27856
  return { content: [{ type: "text", text: `Task ${task_id.slice(0, 8)} approved by ${resolvedApprover || "unknown"}` }] };
27201
27857
  } catch (e) {
27202
27858
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
@@ -27211,15 +27867,11 @@ function registerTaskWorkflowTools(server, ctx) {
27211
27867
  version: exports_external2.number().optional().describe("Expected version for optimistic locking")
27212
27868
  }, async ({ task_id, reason, agent_id, version }) => {
27213
27869
  try {
27214
- const { updateTask: updateTask2 } = (()=>{throw new Error("Cannot require module "+"../db/tasks.js");})();
27870
+ const { failTask: failTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
27215
27871
  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}` }] };
27872
+ versionFor(resolvedId, version);
27873
+ const result = failTask2(resolvedId, agent_id, reason);
27874
+ return { content: [{ type: "text", text: `Task ${task_id.slice(0, 8)} marked failed. Retry count: ${result.task.retry_count}` }] };
27223
27875
  } catch (e) {
27224
27876
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
27225
27877
  }
@@ -27228,12 +27880,12 @@ function registerTaskWorkflowTools(server, ctx) {
27228
27880
  if (shouldRegisterTool("get_my_tasks")) {
27229
27881
  server.tool("get_my_tasks", "Get tasks assigned to the calling agent. Supports focus mode scoping.", {
27230
27882
  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"),
27883
+ status: exports_external2.enum(["pending", "in_progress", "completed", "failed", "cancelled"]).optional().describe("Filter by status"),
27232
27884
  project_id: exports_external2.string().optional().describe("Filter by project (respects focus mode)"),
27233
27885
  limit: exports_external2.number().optional().describe("Max results (default: 50)")
27234
27886
  }, async ({ agent_id, status, project_id, limit }) => {
27235
27887
  try {
27236
- const { listTasks: listTasks3 } = (()=>{throw new Error("Cannot require module "+"../db/tasks.js");})();
27888
+ const { listTasks: listTasks3 } = (init_tasks(), __toCommonJS(exports_tasks));
27237
27889
  const focus = ctx.getAgentFocus(agent_id || "");
27238
27890
  const effectiveAgentId = focus ? focus.agent_id : agent_id || "";
27239
27891
  const effectiveProjectId = focus?.project_id || project_id;
@@ -27253,6 +27905,122 @@ function registerTaskWorkflowTools(server, ctx) {
27253
27905
  }
27254
27906
  });
27255
27907
  }
27908
+ if (shouldRegisterTool("get_next_task")) {
27909
+ server.tool("get_next_task", "Get the best available pending task without claiming it.", {
27910
+ agent_id: exports_external2.string().optional().describe("Agent ID or name for assignment affinity"),
27911
+ project_id: exports_external2.string().optional().describe("Filter by project"),
27912
+ task_list_id: exports_external2.string().optional().describe("Filter by task list"),
27913
+ plan_id: exports_external2.string().optional().describe("Filter by plan"),
27914
+ tags: exports_external2.array(exports_external2.string()).optional().describe("Filter by tags")
27915
+ }, async ({ agent_id, project_id, task_list_id, plan_id, tags }) => {
27916
+ try {
27917
+ const { getNextTask: getNextTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
27918
+ const filters = {};
27919
+ if (project_id)
27920
+ filters.project_id = resolveId(project_id, "projects");
27921
+ if (task_list_id)
27922
+ filters.task_list_id = resolveId(task_list_id, "task_lists");
27923
+ if (plan_id)
27924
+ filters.plan_id = resolveId(plan_id, "plans");
27925
+ if (tags)
27926
+ filters.tags = tags;
27927
+ const task = getNextTask2(agent_id, filters);
27928
+ return { content: [{ type: "text", text: task ? formatTask(task) : "No available task." }] };
27929
+ } catch (e) {
27930
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
27931
+ }
27932
+ });
27933
+ }
27934
+ if (shouldRegisterTool("claim_next_task")) {
27935
+ server.tool("claim_next_task", "Atomically claim and start the best available pending task for an agent.", {
27936
+ agent_id: exports_external2.string().describe("Agent ID or name claiming the task"),
27937
+ project_id: exports_external2.string().optional().describe("Filter by project"),
27938
+ task_list_id: exports_external2.string().optional().describe("Filter by task list"),
27939
+ plan_id: exports_external2.string().optional().describe("Filter by plan"),
27940
+ tags: exports_external2.array(exports_external2.string()).optional().describe("Filter by tags")
27941
+ }, async ({ agent_id, project_id, task_list_id, plan_id, tags }) => {
27942
+ try {
27943
+ const { claimNextTask: claimNextTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
27944
+ const filters = {};
27945
+ if (project_id)
27946
+ filters.project_id = resolveId(project_id, "projects");
27947
+ if (task_list_id)
27948
+ filters.task_list_id = resolveId(task_list_id, "task_lists");
27949
+ if (plan_id)
27950
+ filters.plan_id = resolveId(plan_id, "plans");
27951
+ if (tags)
27952
+ filters.tags = tags;
27953
+ const task = claimNextTask2(agent_id, filters);
27954
+ return { content: [{ type: "text", text: task ? formatTask(task) : "No available task to claim." }] };
27955
+ } catch (e) {
27956
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
27957
+ }
27958
+ });
27959
+ }
27960
+ if (shouldRegisterTool("get_tasks_changed_since")) {
27961
+ server.tool("get_tasks_changed_since", "List tasks changed since an ISO timestamp.", {
27962
+ since: exports_external2.string().describe("ISO timestamp"),
27963
+ project_id: exports_external2.string().optional().describe("Filter by project"),
27964
+ task_list_id: exports_external2.string().optional().describe("Filter by task list"),
27965
+ limit: exports_external2.number().optional().describe("Maximum tasks to return")
27966
+ }, async ({ since, project_id, task_list_id, limit }) => {
27967
+ try {
27968
+ const { getTasksChangedSince: getTasksChangedSince2 } = (init_tasks(), __toCommonJS(exports_tasks));
27969
+ const filters = {};
27970
+ if (project_id)
27971
+ filters.project_id = resolveId(project_id, "projects");
27972
+ if (task_list_id)
27973
+ filters.task_list_id = resolveId(task_list_id, "task_lists");
27974
+ const tasks = getTasksChangedSince2(since, filters).slice(0, limit || 50);
27975
+ if (tasks.length === 0)
27976
+ return { content: [{ type: "text", text: "No changed tasks." }] };
27977
+ return { content: [{ type: "text", text: tasks.map(formatTask).join(`
27978
+ `) }] };
27979
+ } catch (e) {
27980
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
27981
+ }
27982
+ });
27983
+ }
27984
+ function registerContextTool(name, description) {
27985
+ if (!shouldRegisterTool(name))
27986
+ return;
27987
+ server.tool(name, description, {
27988
+ agent_id: exports_external2.string().optional().describe("Agent ID or name"),
27989
+ project_id: exports_external2.string().optional().describe("Filter by project"),
27990
+ task_list_id: exports_external2.string().optional().describe("Filter by task list"),
27991
+ explain_blocked: exports_external2.boolean().optional().describe("Include blocked task details")
27992
+ }, async ({ agent_id, project_id, task_list_id, explain_blocked }) => {
27993
+ try {
27994
+ const { getStatus: getStatus2, getNextTask: getNextTask2, getOverdueTasks: getOverdueTasks2 } = (init_tasks(), __toCommonJS(exports_tasks));
27995
+ const { getLatestHandoff: getLatestHandoff2 } = (init_handoffs(), __toCommonJS(exports_handoffs));
27996
+ const filters = {};
27997
+ if (project_id)
27998
+ filters.project_id = resolveId(project_id, "projects");
27999
+ if (task_list_id)
28000
+ filters.task_list_id = resolveId(task_list_id, "task_lists");
28001
+ const status = getStatus2(filters, agent_id, { explain_blocked });
28002
+ const next_task = getNextTask2(agent_id, filters);
28003
+ const overdue = getOverdueTasks2(filters.project_id);
28004
+ const latest_handoff = getLatestHandoff2(agent_id, filters.project_id);
28005
+ return {
28006
+ content: [{
28007
+ type: "text",
28008
+ text: JSON.stringify({
28009
+ status,
28010
+ next_task,
28011
+ overdue_count: overdue.length,
28012
+ latest_handoff,
28013
+ as_of: new Date().toISOString()
28014
+ }, null, 2)
28015
+ }]
28016
+ };
28017
+ } catch (e) {
28018
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
28019
+ }
28020
+ });
28021
+ }
28022
+ registerContextTool("get_context", "Get session start context: queue status, next task, overdue count, and latest handoff.");
28023
+ registerContextTool("bootstrap", "Bootstrap an agent session with queue context and the next available task.");
27256
28024
  if (shouldRegisterTool("get_org_chart")) {
27257
28025
  server.tool("get_org_chart", "Get the global org chart (agent hierarchy + titles).", {
27258
28026
  format: exports_external2.enum(["text", "json"]).optional().describe("Output format (default: text)")
@@ -27273,7 +28041,7 @@ function registerTaskWorkflowTools(server, ctx) {
27273
28041
  }).join(`
27274
28042
  `);
27275
28043
  };
27276
- const { getOrgChart: getOrgChart2 } = (()=>{throw new Error("Cannot require module "+"../db/agents.js");})();
28044
+ const { getOrgChart: getOrgChart2 } = (init_agents(), __toCommonJS(exports_agents));
27277
28045
  const tree = getOrgChart2();
27278
28046
  if (format === "json") {
27279
28047
  return { content: [{ type: "text", text: JSON.stringify(tree, null, 2) }] };
@@ -27293,10 +28061,10 @@ function registerTaskWorkflowTools(server, ctx) {
27293
28061
  reports_to: exports_external2.string().describe("Manager agent ID or name")
27294
28062
  }, async ({ agent_id, reports_to }) => {
27295
28063
  try {
27296
- const { setReportsTo } = (()=>{throw new Error("Cannot require module "+"../db/agents.js");})();
28064
+ const { updateAgent: updateAgent2 } = (init_agents(), __toCommonJS(exports_agents));
27297
28065
  const resolvedAgent = resolveId(agent_id, "agents");
27298
28066
  const resolvedManager = resolveId(reports_to, "agents");
27299
- setReportsTo(resolvedAgent, resolvedManager);
28067
+ updateAgent2(resolvedAgent, { reports_to: resolvedManager });
27300
28068
  return { content: [{ type: "text", text: `${agent_id} now reports to ${reports_to}` }] };
27301
28069
  } catch (e) {
27302
28070
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
@@ -27306,6 +28074,7 @@ function registerTaskWorkflowTools(server, ctx) {
27306
28074
  }
27307
28075
  var init_task_workflow_tools = __esm(() => {
27308
28076
  init_zod2();
28077
+ init_types();
27309
28078
  });
27310
28079
 
27311
28080
  // src/mcp/tools/task-auto-tools.ts
@@ -27317,9 +28086,9 @@ function registerTaskAutoTools(server, ctx) {
27317
28086
  project_id: exports_external2.string().optional().describe("Scope to a project")
27318
28087
  }, async ({ days = 7, project_id }) => {
27319
28088
  try {
27320
- const { archiveCompletedTasks } = (init_tasks(), __toCommonJS(exports_tasks));
28089
+ const { archiveCompletedTasks: archiveCompletedTasks2 } = (init_tasks(), __toCommonJS(exports_tasks));
27321
28090
  const resolvedProjectId = project_id ? resolveId(project_id, "projects") : undefined;
27322
- const count = archiveCompletedTasks(days, resolvedProjectId);
28091
+ const count = archiveCompletedTasks2(days, resolvedProjectId);
27323
28092
  return { content: [{ type: "text", text: `Archived ${count} completed task(s) older than ${days} days.` }] };
27324
28093
  } catch (e) {
27325
28094
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
@@ -27345,9 +28114,9 @@ function registerTaskAutoTools(server, ctx) {
27345
28114
  limit: exports_external2.number().optional().describe("Max results (default: 50)")
27346
28115
  }, async ({ project_id, limit }) => {
27347
28116
  try {
27348
- const { getArchivedTasks } = (init_tasks(), __toCommonJS(exports_tasks));
28117
+ const { getArchivedTasks: getArchivedTasks2 } = (init_tasks(), __toCommonJS(exports_tasks));
27349
28118
  const resolvedProjectId = project_id ? resolveId(project_id, "projects") : undefined;
27350
- const tasks = getArchivedTasks({ project_id: resolvedProjectId, limit: limit || 50 });
28119
+ const tasks = getArchivedTasks2({ project_id: resolvedProjectId, limit: limit || 50 });
27351
28120
  if (tasks.length === 0)
27352
28121
  return { content: [{ type: "text", text: "No archived tasks." }] };
27353
28122
  const lines = tasks.map((t) => `${t.short_id || t.id.slice(0, 8)} ${t.title} archived ${t.archived_at}`);
@@ -27363,14 +28132,13 @@ function registerTaskAutoTools(server, ctx) {
27363
28132
  task_id: exports_external2.string().describe("Task ID")
27364
28133
  }, async ({ task_id }) => {
27365
28134
  try {
27366
- const { autoAssign } = (init_agents(), __toCommonJS(exports_agents));
27367
28135
  const resolvedId = resolveId(task_id);
27368
- const assignment = autoAssign(resolvedId);
27369
- if (!assignment)
28136
+ const { autoAssignTask: autoAssignTask2 } = (init_auto_assign(), __toCommonJS(exports_auto_assign));
28137
+ const assignment = await autoAssignTask2(resolvedId);
28138
+ if (!assignment.assigned_to)
27370
28139
  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)})` }] };
28140
+ const reason = assignment.reason ? ` \u2014 ${assignment.reason}` : "";
28141
+ return { content: [{ type: "text", text: `Task ${task_id.slice(0, 8)} assigned to ${assignment.agent_name || assignment.assigned_to} via ${assignment.method}${reason}` }] };
27374
28142
  } catch (e) {
27375
28143
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
27376
28144
  }
@@ -27381,10 +28149,23 @@ function registerTaskAutoTools(server, ctx) {
27381
28149
  agent_id: exports_external2.string().optional().describe("Agent ID (defaults to context agent)")
27382
28150
  }, async ({ agent_id }) => {
27383
28151
  try {
27384
- const { getAgentWorkload } = (init_agents(), __toCommonJS(exports_agents));
28152
+ const { listTasks: listTasks3, getBlockedTasks: getBlockedTasks2 } = (init_tasks(), __toCommonJS(exports_tasks));
27385
28153
  const focus = ctx.getAgentFocus(agent_id || "");
27386
28154
  const effectiveAgentId = focus ? focus.agent_id : agent_id || "";
27387
- const workload = getAgentWorkload(effectiveAgentId);
28155
+ if (!effectiveAgentId) {
28156
+ return { content: [{ type: "text", text: "No agent_id provided and no agent focus is active." }], isError: true };
28157
+ }
28158
+ const assigned = listTasks3({ assigned_to: effectiveAgentId, limit: 500 }, undefined);
28159
+ const now2 = Date.now();
28160
+ const dueSoonCutoff = now2 + 24 * 60 * 60 * 1000;
28161
+ const blocked = getBlockedTasks2().filter((t) => t.assigned_to === effectiveAgentId);
28162
+ const workload = {
28163
+ in_progress: assigned.filter((t) => t.status === "in_progress").length,
28164
+ pending: assigned.filter((t) => t.status === "pending").length,
28165
+ completed_recent: assigned.filter((t) => t.status === "completed" && t.completed_at && now2 - new Date(t.completed_at).getTime() <= 7 * 24 * 60 * 60 * 1000).length,
28166
+ 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,
28167
+ blocked: blocked.length
28168
+ };
27388
28169
  const lines = [
27389
28170
  `Agent: ${effectiveAgentId}`,
27390
28171
  `In Progress: ${workload.in_progress}`,
@@ -27406,10 +28187,29 @@ function registerTaskAutoTools(server, ctx) {
27406
28187
  max_per_agent: exports_external2.number().optional().describe("Max tasks per agent (default: 5)")
27407
28188
  }, async ({ project_id, max_per_agent }) => {
27408
28189
  try {
27409
- const { rebalanceWorkload } = (init_agents(), __toCommonJS(exports_agents));
28190
+ const { listAgents: listAgents2 } = (init_agents(), __toCommonJS(exports_agents));
28191
+ const { listTasks: listTasks3, updateTask: updateTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
27410
28192
  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.` }] };
28193
+ const limit = max_per_agent || 5;
28194
+ const agents = listAgents2().filter((agent) => agent.status === "active");
28195
+ if (agents.length === 0)
28196
+ return { content: [{ type: "text", text: "No active agents available for rebalancing." }] };
28197
+ const activeTasks = listTasks3({ project_id: resolvedProjectId, status: ["pending", "in_progress"], limit: 1000 }, undefined);
28198
+ const load = new Map(agents.map((agent) => [agent.id, activeTasks.filter((t) => t.assigned_to === agent.id).length]));
28199
+ let moved = 0;
28200
+ let skipped = 0;
28201
+ for (const task of activeTasks.filter((t) => t.status === "pending" && t.assigned_to && (load.get(t.assigned_to) ?? 0) > limit)) {
28202
+ const target = agents.filter((agent) => agent.id !== task.assigned_to).sort((a, b) => (load.get(a.id) ?? 0) - (load.get(b.id) ?? 0))[0];
28203
+ if (!target || (load.get(target.id) ?? 0) >= limit) {
28204
+ skipped++;
28205
+ continue;
28206
+ }
28207
+ updateTask2(task.id, { assigned_to: target.id, version: task.version });
28208
+ load.set(task.assigned_to, (load.get(task.assigned_to) ?? 1) - 1);
28209
+ load.set(target.id, (load.get(target.id) ?? 0) + 1);
28210
+ moved++;
28211
+ }
28212
+ return { content: [{ type: "text", text: `Rebalanced: moved ${moved} task(s), ${skipped} skipped.` }] };
27413
28213
  } catch (e) {
27414
28214
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
27415
28215
  }
@@ -27422,13 +28222,13 @@ function registerTaskAutoTools(server, ctx) {
27422
28222
  agent_id: exports_external2.string().optional().describe("Filter by assignee")
27423
28223
  }, async ({ hours = 24, project_id, agent_id }) => {
27424
28224
  try {
27425
- const { notifyUpcomingDeadlines } = (init_tasks(), __toCommonJS(exports_tasks));
28225
+ const { notifyUpcomingDeadlines: notifyUpcomingDeadlines2 } = (init_tasks(), __toCommonJS(exports_tasks));
27426
28226
  const resolvedProjectId = project_id ? resolveId(project_id, "projects") : undefined;
27427
28227
  const resolvedAgentId = agent_id ? resolveId(agent_id, "agents") : undefined;
27428
- const tasks = notifyUpcomingDeadlines({ hours, project_id: resolvedProjectId, agent_id: resolvedAgentId });
28228
+ const tasks = notifyUpcomingDeadlines2({ hours, project_id: resolvedProjectId, agent_id: resolvedAgentId });
27429
28229
  if (tasks.length === 0)
27430
28230
  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}`);
28231
+ const lines = tasks.map((t) => `${t.short_id || t.id.slice(0, 8)} ${t.title} due ${t.due_at}`);
27432
28232
  return { content: [{ type: "text", text: `${tasks.length} task(s) due within ${hours}h:
27433
28233
  ${lines.join(`
27434
28234
  `)}` }] };
@@ -27462,9 +28262,9 @@ ${lines.join(`
27462
28262
  project_id: exports_external2.string().optional().describe("Filter by project")
27463
28263
  }, async ({ project_id }) => {
27464
28264
  try {
27465
- const { getBlockedTasks } = (init_tasks(), __toCommonJS(exports_tasks));
28265
+ const { getBlockedTasks: getBlockedTasks2 } = (init_tasks(), __toCommonJS(exports_tasks));
27466
28266
  const resolvedProjectId = project_id ? resolveId(project_id, "projects") : undefined;
27467
- const tasks = getBlockedTasks(resolvedProjectId);
28267
+ const tasks = getBlockedTasks2(resolvedProjectId);
27468
28268
  if (tasks.length === 0)
27469
28269
  return { content: [{ type: "text", text: "No blocked tasks." }] };
27470
28270
  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 +28281,9 @@ ${lines.join(`
27481
28281
  project_id: exports_external2.string().optional().describe("Filter by project")
27482
28282
  }, async ({ project_id }) => {
27483
28283
  try {
27484
- const { getBlockingTasks } = (init_tasks(), __toCommonJS(exports_tasks));
28284
+ const { getBlockingTasks: getBlockingTasks2 } = (init_tasks(), __toCommonJS(exports_tasks));
27485
28285
  const resolvedProjectId = project_id ? resolveId(project_id, "projects") : undefined;
27486
- const tasks = getBlockingTasks(resolvedProjectId);
28286
+ const tasks = getBlockingTasks2(resolvedProjectId);
27487
28287
  if (tasks.length === 0)
27488
28288
  return { content: [{ type: "text", text: "No tasks blocking others." }] };
27489
28289
  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 +28298,18 @@ ${lines.join(`
27498
28298
  if (shouldRegisterTool("get_health")) {
27499
28299
  server.tool("get_health", "Get system health: task counts by status, active agents, project summary.", async () => {
27500
28300
  try {
27501
- const { listTasks: listTasks3 } = (init_tasks(), __toCommonJS(exports_tasks));
27502
- const { listProjects: listProjects2 } = (init_tasks(), __toCommonJS(exports_tasks));
28301
+ const { countTasks: countTasks2 } = (init_tasks(), __toCommonJS(exports_tasks));
28302
+ const { listProjects: listProjects2 } = (init_projects(), __toCommonJS(exports_projects));
27503
28303
  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 });
28304
+ const pending = countTasks2({ status: "pending" });
28305
+ const inProgress = countTasks2({ status: "in_progress" });
28306
+ const completed = countTasks2({ status: "completed" });
28307
+ const cancelled = countTasks2({ status: "cancelled" });
28308
+ const projects = listProjects2();
28309
+ const agents = listAgents2();
27512
28310
  const lines = [
27513
28311
  `=== System Health ===`,
27514
- `Tasks: ${pending.total} pending | ${inProgress.total} in progress | ${completed.total} completed | ${cancelled.total} cancelled`,
28312
+ `Tasks: ${pending} pending | ${inProgress} in progress | ${completed} completed | ${cancelled} cancelled`,
27515
28313
  `Projects: ${projects.length} total`,
27516
28314
  `Agents: ${agents.length} registered`
27517
28315
  ];
@@ -27657,17 +28455,31 @@ var init_task_relationships = __esm(() => {
27657
28455
  function registerTaskAdvTools(server, ctx) {
27658
28456
  const { shouldRegisterTool, resolveId, formatError, formatTask, formatTaskDetail } = ctx;
27659
28457
  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 }) => {
28458
+ server.tool("get_status", "Get queue status summary, or pass task_id for a task's detailed status.", {
28459
+ task_id: exports_external2.string().optional().describe("Task ID for task-specific status"),
28460
+ project_id: exports_external2.string().optional().describe("Filter summary by project"),
28461
+ task_list_id: exports_external2.string().optional().describe("Filter summary by task list"),
28462
+ agent_id: exports_external2.string().optional().describe("Agent for next-task affinity"),
28463
+ explain_blocked: exports_external2.boolean().optional().describe("Include blocked task explanations in summary")
28464
+ }, async ({ task_id, project_id, task_list_id, agent_id, explain_blocked }) => {
27663
28465
  try {
28466
+ if (!task_id) {
28467
+ const { getStatus: getStatus2 } = (init_tasks(), __toCommonJS(exports_tasks));
28468
+ const filters = {};
28469
+ if (project_id)
28470
+ filters.project_id = resolveId(project_id, "projects");
28471
+ if (task_list_id)
28472
+ filters.task_list_id = resolveId(task_list_id, "task_lists");
28473
+ const status = getStatus2(filters, agent_id, { explain_blocked });
28474
+ return { content: [{ type: "text", text: JSON.stringify(status, null, 2) }] };
28475
+ }
27664
28476
  const resolvedId = resolveId(task_id);
27665
- const { getTask: getTask4 } = (init_tasks(), __toCommonJS(exports_tasks));
27666
- const task = getTask4(resolvedId);
28477
+ const { getTask: getTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
28478
+ const task = getTask2(resolvedId);
27667
28479
  if (!task)
27668
28480
  throw new Error(`Task not found: ${task_id}`);
27669
28481
  const { getTaskDependencies: getTaskDependencies3 } = (init_tasks(), __toCommonJS(exports_tasks));
27670
- const { listComments: listComments2 } = (init_tasks(), __toCommonJS(exports_tasks));
28482
+ const { listComments: listComments2 } = (init_comments(), __toCommonJS(exports_comments));
27671
28483
  const { listTaskFiles: listTaskFiles2 } = (init_task_files(), __toCommonJS(exports_task_files));
27672
28484
  const [deps, comments, files] = await Promise.all([
27673
28485
  Promise.resolve(getTaskDependencies3(resolvedId, "both")),
@@ -27677,14 +28489,14 @@ function registerTaskAdvTools(server, ctx) {
27677
28489
  const lines = [
27678
28490
  `Status: ${task.status} | Priority: ${task.priority}`,
27679
28491
  task.assigned_to ? `Assigned: ${task.assigned_to}` : "Unassigned",
27680
- task.deadline ? `Deadline: ${task.deadline}` : null,
28492
+ task.due_at ? `Due: ${task.due_at}` : null,
27681
28493
  task.confidence != null ? `Confidence: ${task.confidence}` : null,
27682
28494
  deps.length > 0 ? `
27683
28495
  Dependencies (${deps.length}):` : null,
27684
28496
  ...deps.map((d) => ` [${d.direction}] ${d.task_id.slice(0, 8)} (${d.status})`),
27685
28497
  comments.length > 0 ? `
27686
28498
  Comments (${comments.length}):` : null,
27687
- ...comments.map((c) => ` [${c.author || "?"}] ${c.created_at?.slice(0, 16)}: ${c.body.slice(0, 80)}`),
28499
+ ...comments.map((c) => ` [${c.agent_id || "?"}] ${c.created_at?.slice(0, 16)}: ${c.content.slice(0, 80)}`),
27688
28500
  files.length > 0 ? `
27689
28501
  Files (${files.length}):` : null,
27690
28502
  ...files.map((f) => ` ${f.status} ${f.path}`)
@@ -27702,15 +28514,15 @@ Files (${files.length}):` : null,
27702
28514
  }, async ({ task_id }) => {
27703
28515
  try {
27704
28516
  const resolvedId = resolveId(task_id);
27705
- const { getTask: getTask4 } = (init_tasks(), __toCommonJS(exports_tasks));
27706
- const task = getTask4(resolvedId);
28517
+ const { getTask: getTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
28518
+ const task = getTask2(resolvedId);
27707
28519
  if (!task)
27708
28520
  throw new Error(`Task not found: ${task_id}`);
27709
28521
  const { getTaskDependencies: getTaskDependencies3 } = (init_tasks(), __toCommonJS(exports_tasks));
27710
28522
  const { getTaskRelationships: getTaskRelationships2 } = (init_task_relationships(), __toCommonJS(exports_task_relationships));
27711
- const { listComments: listComments2 } = (init_tasks(), __toCommonJS(exports_tasks));
28523
+ const { listComments: listComments2 } = (init_comments(), __toCommonJS(exports_comments));
27712
28524
  const { listTaskFiles: listTaskFiles2 } = (init_task_files(), __toCommonJS(exports_task_files));
27713
- const { getTaskCommits: getTaskCommits2 } = (init_tasks(), __toCommonJS(exports_tasks));
28525
+ const { getTaskCommits: getTaskCommits2 } = (init_task_commits(), __toCommonJS(exports_task_commits));
27714
28526
  const { getTaskWatchers: getTaskWatchers2 } = (init_tasks(), __toCommonJS(exports_tasks));
27715
28527
  const [deps, rels, comments, files, commits, watchers] = await Promise.all([
27716
28528
  Promise.resolve(getTaskDependencies3(resolvedId, "both")),
@@ -27731,7 +28543,7 @@ Files (${files.length}):` : null,
27731
28543
  task.tags?.length ? `Tags: ${task.tags.join(", ")}` : null,
27732
28544
  task.created_at ? `Created: ${task.created_at}` : null,
27733
28545
  task.updated_at ? `Updated: ${task.updated_at}` : null,
27734
- task.deadline ? `Deadline: ${task.deadline}` : null,
28546
+ task.due_at ? `Due: ${task.due_at}` : null,
27735
28547
  task.completed_at ? `Completed: ${task.completed_at}` : null,
27736
28548
  deps.length > 0 ? `
27737
28549
  --- Dependencies (${deps.length}) ---` : null,
@@ -27741,7 +28553,7 @@ Files (${files.length}):` : null,
27741
28553
  ...rels.map((r) => ` ${r.source_task_id.slice(0, 8)} --[${r.relationship_type}]--> ${r.target_task_id.slice(0, 8)}`),
27742
28554
  comments.length > 0 ? `
27743
28555
  --- Comments (${comments.length}) ---` : null,
27744
- ...comments.map((c) => ` [${c.author || "?"}] ${c.created_at?.slice(0, 16)}: ${c.body.slice(0, 120)}`),
28556
+ ...comments.map((c) => ` [${c.agent_id || "?"}] ${c.created_at?.slice(0, 16)}: ${c.content.slice(0, 120)}`),
27745
28557
  files.length > 0 ? `
27746
28558
  --- Files (${files.length}) ---` : null,
27747
28559
  ...files.map((f) => ` [${f.status}] ${f.path}`),
@@ -27790,8 +28602,8 @@ ${JSON.stringify(task.metadata, null, 2)}` : null
27790
28602
  limit: 20
27791
28603
  }, undefined);
27792
28604
  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);
28605
+ const { getBlockedTasks: getBlockedTasks2 } = (init_tasks(), __toCommonJS(exports_tasks));
28606
+ const blocked = getBlockedTasks2(effectiveProjectId ? resolveId(effectiveProjectId, "projects") : undefined).filter((t) => t.assigned_to === effectiveAgentId);
27795
28607
  const lines = [
27796
28608
  `Standup for ${effectiveAgentId} (${effectiveProjectId ? `project: ${effectiveProjectId.slice(0, 8)}` : "all projects"})`,
27797
28609
  inProgress.length > 0 ? `
@@ -27820,11 +28632,11 @@ No blocked tasks.`,
27820
28632
  agent_id: exports_external2.string().optional().describe("Agent claiming (defaults to context)")
27821
28633
  }, async ({ task_id, agent_id }) => {
27822
28634
  try {
27823
- const { updateTask: updateTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
28635
+ const { startTask: startTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
27824
28636
  const resolvedId = resolveId(task_id);
27825
28637
  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 });
28638
+ const effectiveAgent = focus ? focus.agent_id : agent_id || "mcp";
28639
+ const task = startTask2(resolvedId, effectiveAgent);
27828
28640
  return { content: [{ type: "text", text: formatTask(task) }] };
27829
28641
  } catch (e) {
27830
28642
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
@@ -27836,8 +28648,12 @@ No blocked tasks.`,
27836
28648
  task_id: exports_external2.string().describe("Task ID")
27837
28649
  }, async ({ task_id }) => {
27838
28650
  try {
27839
- const { updateTask: updateTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
27840
- const task = updateTask2(resolveId(task_id), { status: "pending", assigned_to: null });
28651
+ const { getTask: getTask2, updateTask: updateTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
28652
+ const resolvedId = resolveId(task_id);
28653
+ const current = getTask2(resolvedId);
28654
+ if (!current)
28655
+ throw new Error(`Task not found: ${task_id}`);
28656
+ const task = updateTask2(resolvedId, { status: "pending", assigned_to: null, version: current.version });
27841
28657
  return { content: [{ type: "text", text: formatTask(task) }] };
27842
28658
  } catch (e) {
27843
28659
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
@@ -27850,13 +28666,13 @@ No blocked tasks.`,
27850
28666
  minutes: exports_external2.number().describe("Additional minutes to add to estimate")
27851
28667
  }, async ({ task_id, minutes }) => {
27852
28668
  try {
27853
- const { getTask: getTask4, updateTask: updateTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
28669
+ const { getTask: getTask2, updateTask: updateTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
27854
28670
  const resolvedId = resolveId(task_id);
27855
- const task = getTask4(resolvedId);
28671
+ const task = getTask2(resolvedId);
27856
28672
  if (!task)
27857
28673
  throw new Error(`Task not found: ${task_id}`);
27858
28674
  const currentEstimate = task.estimated_minutes || 0;
27859
- const updated = updateTask2(resolvedId, { estimated_minutes: currentEstimate + minutes });
28675
+ const updated = updateTask2(resolvedId, { estimated_minutes: currentEstimate + minutes, version: task.version });
27860
28676
  return { content: [{ type: "text", text: `Estimate updated: ${currentEstimate} \u2192 ${updated.estimated_minutes} min (+${minutes})` }] };
27861
28677
  } catch (e) {
27862
28678
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
@@ -27870,10 +28686,10 @@ No blocked tasks.`,
27870
28686
  author: exports_external2.string().optional().describe("Author agent ID or name")
27871
28687
  }, async ({ task_id, body, author }) => {
27872
28688
  try {
27873
- const { createComment } = (init_tasks(), __toCommonJS(exports_tasks));
28689
+ const { addComment: addComment2 } = (init_comments(), __toCommonJS(exports_comments));
27874
28690
  const resolvedId = resolveId(task_id);
27875
28691
  const resolvedAuthor = author ? resolveId(author, "agents") : undefined;
27876
- createComment({ task_id: resolvedId, body, author: resolvedAuthor });
28692
+ addComment2({ task_id: resolvedId, content: body, agent_id: resolvedAuthor });
27877
28693
  return { content: [{ type: "text", text: `Comment added to ${task_id.slice(0, 8)}` }] };
27878
28694
  } catch (e) {
27879
28695
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
@@ -27885,11 +28701,11 @@ No blocked tasks.`,
27885
28701
  task_id: exports_external2.string().describe("Task ID")
27886
28702
  }, async ({ task_id }) => {
27887
28703
  try {
27888
- const { listComments: listComments2 } = (init_tasks(), __toCommonJS(exports_tasks));
28704
+ const { listComments: listComments2 } = (init_comments(), __toCommonJS(exports_comments));
27889
28705
  const comments = listComments2(resolveId(task_id));
27890
28706
  if (comments.length === 0)
27891
28707
  return { content: [{ type: "text", text: "No comments." }] };
27892
- const lines = comments.map((c) => `[${c.author || "?"}] ${c.created_at?.slice(0, 16)}: ${c.body}`);
28708
+ const lines = comments.map((c) => `[${c.agent_id || "?"}] ${c.created_at?.slice(0, 16)}: ${c.content}`);
27893
28709
  return { content: [{ type: "text", text: lines.join(`
27894
28710
 
27895
28711
  `) }] };
@@ -27901,7 +28717,7 @@ No blocked tasks.`,
27901
28717
  if (shouldRegisterTool("list_my_tasks")) {
27902
28718
  server.tool("list_my_tasks", "Alias for get_my_tasks.", {
27903
28719
  agent_id: exports_external2.string().optional().describe("Agent ID (defaults to context agent)"),
27904
- status: exports_external2.enum(["pending", "in_progress", "completed", "cancelled"]).optional(),
28720
+ status: exports_external2.enum(["pending", "in_progress", "completed", "failed", "cancelled"]).optional(),
27905
28721
  project_id: exports_external2.string().optional().describe("Filter by project"),
27906
28722
  limit: exports_external2.number().optional()
27907
28723
  }, async ({ agent_id, status, project_id, limit }) => {
@@ -27926,126 +28742,6 @@ No blocked tasks.`,
27926
28742
  }
27927
28743
  });
27928
28744
  }
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
28745
  }
28050
28746
  var init_task_adv_tools = __esm(() => {
28051
28747
  init_zod2();
@@ -28090,6 +28786,11 @@ function registerTaskMetaTools(server, ctx) {
28090
28786
  prioritize_task: "prioritize_task \u2014 Set priority. Params: task_id, priority, version",
28091
28787
  search_tasks: "search_tasks \u2014 Full-text search. Params: query, project_id, status, limit",
28092
28788
  get_my_tasks: "get_my_tasks \u2014 Get tasks for calling agent. Params: agent_id, status, project_id, limit",
28789
+ 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",
28790
+ 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",
28791
+ get_tasks_changed_since: "get_tasks_changed_since \u2014 List tasks changed since an ISO timestamp. Params: since, project_id, task_list_id, limit",
28792
+ get_context: "get_context \u2014 Get session start context. Params: agent_id, project_id, task_list_id, explain_blocked",
28793
+ bootstrap: "bootstrap \u2014 Bootstrap an agent session with queue context. Params: agent_id, project_id, task_list_id, explain_blocked",
28093
28794
  standup: "standup \u2014 Get standup report. Params: agent_id, project_id",
28094
28795
  patrol_tasks: "patrol_tasks \u2014 Scan for task issues. Params: stuck_minutes, confidence_threshold, project_id",
28095
28796
  get_review_queue: "get_review_queue \u2014 Get tasks needing review. Params: project_id, limit",
@@ -28147,9 +28848,20 @@ function registerTaskMetaTools(server, ctx) {
28147
28848
  get_health: "get_health \u2014 Get system health stats",
28148
28849
  approve_task: "approve_task \u2014 Approve a task. Params: task_id, approved_by, notes, version",
28149
28850
  fail_task: "fail_task \u2014 Mark task failed. Params: task_id, reason, agent_id, version",
28851
+ register_agent: "register_agent \u2014 Register an agent. Params: name, description, role, title, capabilities, session_id, working_dir, force",
28852
+ list_agents: "list_agents \u2014 List registered agents. Params: include_archived",
28853
+ get_agent: "get_agent \u2014 Get agent details. Params: agent_id, id, name",
28854
+ update_agent: "update_agent \u2014 Update an agent. Params: agent_id, id, name, description, role, title, level, capabilities, permissions, metadata",
28855
+ delete_agent: "delete_agent \u2014 Archive an agent. Params: agent_id, id, name",
28856
+ unarchive_agent: "unarchive_agent \u2014 Restore an archived agent. Params: agent_id, id, name",
28857
+ heartbeat: "heartbeat \u2014 Update an agent heartbeat. Params: agent_id",
28858
+ release_agent: "release_agent \u2014 Release an agent session/name. Params: agent_id, session_id",
28859
+ set_focus: "set_focus \u2014 Focus an agent on a project. Params: agent_id, project_id, task_list_id",
28860
+ get_focus: "get_focus \u2014 Get current agent focus. Params: agent_id",
28861
+ unfocus: "unfocus \u2014 Clear agent focus. Params: agent_id",
28862
+ suggest_agent_name: "suggest_agent_name \u2014 Suggest available agent names. Params: working_dir",
28150
28863
  get_org_chart: "get_org_chart \u2014 Get global org chart. Params: format",
28151
28864
  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
28865
  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
28866
  migrate_pg: "migrate_pg \u2014 Apply PostgreSQL migrations. Params: connection_string"
28155
28867
  };
@@ -28167,6 +28879,74 @@ var init_task_meta_tools = __esm(() => {
28167
28879
  init_zod2();
28168
28880
  });
28169
28881
 
28882
+ // src/db/file-locks.ts
28883
+ var exports_file_locks = {};
28884
+ __export(exports_file_locks, {
28885
+ unlockFile: () => unlockFile,
28886
+ lockFile: () => lockFile,
28887
+ listFileLocks: () => listFileLocks,
28888
+ forceUnlockFile: () => forceUnlockFile,
28889
+ cleanExpiredFileLocks: () => cleanExpiredFileLocks,
28890
+ checkFileLock: () => checkFileLock,
28891
+ FILE_LOCK_DEFAULT_TTL_SECONDS: () => FILE_LOCK_DEFAULT_TTL_SECONDS
28892
+ });
28893
+ function expiresAt(ttlSeconds) {
28894
+ return new Date(Date.now() + ttlSeconds * 1000).toISOString();
28895
+ }
28896
+ function cleanExpiredFileLocks(db) {
28897
+ const d = db || getDatabase();
28898
+ const result = d.run("DELETE FROM file_locks WHERE expires_at <= ?", [now()]);
28899
+ return result.changes;
28900
+ }
28901
+ function lockFile(input, db) {
28902
+ const d = db || getDatabase();
28903
+ const ttl = input.ttl_seconds ?? FILE_LOCK_DEFAULT_TTL_SECONDS;
28904
+ const expiry = expiresAt(ttl);
28905
+ const timestamp = now();
28906
+ cleanExpiredFileLocks(d);
28907
+ const existing = d.query("SELECT * FROM file_locks WHERE path = ?").get(input.path);
28908
+ if (existing) {
28909
+ if (existing.agent_id === input.agent_id) {
28910
+ d.run("UPDATE file_locks SET expires_at = ?, task_id = COALESCE(?, task_id) WHERE id = ?", [expiry, input.task_id ?? null, existing.id]);
28911
+ return d.query("SELECT * FROM file_locks WHERE id = ?").get(existing.id);
28912
+ }
28913
+ throw new LockError(input.path, existing.agent_id);
28914
+ }
28915
+ const id = uuid();
28916
+ 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]);
28917
+ return d.query("SELECT * FROM file_locks WHERE id = ?").get(id);
28918
+ }
28919
+ function unlockFile(path, agentId, db) {
28920
+ const d = db || getDatabase();
28921
+ cleanExpiredFileLocks(d);
28922
+ const result = d.run("DELETE FROM file_locks WHERE path = ? AND agent_id = ?", [path, agentId]);
28923
+ return result.changes > 0;
28924
+ }
28925
+ function checkFileLock(path, db) {
28926
+ const d = db || getDatabase();
28927
+ cleanExpiredFileLocks(d);
28928
+ return d.query("SELECT * FROM file_locks WHERE path = ?").get(path);
28929
+ }
28930
+ function listFileLocks(agentId, db) {
28931
+ const d = db || getDatabase();
28932
+ cleanExpiredFileLocks(d);
28933
+ if (agentId) {
28934
+ return d.query("SELECT * FROM file_locks WHERE agent_id = ? ORDER BY created_at DESC").all(agentId);
28935
+ }
28936
+ return d.query("SELECT * FROM file_locks ORDER BY created_at DESC").all();
28937
+ }
28938
+ function forceUnlockFile(path, db) {
28939
+ const d = db || getDatabase();
28940
+ const result = d.run("DELETE FROM file_locks WHERE path = ?", [path]);
28941
+ return result.changes > 0;
28942
+ }
28943
+ var FILE_LOCK_DEFAULT_TTL_SECONDS;
28944
+ var init_file_locks = __esm(() => {
28945
+ init_database();
28946
+ init_types();
28947
+ FILE_LOCK_DEFAULT_TTL_SECONDS = 30 * 60;
28948
+ });
28949
+
28170
28950
  // src/mcp/tools/task-resources.ts
28171
28951
  function registerTaskResources(server, ctx) {
28172
28952
  const { shouldRegisterTool, resolveId, formatError } = ctx;
@@ -28192,7 +28972,7 @@ function registerTaskResources(server, ctx) {
28192
28972
  note: exports_external2.string().optional().describe("Note about why this file is linked")
28193
28973
  }, async ({ task_id, path, paths: multiplePaths, status, agent_id, note }) => {
28194
28974
  try {
28195
- const { addTaskFile: addTaskFile2, bulkAddTaskFiles: bulkAddTaskFiles2, detectFileConflicts: detectFileConflicts2 } = (()=>{throw new Error("Cannot require module "+"../db/task-files.js");})();
28975
+ const { addTaskFile: addTaskFile2, bulkAddTaskFiles: bulkAddTaskFiles2, detectFileConflicts: detectFileConflicts2 } = (init_task_files(), __toCommonJS(exports_task_files));
28196
28976
  const resolvedId = resolveId(task_id);
28197
28977
  let addedFiles;
28198
28978
  if (multiplePaths && multiplePaths.length > 0) {
@@ -28236,7 +29016,7 @@ function registerTaskResources(server, ctx) {
28236
29016
  if (shouldRegisterTool("list_task_files")) {
28237
29017
  server.tool("list_task_files", "List all files linked to a task.", { task_id: exports_external2.string().describe("Task ID") }, async ({ task_id }) => {
28238
29018
  try {
28239
- const { listTaskFiles: listTaskFiles2 } = (()=>{throw new Error("Cannot require module "+"../db/task-files.js");})();
29019
+ const { listTaskFiles: listTaskFiles2 } = (init_task_files(), __toCommonJS(exports_task_files));
28240
29020
  const resolvedId = resolveId(task_id);
28241
29021
  const files = listTaskFiles2(resolvedId);
28242
29022
  if (files.length === 0)
@@ -28253,7 +29033,7 @@ ${lines.join(`
28253
29033
  if (shouldRegisterTool("find_tasks_by_file")) {
28254
29034
  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
29035
  try {
28256
- const { findTasksByFile: findTasksByFile2 } = (()=>{throw new Error("Cannot require module "+"../db/task-files.js");})();
29036
+ const { findTasksByFile: findTasksByFile2 } = (init_task_files(), __toCommonJS(exports_task_files));
28257
29037
  const files = findTasksByFile2(path);
28258
29038
  if (files.length === 0)
28259
29039
  return { content: [{ type: "text", text: `No tasks linked to ${path}` }] };
@@ -28273,7 +29053,7 @@ ${lines.join(`
28273
29053
  min_edits: exports_external2.number().optional().describe("Minimum edit count to include (default: 1)")
28274
29054
  }, async ({ limit, project_id, min_edits }) => {
28275
29055
  try {
28276
- const { getFileHeatMap: getFileHeatMap2 } = (()=>{throw new Error("Cannot require module "+"../db/task-files.js");})();
29056
+ const { getFileHeatMap: getFileHeatMap2 } = (init_task_files(), __toCommonJS(exports_task_files));
28277
29057
  const resolvedProjectId = project_id ? resolveId(project_id, "projects") : undefined;
28278
29058
  const results = getFileHeatMap2({ limit, project_id: resolvedProjectId, min_edits });
28279
29059
  return { content: [{ type: "text", text: JSON.stringify(results, null, 2) }] };
@@ -28287,7 +29067,7 @@ ${lines.join(`
28287
29067
  paths: exports_external2.array(exports_external2.string()).describe("Array of file paths to check")
28288
29068
  }, async ({ paths }) => {
28289
29069
  try {
28290
- const { bulkFindTasksByFiles: bulkFindTasksByFiles2 } = (()=>{throw new Error("Cannot require module "+"../db/task-files.js");})();
29070
+ const { bulkFindTasksByFiles: bulkFindTasksByFiles2 } = (init_task_files(), __toCommonJS(exports_task_files));
28291
29071
  const results = bulkFindTasksByFiles2(paths);
28292
29072
  return { content: [{ type: "text", text: JSON.stringify(results, null, 2) }] };
28293
29073
  } catch (e) {
@@ -28300,11 +29080,11 @@ ${lines.join(`
28300
29080
  project_id: exports_external2.string().optional().describe("Filter by project")
28301
29081
  }, async ({ project_id }) => {
28302
29082
  try {
28303
- const { listActiveFiles: listActiveFiles2 } = (()=>{throw new Error("Cannot require module "+"../db/task-files.js");})();
29083
+ const { listActiveFiles: listActiveFiles2 } = (init_task_files(), __toCommonJS(exports_task_files));
28304
29084
  let files = listActiveFiles2();
28305
29085
  if (project_id) {
28306
29086
  const pid = resolveId(project_id, "projects");
28307
- const db = (()=>{throw new Error("Cannot require module "+"../db/database.js");})().getDatabase();
29087
+ const db = (init_database(), __toCommonJS(exports_database)).getDatabase();
28308
29088
  files = db.query(`
28309
29089
  SELECT
28310
29090
  tf.path,
@@ -28386,8 +29166,8 @@ ${lines.join(`
28386
29166
  ttl_seconds: exports_external2.number().optional().describe("Lock TTL in seconds (default: 1800 = 30 min)")
28387
29167
  }, async ({ path, agent_id, task_id, ttl_seconds }) => {
28388
29168
  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 });
29169
+ const { lockFile: lockFile2 } = (init_file_locks(), __toCommonJS(exports_file_locks));
29170
+ const lock = lockFile2({ path, agent_id, task_id, ttl_seconds });
28391
29171
  return { content: [{ type: "text", text: JSON.stringify(lock, null, 2) }] };
28392
29172
  } catch (e) {
28393
29173
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
@@ -28400,8 +29180,8 @@ ${lines.join(`
28400
29180
  agent_id: exports_external2.string().describe("Agent releasing the lock (must be the lock holder)")
28401
29181
  }, async ({ path, agent_id }) => {
28402
29182
  try {
28403
- const { unlockFile } = (()=>{throw new Error("Cannot require module "+"../db/file-locks.js");})();
28404
- const released = unlockFile(path, agent_id);
29183
+ const { unlockFile: unlockFile2 } = (init_file_locks(), __toCommonJS(exports_file_locks));
29184
+ const released = unlockFile2(path, agent_id);
28405
29185
  return { content: [{ type: "text", text: JSON.stringify({ released, path }) }] };
28406
29186
  } catch (e) {
28407
29187
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
@@ -28413,8 +29193,8 @@ ${lines.join(`
28413
29193
  path: exports_external2.string().describe("File path to check")
28414
29194
  }, async ({ path }) => {
28415
29195
  try {
28416
- const { checkFileLock } = (()=>{throw new Error("Cannot require module "+"../db/file-locks.js");})();
28417
- const lock = checkFileLock(path);
29196
+ const { checkFileLock: checkFileLock2 } = (init_file_locks(), __toCommonJS(exports_file_locks));
29197
+ const lock = checkFileLock2(path);
28418
29198
  if (!lock)
28419
29199
  return { content: [{ type: "text", text: JSON.stringify({ path, locked: false }) }] };
28420
29200
  return { content: [{ type: "text", text: JSON.stringify({ path, locked: true, ...lock }) }] };
@@ -28428,8 +29208,8 @@ ${lines.join(`
28428
29208
  agent_id: exports_external2.string().optional().describe("Filter locks by agent")
28429
29209
  }, async ({ agent_id }) => {
28430
29210
  try {
28431
- const { listFileLocks } = (()=>{throw new Error("Cannot require module "+"../db/file-locks.js");})();
28432
- const locks = listFileLocks(agent_id);
29211
+ const { listFileLocks: listFileLocks2 } = (init_file_locks(), __toCommonJS(exports_file_locks));
29212
+ const locks = listFileLocks2(agent_id);
28433
29213
  return { content: [{ type: "text", text: JSON.stringify(locks, null, 2) }] };
28434
29214
  } catch (e) {
28435
29215
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
@@ -28445,49 +29225,576 @@ var init_task_resources = __esm(() => {
28445
29225
  init_task_commits();
28446
29226
  });
28447
29227
 
28448
- // src/mcp/tools/task-rel-tools.ts
28449
- function registerTaskRelTools(server, ctx) {
28450
- const { shouldRegisterTool, resolveId, formatError, formatTask } = ctx;
28451
- if (shouldRegisterTool("create_handoff")) {
28452
- server.tool("create_handoff", "Create a session handoff note for agent coordination.", {
28453
- agent_id: exports_external2.string().optional().describe("Agent creating the handoff"),
28454
- project_id: exports_external2.string().optional().describe("Project ID"),
28455
- summary: exports_external2.string().describe("What was accomplished this session"),
28456
- completed: exports_external2.array(exports_external2.string()).optional().describe("Items completed"),
28457
- in_progress: exports_external2.array(exports_external2.string()).optional().describe("Items still in progress"),
28458
- blockers: exports_external2.array(exports_external2.string()).optional().describe("Blocking issues"),
28459
- next_steps: exports_external2.array(exports_external2.string()).optional().describe("Recommended next actions")
28460
- }, async ({ agent_id, project_id, summary, completed, in_progress, blockers, next_steps }) => {
28461
- try {
28462
- const { createHandoff: createHandoff2 } = (()=>{throw new Error("Cannot require module "+"../db/handoffs.js");})();
28463
- const handoff = createHandoff2({
28464
- agent_id,
28465
- project_id: project_id ? resolveId(project_id, "projects") : undefined,
28466
- summary,
28467
- completed,
28468
- in_progress,
28469
- blockers,
28470
- next_steps
28471
- });
28472
- return { content: [{ type: "text", text: `Handoff created: ${handoff.id.slice(0, 8)} by ${handoff.agent_id || "unknown"}` }] };
28473
- } catch (e) {
28474
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
29228
+ // src/db/kg.ts
29229
+ var exports_kg = {};
29230
+ __export(exports_kg, {
29231
+ syncKgEdges: () => syncKgEdges,
29232
+ removeKgEdges: () => removeKgEdges,
29233
+ getRelated: () => getRelated,
29234
+ getImpactAnalysis: () => getImpactAnalysis,
29235
+ getCriticalPath: () => getCriticalPath,
29236
+ findPath: () => findPath,
29237
+ addKgEdge: () => addKgEdge
29238
+ });
29239
+ function rowToEdge(row) {
29240
+ return {
29241
+ ...row,
29242
+ metadata: JSON.parse(row.metadata || "{}")
29243
+ };
29244
+ }
29245
+ function syncKgEdges(db) {
29246
+ const d = db || getDatabase();
29247
+ let synced = 0;
29248
+ const tx = d.transaction(() => {
29249
+ const deps = d.query("SELECT task_id, depends_on FROM task_dependencies").all();
29250
+ for (const dep of deps) {
29251
+ synced += upsertEdge(d, dep.task_id, "task", dep.depends_on, "task", "depends_on");
29252
+ }
29253
+ const assignments = d.query("SELECT id, assigned_to FROM tasks WHERE assigned_to IS NOT NULL").all();
29254
+ for (const a of assignments) {
29255
+ synced += upsertEdge(d, a.id, "task", a.assigned_to, "agent", "assigned_to");
29256
+ }
29257
+ const agents = d.query("SELECT id, reports_to FROM agents WHERE reports_to IS NOT NULL").all();
29258
+ for (const a of agents) {
29259
+ synced += upsertEdge(d, a.id, "agent", a.reports_to, "agent", "reports_to");
29260
+ }
29261
+ const files = d.query("SELECT task_id, path FROM task_files WHERE status != 'removed'").all();
29262
+ for (const f of files) {
29263
+ synced += upsertEdge(d, f.task_id, "task", f.path, "file", "references_file");
29264
+ }
29265
+ const taskProjects = d.query("SELECT id, project_id FROM tasks WHERE project_id IS NOT NULL").all();
29266
+ for (const tp of taskProjects) {
29267
+ synced += upsertEdge(d, tp.id, "task", tp.project_id, "project", "in_project");
29268
+ }
29269
+ const taskPlans = d.query("SELECT id, plan_id FROM tasks WHERE plan_id IS NOT NULL").all();
29270
+ for (const tp of taskPlans) {
29271
+ synced += upsertEdge(d, tp.id, "task", tp.plan_id, "plan", "in_plan");
29272
+ }
29273
+ try {
29274
+ const rels = d.query("SELECT source_task_id, target_task_id, relationship_type FROM task_relationships").all();
29275
+ for (const r of rels) {
29276
+ synced += upsertEdge(d, r.source_task_id, "task", r.target_task_id, "task", r.relationship_type);
28475
29277
  }
28476
- });
29278
+ } catch {}
29279
+ });
29280
+ tx();
29281
+ return { synced };
29282
+ }
29283
+ function upsertEdge(d, sourceId, sourceType, targetId, targetType, relationType, weight = 1) {
29284
+ try {
29285
+ d.run(`INSERT OR IGNORE INTO kg_edges (id, source_id, source_type, target_id, target_type, relation_type, weight, metadata, created_at)
29286
+ VALUES (?, ?, ?, ?, ?, ?, ?, '{}', ?)`, [uuid(), sourceId, sourceType, targetId, targetType, relationType, weight, now()]);
29287
+ return 1;
29288
+ } catch {
29289
+ return 0;
28477
29290
  }
28478
- if (shouldRegisterTool("get_latest_handoff")) {
28479
- server.tool("get_latest_handoff", "Get the most recent handoff for an agent or project.", {
28480
- agent_id: exports_external2.string().optional().describe("Filter by agent"),
28481
- project_id: exports_external2.string().optional().describe("Filter by project")
28482
- }, async ({ agent_id, project_id }) => {
28483
- try {
28484
- const { getLatestHandoff: getLatestHandoff2 } = (()=>{throw new Error("Cannot require module "+"../db/handoffs.js");})();
28485
- const handoff = getLatestHandoff2(agent_id, project_id ? resolveId(project_id, "projects") : undefined);
28486
- if (!handoff)
28487
- return { content: [{ type: "text", text: "No handoffs found." }] };
28488
- const lines = [
28489
- `${handoff.created_at.slice(0, 16)} ${handoff.agent_id || "unknown"}`,
28490
- handoff.summary
29291
+ }
29292
+ function getRelated(entityId, opts, db) {
29293
+ const d = db || getDatabase();
29294
+ const direction = opts?.direction || "both";
29295
+ const conditions = [];
29296
+ const params = [];
29297
+ if (direction === "outgoing" || direction === "both") {
29298
+ conditions.push("source_id = ?");
29299
+ params.push(entityId);
29300
+ }
29301
+ if (direction === "incoming" || direction === "both") {
29302
+ conditions.push("target_id = ?");
29303
+ params.push(entityId);
29304
+ }
29305
+ let sql = `SELECT * FROM kg_edges WHERE (${conditions.join(" OR ")})`;
29306
+ if (opts?.relation_type) {
29307
+ sql += " AND relation_type = ?";
29308
+ params.push(opts.relation_type);
29309
+ }
29310
+ if (opts?.entity_type) {
29311
+ sql += " AND (source_type = ? OR target_type = ?)";
29312
+ params.push(opts.entity_type, opts.entity_type);
29313
+ }
29314
+ sql += " ORDER BY weight DESC, created_at DESC";
29315
+ if (opts?.limit) {
29316
+ sql += " LIMIT ?";
29317
+ params.push(opts.limit);
29318
+ }
29319
+ return d.query(sql).all(...params).map(rowToEdge);
29320
+ }
29321
+ function findPath(sourceId, targetId, opts, db) {
29322
+ const d = db || getDatabase();
29323
+ const maxDepth = opts?.max_depth || 5;
29324
+ const visited = new Set;
29325
+ const queue = [{ id: sourceId, path: [] }];
29326
+ const results = [];
29327
+ visited.add(sourceId);
29328
+ while (queue.length > 0) {
29329
+ const current = queue.shift();
29330
+ if (current.path.length >= maxDepth)
29331
+ continue;
29332
+ let sql = "SELECT * FROM kg_edges WHERE source_id = ?";
29333
+ const params = [current.id];
29334
+ if (opts?.relation_types && opts.relation_types.length > 0) {
29335
+ const placeholders = opts.relation_types.map(() => "?").join(",");
29336
+ sql += ` AND relation_type IN (${placeholders})`;
29337
+ params.push(...opts.relation_types);
29338
+ }
29339
+ const edges = d.query(sql).all(...params).map(rowToEdge);
29340
+ for (const edge of edges) {
29341
+ const nextId = edge.target_id;
29342
+ const newPath = [...current.path, edge];
29343
+ if (nextId === targetId) {
29344
+ results.push(newPath);
29345
+ if (results.length >= 3)
29346
+ return results;
29347
+ continue;
29348
+ }
29349
+ if (!visited.has(nextId)) {
29350
+ visited.add(nextId);
29351
+ queue.push({ id: nextId, path: newPath });
29352
+ }
29353
+ }
29354
+ }
29355
+ return results;
29356
+ }
29357
+ function getImpactAnalysis(entityId, opts, db) {
29358
+ const d = db || getDatabase();
29359
+ const maxDepth = opts?.max_depth || 3;
29360
+ const results = [];
29361
+ const visited = new Set;
29362
+ visited.add(entityId);
29363
+ const queue = [{ id: entityId, depth: 0 }];
29364
+ while (queue.length > 0) {
29365
+ const current = queue.shift();
29366
+ if (current.depth >= maxDepth)
29367
+ continue;
29368
+ let sql = "SELECT * FROM kg_edges WHERE source_id = ?";
29369
+ const params = [current.id];
29370
+ if (opts?.relation_types && opts.relation_types.length > 0) {
29371
+ const placeholders = opts.relation_types.map(() => "?").join(",");
29372
+ sql += ` AND relation_type IN (${placeholders})`;
29373
+ params.push(...opts.relation_types);
29374
+ }
29375
+ const edges = d.query(sql).all(...params).map(rowToEdge);
29376
+ for (const edge of edges) {
29377
+ if (!visited.has(edge.target_id)) {
29378
+ visited.add(edge.target_id);
29379
+ results.push({
29380
+ entity_id: edge.target_id,
29381
+ entity_type: edge.target_type,
29382
+ depth: current.depth + 1,
29383
+ relation: edge.relation_type
29384
+ });
29385
+ queue.push({ id: edge.target_id, depth: current.depth + 1 });
29386
+ }
29387
+ }
29388
+ }
29389
+ return results;
29390
+ }
29391
+ function getCriticalPath(opts, db) {
29392
+ const d = db || getDatabase();
29393
+ let sql = `SELECT source_id, target_id FROM kg_edges WHERE relation_type = 'depends_on'`;
29394
+ const params = [];
29395
+ if (opts?.project_id) {
29396
+ sql += ` AND source_id IN (SELECT id FROM tasks WHERE project_id = ?)`;
29397
+ params.push(opts.project_id);
29398
+ }
29399
+ const edges = d.query(sql).all(...params);
29400
+ const blocks = new Map;
29401
+ for (const e of edges) {
29402
+ if (!blocks.has(e.target_id))
29403
+ blocks.set(e.target_id, new Set);
29404
+ blocks.get(e.target_id).add(e.source_id);
29405
+ }
29406
+ const results = [];
29407
+ for (const [taskId] of blocks) {
29408
+ const visited = new Set;
29409
+ const q = [taskId];
29410
+ let maxDepth = 0;
29411
+ let depth = 0;
29412
+ let levelSize = q.length;
29413
+ while (q.length > 0) {
29414
+ const node = q.shift();
29415
+ levelSize--;
29416
+ const downstream = blocks.get(node);
29417
+ if (downstream) {
29418
+ for (const d2 of downstream) {
29419
+ if (!visited.has(d2)) {
29420
+ visited.add(d2);
29421
+ q.push(d2);
29422
+ }
29423
+ }
29424
+ }
29425
+ if (levelSize === 0) {
29426
+ depth++;
29427
+ maxDepth = Math.max(maxDepth, depth);
29428
+ levelSize = q.length;
29429
+ }
29430
+ }
29431
+ if (visited.size > 0) {
29432
+ results.push({ task_id: taskId, blocking_count: visited.size, depth: maxDepth });
29433
+ }
29434
+ }
29435
+ results.sort((a, b) => b.blocking_count - a.blocking_count);
29436
+ return results.slice(0, opts?.limit || 20);
29437
+ }
29438
+ function addKgEdge(sourceId, sourceType, targetId, targetType, relationType, weight = 1, metadata, db) {
29439
+ const d = db || getDatabase();
29440
+ const id = uuid();
29441
+ const timestamp = now();
29442
+ d.run(`INSERT OR IGNORE INTO kg_edges (id, source_id, source_type, target_id, target_type, relation_type, weight, metadata, created_at)
29443
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`, [id, sourceId, sourceType, targetId, targetType, relationType, weight, JSON.stringify(metadata || {}), timestamp]);
29444
+ return { id, source_id: sourceId, source_type: sourceType, target_id: targetId, target_type: targetType, relation_type: relationType, weight, metadata: metadata || {}, created_at: timestamp };
29445
+ }
29446
+ function removeKgEdges(sourceId, targetId, relationType, db) {
29447
+ const d = db || getDatabase();
29448
+ if (relationType) {
29449
+ return d.run("DELETE FROM kg_edges WHERE source_id = ? AND target_id = ? AND relation_type = ?", [sourceId, targetId, relationType]).changes;
29450
+ }
29451
+ return d.run("DELETE FROM kg_edges WHERE source_id = ? AND target_id = ?", [sourceId, targetId]).changes;
29452
+ }
29453
+ var init_kg = __esm(() => {
29454
+ init_database();
29455
+ });
29456
+
29457
+ // src/db/project-agent-roles.ts
29458
+ var exports_project_agent_roles = {};
29459
+ __export(exports_project_agent_roles, {
29460
+ setProjectAgentRole: () => setProjectAgentRole,
29461
+ removeProjectAgentRole: () => removeProjectAgentRole,
29462
+ listProjectAgentRoles: () => listProjectAgentRoles,
29463
+ getProjectOrgChart: () => getProjectOrgChart,
29464
+ getAgentProjectRoles: () => getAgentProjectRoles
29465
+ });
29466
+ function rowToRole(row) {
29467
+ return { ...row, is_lead: row.is_lead === 1 };
29468
+ }
29469
+ function setProjectAgentRole(projectId, agentId, role, isLead = false, db) {
29470
+ const d = db || getDatabase();
29471
+ const existing = d.query("SELECT * FROM project_agent_roles WHERE project_id = ? AND agent_id = ? AND role = ?").get(projectId, agentId, role);
29472
+ if (existing) {
29473
+ d.run("UPDATE project_agent_roles SET is_lead = ? WHERE id = ?", [isLead ? 1 : 0, existing.id]);
29474
+ return rowToRole(d.query("SELECT * FROM project_agent_roles WHERE id = ?").get(existing.id));
29475
+ }
29476
+ const id = uuid();
29477
+ 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()]);
29478
+ return rowToRole(d.query("SELECT * FROM project_agent_roles WHERE id = ?").get(id));
29479
+ }
29480
+ function removeProjectAgentRole(projectId, agentId, role, db) {
29481
+ const d = db || getDatabase();
29482
+ if (role) {
29483
+ return d.run("DELETE FROM project_agent_roles WHERE project_id = ? AND agent_id = ? AND role = ?", [projectId, agentId, role]).changes;
29484
+ }
29485
+ return d.run("DELETE FROM project_agent_roles WHERE project_id = ? AND agent_id = ?", [projectId, agentId]).changes;
29486
+ }
29487
+ function listProjectAgentRoles(projectId, db) {
29488
+ const d = db || getDatabase();
29489
+ return d.query("SELECT * FROM project_agent_roles WHERE project_id = ? ORDER BY role, created_at").all(projectId).map(rowToRole);
29490
+ }
29491
+ function getAgentProjectRoles(agentId, db) {
29492
+ const d = db || getDatabase();
29493
+ return d.query("SELECT * FROM project_agent_roles WHERE agent_id = ? ORDER BY project_id, role").all(agentId).map(rowToRole);
29494
+ }
29495
+ function getProjectOrgChart(projectId, opts, db) {
29496
+ const d = db || getDatabase();
29497
+ const globalTree = getOrgChart(d);
29498
+ const projectRoles = listProjectAgentRoles(projectId, d);
29499
+ const rolesByAgent = new Map;
29500
+ for (const pr of projectRoles) {
29501
+ if (!rolesByAgent.has(pr.agent_id))
29502
+ rolesByAgent.set(pr.agent_id, { roles: [], isLead: false });
29503
+ const entry = rolesByAgent.get(pr.agent_id);
29504
+ entry.roles.push(pr.role);
29505
+ if (pr.is_lead)
29506
+ entry.isLead = true;
29507
+ }
29508
+ function augmentTree(nodes) {
29509
+ return nodes.map((n) => {
29510
+ const override = rolesByAgent.get(n.agent.id);
29511
+ return {
29512
+ ...n,
29513
+ reports: augmentTree(n.reports),
29514
+ project_roles: override?.roles ?? [],
29515
+ is_project_lead: override?.isLead ?? false
29516
+ };
29517
+ }).filter((n) => {
29518
+ if (!opts?.filter_to_project)
29519
+ return true;
29520
+ const hasRole = n.project_roles.length > 0;
29521
+ const hasDescendant = n.reports.length > 0;
29522
+ return hasRole || hasDescendant;
29523
+ });
29524
+ }
29525
+ return augmentTree(globalTree);
29526
+ }
29527
+ var init_project_agent_roles = __esm(() => {
29528
+ init_database();
29529
+ init_agents();
29530
+ });
29531
+
29532
+ // src/db/patrol.ts
29533
+ var exports_patrol = {};
29534
+ __export(exports_patrol, {
29535
+ patrolTasks: () => patrolTasks,
29536
+ getReviewQueue: () => getReviewQueue
29537
+ });
29538
+ function rowToTask3(row) {
29539
+ return {
29540
+ ...row,
29541
+ status: row.status,
29542
+ priority: row.priority,
29543
+ tags: JSON.parse(row.tags || "[]"),
29544
+ metadata: JSON.parse(row.metadata || "{}"),
29545
+ requires_approval: Boolean(row.requires_approval)
29546
+ };
29547
+ }
29548
+ function patrolTasks(opts, db) {
29549
+ const d = db || getDatabase();
29550
+ const stuckMinutes = opts?.stuck_minutes || 60;
29551
+ const confidenceThreshold = opts?.confidence_threshold || 0.5;
29552
+ const issues = [];
29553
+ let projectFilter = "";
29554
+ const projectParams = [];
29555
+ if (opts?.project_id) {
29556
+ projectFilter = " AND project_id = ?";
29557
+ projectParams.push(opts.project_id);
29558
+ }
29559
+ const stuckCutoff = new Date(Date.now() - stuckMinutes * 60 * 1000).toISOString();
29560
+ const stuckRows = d.query(`SELECT * FROM tasks WHERE status = 'in_progress' AND updated_at < ?${projectFilter} ORDER BY updated_at ASC`).all(stuckCutoff, ...projectParams);
29561
+ for (const row of stuckRows) {
29562
+ const task = rowToTask3(row);
29563
+ const minutesStuck = Math.round((Date.now() - new Date(task.updated_at).getTime()) / 60000);
29564
+ issues.push({
29565
+ type: "stuck",
29566
+ task_id: task.id,
29567
+ task_title: task.title,
29568
+ severity: minutesStuck > 480 ? "critical" : minutesStuck > 120 ? "high" : "medium",
29569
+ detail: `In progress for ${minutesStuck} minutes without update`
29570
+ });
29571
+ }
29572
+ 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);
29573
+ for (const row of lowConfRows) {
29574
+ const task = rowToTask3(row);
29575
+ issues.push({
29576
+ type: "low_confidence",
29577
+ task_id: task.id,
29578
+ task_title: task.title,
29579
+ severity: (task.confidence ?? 0) < 0.3 ? "high" : "medium",
29580
+ detail: `Completed with confidence ${task.confidence} (threshold: ${confidenceThreshold})`
29581
+ });
29582
+ }
29583
+ 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();
29584
+ for (const row of orphanedRows) {
29585
+ const task = rowToTask3(row);
29586
+ issues.push({
29587
+ type: "orphaned",
29588
+ task_id: task.id,
29589
+ task_title: task.title,
29590
+ severity: "low",
29591
+ detail: "Pending task with no project, no agent, and no assignee"
29592
+ });
29593
+ }
29594
+ 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);
29595
+ for (const row of needsReviewRows) {
29596
+ const task = rowToTask3(row);
29597
+ issues.push({
29598
+ type: "needs_review",
29599
+ task_id: task.id,
29600
+ task_title: task.title,
29601
+ severity: "medium",
29602
+ detail: "Completed but requires approval \u2014 not yet reviewed"
29603
+ });
29604
+ }
29605
+ const pendingWithDeps = d.query(`SELECT t.* FROM tasks t
29606
+ WHERE t.status = 'pending'${projectFilter}
29607
+ AND t.id IN (SELECT task_id FROM task_dependencies)`).all(...projectParams);
29608
+ for (const row of pendingWithDeps) {
29609
+ const deps = d.query(`SELECT d.depends_on, t.status FROM task_dependencies d
29610
+ JOIN tasks t ON t.id = d.depends_on
29611
+ WHERE d.task_id = ?`).all(row.id);
29612
+ if (deps.length > 0 && deps.every((dep) => dep.status === "failed" || dep.status === "cancelled")) {
29613
+ const task = rowToTask3(row);
29614
+ issues.push({
29615
+ type: "zombie_blocked",
29616
+ task_id: task.id,
29617
+ task_title: task.title,
29618
+ severity: "high",
29619
+ detail: `Blocked by ${deps.length} task(s) that are all failed/cancelled \u2014 will never unblock`
29620
+ });
29621
+ }
29622
+ }
29623
+ const severityOrder = { critical: 0, high: 1, medium: 2, low: 3 };
29624
+ issues.sort((a, b) => severityOrder[a.severity] - severityOrder[b.severity]);
29625
+ return {
29626
+ issues,
29627
+ total_issues: issues.length,
29628
+ scanned_at: new Date().toISOString()
29629
+ };
29630
+ }
29631
+ function getReviewQueue(opts, db) {
29632
+ const d = db || getDatabase();
29633
+ let sql = `SELECT * FROM tasks WHERE status = 'completed' AND (
29634
+ (requires_approval = 1 AND approved_by IS NULL)
29635
+ OR (confidence IS NOT NULL AND confidence < 0.5)
29636
+ )`;
29637
+ const params = [];
29638
+ if (opts?.project_id) {
29639
+ sql += " AND project_id = ?";
29640
+ params.push(opts.project_id);
29641
+ }
29642
+ sql += " ORDER BY CASE WHEN requires_approval = 1 AND approved_by IS NULL THEN 0 ELSE 1 END, confidence ASC";
29643
+ if (opts?.limit) {
29644
+ sql += " LIMIT ?";
29645
+ params.push(opts.limit);
29646
+ }
29647
+ return d.query(sql).all(...params).map(rowToTask3);
29648
+ }
29649
+ var init_patrol = __esm(() => {
29650
+ init_database();
29651
+ });
29652
+
29653
+ // src/db/agent-metrics.ts
29654
+ var exports_agent_metrics = {};
29655
+ __export(exports_agent_metrics, {
29656
+ scoreTask: () => scoreTask,
29657
+ getLeaderboard: () => getLeaderboard,
29658
+ getAgentMetrics: () => getAgentMetrics
29659
+ });
29660
+ function getAgentMetrics(agentId, opts, db) {
29661
+ const d = db || getDatabase();
29662
+ const agent = d.query("SELECT id, name FROM agents WHERE id = ? OR LOWER(name) = LOWER(?)").get(agentId, agentId);
29663
+ if (!agent)
29664
+ return null;
29665
+ let projectFilter = "";
29666
+ const params = [agent.id, agent.id];
29667
+ if (opts?.project_id) {
29668
+ projectFilter = " AND project_id = ?";
29669
+ params.push(opts.project_id);
29670
+ }
29671
+ const completed = d.query(`SELECT COUNT(*) as count FROM tasks WHERE (agent_id = ? OR assigned_to = ?) AND status = 'completed'${projectFilter}`).get(...params).count;
29672
+ const failed = d.query(`SELECT COUNT(*) as count FROM tasks WHERE (agent_id = ? OR assigned_to = ?) AND status = 'failed'${projectFilter}`).get(...params).count;
29673
+ const inProgress = d.query(`SELECT COUNT(*) as count FROM tasks WHERE (agent_id = ? OR assigned_to = ?) AND status = 'in_progress'${projectFilter}`).get(...params).count;
29674
+ const total = completed + failed;
29675
+ const completionRate = total > 0 ? completed / total : 0;
29676
+ const avgTime = d.query(`SELECT AVG(
29677
+ (julianday(completed_at) - julianday(created_at)) * 24 * 60
29678
+ ) as avg_minutes
29679
+ FROM tasks
29680
+ WHERE (agent_id = ? OR assigned_to = ?) AND status = 'completed' AND completed_at IS NOT NULL${projectFilter}`).get(...params);
29681
+ const avgConf = d.query(`SELECT AVG(confidence) as avg_confidence
29682
+ FROM tasks
29683
+ WHERE (agent_id = ? OR assigned_to = ?) AND status = 'completed' AND confidence IS NOT NULL${projectFilter}`).get(...params);
29684
+ const reviewTasks = d.query(`SELECT metadata FROM tasks
29685
+ WHERE (agent_id = ? OR assigned_to = ?) AND status = 'completed'${projectFilter}
29686
+ AND metadata LIKE '%_review_score%'`).all(...params);
29687
+ let reviewScoreAvg = null;
29688
+ if (reviewTasks.length > 0) {
29689
+ let total2 = 0;
29690
+ let count = 0;
29691
+ for (const row of reviewTasks) {
29692
+ try {
29693
+ const meta = JSON.parse(row.metadata);
29694
+ if (typeof meta._review_score === "number") {
29695
+ total2 += meta._review_score;
29696
+ count++;
29697
+ }
29698
+ } catch {}
29699
+ }
29700
+ if (count > 0)
29701
+ reviewScoreAvg = total2 / count;
29702
+ }
29703
+ const speedScore = avgTime?.avg_minutes != null ? Math.max(0, 1 - avgTime.avg_minutes / (60 * 24)) : 0.5;
29704
+ const confidenceScore = avgConf?.avg_confidence ?? 0.5;
29705
+ const volumeScore = Math.min(1, completed / 50);
29706
+ const compositeScore = completionRate * 0.3 + speedScore * 0.2 + confidenceScore * 0.3 + volumeScore * 0.2;
29707
+ return {
29708
+ agent_id: agent.id,
29709
+ agent_name: agent.name,
29710
+ tasks_completed: completed,
29711
+ tasks_failed: failed,
29712
+ tasks_in_progress: inProgress,
29713
+ completion_rate: Math.round(completionRate * 1000) / 1000,
29714
+ avg_completion_minutes: avgTime?.avg_minutes != null ? Math.round(avgTime.avg_minutes * 10) / 10 : null,
29715
+ avg_confidence: avgConf?.avg_confidence != null ? Math.round(avgConf.avg_confidence * 1000) / 1000 : null,
29716
+ review_score_avg: reviewScoreAvg != null ? Math.round(reviewScoreAvg * 1000) / 1000 : null,
29717
+ composite_score: Math.round(compositeScore * 1000) / 1000
29718
+ };
29719
+ }
29720
+ function getLeaderboard(opts, db) {
29721
+ const d = db || getDatabase();
29722
+ const agents = d.query("SELECT id FROM agents ORDER BY name").all();
29723
+ const entries = [];
29724
+ for (const agent of agents) {
29725
+ const metrics = getAgentMetrics(agent.id, { project_id: opts?.project_id }, d);
29726
+ if (metrics && (metrics.tasks_completed > 0 || metrics.tasks_failed > 0 || metrics.tasks_in_progress > 0)) {
29727
+ entries.push(metrics);
29728
+ }
29729
+ }
29730
+ entries.sort((a, b) => b.composite_score - a.composite_score);
29731
+ const limit = opts?.limit || 20;
29732
+ return entries.slice(0, limit).map((entry, idx) => ({
29733
+ ...entry,
29734
+ rank: idx + 1
29735
+ }));
29736
+ }
29737
+ function scoreTask(taskId, score, reviewerId, db) {
29738
+ const d = db || getDatabase();
29739
+ if (score < 0 || score > 1)
29740
+ throw new Error("Score must be between 0 and 1");
29741
+ const task = d.query("SELECT metadata FROM tasks WHERE id = ?").get(taskId);
29742
+ if (!task)
29743
+ throw new Error(`Task not found: ${taskId}`);
29744
+ const metadata = JSON.parse(task.metadata || "{}");
29745
+ metadata._review_score = score;
29746
+ if (reviewerId)
29747
+ metadata._reviewed_by = reviewerId;
29748
+ metadata._reviewed_at = now();
29749
+ d.run("UPDATE tasks SET metadata = ?, updated_at = ? WHERE id = ?", [JSON.stringify(metadata), now(), taskId]);
29750
+ }
29751
+ var init_agent_metrics = __esm(() => {
29752
+ init_database();
29753
+ });
29754
+
29755
+ // src/mcp/tools/task-rel-tools.ts
29756
+ function registerTaskRelTools(server, ctx) {
29757
+ const { shouldRegisterTool, resolveId, formatError, formatTask } = ctx;
29758
+ if (shouldRegisterTool("create_handoff")) {
29759
+ server.tool("create_handoff", "Create a session handoff note for agent coordination.", {
29760
+ agent_id: exports_external2.string().optional().describe("Agent creating the handoff"),
29761
+ project_id: exports_external2.string().optional().describe("Project ID"),
29762
+ summary: exports_external2.string().describe("What was accomplished this session"),
29763
+ completed: exports_external2.array(exports_external2.string()).optional().describe("Items completed"),
29764
+ in_progress: exports_external2.array(exports_external2.string()).optional().describe("Items still in progress"),
29765
+ blockers: exports_external2.array(exports_external2.string()).optional().describe("Blocking issues"),
29766
+ next_steps: exports_external2.array(exports_external2.string()).optional().describe("Recommended next actions")
29767
+ }, async ({ agent_id, project_id, summary, completed, in_progress, blockers, next_steps }) => {
29768
+ try {
29769
+ const { createHandoff: createHandoff2 } = (init_handoffs(), __toCommonJS(exports_handoffs));
29770
+ const handoff = createHandoff2({
29771
+ agent_id,
29772
+ project_id: project_id ? resolveId(project_id, "projects") : undefined,
29773
+ summary,
29774
+ completed,
29775
+ in_progress,
29776
+ blockers,
29777
+ next_steps
29778
+ });
29779
+ return { content: [{ type: "text", text: `Handoff created: ${handoff.id.slice(0, 8)} by ${handoff.agent_id || "unknown"}` }] };
29780
+ } catch (e) {
29781
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
29782
+ }
29783
+ });
29784
+ }
29785
+ if (shouldRegisterTool("get_latest_handoff")) {
29786
+ server.tool("get_latest_handoff", "Get the most recent handoff for an agent or project.", {
29787
+ agent_id: exports_external2.string().optional().describe("Filter by agent"),
29788
+ project_id: exports_external2.string().optional().describe("Filter by project")
29789
+ }, async ({ agent_id, project_id }) => {
29790
+ try {
29791
+ const { getLatestHandoff: getLatestHandoff2 } = (init_handoffs(), __toCommonJS(exports_handoffs));
29792
+ const handoff = getLatestHandoff2(agent_id, project_id ? resolveId(project_id, "projects") : undefined);
29793
+ if (!handoff)
29794
+ return { content: [{ type: "text", text: "No handoffs found." }] };
29795
+ const lines = [
29796
+ `${handoff.created_at.slice(0, 16)} ${handoff.agent_id || "unknown"}`,
29797
+ handoff.summary
28491
29798
  ];
28492
29799
  if (handoff.completed?.length)
28493
29800
  lines.push(`Done: ${handoff.completed.join(", ")}`);
@@ -28512,7 +29819,7 @@ function registerTaskRelTools(server, ctx) {
28512
29819
  created_by: exports_external2.string().optional().describe("Agent ID who created this relationship")
28513
29820
  }, async ({ source_task_id, target_task_id, relationship_type, created_by }) => {
28514
29821
  try {
28515
- const { addTaskRelationship: addTaskRelationship2 } = (()=>{throw new Error("Cannot require module "+"../db/task-relationships.js");})();
29822
+ const { addTaskRelationship: addTaskRelationship2 } = (init_task_relationships(), __toCommonJS(exports_task_relationships));
28516
29823
  const rel = addTaskRelationship2({
28517
29824
  source_task_id: resolveId(source_task_id),
28518
29825
  target_task_id: resolveId(target_task_id),
@@ -28533,7 +29840,7 @@ function registerTaskRelTools(server, ctx) {
28533
29840
  relationship_type: exports_external2.enum(["related_to", "conflicts_with", "similar_to", "duplicates", "supersedes", "modifies_same_file"]).optional()
28534
29841
  }, async ({ id, source_task_id, target_task_id, relationship_type }) => {
28535
29842
  try {
28536
- const { removeTaskRelationship: removeTaskRelationship2, removeTaskRelationshipByPair: removeTaskRelationshipByPair2 } = (()=>{throw new Error("Cannot require module "+"../db/task-relationships.js");})();
29843
+ const { removeTaskRelationship: removeTaskRelationship2, removeTaskRelationshipByPair: removeTaskRelationshipByPair2 } = (init_task_relationships(), __toCommonJS(exports_task_relationships));
28537
29844
  let removed = false;
28538
29845
  if (id) {
28539
29846
  removed = removeTaskRelationship2(id);
@@ -28554,7 +29861,7 @@ function registerTaskRelTools(server, ctx) {
28554
29861
  relationship_type: exports_external2.enum(["related_to", "conflicts_with", "similar_to", "duplicates", "supersedes", "modifies_same_file"]).optional()
28555
29862
  }, async ({ task_id, relationship_type }) => {
28556
29863
  try {
28557
- const { getTaskRelationships: getTaskRelationships2 } = (()=>{throw new Error("Cannot require module "+"../db/task-relationships.js");})();
29864
+ const { getTaskRelationships: getTaskRelationships2 } = (init_task_relationships(), __toCommonJS(exports_task_relationships));
28558
29865
  const rels = getTaskRelationships2(resolveId(task_id), relationship_type);
28559
29866
  if (rels.length === 0)
28560
29867
  return { content: [{ type: "text", text: "No relationships found." }] };
@@ -28571,7 +29878,7 @@ function registerTaskRelTools(server, ctx) {
28571
29878
  task_id: exports_external2.string().describe("Task ID to detect file relationships for")
28572
29879
  }, async ({ task_id }) => {
28573
29880
  try {
28574
- const { autoDetectFileRelationships: autoDetectFileRelationships2 } = (()=>{throw new Error("Cannot require module "+"../db/task-relationships.js");})();
29881
+ const { autoDetectFileRelationships: autoDetectFileRelationships2 } = (init_task_relationships(), __toCommonJS(exports_task_relationships));
28575
29882
  const created = autoDetectFileRelationships2(resolveId(task_id));
28576
29883
  return { content: [{ type: "text", text: created.length > 0 ? `Created ${created.length} file relationship(s).` : "No file overlaps detected." }] };
28577
29884
  } catch (e) {
@@ -28582,8 +29889,8 @@ function registerTaskRelTools(server, ctx) {
28582
29889
  if (shouldRegisterTool("sync_kg")) {
28583
29890
  server.tool("sync_kg", "Sync all existing relationships into the knowledge graph edges table. Idempotent.", {}, async () => {
28584
29891
  try {
28585
- const { syncKgEdges } = (()=>{throw new Error("Cannot require module "+"../db/kg.js");})();
28586
- const result = syncKgEdges();
29892
+ const { syncKgEdges: syncKgEdges2 } = (init_kg(), __toCommonJS(exports_kg));
29893
+ const result = syncKgEdges2();
28587
29894
  return { content: [{ type: "text", text: `Knowledge graph synced: ${result.synced} edge(s) processed.` }] };
28588
29895
  } catch (e) {
28589
29896
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
@@ -28599,8 +29906,8 @@ function registerTaskRelTools(server, ctx) {
28599
29906
  limit: exports_external2.number().optional().describe("Max results")
28600
29907
  }, async ({ entity_id, relation_type, entity_type, direction, limit }) => {
28601
29908
  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 });
29909
+ const { getRelated: getRelated2 } = (init_kg(), __toCommonJS(exports_kg));
29910
+ const edges = getRelated2(entity_id, { relation_type, entity_type, direction, limit });
28604
29911
  if (edges.length === 0)
28605
29912
  return { content: [{ type: "text", text: "No related entities found." }] };
28606
29913
  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 +29926,8 @@ function registerTaskRelTools(server, ctx) {
28619
29926
  relation_types: exports_external2.array(exports_external2.string()).optional().describe("Filter by relation types")
28620
29927
  }, async ({ source_id, target_id, max_depth, relation_types }) => {
28621
29928
  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 });
29929
+ const { findPath: findPath2 } = (init_kg(), __toCommonJS(exports_kg));
29930
+ const paths = findPath2(source_id, target_id, { max_depth, relation_types });
28624
29931
  if (paths.length === 0)
28625
29932
  return { content: [{ type: "text", text: "No path found." }] };
28626
29933
  const lines = paths.map((path, i) => {
@@ -28644,8 +29951,8 @@ function registerTaskRelTools(server, ctx) {
28644
29951
  relation_types: exports_external2.array(exports_external2.string()).optional().describe("Filter by relation types")
28645
29952
  }, async ({ entity_id, max_depth, relation_types }) => {
28646
29953
  try {
28647
- const { getImpactAnalysis } = (()=>{throw new Error("Cannot require module "+"../db/kg.js");})();
28648
- const impact = getImpactAnalysis(entity_id, { max_depth, relation_types });
29954
+ const { getImpactAnalysis: getImpactAnalysis2 } = (init_kg(), __toCommonJS(exports_kg));
29955
+ const impact = getImpactAnalysis2(entity_id, { max_depth, relation_types });
28649
29956
  if (impact.length === 0)
28650
29957
  return { content: [{ type: "text", text: "No downstream impact detected." }] };
28651
29958
  const byDepth = new Map;
@@ -28675,8 +29982,8 @@ Depth ${depth}:`);
28675
29982
  limit: exports_external2.number().optional().describe("Max results (default: 20)")
28676
29983
  }, async ({ project_id, limit }) => {
28677
29984
  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 });
29985
+ const { getCriticalPath: getCriticalPath2 } = (init_kg(), __toCommonJS(exports_kg));
29986
+ const result = getCriticalPath2({ project_id: project_id ? resolveId(project_id, "projects") : undefined, limit });
28680
29987
  if (result.length === 0)
28681
29988
  return { content: [{ type: "text", text: "No critical path data. Run sync_kg first to populate the knowledge graph." }] };
28682
29989
  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 +30003,13 @@ ${lines.join(`
28696
30003
  is_lead: exports_external2.coerce.boolean().optional().describe("Whether this agent is the project lead for this role")
28697
30004
  }, async ({ project_id, agent_name, role, is_lead }) => {
28698
30005
  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");})();
30006
+ const { setProjectAgentRole: setProjectAgentRole2 } = (init_project_agent_roles(), __toCommonJS(exports_project_agent_roles));
30007
+ const { getAgentByName: getAgentByName2 } = (init_agents(), __toCommonJS(exports_agents));
28701
30008
  const agent = getAgentByName2(agent_name);
28702
30009
  if (!agent)
28703
30010
  return { content: [{ type: "text", text: `Agent not found: ${agent_name}` }], isError: true };
28704
30011
  const pid = resolveId(project_id, "projects");
28705
- const result = setProjectAgentRole(pid, agent.id, role, is_lead ?? false);
30012
+ const result = setProjectAgentRole2(pid, agent.id, role, is_lead ?? false);
28706
30013
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
28707
30014
  } catch (e) {
28708
30015
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
@@ -28732,9 +30039,9 @@ ${lines.join(`
28732
30039
  }).join(`
28733
30040
  `);
28734
30041
  };
28735
- const { getProjectOrgChart } = (()=>{throw new Error("Cannot require module "+"../db/project-agent-roles.js");})();
30042
+ const { getProjectOrgChart: getProjectOrgChart2 } = (init_project_agent_roles(), __toCommonJS(exports_project_agent_roles));
28736
30043
  const pid = resolveId(project_id, "projects");
28737
- const tree = getProjectOrgChart(pid, { filter_to_project });
30044
+ const tree = getProjectOrgChart2(pid, { filter_to_project });
28738
30045
  if (format === "json") {
28739
30046
  return { content: [{ type: "text", text: JSON.stringify(tree, null, 2) }] };
28740
30047
  }
@@ -28752,9 +30059,9 @@ ${lines.join(`
28752
30059
  project_id: exports_external2.string().describe("Project ID")
28753
30060
  }, async ({ project_id }) => {
28754
30061
  try {
28755
- const { listProjectAgentRoles } = (()=>{throw new Error("Cannot require module "+"../db/project-agent-roles.js");})();
30062
+ const { listProjectAgentRoles: listProjectAgentRoles2 } = (init_project_agent_roles(), __toCommonJS(exports_project_agent_roles));
28756
30063
  const pid = resolveId(project_id, "projects");
28757
- const roles = listProjectAgentRoles(pid);
30064
+ const roles = listProjectAgentRoles2(pid);
28758
30065
  return { content: [{ type: "text", text: JSON.stringify(roles, null, 2) }] };
28759
30066
  } catch (e) {
28760
30067
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
@@ -28768,7 +30075,7 @@ ${lines.join(`
28768
30075
  limit: exports_external2.number().optional().describe("Max results")
28769
30076
  }, async ({ capabilities, min_score, limit }) => {
28770
30077
  try {
28771
- const { getCapableAgents: getCapableAgents2 } = (()=>{throw new Error("Cannot require module "+"../db/agents.js");})();
30078
+ const { getCapableAgents: getCapableAgents2 } = (init_agents(), __toCommonJS(exports_agents));
28772
30079
  const results = getCapableAgents2(capabilities, { min_score, limit });
28773
30080
  if (results.length === 0)
28774
30081
  return { content: [{ type: "text", text: "No agents match the given capabilities." }] };
@@ -28787,8 +30094,8 @@ ${lines.join(`
28787
30094
  project_id: exports_external2.string().optional().describe("Filter by project")
28788
30095
  }, async ({ stuck_minutes, confidence_threshold, project_id }) => {
28789
30096
  try {
28790
- const { patrolTasks } = (()=>{throw new Error("Cannot require module "+"../db/patrol.js");})();
28791
- const result = patrolTasks({
30097
+ const { patrolTasks: patrolTasks2 } = (init_patrol(), __toCommonJS(exports_patrol));
30098
+ const result = patrolTasks2({
28792
30099
  stuck_minutes,
28793
30100
  confidence_threshold,
28794
30101
  project_id: project_id ? resolveId(project_id, "projects") : undefined
@@ -28814,8 +30121,8 @@ ${lines.join(`
28814
30121
  limit: exports_external2.number().optional().describe("Max results (default: all)")
28815
30122
  }, async ({ project_id, limit }) => {
28816
30123
  try {
28817
- const { getReviewQueue } = (()=>{throw new Error("Cannot require module "+"../db/patrol.js");})();
28818
- const tasks = getReviewQueue({
30124
+ const { getReviewQueue: getReviewQueue2 } = (init_patrol(), __toCommonJS(exports_patrol));
30125
+ const tasks = getReviewQueue2({
28819
30126
  project_id: project_id ? resolveId(project_id, "projects") : undefined,
28820
30127
  limit
28821
30128
  });
@@ -28841,8 +30148,8 @@ ${lines.join(`
28841
30148
  reviewer_id: exports_external2.string().optional().describe("Agent ID of reviewer")
28842
30149
  }, async ({ task_id, score, reviewer_id }) => {
28843
30150
  try {
28844
- const { scoreTask } = (()=>{throw new Error("Cannot require module "+"../db/agent-metrics.js");})();
28845
- scoreTask(resolveId(task_id), score, reviewer_id);
30151
+ const { scoreTask: scoreTask2 } = (init_agent_metrics(), __toCommonJS(exports_agent_metrics));
30152
+ scoreTask2(resolveId(task_id), score, reviewer_id);
28846
30153
  return { content: [{ type: "text", text: `Task ${task_id.slice(0, 8)} scored: ${score}` }] };
28847
30154
  } catch (e) {
28848
30155
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
@@ -28859,7 +30166,7 @@ ${lines.join(`
28859
30166
  notes: exports_external2.string().optional().describe("Notes about what was done")
28860
30167
  }, async ({ task_id, minutes, agent_id, started_at, ended_at, notes }) => {
28861
30168
  try {
28862
- const { logTime: logTime2 } = (()=>{throw new Error("Cannot require module "+"../db/tasks.js");})();
30169
+ const { logTime: logTime2 } = (init_tasks(), __toCommonJS(exports_tasks));
28863
30170
  logTime2({ task_id: resolveId(task_id), minutes, agent_id, started_at, ended_at, notes });
28864
30171
  return { content: [{ type: "text", text: `Logged ${minutes} min on task ${task_id.slice(0, 8)}` }] };
28865
30172
  } catch (e) {
@@ -28874,7 +30181,7 @@ ${lines.join(`
28874
30181
  since: exports_external2.string().optional().describe("ISO date \u2014 only tasks completed after this date")
28875
30182
  }, async ({ project_id, agent_id, since }) => {
28876
30183
  try {
28877
- const { getTimeReport: getTimeReport2 } = (()=>{throw new Error("Cannot require module "+"../db/tasks.js");})();
30184
+ const { getTimeReport: getTimeReport2 } = (init_tasks(), __toCommonJS(exports_tasks));
28878
30185
  const report = getTimeReport2({ project_id: project_id ? resolveId(project_id, "projects") : undefined, agent_id, since });
28879
30186
  if (report.length === 0)
28880
30187
  return { content: [{ type: "text", text: "No completed tasks found." }] };
@@ -28898,7 +30205,7 @@ ${lines.join(`
28898
30205
  agent_id: exports_external2.string().optional().describe("Agent subscribing (defaults to context agent)")
28899
30206
  }, async ({ task_id, agent_id }) => {
28900
30207
  try {
28901
- const { watchTask: watchTask2 } = (()=>{throw new Error("Cannot require module "+"../db/tasks.js");})();
30208
+ const { watchTask: watchTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
28902
30209
  watchTask2(resolveId(task_id), agent_id || "");
28903
30210
  return { content: [{ type: "text", text: `Now watching task ${task_id.slice(0, 8)}` }] };
28904
30211
  } catch (e) {
@@ -28912,7 +30219,7 @@ ${lines.join(`
28912
30219
  agent_id: exports_external2.string().optional().describe("Agent unsubscribing (defaults to context agent)")
28913
30220
  }, async ({ task_id, agent_id }) => {
28914
30221
  try {
28915
- const { unwatchTask: unwatchTask2 } = (()=>{throw new Error("Cannot require module "+"../db/tasks.js");})();
30222
+ const { unwatchTask: unwatchTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
28916
30223
  unwatchTask2(resolveId(task_id), agent_id || "");
28917
30224
  return { content: [{ type: "text", text: `Stopped watching task ${task_id.slice(0, 8)}` }] };
28918
30225
  } catch (e) {
@@ -28925,7 +30232,7 @@ ${lines.join(`
28925
30232
  task_id: exports_external2.string().describe("Task ID")
28926
30233
  }, async ({ task_id }) => {
28927
30234
  try {
28928
- const { getTaskWatchers: getTaskWatchers2 } = (()=>{throw new Error("Cannot require module "+"../db/tasks.js");})();
30235
+ const { getTaskWatchers: getTaskWatchers2 } = (init_tasks(), __toCommonJS(exports_tasks));
28929
30236
  const watchers = getTaskWatchers2(resolveId(task_id));
28930
30237
  if (watchers.length === 0)
28931
30238
  return { content: [{ type: "text", text: "No watchers." }] };
@@ -28944,15 +30251,15 @@ ${lines.join(`
28944
30251
  agent_id: exports_external2.string().optional().describe("Filter by assignee")
28945
30252
  }, async ({ project_id, plan_id, task_list_id, since, agent_id }) => {
28946
30253
  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");})();
30254
+ const { listTasks: listTasks3 } = (init_tasks(), __toCommonJS(exports_tasks));
30255
+ const { patrolTasks: patrolTasks2 } = (init_patrol(), __toCommonJS(exports_patrol));
28949
30256
  const completed = listTasks3({ status: "completed", project_id, plan_id, task_list_id, assigned_to: agent_id, limit: 500 }, undefined);
28950
30257
  const filtered = since ? completed.filter((t) => t.completed_at && t.completed_at >= since) : completed;
28951
30258
  const total = filtered.length;
28952
30259
  const lowConf = filtered.filter((t) => t.confidence != null && t.confidence < 0.7).length;
28953
30260
  const withEstimate = filtered.filter((t) => t.estimated_minutes != null && t.actual_minutes != null);
28954
30261
  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 });
30262
+ const patrolResult = patrolTasks2({ project_id: project_id ? resolveId(project_id, "projects") : undefined });
28956
30263
  const stuck = patrolResult.issues.filter((i) => i.type === "stuck").length;
28957
30264
  const lines = [
28958
30265
  `Retro (${total} completed tasks${since ? ` since ${since}` : ""})`,
@@ -28973,7 +30280,7 @@ ${lines.join(`
28973
30280
  limit: exports_external2.number().optional().describe("Max results (default: 20)")
28974
30281
  }, async ({ project_id, limit }) => {
28975
30282
  try {
28976
- const { listTasks: listTasks3 } = (()=>{throw new Error("Cannot require module "+"../db/tasks.js");})();
30283
+ const { listTasks: listTasks3 } = (init_tasks(), __toCommonJS(exports_tasks));
28977
30284
  const tasks = listTasks3({ status: "pending", project_id, assigned_to: "", limit: limit || 20 }, undefined);
28978
30285
  if (tasks.length === 0)
28979
30286
  return { content: [{ type: "text", text: "Inbox is empty." }] };
@@ -28992,8 +30299,8 @@ ${lines.join(`
28992
30299
  project_id: exports_external2.string().optional().describe("Filter by project")
28993
30300
  }, async ({ agent_id, project_id }) => {
28994
30301
  try {
28995
- const { getAgentMetrics } = (()=>{throw new Error("Cannot require module "+"../db/agent-metrics.js");})();
28996
- const metrics = getAgentMetrics(agent_id, {
30302
+ const { getAgentMetrics: getAgentMetrics2 } = (init_agent_metrics(), __toCommonJS(exports_agent_metrics));
30303
+ const metrics = getAgentMetrics2(agent_id, {
28997
30304
  project_id: project_id ? resolveId(project_id, "projects") : undefined
28998
30305
  });
28999
30306
  if (!metrics)
@@ -29020,8 +30327,8 @@ ${lines.join(`
29020
30327
  limit: exports_external2.number().optional().describe("Max entries (default: 20)")
29021
30328
  }, async ({ project_id, limit }) => {
29022
30329
  try {
29023
- const { getLeaderboard } = (()=>{throw new Error("Cannot require module "+"../db/agent-metrics.js");})();
29024
- const entries = getLeaderboard({
30330
+ const { getLeaderboard: getLeaderboard2 } = (init_agent_metrics(), __toCommonJS(exports_agent_metrics));
30331
+ const entries = getLeaderboard2({
29025
30332
  project_id: project_id ? resolveId(project_id, "projects") : undefined,
29026
30333
  limit
29027
30334
  });
@@ -29868,116 +31175,493 @@ SSH: ${machine.ssh_address ?? "(not set)"}`
29868
31175
  }
29869
31176
  ]
29870
31177
  };
29871
- } catch (error) {
29872
- return { content: [{ type: "text", text: ctx.formatError(error) }] };
31178
+ } catch (error) {
31179
+ return { content: [{ type: "text", text: ctx.formatError(error) }] };
31180
+ }
31181
+ });
31182
+ }
31183
+ if (ctx.shouldRegisterTool("machines_list")) {
31184
+ server.tool("machines_list", "List all registered machines", {
31185
+ include_archived: exports_external2.boolean().optional().describe("Include archived machines")
31186
+ }, async (params) => {
31187
+ try {
31188
+ const db = getDb();
31189
+ const machines = listMachines(db, params.include_archived);
31190
+ if (machines.length === 0) {
31191
+ return {
31192
+ content: [{ type: "text", text: "No machines registered. Use machines_register to add one." }]
31193
+ };
31194
+ }
31195
+ const lines = machines.map((m) => {
31196
+ const primaryTag = m.is_primary ? " [PRIMARY]" : "";
31197
+ const archivedTag = m.archived_at ? " [ARCHIVED]" : "";
31198
+ return `${m.name} (${m.id.slice(0, 8)}) | ${m.hostname ?? "unknown"} | ${m.platform ?? "unknown"} | last: ${m.last_seen_at}${primaryTag}${archivedTag}`;
31199
+ });
31200
+ return { content: [{ type: "text", text: `Machines:
31201
+ ${lines.join(`
31202
+ `)}` }] };
31203
+ } catch (error) {
31204
+ return { content: [{ type: "text", text: ctx.formatError(error) }] };
31205
+ }
31206
+ });
31207
+ }
31208
+ if (ctx.shouldRegisterTool("machines_set_primary")) {
31209
+ server.tool("machines_set_primary", "Set the primary machine", {
31210
+ name: exports_external2.string().describe("Machine name to set as primary")
31211
+ }, async (params) => {
31212
+ try {
31213
+ const db = getDb();
31214
+ const machine = setPrimaryMachine(params.name, db);
31215
+ return {
31216
+ content: [
31217
+ { type: "text", text: `Primary machine set to: ${machine.name} (${machine.id.slice(0, 8)})` }
31218
+ ]
31219
+ };
31220
+ } catch (error) {
31221
+ return { content: [{ type: "text", text: ctx.formatError(error) }] };
31222
+ }
31223
+ });
31224
+ }
31225
+ if (ctx.shouldRegisterTool("machines_archive")) {
31226
+ server.tool("machines_archive", "Archive (soft-delete) a machine. Cannot archive primary or machines with active tasks.", {
31227
+ name: exports_external2.string().describe("Machine name to archive")
31228
+ }, async (params) => {
31229
+ try {
31230
+ const db = getDb();
31231
+ const machine = getMachineByName(params.name, db);
31232
+ if (!machine) {
31233
+ return { content: [{ type: "text", text: `Machine '${params.name}' not found` }] };
31234
+ }
31235
+ archiveMachine(machine.id, db);
31236
+ return { content: [{ type: "text", text: `Machine '${params.name}' archived` }] };
31237
+ } catch (error) {
31238
+ return { content: [{ type: "text", text: ctx.formatError(error) }] };
31239
+ }
31240
+ });
31241
+ }
31242
+ if (ctx.shouldRegisterTool("machines_unarchive")) {
31243
+ server.tool("machines_unarchive", "Unarchive a machine", {
31244
+ name: exports_external2.string().describe("Machine name to unarchive")
31245
+ }, async (params) => {
31246
+ try {
31247
+ const db = getDb();
31248
+ const machine = getMachineByName(params.name, db);
31249
+ if (!machine) {
31250
+ return { content: [{ type: "text", text: `Machine '${params.name}' not found` }] };
31251
+ }
31252
+ const result = unarchiveMachine(machine.id, db);
31253
+ return {
31254
+ content: [
31255
+ { type: "text", text: `Machine '${params.name}' unarchived${result ? ` (${result.id.slice(0, 8)})` : ""}` }
31256
+ ]
31257
+ };
31258
+ } catch (error) {
31259
+ return { content: [{ type: "text", text: ctx.formatError(error) }] };
31260
+ }
31261
+ });
31262
+ }
31263
+ if (ctx.shouldRegisterTool("machines_delete")) {
31264
+ server.tool("machines_delete", "Hard-delete a machine. Cannot delete primary or machines with active tasks.", {
31265
+ name: exports_external2.string().describe("Machine name to delete")
31266
+ }, async (params) => {
31267
+ try {
31268
+ const db = getDb();
31269
+ const machine = getMachineByName(params.name, db);
31270
+ if (!machine) {
31271
+ return { content: [{ type: "text", text: `Machine '${params.name}' not found` }] };
31272
+ }
31273
+ const result = deleteMachine(machine.id, db);
31274
+ return {
31275
+ content: [
31276
+ { type: "text", text: result ? `Machine '${params.name}' deleted` : `Failed to delete machine '${params.name}'` }
31277
+ ]
31278
+ };
31279
+ } catch (error) {
31280
+ return { content: [{ type: "text", text: ctx.formatError(error) }] };
31281
+ }
31282
+ });
31283
+ }
31284
+ }
31285
+ var init_machines2 = __esm(() => {
31286
+ init_zod2();
31287
+ init_machines();
31288
+ init_database();
31289
+ });
31290
+
31291
+ // src/mcp/tools/agents.ts
31292
+ function registerAgentTools(server, { shouldRegisterTool, resolveId, formatError, agentFocusMap, getAgentFocus }) {
31293
+ if (shouldRegisterTool("set_focus")) {
31294
+ server.tool("set_focus", "Focus this agent on a project. All list/search/status tools will default to this project.", {
31295
+ agent_id: exports_external2.string().describe("Agent ID or name"),
31296
+ project_id: exports_external2.string().optional().describe("Project to focus on. Omit to clear."),
31297
+ task_list_id: exports_external2.string().optional().describe("Task list to focus on")
31298
+ }, async ({ agent_id, project_id, task_list_id }) => {
31299
+ try {
31300
+ const resolvedProject = project_id ? resolveId(project_id, "projects") : undefined;
31301
+ const focus = { agent_id, project_id: resolvedProject, task_list_id };
31302
+ agentFocusMap.set(agent_id, focus);
31303
+ try {
31304
+ const agent = getAgentByName(agent_id) || getAgent(agent_id);
31305
+ if (agent) {
31306
+ const db = getDatabase();
31307
+ db.run("UPDATE agents SET active_project_id = ? WHERE id = ?", [resolvedProject || null, agent.id]);
31308
+ }
31309
+ } catch {}
31310
+ const projectName = resolvedProject ? ` (${resolvedProject.slice(0, 8)})` : "";
31311
+ return { content: [{ type: "text", text: `Focused on project${projectName}. Read tools will default to this scope. Pass explicit project_id to override.` }] };
31312
+ } catch (e) {
31313
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
31314
+ }
31315
+ });
31316
+ }
31317
+ if (shouldRegisterTool("get_focus")) {
31318
+ server.tool("get_focus", "Get the current focus for an agent.", { agent_id: exports_external2.string().describe("Agent ID or name") }, async ({ agent_id }) => {
31319
+ const focus = getAgentFocus(agent_id);
31320
+ if (!focus?.project_id) {
31321
+ return { content: [{ type: "text", text: "No focus set. Showing all projects." }] };
31322
+ }
31323
+ return { content: [{ type: "text", text: `Focused on project: ${focus.project_id}${focus.task_list_id ? `, task list: ${focus.task_list_id}` : ""}` }] };
31324
+ });
31325
+ }
31326
+ if (shouldRegisterTool("unfocus")) {
31327
+ server.tool("unfocus", "Clear focus \u2014 show all projects and tasks.", { agent_id: exports_external2.string().describe("Agent ID or name") }, async ({ agent_id }) => {
31328
+ agentFocusMap.delete(agent_id);
31329
+ try {
31330
+ const agent = getAgentByName(agent_id) || getAgent(agent_id);
31331
+ if (agent) {
31332
+ const db = getDatabase();
31333
+ db.run("UPDATE agents SET active_project_id = NULL WHERE id = ?", [agent.id]);
31334
+ }
31335
+ } catch {}
31336
+ return { content: [{ type: "text", text: "Focus cleared. Showing all projects." }] };
31337
+ });
31338
+ }
31339
+ if (shouldRegisterTool("register_agent")) {
31340
+ server.tool("register_agent", "Register an agent with a distinctive one-word name. Generic/generated names like agent, agent-1, assistant, or worker-2 are rejected. Prefer Roman or Greek names from suggest_agent_name.", {
31341
+ name: exports_external2.string().describe("Distinctive one-word agent name. Use suggest_agent_name first; avoid agent, agent-1, and other numbered generated names."),
31342
+ description: exports_external2.string().optional(),
31343
+ role: exports_external2.string().optional(),
31344
+ title: exports_external2.string().optional(),
31345
+ level: exports_external2.string().optional(),
31346
+ permissions: exports_external2.array(exports_external2.string()).optional(),
31347
+ capabilities: exports_external2.array(exports_external2.string()).optional().describe("Agent capabilities/skills for task routing (e.g. ['typescript', 'testing', 'devops'])"),
31348
+ 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."),
31349
+ 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"),
31350
+ 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.")
31351
+ }, async ({ name, description, role, title, level, permissions, capabilities, session_id, working_dir, force }) => {
31352
+ try {
31353
+ const pool = getAgentPoolForProject(working_dir);
31354
+ const result = registerAgent({ name, description, role, title, level, permissions, capabilities, session_id, working_dir, force, pool: pool || undefined });
31355
+ if (isAgentConflict(result)) {
31356
+ const suggestLine = result.suggestions && result.suggestions.length > 0 ? `
31357
+ Available names: ${result.suggestions.join(", ")}` : "";
31358
+ const hint = `CONFLICT: ${result.message}${suggestLine}`;
31359
+ return {
31360
+ content: [{ type: "text", text: hint }],
31361
+ isError: true
31362
+ };
31363
+ }
31364
+ const agent = result;
31365
+ const poolLine = pool ? `
31366
+ Pool: [${pool.join(", ")}]` : "";
31367
+ return {
31368
+ content: [{
31369
+ type: "text",
31370
+ text: `Agent registered:
31371
+ ID: ${agent.id}
31372
+ Name: ${agent.name}${agent.description ? `
31373
+ Description: ${agent.description}` : ""}
31374
+ Session: ${agent.session_id ?? "unbound"}${poolLine}
31375
+ Created: ${agent.created_at}
31376
+ Last seen: ${agent.last_seen_at}`
31377
+ }]
31378
+ };
31379
+ } catch (e) {
31380
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
31381
+ }
31382
+ });
31383
+ }
31384
+ if (shouldRegisterTool("suggest_agent_name")) {
31385
+ server.tool("suggest_agent_name", "Get available Roman/Greek-style agent names for a project. Use these instead of generic generated names.", {
31386
+ working_dir: exports_external2.string().optional().describe("Your working directory \u2014 used to look up the project's allowed name pool from config")
31387
+ }, async ({ working_dir }) => {
31388
+ try {
31389
+ const pool = getAgentPoolForProject(working_dir);
31390
+ const cutoff = new Date(Date.now() - 30 * 60 * 1000).toISOString();
31391
+ const allActive = listAgents().filter((a) => a.last_seen_at > cutoff);
31392
+ if (!pool) {
31393
+ const suggestions = getAvailableNamesFromPool([
31394
+ "caesar",
31395
+ "augustus",
31396
+ "marcus",
31397
+ "brutus",
31398
+ "cicero",
31399
+ "cato",
31400
+ "nero",
31401
+ "claudius",
31402
+ "tiberius",
31403
+ "hadrian",
31404
+ "athena",
31405
+ "apollo",
31406
+ "artemis",
31407
+ "iris",
31408
+ "hector",
31409
+ "sophia",
31410
+ "thalia",
31411
+ "phoebe",
31412
+ "daphne"
31413
+ ], getDatabase());
31414
+ const lines2 = [
31415
+ "No project pool configured. Use a distinctive one-word name; generic generated names are blocked.",
31416
+ `Suggested names: ${suggestions.slice(0, 8).join(", ")}`,
31417
+ 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.",
31418
+ `
31419
+ To restrict names, configure agent_pool or project_pools in ~/.hasna/todos/config.json`
31420
+ ];
31421
+ return { content: [{ type: "text", text: lines2.join(`
31422
+ `) }] };
31423
+ }
31424
+ const available = getAvailableNamesFromPool(pool, getDatabase());
31425
+ const activeInPool = allActive.filter((a) => pool.map((n) => n.toLowerCase()).includes(a.name));
31426
+ const lines = [
31427
+ `Project pool: ${pool.join(", ")}`,
31428
+ `Available now (${available.length}): ${available.length > 0 ? available.join(", ") : "none \u2014 all names in use"}`,
31429
+ 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",
31430
+ available.length > 0 ? `
31431
+ Suggested: ${available[0]}` : `
31432
+ No names available. Wait for an active agent to go stale (30min timeout).`
31433
+ ];
31434
+ return { content: [{ type: "text", text: lines.join(`
31435
+ `) }] };
31436
+ } catch (e) {
31437
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
31438
+ }
31439
+ });
31440
+ }
31441
+ if (shouldRegisterTool("list_agents")) {
31442
+ server.tool("list_agents", "List all registered agents. By default shows only active agents \u2014 set include_archived to see archived ones too.", {
31443
+ include_archived: exports_external2.boolean().optional().describe("Include archived agents in the list (default: false)")
31444
+ }, async ({ include_archived }) => {
31445
+ try {
31446
+ const agents = listAgents({ include_archived: include_archived ?? false });
31447
+ if (agents.length === 0) {
31448
+ return { content: [{ type: "text", text: "No agents registered." }] };
31449
+ }
31450
+ const text = agents.map((a) => {
31451
+ const statusTag = a.status === "archived" ? " [archived]" : "";
31452
+ return `${a.id} | ${a.name}${statusTag}${a.description ? ` - ${a.description}` : ""} (last seen: ${a.last_seen_at})`;
31453
+ }).join(`
31454
+ `);
31455
+ return { content: [{ type: "text", text: `${agents.length} agent(s):
31456
+ ${text}` }] };
31457
+ } catch (e) {
31458
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
31459
+ }
31460
+ });
31461
+ }
31462
+ if (shouldRegisterTool("get_agent")) {
31463
+ server.tool("get_agent", "Get agent details by ID or name. Provide one of id or name.", {
31464
+ agent_id: exports_external2.string().optional(),
31465
+ id: exports_external2.string().optional(),
31466
+ name: exports_external2.string().optional()
31467
+ }, async ({ agent_id, id, name }) => {
31468
+ try {
31469
+ const identifier = agent_id || id || name;
31470
+ if (!identifier) {
31471
+ return { content: [{ type: "text", text: "Provide agent_id, id, or name." }], isError: true };
31472
+ }
31473
+ const agent = getAgent(identifier) || getAgentByName(identifier);
31474
+ if (!agent) {
31475
+ return { content: [{ type: "text", text: `Agent not found: ${identifier}` }], isError: true };
31476
+ }
31477
+ const parts = [
31478
+ `ID: ${agent.id}`,
31479
+ `Name: ${agent.name}`
31480
+ ];
31481
+ if (agent.description)
31482
+ parts.push(`Description: ${agent.description}`);
31483
+ if (Object.keys(agent.metadata).length > 0)
31484
+ parts.push(`Metadata: ${JSON.stringify(agent.metadata)}`);
31485
+ parts.push(`Created: ${agent.created_at}`);
31486
+ parts.push(`Last seen: ${agent.last_seen_at}`);
31487
+ return { content: [{ type: "text", text: parts.join(`
31488
+ `) }] };
31489
+ } catch (e) {
31490
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
31491
+ }
31492
+ });
31493
+ }
31494
+ if (shouldRegisterTool("rename_agent")) {
31495
+ server.tool("rename_agent", "Rename an agent. Resolve by id or current name.", {
31496
+ id: exports_external2.string().optional(),
31497
+ name: exports_external2.string().optional(),
31498
+ new_name: exports_external2.string()
31499
+ }, async ({ id, name, new_name }) => {
31500
+ try {
31501
+ if (!id && !name) {
31502
+ return { content: [{ type: "text", text: "Provide either id or name." }], isError: true };
31503
+ }
31504
+ const agent = id ? getAgent(id) : getAgentByName(name);
31505
+ if (!agent) {
31506
+ return { content: [{ type: "text", text: `Agent not found: ${id || name}` }], isError: true };
31507
+ }
31508
+ const oldName = agent.name;
31509
+ const updated = updateAgent(agent.id, { name: new_name });
31510
+ const db = getDatabase();
31511
+ const tasksResult = db.run("UPDATE tasks SET assigned_to = ? WHERE assigned_to = ?", [new_name, oldName]);
31512
+ const taskNote = tasksResult.changes > 0 ? `
31513
+ Updated assigned_to on ${tasksResult.changes} task(s).` : "";
31514
+ return {
31515
+ content: [{
31516
+ type: "text",
31517
+ text: `Agent renamed: ${oldName} -> ${updated.name}
31518
+ ID: ${updated.id}${taskNote}`
31519
+ }]
31520
+ };
31521
+ } catch (e) {
31522
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
29873
31523
  }
29874
31524
  });
29875
31525
  }
29876
- if (ctx.shouldRegisterTool("machines_list")) {
29877
- server.tool("machines_list", "List all registered machines", {
29878
- include_archived: exports_external2.boolean().optional().describe("Include archived machines")
29879
- }, async (params) => {
31526
+ if (shouldRegisterTool("update_agent")) {
31527
+ server.tool("update_agent", "Update an agent's description, role, title, or other metadata. Resolve by id or name.", {
31528
+ agent_id: exports_external2.string().optional(),
31529
+ id: exports_external2.string().optional(),
31530
+ name: exports_external2.string().optional(),
31531
+ description: exports_external2.string().optional(),
31532
+ role: exports_external2.string().optional(),
31533
+ title: exports_external2.string().optional(),
31534
+ level: exports_external2.string().optional(),
31535
+ capabilities: exports_external2.array(exports_external2.string()).optional(),
31536
+ permissions: exports_external2.array(exports_external2.string()).optional(),
31537
+ metadata: exports_external2.record(exports_external2.unknown()).optional()
31538
+ }, async ({ agent_id, id, name, ...updates }) => {
29880
31539
  try {
29881
- const db = getDb();
29882
- const machines = listMachines(db, params.include_archived);
29883
- if (machines.length === 0) {
29884
- return {
29885
- content: [{ type: "text", text: "No machines registered. Use machines_register to add one." }]
29886
- };
31540
+ const identifier = agent_id || id || name;
31541
+ if (!identifier) {
31542
+ return { content: [{ type: "text", text: "Provide agent_id, id, or name." }], isError: true };
29887
31543
  }
29888
- const lines = machines.map((m) => {
29889
- const primaryTag = m.is_primary ? " [PRIMARY]" : "";
29890
- const archivedTag = m.archived_at ? " [ARCHIVED]" : "";
29891
- return `${m.name} (${m.id.slice(0, 8)}) | ${m.hostname ?? "unknown"} | ${m.platform ?? "unknown"} | last: ${m.last_seen_at}${primaryTag}${archivedTag}`;
29892
- });
29893
- return { content: [{ type: "text", text: `Machines:
29894
- ${lines.join(`
29895
- `)}` }] };
29896
- } catch (error) {
29897
- return { content: [{ type: "text", text: ctx.formatError(error) }] };
31544
+ const agent = getAgent(identifier) || getAgentByName(identifier);
31545
+ if (!agent) {
31546
+ return { content: [{ type: "text", text: `Agent not found: ${identifier}` }], isError: true };
31547
+ }
31548
+ const updated = updateAgent(agent.id, updates);
31549
+ return { content: [{ type: "text", text: `Agent updated: ${updated.name} (${updated.id.slice(0, 8)})` }] };
31550
+ } catch (e) {
31551
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
29898
31552
  }
29899
31553
  });
29900
31554
  }
29901
- if (ctx.shouldRegisterTool("machines_set_primary")) {
29902
- server.tool("machines_set_primary", "Set the primary machine", {
29903
- name: exports_external2.string().describe("Machine name to set as primary")
29904
- }, async (params) => {
31555
+ if (shouldRegisterTool("delete_agent")) {
31556
+ 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.", {
31557
+ agent_id: exports_external2.string().optional(),
31558
+ id: exports_external2.string().optional(),
31559
+ name: exports_external2.string().optional()
31560
+ }, async ({ agent_id, id, name }) => {
29905
31561
  try {
29906
- const db = getDb();
29907
- const machine = setPrimaryMachine(params.name, db);
31562
+ const identifier = agent_id || id || name;
31563
+ if (!identifier) {
31564
+ return { content: [{ type: "text", text: "Provide agent_id, id, or name." }], isError: true };
31565
+ }
31566
+ const agent = getAgent(identifier) || getAgentByName(identifier);
31567
+ if (!agent) {
31568
+ return { content: [{ type: "text", text: `Agent not found: ${identifier}` }], isError: true };
31569
+ }
31570
+ const archived = archiveAgent(agent.id);
29908
31571
  return {
29909
- content: [
29910
- { type: "text", text: `Primary machine set to: ${machine.name} (${machine.id.slice(0, 8)})` }
29911
- ]
31572
+ content: [{
31573
+ type: "text",
31574
+ text: archived ? `Agent archived: ${agent.name} (${agent.id}). Use unarchive_agent to restore.` : `Failed to archive agent: ${agent.name}`
31575
+ }],
31576
+ isError: !archived
29912
31577
  };
29913
- } catch (error) {
29914
- return { content: [{ type: "text", text: ctx.formatError(error) }] };
31578
+ } catch (e) {
31579
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
29915
31580
  }
29916
31581
  });
29917
31582
  }
29918
- if (ctx.shouldRegisterTool("machines_archive")) {
29919
- server.tool("machines_archive", "Archive (soft-delete) a machine. Cannot archive primary or machines with active tasks.", {
29920
- name: exports_external2.string().describe("Machine name to archive")
29921
- }, async (params) => {
31583
+ if (shouldRegisterTool("unarchive_agent")) {
31584
+ server.tool("unarchive_agent", "Restore an archived agent back to active status. Resolve by id or name.", {
31585
+ agent_id: exports_external2.string().optional(),
31586
+ id: exports_external2.string().optional(),
31587
+ name: exports_external2.string().optional()
31588
+ }, async ({ agent_id, id, name }) => {
29922
31589
  try {
29923
- const db = getDb();
29924
- const machine = getMachineByName(params.name, db);
29925
- if (!machine) {
29926
- return { content: [{ type: "text", text: `Machine '${params.name}' not found` }] };
31590
+ const identifier = agent_id || id || name;
31591
+ if (!identifier) {
31592
+ return { content: [{ type: "text", text: "Provide agent_id, id, or name." }], isError: true };
29927
31593
  }
29928
- archiveMachine(machine.id, db);
29929
- return { content: [{ type: "text", text: `Machine '${params.name}' archived` }] };
29930
- } catch (error) {
29931
- return { content: [{ type: "text", text: ctx.formatError(error) }] };
31594
+ const agent = getAgent(identifier) || getAgentByName(identifier);
31595
+ if (!agent) {
31596
+ return { content: [{ type: "text", text: `Agent not found: ${identifier}` }], isError: true };
31597
+ }
31598
+ if (agent.status === "active") {
31599
+ return { content: [{ type: "text", text: `Agent ${agent.name} is already active.` }] };
31600
+ }
31601
+ const restored = unarchiveAgent(agent.id);
31602
+ return {
31603
+ content: [{
31604
+ type: "text",
31605
+ text: restored ? `Agent restored: ${agent.name} (${agent.id}) is now active.` : `Failed to restore agent: ${agent.name}`
31606
+ }],
31607
+ isError: !restored
31608
+ };
31609
+ } catch (e) {
31610
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
29932
31611
  }
29933
31612
  });
29934
31613
  }
29935
- if (ctx.shouldRegisterTool("machines_unarchive")) {
29936
- server.tool("machines_unarchive", "Unarchive a machine", {
29937
- name: exports_external2.string().describe("Machine name to unarchive")
29938
- }, async (params) => {
31614
+ if (shouldRegisterTool("heartbeat")) {
31615
+ 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.", {
31616
+ agent_id: exports_external2.string().describe("Your agent ID or name.")
31617
+ }, async ({ agent_id }) => {
29939
31618
  try {
29940
- const db = getDb();
29941
- const machine = getMachineByName(params.name, db);
29942
- if (!machine) {
29943
- return { content: [{ type: "text", text: `Machine '${params.name}' not found` }] };
31619
+ const agent = getAgent(agent_id) || getAgentByName(agent_id);
31620
+ if (!agent) {
31621
+ return { content: [{ type: "text", text: `Agent not found: ${agent_id}` }], isError: true };
29944
31622
  }
29945
- const result = unarchiveMachine(machine.id, db);
31623
+ updateAgentActivity(agent.id);
29946
31624
  return {
29947
- content: [
29948
- { type: "text", text: `Machine '${params.name}' unarchived${result ? ` (${result.id.slice(0, 8)})` : ""}` }
29949
- ]
31625
+ content: [{
31626
+ type: "text",
31627
+ text: `Heartbeat: ${agent.name} (${agent.id}) \u2014 last_seen_at updated to ${new Date().toISOString()}`
31628
+ }]
29950
31629
  };
29951
- } catch (error) {
29952
- return { content: [{ type: "text", text: ctx.formatError(error) }] };
31630
+ } catch (e) {
31631
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
29953
31632
  }
29954
31633
  });
29955
31634
  }
29956
- if (ctx.shouldRegisterTool("machines_delete")) {
29957
- server.tool("machines_delete", "Hard-delete a machine. Cannot delete primary or machines with active tasks.", {
29958
- name: exports_external2.string().describe("Machine name to delete")
29959
- }, async (params) => {
31635
+ if (shouldRegisterTool("release_agent")) {
31636
+ 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.", {
31637
+ agent_id: exports_external2.string().describe("Your agent ID or name."),
31638
+ 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).")
31639
+ }, async ({ agent_id, session_id }) => {
29960
31640
  try {
29961
- const db = getDb();
29962
- const machine = getMachineByName(params.name, db);
29963
- if (!machine) {
29964
- return { content: [{ type: "text", text: `Machine '${params.name}' not found` }] };
31641
+ const agent = getAgent(agent_id) || getAgentByName(agent_id);
31642
+ if (!agent) {
31643
+ return { content: [{ type: "text", text: `Agent not found: ${agent_id}` }], isError: true };
31644
+ }
31645
+ const released = releaseAgent(agent.id, session_id);
31646
+ if (!released) {
31647
+ return { content: [{ type: "text", text: `Release denied: session_id does not match agent's current session.` }], isError: true };
29965
31648
  }
29966
- const result = deleteMachine(machine.id, db);
29967
31649
  return {
29968
- content: [
29969
- { type: "text", text: result ? `Machine '${params.name}' deleted` : `Failed to delete machine '${params.name}'` }
29970
- ]
31650
+ content: [{
31651
+ type: "text",
31652
+ text: `Agent released: ${agent.name} (${agent.id}) \u2014 session cleared, name is now available.`
31653
+ }]
29971
31654
  };
29972
- } catch (error) {
29973
- return { content: [{ type: "text", text: ctx.formatError(error) }] };
31655
+ } catch (e) {
31656
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
29974
31657
  }
29975
31658
  });
29976
31659
  }
29977
31660
  }
29978
- var init_machines2 = __esm(() => {
31661
+ var init_agents2 = __esm(() => {
29979
31662
  init_zod2();
29980
- init_machines();
31663
+ init_agents();
31664
+ init_config();
29981
31665
  init_database();
29982
31666
  });
29983
31667
 
@@ -29988,17 +31672,23 @@ __export(exports_mcp, {
29988
31672
  });
29989
31673
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
29990
31674
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
29991
- import { readFileSync as readFileSync7 } from "fs";
31675
+ import { existsSync as existsSync9, readFileSync as readFileSync7 } from "fs";
29992
31676
  import { join as join12, dirname as dirname6 } from "path";
29993
31677
  import { fileURLToPath as fileURLToPath3 } from "url";
29994
31678
  function getMcpVersion() {
29995
31679
  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";
31680
+ let dir = dirname6(fileURLToPath3(import.meta.url));
31681
+ for (let i = 0;i < 4; i++) {
31682
+ const pkgPath = join12(dir, "package.json");
31683
+ if (existsSync9(pkgPath)) {
31684
+ return JSON.parse(readFileSync7(pkgPath, "utf-8")).version || "0.0.0";
31685
+ }
31686
+ dir = dirname6(dir);
31687
+ }
29999
31688
  } catch {
30000
31689
  return "0.0.0";
30001
31690
  }
31691
+ return "0.0.0";
30002
31692
  }
30003
31693
  function shouldRegisterTool(name) {
30004
31694
  if (TODOS_PROFILE === "minimal")
@@ -30159,6 +31849,7 @@ var init_mcp = __esm(() => {
30159
31849
  init_task_rel_tools();
30160
31850
  init_code_tools();
30161
31851
  init_machines2();
31852
+ init_agents2();
30162
31853
  server = new McpServer({
30163
31854
  name: "todos",
30164
31855
  version: getMcpVersion()
@@ -30176,6 +31867,7 @@ var init_mcp = __esm(() => {
30176
31867
  "get_next_task",
30177
31868
  "bootstrap",
30178
31869
  "get_tasks_changed_since",
31870
+ "get_health",
30179
31871
  "heartbeat",
30180
31872
  "release_agent"
30181
31873
  ]);
@@ -30216,6 +31908,7 @@ var init_mcp = __esm(() => {
30216
31908
  registerTaskResources(server, toolContext);
30217
31909
  registerTaskRelTools(server, toolContext);
30218
31910
  registerCodeTools(server, toolContext);
31911
+ registerAgentTools(server, { ...toolContext, agentFocusMap });
30219
31912
  registerMachineTools(server, { shouldRegisterTool, formatError });
30220
31913
  registerDispatchTools(server, { shouldRegisterTool, resolveId, formatError });
30221
31914
  registerCloudSyncTools(server, { shouldRegisterTool, formatError });
@@ -30766,7 +32459,7 @@ ${chalk2.cyan(sid)} ${statusColor(task.status)} ${prioColor(task.priority)} ${ta
30766
32459
  opts.tags = opts.tags || opts.tag;
30767
32460
  opts.list = opts.list || opts.taskList;
30768
32461
  const resolvedId = resolveTaskId(id);
30769
- const current = getTask2(resolvedId);
32462
+ const current = getTask(resolvedId);
30770
32463
  if (!current) {
30771
32464
  console.error(chalk2.red(`Task not found: ${id}`));
30772
32465
  process.exit(1);
@@ -30827,7 +32520,7 @@ ${chalk2.cyan(sid)} ${statusColor(task.status)} ${prioColor(task.priority)} ${ta
30827
32520
  program2.command("approve <id>").description("Approve a task that requires approval").action((id) => {
30828
32521
  const globalOpts = program2.opts();
30829
32522
  const resolvedId = resolveTaskId(id);
30830
- const task = getTask2(resolvedId);
32523
+ const task = getTask(resolvedId);
30831
32524
  if (!task) {
30832
32525
  console.error(chalk2.red(`Task not found: ${id}`));
30833
32526
  process.exit(1);
@@ -31429,7 +33122,7 @@ function pushToClaudeTaskList(taskListId, projectId, options = {}) {
31429
33122
  updated.activeForm = existing.task.activeForm;
31430
33123
  writeClaudeTask(dir, updated);
31431
33124
  if (recordConflict) {
31432
- const latest = getTask2(task.id);
33125
+ const latest = getTask(task.id);
31433
33126
  if (latest) {
31434
33127
  const conflict = {
31435
33128
  agent: "claude",
@@ -31452,7 +33145,7 @@ function pushToClaudeTaskList(taskListId, projectId, options = {}) {
31452
33145
  ct.subject = formatPrefixedSubject(task.title, prefixConfig.prefix, prefixCounter);
31453
33146
  }
31454
33147
  writeClaudeTask(dir, ct);
31455
- const current = getTask2(task.id);
33148
+ const current = getTask(task.id);
31456
33149
  if (current) {
31457
33150
  const newMeta = { ...current.metadata, claude_task_id: claudeId };
31458
33151
  updateTask(task.id, { version: current.version, metadata: newMeta });
@@ -31654,7 +33347,7 @@ function pushToAgentTaskList(agent, taskListId, projectId, options = {}) {
31654
33347
  const updated = taskToAgentTask(task, existing.task.id, existing.task.metadata);
31655
33348
  writeAgentTask(dir, updated);
31656
33349
  if (recordConflict) {
31657
- const latest = getTask2(task.id);
33350
+ const latest = getTask(task.id);
31658
33351
  if (latest) {
31659
33352
  const conflict = {
31660
33353
  agent,
@@ -31673,7 +33366,7 @@ function pushToAgentTaskList(agent, taskListId, projectId, options = {}) {
31673
33366
  hwm++;
31674
33367
  const at = taskToAgentTask(task, externalId);
31675
33368
  writeAgentTask(dir, at);
31676
- const current = getTask2(task.id);
33369
+ const current = getTask(task.id);
31677
33370
  if (current) {
31678
33371
  const newMeta = { ...current.metadata, [metaKey]: externalId };
31679
33372
  updateTask(task.id, { version: current.version, metadata: newMeta });
@@ -32324,6 +34017,27 @@ Use ${chalk5.cyan(`--agent ${result.id}`)} on future commands.`);
32324
34017
  handleError(e);
32325
34018
  }
32326
34019
  });
34020
+ program2.command("agents-normalize").alias("normalize-agents").description("Rename invalid/generated agent names (agent, agent-1, name-2, two-word names) to safe one-word names").action(async () => {
34021
+ const globalOpts = program2.opts();
34022
+ try {
34023
+ const db = getDatabase();
34024
+ const renamed = normalizeGeneratedAgentNames(db);
34025
+ if (globalOpts.json) {
34026
+ output({ renamed, suggestions: suggestAgentNames(listAgents().map((agent) => agent.name)).slice(0, 5) }, true);
34027
+ return;
34028
+ }
34029
+ if (renamed.length === 0) {
34030
+ console.log(chalk5.green("No invalid or generated agent names found."));
34031
+ return;
34032
+ }
34033
+ console.log(chalk5.green(`Normalized ${renamed.length} agent name(s):`));
34034
+ for (const item of renamed) {
34035
+ console.log(` ${chalk5.cyan(item.id)} ${chalk5.red(item.old_name)} ${chalk5.dim("->")} ${chalk5.bold(item.new_name)} ${chalk5.dim(`(${item.reference_updates} reference updates)`)}`);
34036
+ }
34037
+ } catch (e) {
34038
+ handleError(e);
34039
+ }
34040
+ });
32327
34041
  program2.command("agent-update <name>").alias("agents-update").description("Update an agent's description, role, or other fields").option("--description <text>", "New description").option("--role <role>", "New role").option("--title <title>", "New title").action(async (name, opts) => {
32328
34042
  const globalOpts = program2.opts();
32329
34043
  try {
@@ -32624,7 +34338,7 @@ function registerConfigServeCommands(program2) {
32624
34338
  console.log(JSON.stringify(config, null, 2));
32625
34339
  }
32626
34340
  });
32627
- program2.command("serve").description("Start the web dashboard").option("--port <port>", "Port number", "19427").option("--host <host>", "Host to bind (default: 127.0.0.1 localhost only, use 0.0.0.0 for all interfaces)").option("--no-open", "Don't open browser automatically").action(async (opts) => {
34341
+ program2.command("serve").description("Start the web dashboard").option("--port <port>", "Port number", "19427").option("--host <host>", "Host to bind (default: 127.0.0.1 localhost only, use 0.0.0.0 for all interfaces)").option("--api-key <key>", "Require this API key for /api/* requests").option("--no-open", "Don't open browser automatically").action(async (opts) => {
32628
34342
  const { startServer: startServer2 } = await Promise.resolve().then(() => (init_serve(), exports_serve));
32629
34343
  const requestedPort = parseInt(opts.port, 10);
32630
34344
  let port = requestedPort;
@@ -32639,7 +34353,7 @@ function registerConfigServeCommands(program2) {
32639
34353
  if (port !== requestedPort) {
32640
34354
  console.log(`Port ${requestedPort} in use, using ${port}`);
32641
34355
  }
32642
- await startServer2(port, { open: opts.open !== false, host: opts.host });
34356
+ await startServer2(port, { open: opts.open !== false, host: opts.host, apiKey: opts.apiKey });
32643
34357
  });
32644
34358
  program2.command("watch").description("Live-updating task list (refreshes every few seconds)").option("-s, --status <status>", "Filter by status (default: pending,in_progress)").option("-i, --interval <seconds>", "Refresh interval in seconds", "5").action(async (opts) => {
32645
34359
  const globalOpts = program2.opts();
@@ -32756,7 +34470,7 @@ ${tasks.length} task(s) shown`));
32756
34470
  program2.command("blame <file>").description("Show which tasks/agents touched a file and why \u2014 combines task_files + task_commits").action(async (filePath) => {
32757
34471
  const globalOpts = program2.opts();
32758
34472
  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));
34473
+ const { getTask: getTask2 } = await Promise.resolve().then(() => (init_tasks(), exports_tasks));
32760
34474
  const db = getDatabase();
32761
34475
  const taskFiles = findTasksByFile2(filePath, db);
32762
34476
  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 +34484,7 @@ Blame: ${filePath}
32770
34484
  if (taskFiles.length > 0) {
32771
34485
  console.log(chalk6.bold("Task File Links:"));
32772
34486
  for (const tf of taskFiles) {
32773
- const task = getTask3(tf.task_id, db);
34487
+ const task = getTask2(tf.task_id, db);
32774
34488
  const title = task ? task.title : "unknown";
32775
34489
  const sid = task?.short_id || tf.task_id.slice(0, 8);
32776
34490
  console.log(` ${chalk6.cyan(sid)} ${title} \u2014 ${chalk6.dim(tf.role || "file")} ${chalk6.dim(tf.updated_at)}`);
@@ -32804,74 +34518,10 @@ Commit Links (${commitRows.length}):`));
32804
34518
  init_database();
32805
34519
  init_tasks();
32806
34520
  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
34521
+ init_handoffs();
32873
34522
  init_recurrence();
32874
34523
  init_helpers();
34524
+ import chalk7 from "chalk";
32875
34525
  function registerQueryCommands(program2) {
32876
34526
  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
34527
  const globalOpts = program2.opts();
@@ -33160,7 +34810,7 @@ No task claimed (nothing available).`));
33160
34810
  const globalOpts = program2.opts();
33161
34811
  const resolvedId = resolveTaskId(id);
33162
34812
  const db = getDatabase();
33163
- const task = getTask2(resolvedId, db);
34813
+ const task = getTask(resolvedId, db);
33164
34814
  if (!task) {
33165
34815
  console.error(chalk7.red(`Task not found: ${id}`));
33166
34816
  process.exit(1);
@@ -33180,7 +34830,7 @@ No task claimed (nothing available).`));
33180
34830
  const globalOpts = program2.opts();
33181
34831
  const resolvedId = resolveTaskId(id);
33182
34832
  const db = getDatabase();
33183
- const task = getTask2(resolvedId, db);
34833
+ const task = getTask(resolvedId, db);
33184
34834
  if (!task) {
33185
34835
  console.error(chalk7.red(`Task not found: ${id}`));
33186
34836
  process.exit(1);
@@ -33200,7 +34850,7 @@ No task claimed (nothing available).`));
33200
34850
  const globalOpts = program2.opts();
33201
34851
  const resolvedId = resolveTaskId(id);
33202
34852
  const db = getDatabase();
33203
- const task = getTask2(resolvedId, db);
34853
+ const task = getTask(resolvedId, db);
33204
34854
  if (!task) {
33205
34855
  console.error(chalk7.red(`Task not found: ${id}`));
33206
34856
  process.exit(1);
@@ -33221,7 +34871,7 @@ No task claimed (nothing available).`));
33221
34871
  const globalOpts = program2.opts();
33222
34872
  const resolvedId = resolveTaskId(id);
33223
34873
  const db = getDatabase();
33224
- const task = getTask2(resolvedId, db);
34874
+ const task = getTask(resolvedId, db);
33225
34875
  if (!task) {
33226
34876
  console.error(chalk7.red(`Task not found: ${id}`));
33227
34877
  process.exit(1);
@@ -34384,7 +36034,7 @@ init_tasks();
34384
36034
  init_helpers();
34385
36035
  import chalk9 from "chalk";
34386
36036
  import { execSync as execSync3 } from "child_process";
34387
- import { existsSync as existsSync9, readFileSync as readFileSync8, writeFileSync as writeFileSync5, mkdirSync as mkdirSync5, chmodSync } from "fs";
36037
+ import { existsSync as existsSync10, readFileSync as readFileSync8, writeFileSync as writeFileSync5, mkdirSync as mkdirSync5, chmodSync } from "fs";
34388
36038
  import { dirname as dirname7, join as join13 } from "path";
34389
36039
  var HOME2 = process.env["HOME"] || process.env["USERPROFILE"] || "~";
34390
36040
  function getMcpBinaryPath() {
@@ -34394,12 +36044,12 @@ function getMcpBinaryPath() {
34394
36044
  return p;
34395
36045
  } catch {}
34396
36046
  const bunBin = join13(HOME2, ".bun", "bin", "todos-mcp");
34397
- if (existsSync9(bunBin))
36047
+ if (existsSync10(bunBin))
34398
36048
  return bunBin;
34399
36049
  return "todos-mcp";
34400
36050
  }
34401
36051
  function readJsonFile2(path) {
34402
- if (!existsSync9(path))
36052
+ if (!existsSync10(path))
34403
36053
  return {};
34404
36054
  try {
34405
36055
  return JSON.parse(readFileSync8(path, "utf-8"));
@@ -34409,19 +36059,19 @@ function readJsonFile2(path) {
34409
36059
  }
34410
36060
  function writeJsonFile2(path, data) {
34411
36061
  const dir = dirname7(path);
34412
- if (!existsSync9(dir))
36062
+ if (!existsSync10(dir))
34413
36063
  mkdirSync5(dir, { recursive: true });
34414
36064
  writeFileSync5(path, JSON.stringify(data, null, 2) + `
34415
36065
  `);
34416
36066
  }
34417
36067
  function readTomlFile(path) {
34418
- if (!existsSync9(path))
36068
+ if (!existsSync10(path))
34419
36069
  return "";
34420
36070
  return readFileSync8(path, "utf-8");
34421
36071
  }
34422
36072
  function writeTomlFile(path, content) {
34423
36073
  const dir = dirname7(path);
34424
- if (!existsSync9(dir))
36074
+ if (!existsSync10(dir))
34425
36075
  mkdirSync5(dir, { recursive: true });
34426
36076
  writeFileSync5(path, content);
34427
36077
  }
@@ -34565,7 +36215,7 @@ function registerMcpHooksCommands(program2) {
34565
36215
  todosBin = p;
34566
36216
  } catch {}
34567
36217
  const hooksDir = join13(process.cwd(), ".claude", "hooks");
34568
- if (!existsSync9(hooksDir))
36218
+ if (!existsSync10(hooksDir))
34569
36219
  mkdirSync5(hooksDir, { recursive: true });
34570
36220
  const hookScript = `#!/usr/bin/env bash
34571
36221
  # Auto-generated by: todos hooks install
@@ -34687,7 +36337,7 @@ exit 0
34687
36337
  const gitDir = execSync3("git rev-parse --git-dir", { encoding: "utf-8" }).trim();
34688
36338
  const hookPath = `${gitDir}/hooks/post-commit`;
34689
36339
  const marker = "# todos-auto-link";
34690
- if (existsSync9(hookPath)) {
36340
+ if (existsSync10(hookPath)) {
34691
36341
  const existing = readFileSync8(hookPath, "utf-8");
34692
36342
  if (existing.includes(marker)) {
34693
36343
  console.log(chalk9.yellow("Hook already installed."));
@@ -34715,7 +36365,7 @@ $(dirname "$0")/../../scripts/post-commit-hook.sh
34715
36365
  const gitDir = execSync3("git rev-parse --git-dir", { encoding: "utf-8" }).trim();
34716
36366
  const hookPath = `${gitDir}/hooks/post-commit`;
34717
36367
  const marker = "# todos-auto-link";
34718
- if (!existsSync9(hookPath)) {
36368
+ if (!existsSync10(hookPath)) {
34719
36369
  console.log(chalk9.dim("No post-commit hook found."));
34720
36370
  return;
34721
36371
  }
@@ -35132,6 +36782,91 @@ Sync complete: ${totalPulled} task(s) pulled.`));
35132
36782
  });
35133
36783
  }
35134
36784
 
36785
+ // src/cli/commands/api-key-commands.ts
36786
+ init_api_keys();
36787
+ init_helpers();
36788
+ import chalk12 from "chalk";
36789
+ function registerApiKeyCommands(program2) {
36790
+ const apiKeys = program2.command("api-keys").alias("api-key").description("Generate, list, and revoke API keys for secured app/API access");
36791
+ apiKeys.command("create <name>").alias("generate").description("Generate a new API key. The plaintext key is shown once.").option("--expires-at <iso>", "Optional ISO timestamp when this key expires").option("--permissions <list>", "Comma-separated permissions (default: *)").action((name, opts) => {
36792
+ const globalOpts = program2.opts();
36793
+ try {
36794
+ const permissions = opts.permissions ? opts.permissions.split(",").map((item) => item.trim()).filter(Boolean) : undefined;
36795
+ const created = createApiKey({ name, permissions, expires_at: opts.expiresAt || null });
36796
+ if (globalOpts.json) {
36797
+ output(created, true);
36798
+ return;
36799
+ }
36800
+ console.log(chalk12.green("API key generated:"));
36801
+ console.log(` ${chalk12.dim("ID:")} ${created.record.id}`);
36802
+ console.log(` ${chalk12.dim("Name:")} ${created.record.name}`);
36803
+ console.log(` ${chalk12.dim("Prefix:")} ${created.record.prefix}`);
36804
+ console.log();
36805
+ console.log(chalk12.yellow("Copy this key now. It will not be shown again:"));
36806
+ console.log(created.key);
36807
+ } catch (e) {
36808
+ handleError(e);
36809
+ }
36810
+ });
36811
+ apiKeys.command("list").description("List API keys without showing plaintext secrets").option("--include-revoked", "Include revoked keys").action((opts) => {
36812
+ const globalOpts = program2.opts();
36813
+ try {
36814
+ const keys = listApiKeys({ include_revoked: opts.includeRevoked ?? false });
36815
+ if (globalOpts.json) {
36816
+ output(keys, true);
36817
+ return;
36818
+ }
36819
+ if (keys.length === 0) {
36820
+ console.log(chalk12.dim("No API keys found."));
36821
+ return;
36822
+ }
36823
+ for (const key of keys) {
36824
+ const state = key.revoked_at ? chalk12.red("revoked") : key.expires_at && key.expires_at < new Date().toISOString() ? chalk12.yellow("expired") : chalk12.green("active");
36825
+ console.log(`${chalk12.cyan(key.id)} ${chalk12.bold(key.name)} ${chalk12.dim(key.prefix)} ${state}`);
36826
+ if (key.last_used_at)
36827
+ console.log(chalk12.dim(` last used: ${key.last_used_at}`));
36828
+ if (key.expires_at)
36829
+ console.log(chalk12.dim(` expires: ${key.expires_at}`));
36830
+ }
36831
+ } catch (e) {
36832
+ handleError(e);
36833
+ }
36834
+ });
36835
+ apiKeys.command("revoke <id-or-prefix>").description("Revoke an API key by id or prefix").action((idOrPrefix) => {
36836
+ const globalOpts = program2.opts();
36837
+ try {
36838
+ const revoked = revokeApiKey(idOrPrefix);
36839
+ if (!revoked) {
36840
+ throw new Error(`API key not found: ${idOrPrefix}`);
36841
+ }
36842
+ if (globalOpts.json) {
36843
+ output(revoked, true);
36844
+ return;
36845
+ }
36846
+ console.log(chalk12.green(`Revoked API key: ${revoked.name} (${revoked.prefix})`));
36847
+ } catch (e) {
36848
+ handleError(e);
36849
+ }
36850
+ });
36851
+ apiKeys.command("verify <key>").description("Verify an API key locally without printing stored hashes").action((key) => {
36852
+ const globalOpts = program2.opts();
36853
+ try {
36854
+ const record = verifyApiKey(key);
36855
+ if (globalOpts.json) {
36856
+ output({ valid: Boolean(record), key: record }, true);
36857
+ return;
36858
+ }
36859
+ if (!record) {
36860
+ console.error(chalk12.red("API key is invalid, revoked, or expired."));
36861
+ process.exit(1);
36862
+ }
36863
+ console.log(chalk12.green(`API key valid: ${record.name} (${record.prefix})`));
36864
+ } catch (e) {
36865
+ handleError(e);
36866
+ }
36867
+ });
36868
+ }
36869
+
35135
36870
  // src/cli/index.tsx
35136
36871
  var program2 = new Command;
35137
36872
  program2.name("todos").description("Universal task management for AI coding agents").version(getPackageVersion()).option("--project <path>", "Project path").option("-j, --json", "Output as JSON").option("--agent <name>", "Agent name").option("--session <id>", "Session ID");
@@ -35145,4 +36880,5 @@ registerCloudCommands2(program2);
35145
36880
  registerMcpHooksCommands(program2);
35146
36881
  registerDispatchCommands(program2);
35147
36882
  registerMachineCommands(program2);
36883
+ registerApiKeyCommands(program2);
35148
36884
  program2.parse();