@hasna/todos 0.11.33 → 0.11.34

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/mcp/index.js CHANGED
@@ -810,6 +810,26 @@ var init_migrations = __esm(() => {
810
810
  ALTER TABLE tasks ADD COLUMN current_step TEXT;
811
811
  ALTER TABLE tasks ADD COLUMN total_steps INTEGER;
812
812
  INSERT OR IGNORE INTO _migrations (id) VALUES (48);
813
+ `,
814
+ `
815
+ CREATE TABLE IF NOT EXISTS cycles (
816
+ id TEXT PRIMARY KEY,
817
+ project_id TEXT REFERENCES projects(id) ON DELETE CASCADE,
818
+ number INTEGER NOT NULL,
819
+ start_date TEXT NOT NULL,
820
+ end_date TEXT NOT NULL,
821
+ duration_weeks INTEGER NOT NULL DEFAULT 1,
822
+ status TEXT NOT NULL DEFAULT 'active' CHECK(status IN ('active', 'completed', 'archived')),
823
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
824
+ updated_at TEXT NOT NULL DEFAULT (datetime('now'))
825
+ );
826
+ CREATE INDEX IF NOT EXISTS idx_cycles_project ON cycles(project_id);
827
+ CREATE INDEX IF NOT EXISTS idx_cycles_number ON cycles(number);
828
+ CREATE INDEX IF NOT EXISTS idx_cycles_status ON cycles(status);
829
+ CREATE INDEX IF NOT EXISTS idx_cycles_dates ON cycles(start_date, end_date);
830
+ ALTER TABLE tasks ADD COLUMN cycle_id TEXT REFERENCES cycles(id) ON DELETE SET NULL;
831
+ CREATE INDEX IF NOT EXISTS idx_tasks_cycle ON tasks(cycle_id) WHERE cycle_id IS NOT NULL;
832
+ INSERT OR IGNORE INTO _migrations (id) VALUES (49);
813
833
  `
814
834
  ];
815
835
  });
@@ -13203,6 +13223,23 @@ function loadConfig() {
13203
13223
  cached = config;
13204
13224
  return cached;
13205
13225
  }
13226
+ function getAgentPoolForProject(workingDir) {
13227
+ const config = loadConfig();
13228
+ if (workingDir && config.project_pools) {
13229
+ let bestKey = null;
13230
+ let bestLen = 0;
13231
+ for (const key of Object.keys(config.project_pools)) {
13232
+ if (workingDir.startsWith(key) && key.length > bestLen) {
13233
+ bestKey = key;
13234
+ bestLen = key.length;
13235
+ }
13236
+ }
13237
+ if (bestKey && config.project_pools[bestKey]) {
13238
+ return config.project_pools[bestKey];
13239
+ }
13240
+ }
13241
+ return config.agent_pool || null;
13242
+ }
13206
13243
  function getCompletionGuardConfig(projectPath) {
13207
13244
  const config = loadConfig();
13208
13245
  const global2 = { ...GUARD_DEFAULTS, ...config.completion_guard };
@@ -13224,6 +13261,27 @@ var init_config2 = __esm(() => {
13224
13261
  });
13225
13262
 
13226
13263
  // src/db/projects.ts
13264
+ var exports_projects = {};
13265
+ __export(exports_projects, {
13266
+ updateProject: () => updateProject,
13267
+ slugify: () => slugify,
13268
+ setMachineLocalPath: () => setMachineLocalPath,
13269
+ renameProject: () => renameProject,
13270
+ removeProjectSource: () => removeProjectSource,
13271
+ removeMachineLocalPath: () => removeMachineLocalPath,
13272
+ nextTaskShortId: () => nextTaskShortId,
13273
+ listProjects: () => listProjects,
13274
+ listProjectSources: () => listProjectSources,
13275
+ listMachineLocalPaths: () => listMachineLocalPaths,
13276
+ getProjectWithSources: () => getProjectWithSources,
13277
+ getProjectByPath: () => getProjectByPath,
13278
+ getProject: () => getProject,
13279
+ getMachineLocalPath: () => getMachineLocalPath,
13280
+ ensureProject: () => ensureProject,
13281
+ deleteProject: () => deleteProject,
13282
+ createProject: () => createProject,
13283
+ addProjectSource: () => addProjectSource
13284
+ });
13227
13285
  function slugify(name) {
13228
13286
  return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
13229
13287
  }
@@ -13262,6 +13320,18 @@ function getProject(id, db) {
13262
13320
  const row = d.query("SELECT * FROM projects WHERE id = ?").get(id);
13263
13321
  return row;
13264
13322
  }
13323
+ function getProjectByPath(path, db) {
13324
+ const d = db || getDatabase();
13325
+ try {
13326
+ const machineId = getMachineId(d);
13327
+ const machineRow = d.query(`SELECT p.* FROM projects p
13328
+ JOIN project_machine_paths pmp ON pmp.project_id = p.id
13329
+ WHERE pmp.machine_id = ? AND pmp.path = ?`).get(machineId, path);
13330
+ if (machineRow)
13331
+ return machineRow;
13332
+ } catch {}
13333
+ return d.query("SELECT * FROM projects WHERE path = ?").get(path);
13334
+ }
13265
13335
  function listProjects(db) {
13266
13336
  const d = db || getDatabase();
13267
13337
  return d.query("SELECT * FROM projects ORDER BY name").all();
@@ -13293,11 +13363,141 @@ function updateProject(id, input, db) {
13293
13363
  d.run(`UPDATE projects SET ${sets.join(", ")} WHERE id = ?`, params);
13294
13364
  return getProject(id, d);
13295
13365
  }
13366
+ function renameProject(id, input, db) {
13367
+ const d = db || getDatabase();
13368
+ const project = getProject(id, d);
13369
+ if (!project)
13370
+ throw new ProjectNotFoundError(id);
13371
+ let taskListsUpdated = 0;
13372
+ const ts = now();
13373
+ if (input.new_slug !== undefined) {
13374
+ const normalised = input.new_slug.toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-|-$/g, "");
13375
+ if (!normalised)
13376
+ throw new Error("Invalid slug \u2014 must be non-empty kebab-case");
13377
+ const conflict = d.query("SELECT id FROM projects WHERE task_list_id = ? AND id != ?").get(normalised, id);
13378
+ if (conflict)
13379
+ throw new Error(`Slug "${normalised}" is already used by another project`);
13380
+ const oldSlug = project.task_list_id;
13381
+ d.run("UPDATE projects SET task_list_id = ?, updated_at = ? WHERE id = ?", [normalised, ts, id]);
13382
+ if (oldSlug) {
13383
+ const result = d.run("UPDATE task_lists SET slug = ?, name = COALESCE(?, name), updated_at = ? WHERE project_id = ? AND slug = ?", [normalised, input.name ?? null, ts, id, oldSlug]);
13384
+ taskListsUpdated = result.changes;
13385
+ }
13386
+ }
13387
+ if (input.name !== undefined) {
13388
+ d.run("UPDATE projects SET name = ?, updated_at = ? WHERE id = ?", [input.name, ts, id]);
13389
+ }
13390
+ return { project: getProject(id, d), task_lists_updated: taskListsUpdated };
13391
+ }
13296
13392
  function deleteProject(id, db) {
13297
13393
  const d = db || getDatabase();
13298
13394
  const result = d.run("DELETE FROM projects WHERE id = ?", [id]);
13299
13395
  return result.changes > 0;
13300
13396
  }
13397
+ function rowToSource(row) {
13398
+ return {
13399
+ ...row,
13400
+ metadata: row.metadata ? JSON.parse(row.metadata) : {}
13401
+ };
13402
+ }
13403
+ function addProjectSource(input, db) {
13404
+ const d = db || getDatabase();
13405
+ const id = uuid();
13406
+ const timestamp = now();
13407
+ d.run(`INSERT INTO project_sources (id, project_id, type, name, uri, description, metadata, created_at, updated_at)
13408
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
13409
+ id,
13410
+ input.project_id,
13411
+ input.type,
13412
+ input.name,
13413
+ input.uri,
13414
+ input.description || null,
13415
+ JSON.stringify(input.metadata || {}),
13416
+ timestamp,
13417
+ timestamp
13418
+ ]);
13419
+ return rowToSource(d.query("SELECT * FROM project_sources WHERE id = ?").get(id));
13420
+ }
13421
+ function removeProjectSource(id, db) {
13422
+ const d = db || getDatabase();
13423
+ const result = d.run("DELETE FROM project_sources WHERE id = ?", [id]);
13424
+ return result.changes > 0;
13425
+ }
13426
+ function listProjectSources(projectId, db) {
13427
+ const d = db || getDatabase();
13428
+ const rows = d.query("SELECT * FROM project_sources WHERE project_id = ? ORDER BY name").all(projectId);
13429
+ return rows.map(rowToSource);
13430
+ }
13431
+ function getProjectWithSources(id, db) {
13432
+ const d = db || getDatabase();
13433
+ const project = getProject(id, d);
13434
+ if (!project)
13435
+ return null;
13436
+ project.sources = listProjectSources(id, d);
13437
+ return project;
13438
+ }
13439
+ function nextTaskShortId(projectId, db) {
13440
+ const d = db || getDatabase();
13441
+ const project = getProject(projectId, d);
13442
+ if (!project || !project.task_prefix)
13443
+ return null;
13444
+ d.run("UPDATE projects SET task_counter = task_counter + 1, updated_at = ? WHERE id = ?", [now(), projectId]);
13445
+ const updated = getProject(projectId, d);
13446
+ const padded = String(updated.task_counter).padStart(5, "0");
13447
+ return `${updated.task_prefix}-${padded}`;
13448
+ }
13449
+ function ensureProject(name, path, db) {
13450
+ const d = db || getDatabase();
13451
+ const existing = getProjectByPath(path, d);
13452
+ if (existing) {
13453
+ if (!existing.task_prefix) {
13454
+ const prefix = generatePrefix(existing.name, d);
13455
+ d.run("UPDATE projects SET task_prefix = ?, updated_at = ? WHERE id = ?", [prefix, now(), existing.id]);
13456
+ return getProject(existing.id, d);
13457
+ }
13458
+ setMachineLocalPath(existing.id, path, d);
13459
+ return existing;
13460
+ }
13461
+ const project = createProject({ name, path }, d);
13462
+ setMachineLocalPath(project.id, path, d);
13463
+ return project;
13464
+ }
13465
+ function setMachineLocalPath(projectId, path, db) {
13466
+ const d = db || getDatabase();
13467
+ const machineId = getMachineId(d);
13468
+ const ts = now();
13469
+ const existing = d.query("SELECT * FROM project_machine_paths WHERE project_id = ? AND machine_id = ?").get(projectId, machineId);
13470
+ if (existing) {
13471
+ if (existing.path !== path) {
13472
+ d.run("UPDATE project_machine_paths SET path = ?, updated_at = ? WHERE id = ?", [path, ts, existing.id]);
13473
+ }
13474
+ return d.query("SELECT * FROM project_machine_paths WHERE id = ?").get(existing.id);
13475
+ }
13476
+ const id = uuid();
13477
+ d.run("INSERT INTO project_machine_paths (id, project_id, machine_id, path, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?)", [id, projectId, machineId, path, ts, ts]);
13478
+ return d.query("SELECT * FROM project_machine_paths WHERE id = ?").get(id);
13479
+ }
13480
+ function getMachineLocalPath(projectId, db) {
13481
+ const d = db || getDatabase();
13482
+ try {
13483
+ const machineId = getMachineId(d);
13484
+ const row = d.query("SELECT path FROM project_machine_paths WHERE project_id = ? AND machine_id = ?").get(projectId, machineId);
13485
+ if (row)
13486
+ return row.path;
13487
+ } catch {}
13488
+ const project = getProject(projectId, d);
13489
+ return project?.path ?? null;
13490
+ }
13491
+ function listMachineLocalPaths(projectId, db) {
13492
+ const d = db || getDatabase();
13493
+ return d.query("SELECT * FROM project_machine_paths WHERE project_id = ? ORDER BY machine_id").all(projectId);
13494
+ }
13495
+ function removeMachineLocalPath(projectId, machineId, db) {
13496
+ const d = db || getDatabase();
13497
+ const mid = machineId ?? getMachineId(d);
13498
+ const result = d.run("DELETE FROM project_machine_paths WHERE project_id = ? AND machine_id = ?", [projectId, mid]);
13499
+ return result.changes > 0;
13500
+ }
13301
13501
  var init_projects = __esm(() => {
13302
13502
  init_types2();
13303
13503
  init_database();
@@ -13558,8 +13758,8 @@ function createTask(input, db) {
13558
13758
  let id = uuid();
13559
13759
  for (let attempt = 0;attempt < 3; attempt++) {
13560
13760
  try {
13561
- 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)
13562
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
13761
+ 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)
13762
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
13563
13763
  id,
13564
13764
  null,
13565
13765
  input.project_id || null,
@@ -13581,6 +13781,10 @@ function createTask(input, db) {
13581
13781
  timestamp,
13582
13782
  input.due_at || null,
13583
13783
  input.estimated_minutes || null,
13784
+ input.confidence ?? null,
13785
+ input.retry_count ?? 0,
13786
+ input.max_retries ?? 3,
13787
+ input.retry_after ?? null,
13584
13788
  input.requires_approval ? 1 : 0,
13585
13789
  null,
13586
13790
  null,
@@ -13605,11 +13809,11 @@ function createTask(input, db) {
13605
13809
  if (tags.length > 0) {
13606
13810
  insertTaskTags(id, tags, d);
13607
13811
  }
13608
- const task = getTask2(id, d);
13812
+ const task = getTask(id, d);
13609
13813
  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(() => {});
13610
13814
  return task;
13611
13815
  }
13612
- function getTask2(id, db) {
13816
+ function getTask(id, db) {
13613
13817
  const d = db || getDatabase();
13614
13818
  const row = d.query("SELECT * FROM tasks WHERE id = ?").get(id);
13615
13819
  if (!row)
@@ -13618,7 +13822,7 @@ function getTask2(id, db) {
13618
13822
  }
13619
13823
  function getTaskWithRelations(id, db) {
13620
13824
  const d = db || getDatabase();
13621
- const task = getTask2(id, d);
13825
+ const task = getTask(id, d);
13622
13826
  if (!task)
13623
13827
  return null;
13624
13828
  const subtaskRows = d.query("SELECT * FROM tasks WHERE parent_id = ? ORDER BY created_at").all(id);
@@ -13632,7 +13836,7 @@ function getTaskWithRelations(id, db) {
13632
13836
  WHERE td.depends_on = ?`).all(id);
13633
13837
  const blocked_by = blockedByRows.map(rowToTask);
13634
13838
  const comments = d.query("SELECT * FROM task_comments WHERE task_id = ? ORDER BY created_at").all(id);
13635
- const parent = task.parent_id ? getTask2(task.parent_id, d) : null;
13839
+ const parent = task.parent_id ? getTask(task.parent_id, d) : null;
13636
13840
  const checklist = getChecklist(id, d);
13637
13841
  return {
13638
13842
  ...task,
@@ -13819,14 +14023,16 @@ function countTasks(filter = {}, db) {
13819
14023
  }
13820
14024
  function updateTask(id, input, db) {
13821
14025
  const d = db || getDatabase();
13822
- const task = getTask2(id, d);
14026
+ const task = getTask(id, d);
13823
14027
  if (!task)
13824
14028
  throw new TaskNotFoundError(id);
13825
14029
  if (task.version !== input.version) {
13826
14030
  throw new VersionConflictError(id, input.version, task.version);
13827
14031
  }
14032
+ const timestamp = now();
14033
+ const completionTimestamp = input.completed_at ?? timestamp;
13828
14034
  const sets = ["version = version + 1", "updated_at = ?"];
13829
- const params = [now()];
14035
+ const params = [timestamp];
13830
14036
  if (input.title !== undefined) {
13831
14037
  sets.push("title = ?");
13832
14038
  params.push(input.title);
@@ -13843,13 +14049,17 @@ function updateTask(id, input, db) {
13843
14049
  params.push(input.status);
13844
14050
  if (input.status === "completed") {
13845
14051
  sets.push("completed_at = ?");
13846
- params.push(now());
14052
+ params.push(completionTimestamp);
13847
14053
  }
13848
14054
  }
13849
14055
  if (input.priority !== undefined) {
13850
14056
  sets.push("priority = ?");
13851
14057
  params.push(input.priority);
13852
14058
  }
14059
+ if (input.project_id !== undefined) {
14060
+ sets.push("project_id = ?");
14061
+ params.push(input.project_id);
14062
+ }
13853
14063
  if (input.assigned_to !== undefined) {
13854
14064
  sets.push("assigned_to = ?");
13855
14065
  params.push(input.assigned_to);
@@ -13878,6 +14088,30 @@ function updateTask(id, input, db) {
13878
14088
  sets.push("estimated_minutes = ?");
13879
14089
  params.push(input.estimated_minutes);
13880
14090
  }
14091
+ if (input.actual_minutes !== undefined) {
14092
+ sets.push("actual_minutes = ?");
14093
+ params.push(input.actual_minutes);
14094
+ }
14095
+ if (input.completed_at !== undefined && input.status !== "completed") {
14096
+ sets.push("completed_at = ?");
14097
+ params.push(input.completed_at);
14098
+ }
14099
+ if (input.confidence !== undefined) {
14100
+ sets.push("confidence = ?");
14101
+ params.push(input.confidence);
14102
+ }
14103
+ if (input.retry_count !== undefined) {
14104
+ sets.push("retry_count = ?");
14105
+ params.push(input.retry_count);
14106
+ }
14107
+ if (input.max_retries !== undefined) {
14108
+ sets.push("max_retries = ?");
14109
+ params.push(input.max_retries);
14110
+ }
14111
+ if (input.retry_after !== undefined) {
14112
+ sets.push("retry_after = ?");
14113
+ params.push(input.retry_after);
14114
+ }
13881
14115
  if (input.requires_approval !== undefined) {
13882
14116
  sets.push("requires_approval = ?");
13883
14117
  params.push(input.requires_approval ? 1 : 0);
@@ -13899,7 +14133,7 @@ function updateTask(id, input, db) {
13899
14133
  params.push(id, input.version);
13900
14134
  const result = d.run(`UPDATE tasks SET ${sets.join(", ")} WHERE id = ? AND version = ?`, params);
13901
14135
  if (result.changes === 0) {
13902
- const current = getTask2(id, d);
14136
+ const current = getTask(id, d);
13903
14137
  throw new VersionConflictError(id, input.version, current?.version ?? -1);
13904
14138
  }
13905
14139
  if (input.tags !== undefined) {
@@ -13928,11 +14162,16 @@ function updateTask(id, input, db) {
13928
14162
  tags: input.tags ?? task.tags,
13929
14163
  metadata: input.metadata ?? task.metadata,
13930
14164
  version: task.version + 1,
13931
- updated_at: now(),
13932
- completed_at: input.status === "completed" ? now() : task.completed_at,
14165
+ updated_at: timestamp,
14166
+ completed_at: input.status === "completed" ? completionTimestamp : input.completed_at !== undefined ? input.completed_at : task.completed_at,
14167
+ actual_minutes: input.actual_minutes ?? task.actual_minutes,
14168
+ confidence: input.confidence !== undefined ? input.confidence : task.confidence,
14169
+ retry_count: input.retry_count ?? task.retry_count,
14170
+ max_retries: input.max_retries ?? task.max_retries,
14171
+ retry_after: input.retry_after !== undefined ? input.retry_after : task.retry_after,
13933
14172
  requires_approval: input.requires_approval !== undefined ? input.requires_approval : task.requires_approval,
13934
14173
  approved_by: input.approved_by ?? task.approved_by,
13935
- approved_at: input.approved_by ? now() : task.approved_at
14174
+ approved_at: input.approved_by ? timestamp : task.approved_at
13936
14175
  };
13937
14176
  }
13938
14177
  function deleteTask(id, db) {
@@ -14085,9 +14324,9 @@ var init_templates = __esm(() => {
14085
14324
  // src/db/task-graph.ts
14086
14325
  function addDependency(taskId, dependsOn, db) {
14087
14326
  const d = db || getDatabase();
14088
- if (!getTask2(taskId, d))
14327
+ if (!getTask(taskId, d))
14089
14328
  throw new TaskNotFoundError(taskId);
14090
- if (!getTask2(dependsOn, d))
14329
+ if (!getTask(dependsOn, d))
14091
14330
  throw new TaskNotFoundError(dependsOn);
14092
14331
  if (wouldCreateCycle(taskId, dependsOn, d)) {
14093
14332
  throw new DependencyCycleError(taskId, dependsOn);
@@ -14109,7 +14348,7 @@ function getTaskDependents(taskId, db) {
14109
14348
  }
14110
14349
  function cloneTask(taskId, overrides, db) {
14111
14350
  const d = db || getDatabase();
14112
- const source = getTask2(taskId, d);
14351
+ const source = getTask(taskId, d);
14113
14352
  if (!source)
14114
14353
  throw new TaskNotFoundError(taskId);
14115
14354
  const input = {
@@ -14132,13 +14371,13 @@ function cloneTask(taskId, overrides, db) {
14132
14371
  }
14133
14372
  function getTaskGraph(taskId, direction = "both", db) {
14134
14373
  const d = db || getDatabase();
14135
- const task = getTask2(taskId, d);
14374
+ const task = getTask(taskId, d);
14136
14375
  if (!task)
14137
14376
  throw new TaskNotFoundError(taskId);
14138
14377
  function toNode(t) {
14139
14378
  const deps = getTaskDependencies(t.id, d);
14140
14379
  const hasUnfinishedDeps = deps.some((dep) => {
14141
- const depTask = getTask2(dep.depends_on, d);
14380
+ const depTask = getTask(dep.depends_on, d);
14142
14381
  return depTask && depTask.status !== "completed";
14143
14382
  });
14144
14383
  return { id: t.id, short_id: t.short_id, title: t.title, status: t.status, priority: t.priority, is_blocked: hasUnfinishedDeps };
@@ -14149,7 +14388,7 @@ function getTaskGraph(taskId, direction = "both", db) {
14149
14388
  visited.add(id);
14150
14389
  const deps = d.query("SELECT depends_on FROM task_dependencies WHERE task_id = ?").all(id);
14151
14390
  return deps.map((dep) => {
14152
- const depTask = getTask2(dep.depends_on, d);
14391
+ const depTask = getTask(dep.depends_on, d);
14153
14392
  if (!depTask)
14154
14393
  return null;
14155
14394
  return { task: toNode(depTask), depends_on: buildUp(dep.depends_on, visited), blocks: [] };
@@ -14161,7 +14400,7 @@ function getTaskGraph(taskId, direction = "both", db) {
14161
14400
  visited.add(id);
14162
14401
  const dependents = d.query("SELECT task_id FROM task_dependencies WHERE depends_on = ?").all(id);
14163
14402
  return dependents.map((dep) => {
14164
- const depTask = getTask2(dep.task_id, d);
14403
+ const depTask = getTask(dep.task_id, d);
14165
14404
  if (!depTask)
14166
14405
  return null;
14167
14406
  return { task: toNode(depTask), depends_on: [], blocks: buildDown(dep.task_id, visited) };
@@ -14174,7 +14413,7 @@ function getTaskGraph(taskId, direction = "both", db) {
14174
14413
  }
14175
14414
  function moveTask(taskId, target, db) {
14176
14415
  const d = db || getDatabase();
14177
- const task = getTask2(taskId, d);
14416
+ const task = getTask(taskId, d);
14178
14417
  if (!task)
14179
14418
  throw new TaskNotFoundError(taskId);
14180
14419
  const sets = ["updated_at = ?", "version = version + 1"];
@@ -14193,7 +14432,7 @@ function moveTask(taskId, target, db) {
14193
14432
  }
14194
14433
  params.push(taskId);
14195
14434
  d.run(`UPDATE tasks SET ${sets.join(", ")} WHERE id = ?`, params);
14196
- return getTask2(taskId, d);
14435
+ return getTask(taskId, d);
14197
14436
  }
14198
14437
  function wouldCreateCycle(taskId, dependsOn, db) {
14199
14438
  const visited = new Set;
@@ -14226,7 +14465,7 @@ function getBlockingDeps(id, db) {
14226
14465
  return [];
14227
14466
  const blocking = [];
14228
14467
  for (const dep of deps) {
14229
- const task = getTask2(dep.depends_on, d);
14468
+ const task = getTask(dep.depends_on, d);
14230
14469
  if (task && task.status !== "completed")
14231
14470
  blocking.push(task);
14232
14471
  }
@@ -14234,7 +14473,7 @@ function getBlockingDeps(id, db) {
14234
14473
  }
14235
14474
  function startTask(id, agentId, db) {
14236
14475
  const d = db || getDatabase();
14237
- const task = getTask2(id, d);
14476
+ const task = getTask(id, d);
14238
14477
  if (!task)
14239
14478
  throw new TaskNotFoundError(id);
14240
14479
  const blocking = getBlockingDeps(id, d);
@@ -14257,7 +14496,7 @@ function startTask(id, agentId, db) {
14257
14496
  }
14258
14497
  function completeTask(id, agentId, db, options) {
14259
14498
  const d = db || getDatabase();
14260
- const task = getTask2(id, d);
14499
+ const task = getTask(id, d);
14261
14500
  if (!task)
14262
14501
  throw new TaskNotFoundError(id);
14263
14502
  if (agentId && task.locked_by && task.locked_by !== agentId && !isLockExpired(task.locked_at)) {
@@ -14273,14 +14512,14 @@ function completeTask(id, agentId, db, options) {
14273
14512
  completionMeta._completion = { confidence: options.confidence };
14274
14513
  }
14275
14514
  const hasMeta = Object.keys(completionMeta).length > 0;
14276
- const timestamp = now();
14515
+ const timestamp = options?.completed_at || now();
14277
14516
  const confidence = options?.confidence !== undefined ? options.confidence : null;
14278
14517
  const tx = d.transaction(() => {
14279
14518
  if (hasMeta) {
14280
14519
  const meta2 = { ...task.metadata, ...completionMeta };
14281
14520
  const metaResult = d.run("UPDATE tasks SET metadata = ?, version = version + 1, updated_at = ? WHERE id = ? AND version = ?", [JSON.stringify(meta2), timestamp, id, task.version]);
14282
14521
  if (metaResult.changes === 0) {
14283
- const current = getTask2(id, d);
14522
+ const current = getTask(id, d);
14284
14523
  throw new VersionConflictError(id, task.version, current?.version ?? -1);
14285
14524
  }
14286
14525
  }
@@ -14337,7 +14576,7 @@ function completeTask(id, agentId, db, options) {
14337
14576
  }
14338
14577
  function lockTask(id, agentId, db) {
14339
14578
  const d = db || getDatabase();
14340
- const task = getTask2(id, d);
14579
+ const task = getTask(id, d);
14341
14580
  if (!task)
14342
14581
  throw new TaskNotFoundError(id);
14343
14582
  if (task.locked_by === agentId && !isLockExpired(task.locked_at)) {
@@ -14348,7 +14587,7 @@ function lockTask(id, agentId, db) {
14348
14587
  const result = d.run(`UPDATE tasks SET locked_by = ?, locked_at = ?, version = version + 1, updated_at = ?
14349
14588
  WHERE id = ? AND (locked_by IS NULL OR locked_by = ? OR locked_at < ?)`, [agentId, timestamp, timestamp, id, agentId, cutoff]);
14350
14589
  if (result.changes === 0) {
14351
- const current = getTask2(id, d);
14590
+ const current = getTask(id, d);
14352
14591
  if (!current)
14353
14592
  throw new TaskNotFoundError(id);
14354
14593
  if (current.locked_by && !isLockExpired(current.locked_at)) {
@@ -14364,7 +14603,7 @@ function lockTask(id, agentId, db) {
14364
14603
  }
14365
14604
  function unlockTask(id, agentId, db) {
14366
14605
  const d = db || getDatabase();
14367
- const task = getTask2(id, d);
14606
+ const task = getTask(id, d);
14368
14607
  if (!task)
14369
14608
  throw new TaskNotFoundError(id);
14370
14609
  if (agentId && task.locked_by && task.locked_by !== agentId) {
@@ -14465,7 +14704,7 @@ function getTasksChangedSince(since, filters, db) {
14465
14704
  }
14466
14705
  function failTask(id, agentId, reason, options, db) {
14467
14706
  const d = db || getDatabase();
14468
- const task = getTask2(id, d);
14707
+ const task = getTask(id, d);
14469
14708
  if (!task)
14470
14709
  throw new TaskNotFoundError(id);
14471
14710
  const meta = {
@@ -14664,7 +14903,7 @@ function getStatus(filters, agentId, options, db) {
14664
14903
  }
14665
14904
  function decomposeTasks(parentId, subtasks, options, db) {
14666
14905
  const d = db || getDatabase();
14667
- const parent = getTask2(parentId, d);
14906
+ const parent = getTask(parentId, d);
14668
14907
  if (!parent)
14669
14908
  throw new TaskNotFoundError(parentId);
14670
14909
  const created = [];
@@ -14695,7 +14934,7 @@ function decomposeTasks(parentId, subtasks, options, db) {
14695
14934
  function setTaskStatus(id, status, _agentId, db) {
14696
14935
  const d = db || getDatabase();
14697
14936
  for (let attempt = 0;attempt < 3; attempt++) {
14698
- const task = getTask2(id, d);
14937
+ const task = getTask(id, d);
14699
14938
  if (!task)
14700
14939
  throw new TaskNotFoundError(id);
14701
14940
  if (task.status === status)
@@ -14713,7 +14952,7 @@ function setTaskStatus(id, status, _agentId, db) {
14713
14952
  function setTaskPriority(id, priority, _agentId, db) {
14714
14953
  const d = db || getDatabase();
14715
14954
  for (let attempt = 0;attempt < 3; attempt++) {
14716
- const task = getTask2(id, d);
14955
+ const task = getTask(id, d);
14717
14956
  if (!task)
14718
14957
  throw new TaskNotFoundError(id);
14719
14958
  if (task.priority === priority)
@@ -14821,7 +15060,7 @@ function bulkUpdateTasks(taskIds, updates, db) {
14821
15060
  const tx = d.transaction(() => {
14822
15061
  for (const id of taskIds) {
14823
15062
  try {
14824
- const task = getTask2(id, d);
15063
+ const task = getTask(id, d);
14825
15064
  if (!task) {
14826
15065
  failed.push({ id, error: "Task not found" });
14827
15066
  continue;
@@ -14836,6 +15075,30 @@ function bulkUpdateTasks(taskIds, updates, db) {
14836
15075
  tx();
14837
15076
  return { updated, failed };
14838
15077
  }
15078
+ function bulkDeleteTasks(taskIds, force = false, db) {
15079
+ const d = db || getDatabase();
15080
+ let deleted = 0;
15081
+ let skipped = 0;
15082
+ const failed = [];
15083
+ const tx = d.transaction(() => {
15084
+ for (const id of taskIds) {
15085
+ try {
15086
+ const childCount = d.query("SELECT COUNT(*) as count FROM tasks WHERE parent_id = ?").get(id);
15087
+ if (!force && childCount.count > 0) {
15088
+ skipped++;
15089
+ continue;
15090
+ }
15091
+ const result = d.run("DELETE FROM tasks WHERE id = ?", [id]);
15092
+ if (result.changes > 0)
15093
+ deleted++;
15094
+ } catch (e) {
15095
+ failed.push({ id, error: e instanceof Error ? e.message : String(e) });
15096
+ }
15097
+ }
15098
+ });
15099
+ tx();
15100
+ return { deleted, skipped, failed };
15101
+ }
14839
15102
  function archiveTasks(options, db) {
14840
15103
  const d = db || getDatabase();
14841
15104
  const conditions = ["archived_at IS NULL"];
@@ -14860,10 +15123,33 @@ function archiveTasks(options, db) {
14860
15123
  const result = d.run(`UPDATE tasks SET archived_at = ? WHERE ${conditions.join(" AND ")}`, [ts, ...params]);
14861
15124
  return { archived: result.changes };
14862
15125
  }
15126
+ function archiveCompletedTasks(days = 7, projectId, db) {
15127
+ return archiveTasks({
15128
+ project_id: projectId,
15129
+ older_than_days: days,
15130
+ status: ["completed"]
15131
+ }, db).archived;
15132
+ }
15133
+ function getArchivedTasks(opts = {}, db) {
15134
+ const d = db || getDatabase();
15135
+ const conditions = ["archived_at IS NOT NULL"];
15136
+ const params = [];
15137
+ if (opts.project_id) {
15138
+ conditions.push("project_id = ?");
15139
+ params.push(opts.project_id);
15140
+ }
15141
+ let limitClause = "";
15142
+ if (opts.limit) {
15143
+ limitClause = " LIMIT ?";
15144
+ params.push(opts.limit);
15145
+ }
15146
+ const rows = d.query(`SELECT * FROM tasks WHERE ${conditions.join(" AND ")} ORDER BY archived_at DESC${limitClause}`).all(...params);
15147
+ return rows.map(rowToTask);
15148
+ }
14863
15149
  function unarchiveTask(id, db) {
14864
15150
  const d = db || getDatabase();
14865
15151
  d.run("UPDATE tasks SET archived_at = NULL WHERE id = ?", [id]);
14866
- return getTask2(id, d);
15152
+ return getTask(id, d);
14867
15153
  }
14868
15154
  function getOverdueTasks(projectId, db) {
14869
15155
  const d = db || getDatabase();
@@ -14878,6 +15164,80 @@ function getOverdueTasks(projectId, db) {
14878
15164
  const rows = d.query(query).all(...params);
14879
15165
  return rows.map(rowToTask);
14880
15166
  }
15167
+ function notifyUpcomingDeadlines(opts = {}, db) {
15168
+ const d = db || getDatabase();
15169
+ const hours = opts.hours ?? 24;
15170
+ const start = new Date().toISOString();
15171
+ const end = new Date(Date.now() + hours * 60 * 60 * 1000).toISOString();
15172
+ const conditions = [
15173
+ "archived_at IS NULL",
15174
+ "due_at IS NOT NULL",
15175
+ "due_at >= ?",
15176
+ "due_at <= ?",
15177
+ "status NOT IN ('completed', 'cancelled', 'failed')"
15178
+ ];
15179
+ const params = [start, end];
15180
+ if (opts.project_id) {
15181
+ conditions.push("project_id = ?");
15182
+ params.push(opts.project_id);
15183
+ }
15184
+ if (opts.agent_id) {
15185
+ conditions.push("assigned_to = ?");
15186
+ params.push(opts.agent_id);
15187
+ }
15188
+ const rows = d.query(`SELECT * FROM tasks WHERE ${conditions.join(" AND ")} ORDER BY due_at ASC`).all(...params);
15189
+ return rows.map(rowToTask);
15190
+ }
15191
+ function getBlockedTasks(projectId, db) {
15192
+ const d = db || getDatabase();
15193
+ const params = [];
15194
+ let projectClause = "";
15195
+ if (projectId) {
15196
+ projectClause = "AND t.project_id = ?";
15197
+ params.push(projectId);
15198
+ }
15199
+ const rows = d.query(`
15200
+ SELECT t.*, GROUP_CONCAT(dep.id) AS blocked_by_ids
15201
+ FROM tasks t
15202
+ JOIN task_dependencies td ON td.task_id = t.id
15203
+ JOIN tasks dep ON dep.id = td.depends_on
15204
+ WHERE t.archived_at IS NULL
15205
+ AND t.status NOT IN ('completed', 'cancelled', 'failed')
15206
+ AND dep.status NOT IN ('completed', 'cancelled')
15207
+ ${projectClause}
15208
+ GROUP BY t.id
15209
+ ORDER BY t.priority DESC, t.created_at DESC
15210
+ `).all(...params);
15211
+ return rows.map((row) => ({
15212
+ ...rowToTask(row),
15213
+ blocked_by: row.blocked_by_ids ? row.blocked_by_ids.split(",") : []
15214
+ }));
15215
+ }
15216
+ function getBlockingTasks(projectId, db) {
15217
+ const d = db || getDatabase();
15218
+ const params = [];
15219
+ let projectClause = "";
15220
+ if (projectId) {
15221
+ projectClause = "AND blocker.project_id = ?";
15222
+ params.push(projectId);
15223
+ }
15224
+ const rows = d.query(`
15225
+ SELECT blocker.*, COUNT(DISTINCT blocked.id) AS blocking_count
15226
+ FROM tasks blocker
15227
+ JOIN task_dependencies td ON td.depends_on = blocker.id
15228
+ JOIN tasks blocked ON blocked.id = td.task_id
15229
+ WHERE blocker.archived_at IS NULL
15230
+ AND blocker.status NOT IN ('completed', 'cancelled', 'failed')
15231
+ AND blocked.status NOT IN ('completed', 'cancelled', 'failed')
15232
+ ${projectClause}
15233
+ GROUP BY blocker.id
15234
+ ORDER BY blocking_count DESC, blocker.created_at DESC
15235
+ `).all(...params);
15236
+ return rows.map((row) => ({
15237
+ ...rowToTask(row),
15238
+ blocking_count: row.blocking_count
15239
+ }));
15240
+ }
14881
15241
  function logTime(input, db) {
14882
15242
  const d = db || getDatabase();
14883
15243
  const id = uuid();
@@ -14958,6 +15318,7 @@ __export(exports_tasks, {
14958
15318
  removeDependency: () => removeDependency,
14959
15319
  redistributeStaleTasks: () => redistributeStaleTasks,
14960
15320
  notifyWatchers: () => notifyWatchers,
15321
+ notifyUpcomingDeadlines: () => notifyUpcomingDeadlines,
14961
15322
  moveTask: () => moveTask,
14962
15323
  logTime: () => logTime,
14963
15324
  logCost: () => logCost,
@@ -14973,12 +15334,15 @@ __export(exports_tasks, {
14973
15334
  getTaskGraph: () => getTaskGraph,
14974
15335
  getTaskDependents: () => getTaskDependents,
14975
15336
  getTaskDependencies: () => getTaskDependencies,
14976
- getTask: () => getTask2,
15337
+ getTask: () => getTask,
14977
15338
  getStatus: () => getStatus,
14978
15339
  getStaleTasks: () => getStaleTasks,
14979
15340
  getOverdueTasks: () => getOverdueTasks,
14980
15341
  getNextTask: () => getNextTask,
15342
+ getBlockingTasks: () => getBlockingTasks,
14981
15343
  getBlockingDeps: () => getBlockingDeps,
15344
+ getBlockedTasks: () => getBlockedTasks,
15345
+ getArchivedTasks: () => getArchivedTasks,
14982
15346
  getActiveWork: () => getActiveWork,
14983
15347
  failTask: () => failTask,
14984
15348
  deleteTask: () => deleteTask,
@@ -14990,8 +15354,10 @@ __export(exports_tasks, {
14990
15354
  claimOrSteal: () => claimOrSteal,
14991
15355
  claimNextTask: () => claimNextTask,
14992
15356
  bulkUpdateTasks: () => bulkUpdateTasks,
15357
+ bulkDeleteTasks: () => bulkDeleteTasks,
14993
15358
  bulkCreateTasks: () => bulkCreateTasks,
14994
15359
  archiveTasks: () => archiveTasks,
15360
+ archiveCompletedTasks: () => archiveCompletedTasks,
14995
15361
  addDependency: () => addDependency
14996
15362
  });
14997
15363
  var init_tasks = __esm(() => {
@@ -15169,90 +15535,495 @@ var init_dispatch = __esm(() => {
15169
15535
  init_dispatch_formatter();
15170
15536
  });
15171
15537
 
15172
- // src/db/task-files.ts
15173
- var exports_task_files = {};
15174
- __export(exports_task_files, {
15175
- updateTaskFileStatus: () => updateTaskFileStatus,
15176
- removeTaskFile: () => removeTaskFile,
15177
- listTaskFiles: () => listTaskFiles,
15178
- listActiveFiles: () => listActiveFiles,
15179
- getTaskFile: () => getTaskFile,
15180
- getFileHeatMap: () => getFileHeatMap,
15181
- findTasksByFile: () => findTasksByFile,
15182
- detectFileConflicts: () => detectFileConflicts,
15183
- bulkFindTasksByFiles: () => bulkFindTasksByFiles,
15184
- bulkAddTaskFiles: () => bulkAddTaskFiles,
15185
- addTaskFile: () => addTaskFile
15538
+ // src/db/comments.ts
15539
+ var exports_comments = {};
15540
+ __export(exports_comments, {
15541
+ updateComment: () => updateComment,
15542
+ logProgress: () => logProgress,
15543
+ listComments: () => listComments,
15544
+ getComment: () => getComment,
15545
+ deleteComment: () => deleteComment,
15546
+ addComment: () => addComment
15186
15547
  });
15187
- function addTaskFile(input, db) {
15548
+ function addComment(input, db) {
15188
15549
  const d = db || getDatabase();
15550
+ if (!getTask(input.task_id, d)) {
15551
+ throw new TaskNotFoundError(input.task_id);
15552
+ }
15189
15553
  const id = uuid();
15190
15554
  const timestamp = now();
15191
- const existing = d.query("SELECT id FROM task_files WHERE task_id = ? AND path = ?").get(input.task_id, input.path);
15192
- if (existing) {
15193
- d.run("UPDATE task_files SET status = ?, agent_id = ?, note = ?, updated_at = ? WHERE id = ?", [input.status || "active", input.agent_id || null, input.note || null, timestamp, existing.id]);
15194
- return getTaskFile(existing.id, d);
15195
- }
15196
- d.run(`INSERT INTO task_files (id, task_id, path, status, agent_id, note, created_at, updated_at)
15197
- VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, [id, input.task_id, input.path, input.status || "active", input.agent_id || null, input.note || null, timestamp, timestamp]);
15198
- return getTaskFile(id, d);
15555
+ d.run(`INSERT INTO task_comments (id, task_id, agent_id, session_id, content, type, progress_pct, created_at)
15556
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, [
15557
+ id,
15558
+ input.task_id,
15559
+ input.agent_id || null,
15560
+ input.session_id || null,
15561
+ input.content,
15562
+ input.type || "comment",
15563
+ input.progress_pct ?? null,
15564
+ timestamp
15565
+ ]);
15566
+ return getComment(id, d);
15199
15567
  }
15200
- function getTaskFile(id, db) {
15201
- const d = db || getDatabase();
15202
- return d.query("SELECT * FROM task_files WHERE id = ?").get(id);
15568
+ function logProgress(taskId, message, pct, agentId, db) {
15569
+ return addComment({ task_id: taskId, content: message, type: "progress", progress_pct: pct, agent_id: agentId }, db);
15203
15570
  }
15204
- function listTaskFiles(taskId, db) {
15571
+ function getComment(id, db) {
15205
15572
  const d = db || getDatabase();
15206
- return d.query("SELECT * FROM task_files WHERE task_id = ? ORDER BY path").all(taskId);
15573
+ return d.query("SELECT * FROM task_comments WHERE id = ?").get(id);
15207
15574
  }
15208
- function findTasksByFile(path, db) {
15575
+ function listComments(taskId, db) {
15209
15576
  const d = db || getDatabase();
15210
- return d.query("SELECT * FROM task_files WHERE path = ? AND status != 'removed' ORDER BY updated_at DESC").all(path);
15577
+ return d.query("SELECT * FROM task_comments WHERE task_id = ? ORDER BY created_at").all(taskId);
15211
15578
  }
15212
- function updateTaskFileStatus(taskId, path, status, agentId, db) {
15579
+ function updateComment(id, input, db) {
15213
15580
  const d = db || getDatabase();
15214
- const timestamp = now();
15215
- d.run("UPDATE task_files SET status = ?, agent_id = COALESCE(?, agent_id), updated_at = ? WHERE task_id = ? AND path = ?", [status, agentId || null, timestamp, taskId, path]);
15216
- const row = d.query("SELECT * FROM task_files WHERE task_id = ? AND path = ?").get(taskId, path);
15217
- return row;
15581
+ d.run("UPDATE task_comments SET content = ? WHERE id = ?", [input.content, id]);
15582
+ const comment = getComment(id, d);
15583
+ if (!comment) {
15584
+ throw new Error(`Comment not found: ${id}`);
15585
+ }
15586
+ return comment;
15218
15587
  }
15219
- function removeTaskFile(taskId, path, db) {
15588
+ function deleteComment(id, db) {
15220
15589
  const d = db || getDatabase();
15221
- const result = d.run("DELETE FROM task_files WHERE task_id = ? AND path = ?", [taskId, path]);
15590
+ const result = d.run("DELETE FROM task_comments WHERE id = ?", [id]);
15222
15591
  return result.changes > 0;
15223
15592
  }
15224
- function detectFileConflicts(taskId, paths, db) {
15225
- const d = db || getDatabase();
15226
- if (paths.length === 0)
15227
- return [];
15228
- const placeholders = paths.map(() => "?").join(", ");
15229
- const rows = d.query(`
15230
- SELECT tf.path, tf.agent_id AS conflicting_agent_id, t.id AS conflicting_task_id,
15231
- t.title AS conflicting_task_title, t.status AS conflicting_task_status
15232
- FROM task_files tf
15233
- JOIN tasks t ON tf.task_id = t.id
15234
- WHERE tf.path IN (${placeholders})
15235
- AND tf.task_id != ?
15236
- AND tf.status != 'removed'
15237
- AND t.status = 'in_progress'
15238
- ORDER BY tf.updated_at DESC
15239
- `).all(...paths, taskId);
15240
- return rows;
15593
+ var init_comments = __esm(() => {
15594
+ init_types2();
15595
+ init_database();
15596
+ init_tasks();
15597
+ });
15598
+
15599
+ // src/lib/search.ts
15600
+ var exports_search = {};
15601
+ __export(exports_search, {
15602
+ searchTasks: () => searchTasks
15603
+ });
15604
+ function rowToTask2(row) {
15605
+ return {
15606
+ ...row,
15607
+ tags: JSON.parse(row.tags || "[]"),
15608
+ metadata: JSON.parse(row.metadata || "{}"),
15609
+ status: row.status,
15610
+ priority: row.priority,
15611
+ requires_approval: Boolean(row.requires_approval)
15612
+ };
15241
15613
  }
15242
- function bulkFindTasksByFiles(paths, db) {
15614
+ function hasFts(db) {
15615
+ try {
15616
+ const result = db.query("SELECT 1 FROM sqlite_master WHERE type='table' AND name='tasks_fts'").get();
15617
+ return result !== null;
15618
+ } catch {
15619
+ return false;
15620
+ }
15621
+ }
15622
+ function escapeFtsQuery(q) {
15623
+ return q.replace(/["*^()]/g, " ").trim().split(/\s+/).filter(Boolean).map((token) => `"${token}"*`).join(" ");
15624
+ }
15625
+ function searchTasks(options, projectId, taskListId, db) {
15626
+ const opts = typeof options === "string" ? { query: options || undefined, project_id: projectId, task_list_id: taskListId } : options;
15243
15627
  const d = db || getDatabase();
15244
- if (paths.length === 0)
15245
- return [];
15246
- const placeholders = paths.map(() => "?").join(", ");
15247
- const rows = d.query(`SELECT tf.*, t.status AS task_status FROM task_files tf
15248
- JOIN tasks t ON tf.task_id = t.id
15249
- WHERE tf.path IN (${placeholders}) AND tf.status != 'removed'
15250
- ORDER BY tf.updated_at DESC`).all(...paths);
15251
- const byPath = new Map;
15252
- for (const path of paths)
15253
- byPath.set(path, []);
15254
- for (const row of rows) {
15255
- byPath.get(row.path)?.push(row);
15628
+ clearExpiredLocks(d);
15629
+ const params = [];
15630
+ let sql;
15631
+ const raw = opts.query?.trim() ?? "";
15632
+ const q = raw === "*" ? "" : raw;
15633
+ if (hasFts(d) && q) {
15634
+ const ftsQuery = escapeFtsQuery(q);
15635
+ sql = `SELECT t.* FROM tasks t
15636
+ INNER JOIN tasks_fts fts ON fts.rowid = t.rowid
15637
+ WHERE tasks_fts MATCH ?`;
15638
+ params.push(ftsQuery);
15639
+ } else if (q) {
15640
+ const pattern = `%${q}%`;
15641
+ sql = `SELECT * FROM tasks t WHERE (t.title LIKE ? OR t.description LIKE ? OR EXISTS (SELECT 1 FROM task_tags WHERE task_tags.task_id = t.id AND tag LIKE ?))`;
15642
+ params.push(pattern, pattern, pattern);
15643
+ } else {
15644
+ sql = `SELECT * FROM tasks t WHERE 1=1`;
15645
+ }
15646
+ if (opts.project_id) {
15647
+ sql += " AND t.project_id = ?";
15648
+ params.push(opts.project_id);
15649
+ }
15650
+ if (opts.task_list_id) {
15651
+ sql += " AND t.task_list_id = ?";
15652
+ params.push(opts.task_list_id);
15653
+ }
15654
+ if (opts.status) {
15655
+ if (Array.isArray(opts.status)) {
15656
+ sql += ` AND t.status IN (${opts.status.map(() => "?").join(",")})`;
15657
+ params.push(...opts.status);
15658
+ } else {
15659
+ sql += " AND t.status = ?";
15660
+ params.push(opts.status);
15661
+ }
15662
+ }
15663
+ if (opts.priority) {
15664
+ if (Array.isArray(opts.priority)) {
15665
+ sql += ` AND t.priority IN (${opts.priority.map(() => "?").join(",")})`;
15666
+ params.push(...opts.priority);
15667
+ } else {
15668
+ sql += " AND t.priority = ?";
15669
+ params.push(opts.priority);
15670
+ }
15671
+ }
15672
+ if (opts.assigned_to) {
15673
+ sql += " AND t.assigned_to = ?";
15674
+ params.push(opts.assigned_to);
15675
+ }
15676
+ if (opts.agent_id) {
15677
+ sql += " AND t.agent_id = ?";
15678
+ params.push(opts.agent_id);
15679
+ }
15680
+ if (opts.created_after) {
15681
+ sql += " AND t.created_at > ?";
15682
+ params.push(opts.created_after);
15683
+ }
15684
+ if (opts.updated_after) {
15685
+ sql += " AND t.updated_at > ?";
15686
+ params.push(opts.updated_after);
15687
+ }
15688
+ if (opts.has_dependencies === true) {
15689
+ sql += " AND t.id IN (SELECT task_id FROM task_dependencies)";
15690
+ } else if (opts.has_dependencies === false) {
15691
+ sql += " AND t.id NOT IN (SELECT task_id FROM task_dependencies)";
15692
+ }
15693
+ if (opts.is_blocked === true) {
15694
+ sql += " AND t.id IN (SELECT td.task_id FROM task_dependencies td JOIN tasks dep ON dep.id = td.depends_on WHERE dep.status != 'completed')";
15695
+ } else if (opts.is_blocked === false) {
15696
+ sql += " AND t.id NOT IN (SELECT td.task_id FROM task_dependencies td JOIN tasks dep ON dep.id = td.depends_on WHERE dep.status != 'completed')";
15697
+ }
15698
+ if (hasFts(d) && q) {
15699
+ sql += ` ORDER BY bm25(tasks_fts),
15700
+ CASE t.priority WHEN 'critical' THEN 0 WHEN 'high' THEN 1 WHEN 'medium' THEN 2 WHEN 'low' THEN 3 END,
15701
+ t.created_at DESC`;
15702
+ } else {
15703
+ sql += ` ORDER BY
15704
+ CASE t.priority WHEN 'critical' THEN 0 WHEN 'high' THEN 1 WHEN 'medium' THEN 2 WHEN 'low' THEN 3 END,
15705
+ t.created_at DESC`;
15706
+ }
15707
+ const rows = d.query(sql).all(...params);
15708
+ return rows.map(rowToTask2);
15709
+ }
15710
+ var init_search = __esm(() => {
15711
+ init_database();
15712
+ });
15713
+
15714
+ // src/db/handoffs.ts
15715
+ var exports_handoffs = {};
15716
+ __export(exports_handoffs, {
15717
+ listHandoffs: () => listHandoffs,
15718
+ getLatestHandoff: () => getLatestHandoff,
15719
+ createHandoff: () => createHandoff
15720
+ });
15721
+ function createHandoff(input, db) {
15722
+ const d = db || getDatabase();
15723
+ const id = uuid();
15724
+ const timestamp = now();
15725
+ d.run(`INSERT INTO handoffs (id, agent_id, project_id, summary, completed, in_progress, blockers, next_steps, created_at)
15726
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
15727
+ id,
15728
+ input.agent_id || null,
15729
+ input.project_id || null,
15730
+ input.summary,
15731
+ input.completed ? JSON.stringify(input.completed) : null,
15732
+ input.in_progress ? JSON.stringify(input.in_progress) : null,
15733
+ input.blockers ? JSON.stringify(input.blockers) : null,
15734
+ input.next_steps ? JSON.stringify(input.next_steps) : null,
15735
+ timestamp
15736
+ ]);
15737
+ return {
15738
+ id,
15739
+ agent_id: input.agent_id || null,
15740
+ project_id: input.project_id || null,
15741
+ summary: input.summary,
15742
+ completed: input.completed || null,
15743
+ in_progress: input.in_progress || null,
15744
+ blockers: input.blockers || null,
15745
+ next_steps: input.next_steps || null,
15746
+ created_at: timestamp
15747
+ };
15748
+ }
15749
+ function rowToHandoff(row) {
15750
+ return {
15751
+ ...row,
15752
+ completed: row.completed ? JSON.parse(row.completed) : null,
15753
+ in_progress: row.in_progress ? JSON.parse(row.in_progress) : null,
15754
+ blockers: row.blockers ? JSON.parse(row.blockers) : null,
15755
+ next_steps: row.next_steps ? JSON.parse(row.next_steps) : null
15756
+ };
15757
+ }
15758
+ function listHandoffs(projectId, limit = 10, db) {
15759
+ const d = db || getDatabase();
15760
+ if (projectId) {
15761
+ return d.query("SELECT * FROM handoffs WHERE project_id = ? ORDER BY rowid DESC LIMIT ?").all(projectId, limit).map(rowToHandoff);
15762
+ }
15763
+ return d.query("SELECT * FROM handoffs ORDER BY rowid DESC LIMIT ?").all(limit).map(rowToHandoff);
15764
+ }
15765
+ function getLatestHandoff(agentId, projectId, db) {
15766
+ const d = db || getDatabase();
15767
+ let query = "SELECT * FROM handoffs WHERE 1=1";
15768
+ const params = [];
15769
+ if (agentId) {
15770
+ query += " AND agent_id = ?";
15771
+ params.push(agentId);
15772
+ }
15773
+ if (projectId) {
15774
+ query += " AND project_id = ?";
15775
+ params.push(projectId);
15776
+ }
15777
+ query += " ORDER BY rowid DESC LIMIT 1";
15778
+ const row = d.query(query).get(...params);
15779
+ return row ? rowToHandoff(row) : null;
15780
+ }
15781
+ var init_handoffs = __esm(() => {
15782
+ init_database();
15783
+ });
15784
+
15785
+ // src/lib/auto-assign.ts
15786
+ var exports_auto_assign = {};
15787
+ __export(exports_auto_assign, {
15788
+ findBestAgent: () => findBestAgent,
15789
+ autoAssignTask: () => autoAssignTask
15790
+ });
15791
+ function findBestAgent(_task, db) {
15792
+ const d = db || getDatabase();
15793
+ const agents = listAgents(d).filter((a) => (a.role || "agent") === "agent");
15794
+ if (agents.length === 0)
15795
+ return null;
15796
+ const inProgressTasks = listTasks({ status: "in_progress" }, d);
15797
+ const idToName = new Map;
15798
+ const load = new Map;
15799
+ for (const a of agents) {
15800
+ idToName.set(a.id, a.name);
15801
+ load.set(a.id, 0);
15802
+ }
15803
+ for (const t of inProgressTasks) {
15804
+ const agentId = t.assigned_to || t.agent_id;
15805
+ if (agentId && load.has(agentId)) {
15806
+ load.set(agentId, (load.get(agentId) || 0) + 1);
15807
+ }
15808
+ }
15809
+ let bestAgent = agents[0].name;
15810
+ let bestLoad = load.get(agents[0].id) ?? 0;
15811
+ for (const a of agents) {
15812
+ const l = load.get(a.id) ?? 0;
15813
+ if (l < bestLoad) {
15814
+ bestAgent = a.name;
15815
+ bestLoad = l;
15816
+ }
15817
+ }
15818
+ return bestAgent;
15819
+ }
15820
+ function getAgentWorkloads(d) {
15821
+ const rows = d.query("SELECT assigned_to, COUNT(*) as count FROM tasks WHERE status = 'in_progress' AND assigned_to IS NOT NULL GROUP BY assigned_to").all();
15822
+ return new Map(rows.map((r) => [r.assigned_to, r.count]));
15823
+ }
15824
+ function buildPrompt(task, agents) {
15825
+ const agentList = agents.map((a) => `- ${a.name} (role: ${a.role}, caps: [${a.capabilities.join(", ")}], active_tasks: ${a.in_progress_tasks})`).join(`
15826
+ `);
15827
+ return `You are a task routing assistant. Given a task and available agents, choose the SINGLE best agent.
15828
+
15829
+ TASK:
15830
+ Title: ${task.title}
15831
+ Priority: ${task.priority}
15832
+ Tags: ${task.tags.join(", ") || "none"}
15833
+ Description: ${task.description?.slice(0, 300) || "none"}
15834
+
15835
+ AVAILABLE AGENTS:
15836
+ ${agentList}
15837
+
15838
+ Rules:
15839
+ - Match task tags/content to agent capabilities
15840
+ - Prefer agents with fewer active tasks
15841
+ - Prefer agents whose role fits the task (lead for critical, developer for features, qa for testing)
15842
+ - If no clear match, pick the agent with fewest active tasks
15843
+
15844
+ Respond with ONLY a JSON object: {"agent_name": "<name>", "reason": "<one sentence>"}`;
15845
+ }
15846
+ async function callCerebras(prompt, apiKey) {
15847
+ try {
15848
+ const resp = await fetch(CEREBRAS_API_URL, {
15849
+ method: "POST",
15850
+ headers: { "Content-Type": "application/json", Authorization: `Bearer ${apiKey}` },
15851
+ body: JSON.stringify({
15852
+ model: CEREBRAS_MODEL,
15853
+ messages: [{ role: "user", content: prompt }],
15854
+ max_tokens: 150,
15855
+ temperature: 0
15856
+ }),
15857
+ signal: AbortSignal.timeout(1e4)
15858
+ });
15859
+ if (!resp.ok)
15860
+ return null;
15861
+ const data = await resp.json();
15862
+ const content = data?.choices?.[0]?.message?.content?.trim();
15863
+ if (!content)
15864
+ return null;
15865
+ const match = content.match(/\{[^}]+\}/s);
15866
+ if (!match)
15867
+ return null;
15868
+ return JSON.parse(match[0]);
15869
+ } catch {
15870
+ return null;
15871
+ }
15872
+ }
15873
+ async function autoAssignTask(taskId, db) {
15874
+ const d = db || getDatabase();
15875
+ const task = getTask(taskId, d);
15876
+ if (!task)
15877
+ throw new Error(`Task ${taskId} not found`);
15878
+ const agents = listAgents(d).filter((a) => a.status === "active");
15879
+ if (agents.length === 0) {
15880
+ return { task_id: taskId, assigned_to: null, agent_name: null, method: "no_agents" };
15881
+ }
15882
+ const workloads = getAgentWorkloads(d);
15883
+ const apiKey = process.env["CEREBRAS_API_KEY"];
15884
+ let selectedAgent = null;
15885
+ let method = "capability_match";
15886
+ let reason;
15887
+ if (apiKey) {
15888
+ const agentData = agents.map((a) => ({
15889
+ id: a.id,
15890
+ name: a.name,
15891
+ role: a.role || "agent",
15892
+ capabilities: a.capabilities || [],
15893
+ in_progress_tasks: workloads.get(a.id) ?? 0
15894
+ }));
15895
+ const result = await callCerebras(buildPrompt({
15896
+ title: task.title,
15897
+ description: task.description,
15898
+ priority: task.priority,
15899
+ tags: task.tags || []
15900
+ }, agentData), apiKey);
15901
+ if (result?.agent_name) {
15902
+ selectedAgent = agents.find((a) => a.name === result.agent_name) ?? null;
15903
+ if (selectedAgent) {
15904
+ method = "cerebras";
15905
+ reason = result.reason;
15906
+ }
15907
+ }
15908
+ }
15909
+ if (!selectedAgent) {
15910
+ const taskTags = task.tags || [];
15911
+ const capable = getCapableAgents(taskTags, { min_score: 0, limit: 10 }, d);
15912
+ if (capable.length > 0) {
15913
+ const sorted = capable.sort((a, b) => {
15914
+ if (b.score !== a.score)
15915
+ return b.score - a.score;
15916
+ return (workloads.get(a.agent.id) ?? 0) - (workloads.get(b.agent.id) ?? 0);
15917
+ });
15918
+ selectedAgent = sorted[0].agent;
15919
+ reason = `Capability match (score: ${sorted[0].score.toFixed(2)})`;
15920
+ } else {
15921
+ selectedAgent = agents.slice().sort((a, b) => (workloads.get(a.id) ?? 0) - (workloads.get(b.id) ?? 0))[0];
15922
+ reason = `Least busy agent (${workloads.get(selectedAgent.id) ?? 0} active tasks)`;
15923
+ }
15924
+ }
15925
+ if (selectedAgent) {
15926
+ updateTask(taskId, { assigned_to: selectedAgent.id, version: task.version }, d);
15927
+ }
15928
+ return {
15929
+ task_id: taskId,
15930
+ assigned_to: selectedAgent?.id ?? null,
15931
+ agent_name: selectedAgent?.name ?? null,
15932
+ method,
15933
+ reason
15934
+ };
15935
+ }
15936
+ var CEREBRAS_API_URL = "https://api.cerebras.ai/v1/chat/completions", CEREBRAS_MODEL = "llama-3.3-70b";
15937
+ var init_auto_assign = __esm(() => {
15938
+ init_database();
15939
+ init_tasks();
15940
+ init_agents();
15941
+ });
15942
+
15943
+ // src/db/task-files.ts
15944
+ var exports_task_files = {};
15945
+ __export(exports_task_files, {
15946
+ updateTaskFileStatus: () => updateTaskFileStatus,
15947
+ removeTaskFile: () => removeTaskFile,
15948
+ listTaskFiles: () => listTaskFiles,
15949
+ listActiveFiles: () => listActiveFiles,
15950
+ getTaskFile: () => getTaskFile,
15951
+ getFileHeatMap: () => getFileHeatMap,
15952
+ findTasksByFile: () => findTasksByFile,
15953
+ detectFileConflicts: () => detectFileConflicts,
15954
+ bulkFindTasksByFiles: () => bulkFindTasksByFiles,
15955
+ bulkAddTaskFiles: () => bulkAddTaskFiles,
15956
+ addTaskFile: () => addTaskFile
15957
+ });
15958
+ function addTaskFile(input, db) {
15959
+ const d = db || getDatabase();
15960
+ const id = uuid();
15961
+ const timestamp = now();
15962
+ const existing = d.query("SELECT id FROM task_files WHERE task_id = ? AND path = ?").get(input.task_id, input.path);
15963
+ if (existing) {
15964
+ d.run("UPDATE task_files SET status = ?, agent_id = ?, note = ?, updated_at = ? WHERE id = ?", [input.status || "active", input.agent_id || null, input.note || null, timestamp, existing.id]);
15965
+ return getTaskFile(existing.id, d);
15966
+ }
15967
+ d.run(`INSERT INTO task_files (id, task_id, path, status, agent_id, note, created_at, updated_at)
15968
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, [id, input.task_id, input.path, input.status || "active", input.agent_id || null, input.note || null, timestamp, timestamp]);
15969
+ return getTaskFile(id, d);
15970
+ }
15971
+ function getTaskFile(id, db) {
15972
+ const d = db || getDatabase();
15973
+ return d.query("SELECT * FROM task_files WHERE id = ?").get(id);
15974
+ }
15975
+ function listTaskFiles(taskId, db) {
15976
+ const d = db || getDatabase();
15977
+ return d.query("SELECT * FROM task_files WHERE task_id = ? ORDER BY path").all(taskId);
15978
+ }
15979
+ function findTasksByFile(path, db) {
15980
+ const d = db || getDatabase();
15981
+ return d.query("SELECT * FROM task_files WHERE path = ? AND status != 'removed' ORDER BY updated_at DESC").all(path);
15982
+ }
15983
+ function updateTaskFileStatus(taskId, path, status, agentId, db) {
15984
+ const d = db || getDatabase();
15985
+ const timestamp = now();
15986
+ d.run("UPDATE task_files SET status = ?, agent_id = COALESCE(?, agent_id), updated_at = ? WHERE task_id = ? AND path = ?", [status, agentId || null, timestamp, taskId, path]);
15987
+ const row = d.query("SELECT * FROM task_files WHERE task_id = ? AND path = ?").get(taskId, path);
15988
+ return row;
15989
+ }
15990
+ function removeTaskFile(taskId, path, db) {
15991
+ const d = db || getDatabase();
15992
+ const result = d.run("DELETE FROM task_files WHERE task_id = ? AND path = ?", [taskId, path]);
15993
+ return result.changes > 0;
15994
+ }
15995
+ function detectFileConflicts(taskId, paths, db) {
15996
+ const d = db || getDatabase();
15997
+ if (paths.length === 0)
15998
+ return [];
15999
+ const placeholders = paths.map(() => "?").join(", ");
16000
+ const rows = d.query(`
16001
+ SELECT tf.path, tf.agent_id AS conflicting_agent_id, t.id AS conflicting_task_id,
16002
+ t.title AS conflicting_task_title, t.status AS conflicting_task_status
16003
+ FROM task_files tf
16004
+ JOIN tasks t ON tf.task_id = t.id
16005
+ WHERE tf.path IN (${placeholders})
16006
+ AND tf.task_id != ?
16007
+ AND tf.status != 'removed'
16008
+ AND t.status = 'in_progress'
16009
+ ORDER BY tf.updated_at DESC
16010
+ `).all(...paths, taskId);
16011
+ return rows;
16012
+ }
16013
+ function bulkFindTasksByFiles(paths, db) {
16014
+ const d = db || getDatabase();
16015
+ if (paths.length === 0)
16016
+ return [];
16017
+ const placeholders = paths.map(() => "?").join(", ");
16018
+ const rows = d.query(`SELECT tf.*, t.status AS task_status FROM task_files tf
16019
+ JOIN tasks t ON tf.task_id = t.id
16020
+ WHERE tf.path IN (${placeholders}) AND tf.status != 'removed'
16021
+ ORDER BY tf.updated_at DESC`).all(...paths);
16022
+ const byPath = new Map;
16023
+ for (const path of paths)
16024
+ byPath.set(path, []);
16025
+ for (const row of rows) {
16026
+ byPath.get(row.path)?.push(row);
15256
16027
  }
15257
16028
  return paths.map((path) => {
15258
16029
  const tasks = byPath.get(path) ?? [];
@@ -15451,14 +16222,653 @@ function autoDetectFileRelationships(taskId, db) {
15451
16222
  var RELATIONSHIP_TYPES;
15452
16223
  var init_task_relationships = __esm(() => {
15453
16224
  init_database();
15454
- RELATIONSHIP_TYPES = [
15455
- "related_to",
15456
- "conflicts_with",
15457
- "similar_to",
15458
- "duplicates",
15459
- "supersedes",
15460
- "modifies_same_file"
15461
- ];
16225
+ RELATIONSHIP_TYPES = [
16226
+ "related_to",
16227
+ "conflicts_with",
16228
+ "similar_to",
16229
+ "duplicates",
16230
+ "supersedes",
16231
+ "modifies_same_file"
16232
+ ];
16233
+ });
16234
+
16235
+ // src/db/task-commits.ts
16236
+ var exports_task_commits = {};
16237
+ __export(exports_task_commits, {
16238
+ unlinkTaskCommit: () => unlinkTaskCommit,
16239
+ linkTaskToCommit: () => linkTaskToCommit,
16240
+ getTaskCommits: () => getTaskCommits,
16241
+ findTaskByCommit: () => findTaskByCommit
16242
+ });
16243
+ function rowToCommit(row) {
16244
+ return {
16245
+ ...row,
16246
+ files_changed: row.files_changed ? JSON.parse(row.files_changed) : null
16247
+ };
16248
+ }
16249
+ function linkTaskToCommit(input, db) {
16250
+ const d = db || getDatabase();
16251
+ const existing = d.query("SELECT * FROM task_commits WHERE task_id = ? AND sha = ?").get(input.task_id, input.sha);
16252
+ if (existing) {
16253
+ d.run("UPDATE task_commits SET message = COALESCE(?, message), author = COALESCE(?, author), files_changed = COALESCE(?, files_changed), committed_at = COALESCE(?, committed_at) WHERE id = ?", [input.message ?? null, input.author ?? null, input.files_changed ? JSON.stringify(input.files_changed) : null, input.committed_at ?? null, existing.id]);
16254
+ return rowToCommit(d.query("SELECT * FROM task_commits WHERE id = ?").get(existing.id));
16255
+ }
16256
+ const id = uuid();
16257
+ d.run("INSERT INTO task_commits (id, task_id, sha, message, author, files_changed, committed_at, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?)", [id, input.task_id, input.sha, input.message ?? null, input.author ?? null, input.files_changed ? JSON.stringify(input.files_changed) : null, input.committed_at ?? null, now()]);
16258
+ return rowToCommit(d.query("SELECT * FROM task_commits WHERE id = ?").get(id));
16259
+ }
16260
+ function getTaskCommits(taskId, db) {
16261
+ const d = db || getDatabase();
16262
+ return d.query("SELECT * FROM task_commits WHERE task_id = ? ORDER BY committed_at DESC, created_at DESC").all(taskId).map(rowToCommit);
16263
+ }
16264
+ function findTaskByCommit(sha, db) {
16265
+ const d = db || getDatabase();
16266
+ const row = d.query("SELECT * FROM task_commits WHERE sha = ? OR sha LIKE ? LIMIT 1").get(sha, `${sha}%`);
16267
+ if (!row)
16268
+ return null;
16269
+ return { task_id: row.task_id, commit: rowToCommit(row) };
16270
+ }
16271
+ function unlinkTaskCommit(taskId, sha, db) {
16272
+ const d = db || getDatabase();
16273
+ return d.run("DELETE FROM task_commits WHERE task_id = ? AND (sha = ? OR sha LIKE ?)", [taskId, sha, `${sha}%`]).changes > 0;
16274
+ }
16275
+ var init_task_commits = __esm(() => {
16276
+ init_database();
16277
+ });
16278
+
16279
+ // src/db/file-locks.ts
16280
+ var exports_file_locks = {};
16281
+ __export(exports_file_locks, {
16282
+ unlockFile: () => unlockFile,
16283
+ lockFile: () => lockFile,
16284
+ listFileLocks: () => listFileLocks,
16285
+ forceUnlockFile: () => forceUnlockFile,
16286
+ cleanExpiredFileLocks: () => cleanExpiredFileLocks,
16287
+ checkFileLock: () => checkFileLock,
16288
+ FILE_LOCK_DEFAULT_TTL_SECONDS: () => FILE_LOCK_DEFAULT_TTL_SECONDS
16289
+ });
16290
+ function expiresAt(ttlSeconds) {
16291
+ return new Date(Date.now() + ttlSeconds * 1000).toISOString();
16292
+ }
16293
+ function cleanExpiredFileLocks(db) {
16294
+ const d = db || getDatabase();
16295
+ const result = d.run("DELETE FROM file_locks WHERE expires_at <= ?", [now()]);
16296
+ return result.changes;
16297
+ }
16298
+ function lockFile(input, db) {
16299
+ const d = db || getDatabase();
16300
+ const ttl = input.ttl_seconds ?? FILE_LOCK_DEFAULT_TTL_SECONDS;
16301
+ const expiry = expiresAt(ttl);
16302
+ const timestamp = now();
16303
+ cleanExpiredFileLocks(d);
16304
+ const existing = d.query("SELECT * FROM file_locks WHERE path = ?").get(input.path);
16305
+ if (existing) {
16306
+ if (existing.agent_id === input.agent_id) {
16307
+ d.run("UPDATE file_locks SET expires_at = ?, task_id = COALESCE(?, task_id) WHERE id = ?", [expiry, input.task_id ?? null, existing.id]);
16308
+ return d.query("SELECT * FROM file_locks WHERE id = ?").get(existing.id);
16309
+ }
16310
+ throw new LockError(input.path, existing.agent_id);
16311
+ }
16312
+ const id = uuid();
16313
+ 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]);
16314
+ return d.query("SELECT * FROM file_locks WHERE id = ?").get(id);
16315
+ }
16316
+ function unlockFile(path, agentId, db) {
16317
+ const d = db || getDatabase();
16318
+ cleanExpiredFileLocks(d);
16319
+ const result = d.run("DELETE FROM file_locks WHERE path = ? AND agent_id = ?", [path, agentId]);
16320
+ return result.changes > 0;
16321
+ }
16322
+ function checkFileLock(path, db) {
16323
+ const d = db || getDatabase();
16324
+ cleanExpiredFileLocks(d);
16325
+ return d.query("SELECT * FROM file_locks WHERE path = ?").get(path);
16326
+ }
16327
+ function listFileLocks(agentId, db) {
16328
+ const d = db || getDatabase();
16329
+ cleanExpiredFileLocks(d);
16330
+ if (agentId) {
16331
+ return d.query("SELECT * FROM file_locks WHERE agent_id = ? ORDER BY created_at DESC").all(agentId);
16332
+ }
16333
+ return d.query("SELECT * FROM file_locks ORDER BY created_at DESC").all();
16334
+ }
16335
+ function forceUnlockFile(path, db) {
16336
+ const d = db || getDatabase();
16337
+ const result = d.run("DELETE FROM file_locks WHERE path = ?", [path]);
16338
+ return result.changes > 0;
16339
+ }
16340
+ var FILE_LOCK_DEFAULT_TTL_SECONDS;
16341
+ var init_file_locks = __esm(() => {
16342
+ init_database();
16343
+ init_types2();
16344
+ FILE_LOCK_DEFAULT_TTL_SECONDS = 30 * 60;
16345
+ });
16346
+
16347
+ // src/db/kg.ts
16348
+ var exports_kg = {};
16349
+ __export(exports_kg, {
16350
+ syncKgEdges: () => syncKgEdges,
16351
+ removeKgEdges: () => removeKgEdges,
16352
+ getRelated: () => getRelated,
16353
+ getImpactAnalysis: () => getImpactAnalysis,
16354
+ getCriticalPath: () => getCriticalPath,
16355
+ findPath: () => findPath,
16356
+ addKgEdge: () => addKgEdge
16357
+ });
16358
+ function rowToEdge(row) {
16359
+ return {
16360
+ ...row,
16361
+ metadata: JSON.parse(row.metadata || "{}")
16362
+ };
16363
+ }
16364
+ function syncKgEdges(db) {
16365
+ const d = db || getDatabase();
16366
+ let synced = 0;
16367
+ const tx = d.transaction(() => {
16368
+ const deps = d.query("SELECT task_id, depends_on FROM task_dependencies").all();
16369
+ for (const dep of deps) {
16370
+ synced += upsertEdge(d, dep.task_id, "task", dep.depends_on, "task", "depends_on");
16371
+ }
16372
+ const assignments = d.query("SELECT id, assigned_to FROM tasks WHERE assigned_to IS NOT NULL").all();
16373
+ for (const a of assignments) {
16374
+ synced += upsertEdge(d, a.id, "task", a.assigned_to, "agent", "assigned_to");
16375
+ }
16376
+ const agents = d.query("SELECT id, reports_to FROM agents WHERE reports_to IS NOT NULL").all();
16377
+ for (const a of agents) {
16378
+ synced += upsertEdge(d, a.id, "agent", a.reports_to, "agent", "reports_to");
16379
+ }
16380
+ const files = d.query("SELECT task_id, path FROM task_files WHERE status != 'removed'").all();
16381
+ for (const f of files) {
16382
+ synced += upsertEdge(d, f.task_id, "task", f.path, "file", "references_file");
16383
+ }
16384
+ const taskProjects = d.query("SELECT id, project_id FROM tasks WHERE project_id IS NOT NULL").all();
16385
+ for (const tp of taskProjects) {
16386
+ synced += upsertEdge(d, tp.id, "task", tp.project_id, "project", "in_project");
16387
+ }
16388
+ const taskPlans = d.query("SELECT id, plan_id FROM tasks WHERE plan_id IS NOT NULL").all();
16389
+ for (const tp of taskPlans) {
16390
+ synced += upsertEdge(d, tp.id, "task", tp.plan_id, "plan", "in_plan");
16391
+ }
16392
+ try {
16393
+ const rels = d.query("SELECT source_task_id, target_task_id, relationship_type FROM task_relationships").all();
16394
+ for (const r of rels) {
16395
+ synced += upsertEdge(d, r.source_task_id, "task", r.target_task_id, "task", r.relationship_type);
16396
+ }
16397
+ } catch {}
16398
+ });
16399
+ tx();
16400
+ return { synced };
16401
+ }
16402
+ function upsertEdge(d, sourceId, sourceType, targetId, targetType, relationType, weight = 1) {
16403
+ try {
16404
+ d.run(`INSERT OR IGNORE INTO kg_edges (id, source_id, source_type, target_id, target_type, relation_type, weight, metadata, created_at)
16405
+ VALUES (?, ?, ?, ?, ?, ?, ?, '{}', ?)`, [uuid(), sourceId, sourceType, targetId, targetType, relationType, weight, now()]);
16406
+ return 1;
16407
+ } catch {
16408
+ return 0;
16409
+ }
16410
+ }
16411
+ function getRelated(entityId, opts, db) {
16412
+ const d = db || getDatabase();
16413
+ const direction = opts?.direction || "both";
16414
+ const conditions = [];
16415
+ const params = [];
16416
+ if (direction === "outgoing" || direction === "both") {
16417
+ conditions.push("source_id = ?");
16418
+ params.push(entityId);
16419
+ }
16420
+ if (direction === "incoming" || direction === "both") {
16421
+ conditions.push("target_id = ?");
16422
+ params.push(entityId);
16423
+ }
16424
+ let sql = `SELECT * FROM kg_edges WHERE (${conditions.join(" OR ")})`;
16425
+ if (opts?.relation_type) {
16426
+ sql += " AND relation_type = ?";
16427
+ params.push(opts.relation_type);
16428
+ }
16429
+ if (opts?.entity_type) {
16430
+ sql += " AND (source_type = ? OR target_type = ?)";
16431
+ params.push(opts.entity_type, opts.entity_type);
16432
+ }
16433
+ sql += " ORDER BY weight DESC, created_at DESC";
16434
+ if (opts?.limit) {
16435
+ sql += " LIMIT ?";
16436
+ params.push(opts.limit);
16437
+ }
16438
+ return d.query(sql).all(...params).map(rowToEdge);
16439
+ }
16440
+ function findPath(sourceId, targetId, opts, db) {
16441
+ const d = db || getDatabase();
16442
+ const maxDepth = opts?.max_depth || 5;
16443
+ const visited = new Set;
16444
+ const queue = [{ id: sourceId, path: [] }];
16445
+ const results = [];
16446
+ visited.add(sourceId);
16447
+ while (queue.length > 0) {
16448
+ const current = queue.shift();
16449
+ if (current.path.length >= maxDepth)
16450
+ continue;
16451
+ let sql = "SELECT * FROM kg_edges WHERE source_id = ?";
16452
+ const params = [current.id];
16453
+ if (opts?.relation_types && opts.relation_types.length > 0) {
16454
+ const placeholders = opts.relation_types.map(() => "?").join(",");
16455
+ sql += ` AND relation_type IN (${placeholders})`;
16456
+ params.push(...opts.relation_types);
16457
+ }
16458
+ const edges = d.query(sql).all(...params).map(rowToEdge);
16459
+ for (const edge of edges) {
16460
+ const nextId = edge.target_id;
16461
+ const newPath = [...current.path, edge];
16462
+ if (nextId === targetId) {
16463
+ results.push(newPath);
16464
+ if (results.length >= 3)
16465
+ return results;
16466
+ continue;
16467
+ }
16468
+ if (!visited.has(nextId)) {
16469
+ visited.add(nextId);
16470
+ queue.push({ id: nextId, path: newPath });
16471
+ }
16472
+ }
16473
+ }
16474
+ return results;
16475
+ }
16476
+ function getImpactAnalysis(entityId, opts, db) {
16477
+ const d = db || getDatabase();
16478
+ const maxDepth = opts?.max_depth || 3;
16479
+ const results = [];
16480
+ const visited = new Set;
16481
+ visited.add(entityId);
16482
+ const queue = [{ id: entityId, depth: 0 }];
16483
+ while (queue.length > 0) {
16484
+ const current = queue.shift();
16485
+ if (current.depth >= maxDepth)
16486
+ continue;
16487
+ let sql = "SELECT * FROM kg_edges WHERE source_id = ?";
16488
+ const params = [current.id];
16489
+ if (opts?.relation_types && opts.relation_types.length > 0) {
16490
+ const placeholders = opts.relation_types.map(() => "?").join(",");
16491
+ sql += ` AND relation_type IN (${placeholders})`;
16492
+ params.push(...opts.relation_types);
16493
+ }
16494
+ const edges = d.query(sql).all(...params).map(rowToEdge);
16495
+ for (const edge of edges) {
16496
+ if (!visited.has(edge.target_id)) {
16497
+ visited.add(edge.target_id);
16498
+ results.push({
16499
+ entity_id: edge.target_id,
16500
+ entity_type: edge.target_type,
16501
+ depth: current.depth + 1,
16502
+ relation: edge.relation_type
16503
+ });
16504
+ queue.push({ id: edge.target_id, depth: current.depth + 1 });
16505
+ }
16506
+ }
16507
+ }
16508
+ return results;
16509
+ }
16510
+ function getCriticalPath(opts, db) {
16511
+ const d = db || getDatabase();
16512
+ let sql = `SELECT source_id, target_id FROM kg_edges WHERE relation_type = 'depends_on'`;
16513
+ const params = [];
16514
+ if (opts?.project_id) {
16515
+ sql += ` AND source_id IN (SELECT id FROM tasks WHERE project_id = ?)`;
16516
+ params.push(opts.project_id);
16517
+ }
16518
+ const edges = d.query(sql).all(...params);
16519
+ const blocks = new Map;
16520
+ for (const e of edges) {
16521
+ if (!blocks.has(e.target_id))
16522
+ blocks.set(e.target_id, new Set);
16523
+ blocks.get(e.target_id).add(e.source_id);
16524
+ }
16525
+ const results = [];
16526
+ for (const [taskId] of blocks) {
16527
+ const visited = new Set;
16528
+ const q = [taskId];
16529
+ let maxDepth = 0;
16530
+ let depth = 0;
16531
+ let levelSize = q.length;
16532
+ while (q.length > 0) {
16533
+ const node = q.shift();
16534
+ levelSize--;
16535
+ const downstream = blocks.get(node);
16536
+ if (downstream) {
16537
+ for (const d2 of downstream) {
16538
+ if (!visited.has(d2)) {
16539
+ visited.add(d2);
16540
+ q.push(d2);
16541
+ }
16542
+ }
16543
+ }
16544
+ if (levelSize === 0) {
16545
+ depth++;
16546
+ maxDepth = Math.max(maxDepth, depth);
16547
+ levelSize = q.length;
16548
+ }
16549
+ }
16550
+ if (visited.size > 0) {
16551
+ results.push({ task_id: taskId, blocking_count: visited.size, depth: maxDepth });
16552
+ }
16553
+ }
16554
+ results.sort((a, b) => b.blocking_count - a.blocking_count);
16555
+ return results.slice(0, opts?.limit || 20);
16556
+ }
16557
+ function addKgEdge(sourceId, sourceType, targetId, targetType, relationType, weight = 1, metadata, db) {
16558
+ const d = db || getDatabase();
16559
+ const id = uuid();
16560
+ const timestamp = now();
16561
+ d.run(`INSERT OR IGNORE INTO kg_edges (id, source_id, source_type, target_id, target_type, relation_type, weight, metadata, created_at)
16562
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`, [id, sourceId, sourceType, targetId, targetType, relationType, weight, JSON.stringify(metadata || {}), timestamp]);
16563
+ return { id, source_id: sourceId, source_type: sourceType, target_id: targetId, target_type: targetType, relation_type: relationType, weight, metadata: metadata || {}, created_at: timestamp };
16564
+ }
16565
+ function removeKgEdges(sourceId, targetId, relationType, db) {
16566
+ const d = db || getDatabase();
16567
+ if (relationType) {
16568
+ return d.run("DELETE FROM kg_edges WHERE source_id = ? AND target_id = ? AND relation_type = ?", [sourceId, targetId, relationType]).changes;
16569
+ }
16570
+ return d.run("DELETE FROM kg_edges WHERE source_id = ? AND target_id = ?", [sourceId, targetId]).changes;
16571
+ }
16572
+ var init_kg = __esm(() => {
16573
+ init_database();
16574
+ });
16575
+
16576
+ // src/db/project-agent-roles.ts
16577
+ var exports_project_agent_roles = {};
16578
+ __export(exports_project_agent_roles, {
16579
+ setProjectAgentRole: () => setProjectAgentRole,
16580
+ removeProjectAgentRole: () => removeProjectAgentRole,
16581
+ listProjectAgentRoles: () => listProjectAgentRoles,
16582
+ getProjectOrgChart: () => getProjectOrgChart,
16583
+ getAgentProjectRoles: () => getAgentProjectRoles
16584
+ });
16585
+ function rowToRole(row) {
16586
+ return { ...row, is_lead: row.is_lead === 1 };
16587
+ }
16588
+ function setProjectAgentRole(projectId, agentId, role, isLead = false, db) {
16589
+ const d = db || getDatabase();
16590
+ const existing = d.query("SELECT * FROM project_agent_roles WHERE project_id = ? AND agent_id = ? AND role = ?").get(projectId, agentId, role);
16591
+ if (existing) {
16592
+ d.run("UPDATE project_agent_roles SET is_lead = ? WHERE id = ?", [isLead ? 1 : 0, existing.id]);
16593
+ return rowToRole(d.query("SELECT * FROM project_agent_roles WHERE id = ?").get(existing.id));
16594
+ }
16595
+ const id = uuid();
16596
+ 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()]);
16597
+ return rowToRole(d.query("SELECT * FROM project_agent_roles WHERE id = ?").get(id));
16598
+ }
16599
+ function removeProjectAgentRole(projectId, agentId, role, db) {
16600
+ const d = db || getDatabase();
16601
+ if (role) {
16602
+ return d.run("DELETE FROM project_agent_roles WHERE project_id = ? AND agent_id = ? AND role = ?", [projectId, agentId, role]).changes;
16603
+ }
16604
+ return d.run("DELETE FROM project_agent_roles WHERE project_id = ? AND agent_id = ?", [projectId, agentId]).changes;
16605
+ }
16606
+ function listProjectAgentRoles(projectId, db) {
16607
+ const d = db || getDatabase();
16608
+ return d.query("SELECT * FROM project_agent_roles WHERE project_id = ? ORDER BY role, created_at").all(projectId).map(rowToRole);
16609
+ }
16610
+ function getAgentProjectRoles(agentId, db) {
16611
+ const d = db || getDatabase();
16612
+ return d.query("SELECT * FROM project_agent_roles WHERE agent_id = ? ORDER BY project_id, role").all(agentId).map(rowToRole);
16613
+ }
16614
+ function getProjectOrgChart(projectId, opts, db) {
16615
+ const d = db || getDatabase();
16616
+ const globalTree = getOrgChart(d);
16617
+ const projectRoles = listProjectAgentRoles(projectId, d);
16618
+ const rolesByAgent = new Map;
16619
+ for (const pr of projectRoles) {
16620
+ if (!rolesByAgent.has(pr.agent_id))
16621
+ rolesByAgent.set(pr.agent_id, { roles: [], isLead: false });
16622
+ const entry = rolesByAgent.get(pr.agent_id);
16623
+ entry.roles.push(pr.role);
16624
+ if (pr.is_lead)
16625
+ entry.isLead = true;
16626
+ }
16627
+ function augmentTree(nodes) {
16628
+ return nodes.map((n) => {
16629
+ const override = rolesByAgent.get(n.agent.id);
16630
+ return {
16631
+ ...n,
16632
+ reports: augmentTree(n.reports),
16633
+ project_roles: override?.roles ?? [],
16634
+ is_project_lead: override?.isLead ?? false
16635
+ };
16636
+ }).filter((n) => {
16637
+ if (!opts?.filter_to_project)
16638
+ return true;
16639
+ const hasRole = n.project_roles.length > 0;
16640
+ const hasDescendant = n.reports.length > 0;
16641
+ return hasRole || hasDescendant;
16642
+ });
16643
+ }
16644
+ return augmentTree(globalTree);
16645
+ }
16646
+ var init_project_agent_roles = __esm(() => {
16647
+ init_database();
16648
+ init_agents();
16649
+ });
16650
+
16651
+ // src/db/patrol.ts
16652
+ var exports_patrol = {};
16653
+ __export(exports_patrol, {
16654
+ patrolTasks: () => patrolTasks,
16655
+ getReviewQueue: () => getReviewQueue
16656
+ });
16657
+ function rowToTask3(row) {
16658
+ return {
16659
+ ...row,
16660
+ status: row.status,
16661
+ priority: row.priority,
16662
+ tags: JSON.parse(row.tags || "[]"),
16663
+ metadata: JSON.parse(row.metadata || "{}"),
16664
+ requires_approval: Boolean(row.requires_approval)
16665
+ };
16666
+ }
16667
+ function patrolTasks(opts, db) {
16668
+ const d = db || getDatabase();
16669
+ const stuckMinutes = opts?.stuck_minutes || 60;
16670
+ const confidenceThreshold = opts?.confidence_threshold || 0.5;
16671
+ const issues = [];
16672
+ let projectFilter = "";
16673
+ const projectParams = [];
16674
+ if (opts?.project_id) {
16675
+ projectFilter = " AND project_id = ?";
16676
+ projectParams.push(opts.project_id);
16677
+ }
16678
+ const stuckCutoff = new Date(Date.now() - stuckMinutes * 60 * 1000).toISOString();
16679
+ const stuckRows = d.query(`SELECT * FROM tasks WHERE status = 'in_progress' AND updated_at < ?${projectFilter} ORDER BY updated_at ASC`).all(stuckCutoff, ...projectParams);
16680
+ for (const row of stuckRows) {
16681
+ const task = rowToTask3(row);
16682
+ const minutesStuck = Math.round((Date.now() - new Date(task.updated_at).getTime()) / 60000);
16683
+ issues.push({
16684
+ type: "stuck",
16685
+ task_id: task.id,
16686
+ task_title: task.title,
16687
+ severity: minutesStuck > 480 ? "critical" : minutesStuck > 120 ? "high" : "medium",
16688
+ detail: `In progress for ${minutesStuck} minutes without update`
16689
+ });
16690
+ }
16691
+ 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);
16692
+ for (const row of lowConfRows) {
16693
+ const task = rowToTask3(row);
16694
+ issues.push({
16695
+ type: "low_confidence",
16696
+ task_id: task.id,
16697
+ task_title: task.title,
16698
+ severity: (task.confidence ?? 0) < 0.3 ? "high" : "medium",
16699
+ detail: `Completed with confidence ${task.confidence} (threshold: ${confidenceThreshold})`
16700
+ });
16701
+ }
16702
+ 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();
16703
+ for (const row of orphanedRows) {
16704
+ const task = rowToTask3(row);
16705
+ issues.push({
16706
+ type: "orphaned",
16707
+ task_id: task.id,
16708
+ task_title: task.title,
16709
+ severity: "low",
16710
+ detail: "Pending task with no project, no agent, and no assignee"
16711
+ });
16712
+ }
16713
+ 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);
16714
+ for (const row of needsReviewRows) {
16715
+ const task = rowToTask3(row);
16716
+ issues.push({
16717
+ type: "needs_review",
16718
+ task_id: task.id,
16719
+ task_title: task.title,
16720
+ severity: "medium",
16721
+ detail: "Completed but requires approval \u2014 not yet reviewed"
16722
+ });
16723
+ }
16724
+ const pendingWithDeps = d.query(`SELECT t.* FROM tasks t
16725
+ WHERE t.status = 'pending'${projectFilter}
16726
+ AND t.id IN (SELECT task_id FROM task_dependencies)`).all(...projectParams);
16727
+ for (const row of pendingWithDeps) {
16728
+ const deps = d.query(`SELECT d.depends_on, t.status FROM task_dependencies d
16729
+ JOIN tasks t ON t.id = d.depends_on
16730
+ WHERE d.task_id = ?`).all(row.id);
16731
+ if (deps.length > 0 && deps.every((dep) => dep.status === "failed" || dep.status === "cancelled")) {
16732
+ const task = rowToTask3(row);
16733
+ issues.push({
16734
+ type: "zombie_blocked",
16735
+ task_id: task.id,
16736
+ task_title: task.title,
16737
+ severity: "high",
16738
+ detail: `Blocked by ${deps.length} task(s) that are all failed/cancelled \u2014 will never unblock`
16739
+ });
16740
+ }
16741
+ }
16742
+ const severityOrder = { critical: 0, high: 1, medium: 2, low: 3 };
16743
+ issues.sort((a, b) => severityOrder[a.severity] - severityOrder[b.severity]);
16744
+ return {
16745
+ issues,
16746
+ total_issues: issues.length,
16747
+ scanned_at: new Date().toISOString()
16748
+ };
16749
+ }
16750
+ function getReviewQueue(opts, db) {
16751
+ const d = db || getDatabase();
16752
+ let sql = `SELECT * FROM tasks WHERE status = 'completed' AND (
16753
+ (requires_approval = 1 AND approved_by IS NULL)
16754
+ OR (confidence IS NOT NULL AND confidence < 0.5)
16755
+ )`;
16756
+ const params = [];
16757
+ if (opts?.project_id) {
16758
+ sql += " AND project_id = ?";
16759
+ params.push(opts.project_id);
16760
+ }
16761
+ sql += " ORDER BY CASE WHEN requires_approval = 1 AND approved_by IS NULL THEN 0 ELSE 1 END, confidence ASC";
16762
+ if (opts?.limit) {
16763
+ sql += " LIMIT ?";
16764
+ params.push(opts.limit);
16765
+ }
16766
+ return d.query(sql).all(...params).map(rowToTask3);
16767
+ }
16768
+ var init_patrol = __esm(() => {
16769
+ init_database();
16770
+ });
16771
+
16772
+ // src/db/agent-metrics.ts
16773
+ var exports_agent_metrics = {};
16774
+ __export(exports_agent_metrics, {
16775
+ scoreTask: () => scoreTask,
16776
+ getLeaderboard: () => getLeaderboard,
16777
+ getAgentMetrics: () => getAgentMetrics
16778
+ });
16779
+ function getAgentMetrics(agentId, opts, db) {
16780
+ const d = db || getDatabase();
16781
+ const agent = d.query("SELECT id, name FROM agents WHERE id = ? OR LOWER(name) = LOWER(?)").get(agentId, agentId);
16782
+ if (!agent)
16783
+ return null;
16784
+ let projectFilter = "";
16785
+ const params = [agent.id, agent.id];
16786
+ if (opts?.project_id) {
16787
+ projectFilter = " AND project_id = ?";
16788
+ params.push(opts.project_id);
16789
+ }
16790
+ const completed = d.query(`SELECT COUNT(*) as count FROM tasks WHERE (agent_id = ? OR assigned_to = ?) AND status = 'completed'${projectFilter}`).get(...params).count;
16791
+ const failed = d.query(`SELECT COUNT(*) as count FROM tasks WHERE (agent_id = ? OR assigned_to = ?) AND status = 'failed'${projectFilter}`).get(...params).count;
16792
+ const inProgress = d.query(`SELECT COUNT(*) as count FROM tasks WHERE (agent_id = ? OR assigned_to = ?) AND status = 'in_progress'${projectFilter}`).get(...params).count;
16793
+ const total = completed + failed;
16794
+ const completionRate = total > 0 ? completed / total : 0;
16795
+ const avgTime = d.query(`SELECT AVG(
16796
+ (julianday(completed_at) - julianday(created_at)) * 24 * 60
16797
+ ) as avg_minutes
16798
+ FROM tasks
16799
+ WHERE (agent_id = ? OR assigned_to = ?) AND status = 'completed' AND completed_at IS NOT NULL${projectFilter}`).get(...params);
16800
+ const avgConf = d.query(`SELECT AVG(confidence) as avg_confidence
16801
+ FROM tasks
16802
+ WHERE (agent_id = ? OR assigned_to = ?) AND status = 'completed' AND confidence IS NOT NULL${projectFilter}`).get(...params);
16803
+ const reviewTasks = d.query(`SELECT metadata FROM tasks
16804
+ WHERE (agent_id = ? OR assigned_to = ?) AND status = 'completed'${projectFilter}
16805
+ AND metadata LIKE '%_review_score%'`).all(...params);
16806
+ let reviewScoreAvg = null;
16807
+ if (reviewTasks.length > 0) {
16808
+ let total2 = 0;
16809
+ let count = 0;
16810
+ for (const row of reviewTasks) {
16811
+ try {
16812
+ const meta = JSON.parse(row.metadata);
16813
+ if (typeof meta._review_score === "number") {
16814
+ total2 += meta._review_score;
16815
+ count++;
16816
+ }
16817
+ } catch {}
16818
+ }
16819
+ if (count > 0)
16820
+ reviewScoreAvg = total2 / count;
16821
+ }
16822
+ const speedScore = avgTime?.avg_minutes != null ? Math.max(0, 1 - avgTime.avg_minutes / (60 * 24)) : 0.5;
16823
+ const confidenceScore = avgConf?.avg_confidence ?? 0.5;
16824
+ const volumeScore = Math.min(1, completed / 50);
16825
+ const compositeScore = completionRate * 0.3 + speedScore * 0.2 + confidenceScore * 0.3 + volumeScore * 0.2;
16826
+ return {
16827
+ agent_id: agent.id,
16828
+ agent_name: agent.name,
16829
+ tasks_completed: completed,
16830
+ tasks_failed: failed,
16831
+ tasks_in_progress: inProgress,
16832
+ completion_rate: Math.round(completionRate * 1000) / 1000,
16833
+ avg_completion_minutes: avgTime?.avg_minutes != null ? Math.round(avgTime.avg_minutes * 10) / 10 : null,
16834
+ avg_confidence: avgConf?.avg_confidence != null ? Math.round(avgConf.avg_confidence * 1000) / 1000 : null,
16835
+ review_score_avg: reviewScoreAvg != null ? Math.round(reviewScoreAvg * 1000) / 1000 : null,
16836
+ composite_score: Math.round(compositeScore * 1000) / 1000
16837
+ };
16838
+ }
16839
+ function getLeaderboard(opts, db) {
16840
+ const d = db || getDatabase();
16841
+ const agents = d.query("SELECT id FROM agents ORDER BY name").all();
16842
+ const entries = [];
16843
+ for (const agent of agents) {
16844
+ const metrics = getAgentMetrics(agent.id, { project_id: opts?.project_id }, d);
16845
+ if (metrics && (metrics.tasks_completed > 0 || metrics.tasks_failed > 0 || metrics.tasks_in_progress > 0)) {
16846
+ entries.push(metrics);
16847
+ }
16848
+ }
16849
+ entries.sort((a, b) => b.composite_score - a.composite_score);
16850
+ const limit = opts?.limit || 20;
16851
+ return entries.slice(0, limit).map((entry, idx) => ({
16852
+ ...entry,
16853
+ rank: idx + 1
16854
+ }));
16855
+ }
16856
+ function scoreTask(taskId, score, reviewerId, db) {
16857
+ const d = db || getDatabase();
16858
+ if (score < 0 || score > 1)
16859
+ throw new Error("Score must be between 0 and 1");
16860
+ const task = d.query("SELECT metadata FROM tasks WHERE id = ?").get(taskId);
16861
+ if (!task)
16862
+ throw new Error(`Task not found: ${taskId}`);
16863
+ const metadata = JSON.parse(task.metadata || "{}");
16864
+ metadata._review_score = score;
16865
+ if (reviewerId)
16866
+ metadata._reviewed_by = reviewerId;
16867
+ metadata._reviewed_at = now();
16868
+ d.run("UPDATE tasks SET metadata = ?, updated_at = ? WHERE id = ?", [JSON.stringify(metadata), now(), taskId]);
16869
+ }
16870
+ var init_agent_metrics = __esm(() => {
16871
+ init_database();
15462
16872
  });
15463
16873
 
15464
16874
  // src/lib/extract.ts
@@ -20707,7 +22117,7 @@ async function logError(message, opts) {
20707
22117
 
20708
22118
  // src/mcp/index.ts
20709
22119
  init_types2();
20710
- import { readFileSync as readFileSync6 } from "fs";
22120
+ import { existsSync as existsSync10, readFileSync as readFileSync6 } from "fs";
20711
22121
  import { join as join10, dirname as dirname3 } from "path";
20712
22122
  import { fileURLToPath } from "url";
20713
22123
 
@@ -20950,14 +22360,24 @@ ${lines.join(`
20950
22360
 
20951
22361
  // src/mcp/tools/task-crud.ts
20952
22362
  init_tasks();
22363
+ init_types2();
20953
22364
  function registerTaskCrudTools(server, ctx) {
20954
22365
  const { shouldRegisterTool, resolveId, formatError, formatTask } = ctx;
22366
+ function versionFor(taskId, version) {
22367
+ const current = getTask(taskId);
22368
+ if (!current)
22369
+ throw new TaskNotFoundError(taskId);
22370
+ if (version !== undefined && current.version !== version) {
22371
+ throw new VersionConflictError(taskId, version, current.version);
22372
+ }
22373
+ return current.version;
22374
+ }
20955
22375
  if (shouldRegisterTool("create_task")) {
20956
22376
  server.tool("create_task", "Create a new task in a project. Pass short_id=null to auto-generate.", {
20957
22377
  title: exports_external.string().describe("Task title"),
20958
22378
  description: exports_external.string().optional().describe("Task description (markdown)"),
20959
- status: exports_external.enum(["pending", "in_progress", "completed", "cancelled"]).optional().describe("Initial status (default: pending)"),
20960
- priority: exports_external.enum(["low", "medium", "high", "urgent"]).optional().describe("Priority (default: medium)"),
22379
+ status: exports_external.enum(["pending", "in_progress", "completed", "failed", "cancelled"]).optional().describe("Initial status (default: pending)"),
22380
+ priority: exports_external.enum(["low", "medium", "high", "critical"]).optional().describe("Priority (default: medium)"),
20961
22381
  project_id: exports_external.string().optional().describe("Project ID"),
20962
22382
  task_list_id: exports_external.string().optional().describe("Task list ID"),
20963
22383
  assigned_to: exports_external.string().optional().describe("Agent ID or name to assign to"),
@@ -20987,9 +22407,9 @@ function registerTaskCrudTools(server, ctx) {
20987
22407
  if (confidence !== undefined)
20988
22408
  resolved.confidence = confidence;
20989
22409
  if (retry_count !== undefined)
20990
- resolved.retry_count = retry_count;
22410
+ resolved.max_retries = retry_count;
20991
22411
  if (deadline)
20992
- resolved.deadline = deadline;
22412
+ resolved.due_at = deadline;
20993
22413
  const task = createTask(resolved);
20994
22414
  return { content: [{ type: "text", text: formatTask(task) }] };
20995
22415
  } catch (e) {
@@ -20999,8 +22419,8 @@ function registerTaskCrudTools(server, ctx) {
20999
22419
  }
21000
22420
  if (shouldRegisterTool("list_tasks")) {
21001
22421
  server.tool("list_tasks", "List tasks with optional filters. Pass empty arrays for multi-value filters (e.g. status=[] shows all).", {
21002
- status: exports_external.union([exports_external.enum(["pending", "in_progress", "completed", "cancelled"]), exports_external.array(exports_external.enum(["pending", "in_progress", "completed", "cancelled"]))]).optional().describe("Filter by status"),
21003
- priority: exports_external.union([exports_external.enum(["low", "medium", "high", "urgent"]), exports_external.array(exports_external.enum(["low", "medium", "high", "urgent"]))]).optional().describe("Filter by priority"),
22422
+ status: exports_external.union([exports_external.enum(["pending", "in_progress", "completed", "failed", "cancelled"]), exports_external.array(exports_external.enum(["pending", "in_progress", "completed", "failed", "cancelled"]))]).optional().describe("Filter by status"),
22423
+ priority: exports_external.union([exports_external.enum(["low", "medium", "high", "critical"]), exports_external.array(exports_external.enum(["low", "medium", "high", "critical"]))]).optional().describe("Filter by priority"),
21004
22424
  project_id: exports_external.string().optional().describe("Filter by project"),
21005
22425
  task_list_id: exports_external.string().optional().describe("Filter by task list"),
21006
22426
  assigned_to: exports_external.string().optional().describe("Filter by assignee (agent ID or name, empty string = unassigned)"),
@@ -21035,9 +22455,9 @@ function registerTaskCrudTools(server, ctx) {
21035
22455
  }, async ({ task_id }) => {
21036
22456
  try {
21037
22457
  const resolvedId = resolveId(task_id);
21038
- const task = getTask2(resolvedId);
22458
+ const task = getTask(resolvedId);
21039
22459
  if (!task)
21040
- throw new NotFoundError(`Task not found: ${task_id}`);
22460
+ throw new TaskNotFoundError(task_id);
21041
22461
  const focus = ctx.getAgentFocus(task.assigned_to || "");
21042
22462
  const lines = [
21043
22463
  `ID: ${task.id}`,
@@ -21051,7 +22471,7 @@ function registerTaskCrudTools(server, ctx) {
21051
22471
  task.estimated_minutes != null ? `Estimate: ${task.estimated_minutes} min` : null,
21052
22472
  task.actual_minutes != null ? `Actual: ${task.actual_minutes} min` : null,
21053
22473
  task.confidence != null ? `Confidence: ${task.confidence}` : null,
21054
- task.deadline ? `Deadline: ${task.deadline}` : null,
22474
+ task.due_at ? `Due: ${task.due_at}` : null,
21055
22475
  task.completed_at ? `Completed: ${task.completed_at}` : null,
21056
22476
  focus ? `Focus: agent=${focus.agent_id} project=${focus.project_id || "(global)"}` : null,
21057
22477
  task.created_at ? `Created: ${task.created_at}` : null,
@@ -21073,8 +22493,8 @@ ${task.description}` : null
21073
22493
  task_id: exports_external.string().describe("Task ID"),
21074
22494
  title: exports_external.string().optional(),
21075
22495
  description: exports_external.string().optional(),
21076
- status: exports_external.enum(["pending", "in_progress", "completed", "cancelled"]).optional(),
21077
- priority: exports_external.enum(["low", "medium", "high", "urgent"]).optional(),
22496
+ status: exports_external.enum(["pending", "in_progress", "completed", "failed", "cancelled"]).optional(),
22497
+ priority: exports_external.enum(["low", "medium", "high", "critical"]).optional(),
21078
22498
  assigned_to: exports_external.string().nullable().optional().describe("Agent ID or name, null to unassign"),
21079
22499
  project_id: exports_external.string().nullable().optional(),
21080
22500
  task_list_id: exports_external.string().nullable().optional(),
@@ -21103,9 +22523,15 @@ ${task.description}` : null
21103
22523
  resolved.task_list_id = resolveId(resolved.task_list_id, "task_lists");
21104
22524
  if (resolved.depends_on && Array.isArray(resolved.depends_on))
21105
22525
  resolved.depends_on = resolved.depends_on.map(resolveId);
21106
- if (resolved.estimate !== undefined)
22526
+ if (resolved.estimate !== undefined) {
21107
22527
  resolved.estimated_minutes = resolved.estimate;
21108
- const task = updateTask(resolvedId, resolved, version);
22528
+ delete resolved.estimate;
22529
+ }
22530
+ if (resolved.deadline !== undefined) {
22531
+ resolved.due_at = resolved.deadline;
22532
+ delete resolved.deadline;
22533
+ }
22534
+ const task = updateTask(resolvedId, { ...resolved, version: versionFor(resolvedId, version) });
21109
22535
  return { content: [{ type: "text", text: formatTask(task) }] };
21110
22536
  } catch (e) {
21111
22537
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
@@ -21202,48 +22628,23 @@ function deletePlan(id, db) {
21202
22628
  return result.changes > 0;
21203
22629
  }
21204
22630
 
21205
- // src/db/comments.ts
21206
- init_types2();
21207
- init_database();
21208
- init_tasks();
21209
- function addComment(input, db) {
21210
- const d = db || getDatabase();
21211
- if (!getTask2(input.task_id, d)) {
21212
- throw new TaskNotFoundError(input.task_id);
21213
- }
21214
- const id = uuid();
21215
- const timestamp = now();
21216
- d.run(`INSERT INTO task_comments (id, task_id, agent_id, session_id, content, type, progress_pct, created_at)
21217
- VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, [
21218
- id,
21219
- input.task_id,
21220
- input.agent_id || null,
21221
- input.session_id || null,
21222
- input.content,
21223
- input.type || "comment",
21224
- input.progress_pct ?? null,
21225
- timestamp
21226
- ]);
21227
- return getComment(id, d);
21228
- }
21229
- function getComment(id, db) {
21230
- const d = db || getDatabase();
21231
- return d.query("SELECT * FROM task_comments WHERE id = ?").get(id);
21232
- }
21233
- function listComments(taskId, db) {
21234
- const d = db || getDatabase();
21235
- return d.query("SELECT * FROM task_comments WHERE task_id = ? ORDER BY created_at").all(taskId);
21236
- }
21237
- function deleteComment(id, db) {
21238
- const d = db || getDatabase();
21239
- const result = d.run("DELETE FROM task_comments WHERE id = ?", [id]);
21240
- return result.changes > 0;
21241
- }
21242
-
21243
22631
  // src/mcp/tools/task-project-tools.ts
22632
+ init_comments();
21244
22633
  init_types2();
21245
22634
  function registerTaskProjectTools(server, ctx) {
21246
22635
  const { shouldRegisterTool, resolveId, formatError, formatTask } = ctx;
22636
+ function versionFor(taskId, version) {
22637
+ const current = getTask(taskId);
22638
+ if (!current)
22639
+ throw new TaskNotFoundError(taskId);
22640
+ if (version !== undefined && current.version !== version) {
22641
+ throw new VersionConflictError(taskId, version, current.version);
22642
+ }
22643
+ return current.version;
22644
+ }
22645
+ function updateWithOptionalVersion(taskId, updates, version) {
22646
+ return updateTask(taskId, { ...updates, version: versionFor(taskId, version) });
22647
+ }
21247
22648
  if (shouldRegisterTool("start_task")) {
21248
22649
  server.tool("start_task", "Mark a task as in_progress. Uses optimistic locking via version if provided.", {
21249
22650
  task_id: exports_external.string().describe("Task ID"),
@@ -21251,7 +22652,12 @@ function registerTaskProjectTools(server, ctx) {
21251
22652
  }, async ({ task_id, version }) => {
21252
22653
  try {
21253
22654
  const resolvedId = resolveId(task_id);
21254
- const task = updateTask(resolvedId, { status: "in_progress" }, version);
22655
+ if (version !== undefined)
22656
+ versionFor(resolvedId, version);
22657
+ const current = getTask(resolvedId);
22658
+ if (!current)
22659
+ throw new TaskNotFoundError(resolvedId);
22660
+ const task = startTask(resolvedId, current.assigned_to || current.agent_id || "mcp");
21255
22661
  return { content: [{ type: "text", text: formatTask(task) }] };
21256
22662
  } catch (e) {
21257
22663
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
@@ -21267,7 +22673,12 @@ function registerTaskProjectTools(server, ctx) {
21267
22673
  }, async ({ task_id, confidence, completed_at, version }) => {
21268
22674
  try {
21269
22675
  const resolvedId = resolveId(task_id);
21270
- const task = updateTask(resolvedId, { status: "completed", confidence, completed_at }, version);
22676
+ if (version !== undefined)
22677
+ versionFor(resolvedId, version);
22678
+ const current = getTask(resolvedId);
22679
+ if (!current)
22680
+ throw new TaskNotFoundError(resolvedId);
22681
+ const task = completeTask(resolvedId, current.assigned_to || current.agent_id || undefined, undefined, { confidence, completed_at });
21271
22682
  return { content: [{ type: "text", text: formatTask(task) }] };
21272
22683
  } catch (e) {
21273
22684
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
@@ -21281,7 +22692,7 @@ function registerTaskProjectTools(server, ctx) {
21281
22692
  }, async ({ task_id, version }) => {
21282
22693
  try {
21283
22694
  const resolvedId = resolveId(task_id);
21284
- const task = updateTask(resolvedId, { status: "cancelled" }, version);
22695
+ const task = version === undefined ? setTaskStatus(resolvedId, "cancelled") : updateWithOptionalVersion(resolvedId, { status: "cancelled" }, version);
21285
22696
  return { content: [{ type: "text", text: formatTask(task) }] };
21286
22697
  } catch (e) {
21287
22698
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
@@ -21297,7 +22708,7 @@ function registerTaskProjectTools(server, ctx) {
21297
22708
  try {
21298
22709
  const resolvedId = resolveId(task_id);
21299
22710
  const resolvedAssignee = resolveId(new_assignee, "agents");
21300
- const task = updateTask(resolvedId, { assigned_to: resolvedAssignee }, version);
22711
+ const task = updateWithOptionalVersion(resolvedId, { assigned_to: resolvedAssignee }, version);
21301
22712
  return { content: [{ type: "text", text: formatTask(task) }] };
21302
22713
  } catch (e) {
21303
22714
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
@@ -21312,7 +22723,7 @@ function registerTaskProjectTools(server, ctx) {
21312
22723
  }, async ({ task_id, deadline, version }) => {
21313
22724
  try {
21314
22725
  const resolvedId = resolveId(task_id);
21315
- const task = updateTask(resolvedId, { deadline }, version);
22726
+ const task = updateWithOptionalVersion(resolvedId, { due_at: deadline }, version);
21316
22727
  return { content: [{ type: "text", text: formatTask(task) }] };
21317
22728
  } catch (e) {
21318
22729
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
@@ -21322,12 +22733,12 @@ function registerTaskProjectTools(server, ctx) {
21322
22733
  if (shouldRegisterTool("prioritize_task")) {
21323
22734
  server.tool("prioritize_task", "Set a task's priority.", {
21324
22735
  task_id: exports_external.string().describe("Task ID"),
21325
- priority: exports_external.enum(["low", "medium", "high", "urgent"]).describe("New priority"),
22736
+ priority: exports_external.enum(["low", "medium", "high", "critical"]).describe("New priority"),
21326
22737
  version: exports_external.number().optional().describe("Expected version for optimistic locking")
21327
22738
  }, async ({ task_id, priority, version }) => {
21328
22739
  try {
21329
22740
  const resolvedId = resolveId(task_id);
21330
- const task = updateTask(resolvedId, { priority }, version);
22741
+ const task = version === undefined ? setTaskPriority(resolvedId, priority) : updateWithOptionalVersion(resolvedId, { priority }, version);
21331
22742
  return { content: [{ type: "text", text: formatTask(task) }] };
21332
22743
  } catch (e) {
21333
22744
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
@@ -21383,18 +22794,18 @@ function registerTaskProjectTools(server, ctx) {
21383
22794
  if (shouldRegisterTool("bulk_update_tasks")) {
21384
22795
  server.tool("bulk_update_tasks", "Update multiple tasks at once. All tasks must pass the dependency check.", {
21385
22796
  task_ids: exports_external.array(exports_external.string()).describe("Array of task IDs to update"),
21386
- status: exports_external.enum(["pending", "in_progress", "completed", "cancelled"]).optional(),
21387
- priority: exports_external.enum(["low", "medium", "high", "urgent"]).optional(),
22797
+ status: exports_external.enum(["pending", "in_progress", "completed", "failed", "cancelled"]).optional(),
22798
+ priority: exports_external.enum(["low", "medium", "high", "critical"]).optional(),
21388
22799
  assigned_to: exports_external.string().nullable().optional().describe("Agent ID or name, null to unassign")
21389
22800
  }, async ({ task_ids, status, priority, assigned_to }) => {
21390
22801
  try {
21391
- const { bulkUpdateTasks: bulkUpdateTasks2 } = (()=>{throw new Error("Cannot require module "+"../db/tasks.js");})();
22802
+ const { bulkUpdateTasks: bulkUpdateTasks2 } = (init_tasks(), __toCommonJS(exports_tasks));
21392
22803
  const resolved = task_ids.map(resolveId);
21393
22804
  let resolvedAssignee = assigned_to;
21394
22805
  if (resolvedAssignee && typeof resolvedAssignee === "string")
21395
22806
  resolvedAssignee = resolveId(resolvedAssignee, "agents");
21396
22807
  const result = bulkUpdateTasks2(resolved, { status, priority, assigned_to: resolvedAssignee });
21397
- return { content: [{ type: "text", text: `${result.updated} task(s) updated, ${result.skipped} skipped (dependency check).` }] };
22808
+ return { content: [{ type: "text", text: `${result.updated} task(s) updated, ${result.failed.length} failed.` }] };
21398
22809
  } catch (e) {
21399
22810
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
21400
22811
  }
@@ -21405,8 +22816,8 @@ function registerTaskProjectTools(server, ctx) {
21405
22816
  tasks: exports_external.array(exports_external.object({
21406
22817
  title: exports_external.string(),
21407
22818
  description: exports_external.string().optional(),
21408
- status: exports_external.enum(["pending", "in_progress", "completed", "cancelled"]).optional(),
21409
- priority: exports_external.enum(["low", "medium", "high", "urgent"]).optional(),
22819
+ status: exports_external.enum(["pending", "in_progress", "completed", "failed", "cancelled"]).optional(),
22820
+ priority: exports_external.enum(["low", "medium", "high", "critical"]).optional(),
21410
22821
  project_id: exports_external.string().optional(),
21411
22822
  task_list_id: exports_external.string().optional(),
21412
22823
  assigned_to: exports_external.string().optional(),
@@ -21417,7 +22828,7 @@ function registerTaskProjectTools(server, ctx) {
21417
22828
  })).describe("Array of task objects")
21418
22829
  }, async ({ tasks }) => {
21419
22830
  try {
21420
- const { bulkCreateTasks: bulkCreateTasks2 } = (()=>{throw new Error("Cannot require module "+"../db/tasks.js");})();
22831
+ const { bulkCreateTasks: bulkCreateTasks2 } = (init_tasks(), __toCommonJS(exports_tasks));
21421
22832
  const resolved = tasks.map((t) => {
21422
22833
  const r = { ...t };
21423
22834
  if (r.project_id)
@@ -21431,7 +22842,7 @@ function registerTaskProjectTools(server, ctx) {
21431
22842
  return r;
21432
22843
  });
21433
22844
  const result = bulkCreateTasks2(resolved);
21434
- return { content: [{ type: "text", text: `${result.created} task(s) created, ${result.skipped} skipped (duplicate short_id).` }] };
22845
+ return { content: [{ type: "text", text: `${result.created.length} task(s) created.` }] };
21435
22846
  } catch (e) {
21436
22847
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
21437
22848
  }
@@ -21443,9 +22854,9 @@ function registerTaskProjectTools(server, ctx) {
21443
22854
  force: exports_external.boolean().optional().describe("Skip child check for all tasks (dangerous)")
21444
22855
  }, async ({ task_ids, force }) => {
21445
22856
  try {
21446
- const { bulkDeleteTasks } = (()=>{throw new Error("Cannot require module "+"../db/tasks.js");})();
22857
+ const { bulkDeleteTasks: bulkDeleteTasks2 } = (init_tasks(), __toCommonJS(exports_tasks));
21447
22858
  const resolved = task_ids.map(resolveId);
21448
- const result = bulkDeleteTasks(resolved, force);
22859
+ const result = bulkDeleteTasks2(resolved, force);
21449
22860
  return { content: [{ type: "text", text: `${result.deleted} task(s) deleted, ${result.skipped} skipped (has children).` }] };
21450
22861
  } catch (e) {
21451
22862
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
@@ -21494,7 +22905,7 @@ function registerTaskProjectTools(server, ctx) {
21494
22905
  const project = getProject(resolvedId);
21495
22906
  if (!project)
21496
22907
  throw new TaskNotFoundError(`Project not found: ${project_id}`);
21497
- const { listTasks: listTasks3 } = (()=>{throw new Error("Cannot require module "+"../db/tasks.js");})();
22908
+ const { listTasks: listTasks3 } = (init_tasks(), __toCommonJS(exports_tasks));
21498
22909
  const tasks = listTasks3({ project_id: resolvedId, limit: 100 }, undefined);
21499
22910
  const lines = [
21500
22911
  `ID: ${project.id}`,
@@ -21595,7 +23006,7 @@ function registerTaskProjectTools(server, ctx) {
21595
23006
  throw new TaskNotFoundError(`Task list not found: ${task_list_id}`);
21596
23007
  let tasks = [];
21597
23008
  if (include_tasks) {
21598
- const { listTasks: listTasks3 } = (()=>{throw new Error("Cannot require module "+"../db/tasks.js");})();
23009
+ const { listTasks: listTasks3 } = (init_tasks(), __toCommonJS(exports_tasks));
21599
23010
  tasks = listTasks3({ task_list_id: resolvedId, limit: 200 }, undefined);
21600
23011
  }
21601
23012
  const lines = [
@@ -21695,7 +23106,7 @@ Tasks:` : null,
21695
23106
  throw new TaskNotFoundError(`Plan not found: ${plan_id}`);
21696
23107
  let tasks = [];
21697
23108
  if (include_tasks) {
21698
- const { listTasks: listTasks3 } = (()=>{throw new Error("Cannot require module "+"../db/tasks.js");})();
23109
+ const { listTasks: listTasks3 } = (init_tasks(), __toCommonJS(exports_tasks));
21699
23110
  tasks = listTasks3({ plan_id: resolvedId, limit: 200 }, undefined);
21700
23111
  }
21701
23112
  const lines = [
@@ -21784,7 +23195,7 @@ Tasks:` : null,
21784
23195
  const tag = getTag(tag_id);
21785
23196
  if (!tag)
21786
23197
  throw new TaskNotFoundError(`Tag not found: ${tag_id}`);
21787
- const { listTasks: listTasks3 } = (()=>{throw new Error("Cannot require module "+"../db/tasks.js");})();
23198
+ const { listTasks: listTasks3 } = (init_tasks(), __toCommonJS(exports_tasks));
21788
23199
  const tasks = listTasks3({ tags: [tag.name], limit: 100 }, undefined);
21789
23200
  const lines = [
21790
23201
  `Tag: ${tag.name}${tag.color ? ` (${tag.color})` : ""}`,
@@ -21863,7 +23274,7 @@ Tasks:` : null,
21863
23274
  const label = getLabel(label_id);
21864
23275
  if (!label)
21865
23276
  throw new TaskNotFoundError(`Label not found: ${label_id}`);
21866
- const { listTasks: listTasks3 } = (()=>{throw new Error("Cannot require module "+"../db/tasks.js");})();
23277
+ const { listTasks: listTasks3 } = (init_tasks(), __toCommonJS(exports_tasks));
21867
23278
  const tasks = listTasks3({ tags: [label.name], limit: 100 }, undefined);
21868
23279
  const lines = [
21869
23280
  `Label: ${label.name}${label.color ? ` (${label.color})` : ""}`,
@@ -21914,7 +23325,7 @@ Tasks:` : null,
21914
23325
  try {
21915
23326
  const resolvedId = resolveId(task_id);
21916
23327
  const resolvedAuthor = author ? resolveId(author, "agents") : undefined;
21917
- const comment = addComment({ task_id: resolvedId, body, author: resolvedAuthor });
23328
+ const comment = addComment({ task_id: resolvedId, content: body, agent_id: resolvedAuthor });
21918
23329
  return { content: [{ type: "text", text: `Comment added to ${task_id.slice(0, 8)}: ${body.slice(0, 50)}${body.length > 50 ? "..." : ""}` }] };
21919
23330
  } catch (e) {
21920
23331
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
@@ -21930,8 +23341,8 @@ Tasks:` : null,
21930
23341
  const comments = listComments(resolvedId);
21931
23342
  if (comments.length === 0)
21932
23343
  return { content: [{ type: "text", text: "No comments." }] };
21933
- const lines = comments.map((c) => `[${c.author || "unknown"}] ${c.created_at?.slice(0, 16)}:
21934
- ${c.body}`);
23344
+ const lines = comments.map((c) => `[${c.agent_id || "unknown"}] ${c.created_at?.slice(0, 16)}:
23345
+ ${c.content}`);
21935
23346
  return { content: [{ type: "text", text: lines.join(`
21936
23347
 
21937
23348
  `) }] };
@@ -21946,7 +23357,7 @@ Tasks:` : null,
21946
23357
  body: exports_external.string().describe("New comment body")
21947
23358
  }, async ({ comment_id, body }) => {
21948
23359
  try {
21949
- const comment = updateComment(comment_id, { body });
23360
+ const comment = updateComment(comment_id, { content: body });
21950
23361
  return { content: [{ type: "text", text: `Comment ${comment_id.slice(0, 8)} updated.` }] };
21951
23362
  } catch (e) {
21952
23363
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
@@ -21969,17 +23380,17 @@ Tasks:` : null,
21969
23380
  server.tool("search_tasks", "Full-text search across task titles and descriptions.", {
21970
23381
  query: exports_external.string().describe("Search query"),
21971
23382
  project_id: exports_external.string().optional().describe("Filter by project"),
21972
- status: exports_external.enum(["pending", "in_progress", "completed", "cancelled"]).optional(),
23383
+ status: exports_external.enum(["pending", "in_progress", "completed", "failed", "cancelled"]).optional(),
21973
23384
  limit: exports_external.number().optional().describe("Max results (default: 20)")
21974
23385
  }, async ({ query, project_id, status, limit }) => {
21975
23386
  try {
21976
- const { searchTasks } = (()=>{throw new Error("Cannot require module "+"../db/tasks.js");})();
23387
+ const { searchTasks: searchTasks2 } = (init_search(), __toCommonJS(exports_search));
21977
23388
  const resolved = { query, limit };
21978
23389
  if (project_id)
21979
23390
  resolved.project_id = resolveId(project_id, "projects");
21980
23391
  if (status)
21981
23392
  resolved.status = status;
21982
- const results = searchTasks(resolved);
23393
+ const results = searchTasks2(resolved);
21983
23394
  if (results.length === 0)
21984
23395
  return { content: [{ type: "text", text: `No results for: ${query}` }] };
21985
23396
  const lines = results.map((t) => `${t.short_id || t.id.slice(0, 8)} [${t.status}] ${t.title}`);
@@ -21991,34 +23402,22 @@ ${lines.join(`
21991
23402
  }
21992
23403
  });
21993
23404
  }
21994
- if (shouldRegisterTool("sync")) {
21995
- server.tool("sync", "Sync tasks from a GitHub PR or external source into the project.", {
21996
- source: exports_external.enum(["github_pr", "linear", "asana"]).describe("Source type"),
21997
- source_id: exports_external.string().describe("PR number, Linear issue ID, or Asana task ID"),
21998
- project_id: exports_external.string().optional().describe("Project ID to import into"),
21999
- options: exports_external.record(exports_external.unknown()).optional().describe("Source-specific options")
22000
- }, async ({ source, source_id, project_id, options }) => {
22001
- try {
22002
- const { syncFromGithubPR, syncFromLinear, syncFromAsana } = (()=>{throw new Error("Cannot require module "+"../lib/sync.js");})();
22003
- let result;
22004
- if (source === "github_pr") {
22005
- result = await syncFromGithubPR({ prNumber: parseInt(source_id), project_id: project_id ? resolveId(project_id, "projects") : undefined, ...options });
22006
- } else if (source === "linear") {
22007
- result = await syncFromLinear({ issueId: source_id, project_id: project_id ? resolveId(project_id, "projects") : undefined, ...options });
22008
- } else {
22009
- result = await syncFromAsana({ taskId: source_id, project_id: project_id ? resolveId(project_id, "projects") : undefined, ...options });
22010
- }
22011
- return { content: [{ type: "text", text: `Synced from ${source}: ${result.task?.title || source_id}` }] };
22012
- } catch (e) {
22013
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
22014
- }
22015
- });
22016
- }
22017
23405
  }
22018
23406
 
22019
23407
  // src/mcp/tools/task-workflow-tools.ts
23408
+ init_types2();
22020
23409
  function registerTaskWorkflowTools(server, ctx) {
22021
23410
  const { shouldRegisterTool, resolveId, formatError, formatTask } = ctx;
23411
+ function versionFor(taskId, version) {
23412
+ const { getTask: getTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
23413
+ const current = getTask2(taskId);
23414
+ if (!current)
23415
+ throw new TaskNotFoundError(taskId);
23416
+ if (version !== undefined && current.version !== version) {
23417
+ throw new VersionConflictError(taskId, version, current.version);
23418
+ }
23419
+ return current.version;
23420
+ }
22022
23421
  if (shouldRegisterTool("approve_task")) {
22023
23422
  server.tool("approve_task", "Approve a task that requires_approval. Records who approved it.", {
22024
23423
  task_id: exports_external.string().describe("Task ID"),
@@ -22027,13 +23426,14 @@ function registerTaskWorkflowTools(server, ctx) {
22027
23426
  version: exports_external.number().optional().describe("Expected version for optimistic locking")
22028
23427
  }, async ({ task_id, approved_by, notes, version }) => {
22029
23428
  try {
22030
- const { updateTask: updateTask2 } = (()=>{throw new Error("Cannot require module "+"../db/tasks.js");})();
23429
+ const { updateTask: updateTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
22031
23430
  const resolvedId = resolveId(task_id);
22032
23431
  const resolvedApprover = approved_by ? resolveId(approved_by, "agents") : undefined;
22033
23432
  const task = updateTask2(resolvedId, {
22034
23433
  approved_by: resolvedApprover,
22035
- metadata: notes ? { approval_notes: notes } : {}
22036
- }, version);
23434
+ metadata: notes ? { approval_notes: notes } : {},
23435
+ version: versionFor(resolvedId, version)
23436
+ });
22037
23437
  return { content: [{ type: "text", text: `Task ${task_id.slice(0, 8)} approved by ${resolvedApprover || "unknown"}` }] };
22038
23438
  } catch (e) {
22039
23439
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
@@ -22048,15 +23448,11 @@ function registerTaskWorkflowTools(server, ctx) {
22048
23448
  version: exports_external.number().optional().describe("Expected version for optimistic locking")
22049
23449
  }, async ({ task_id, reason, agent_id, version }) => {
22050
23450
  try {
22051
- const { updateTask: updateTask2 } = (()=>{throw new Error("Cannot require module "+"../db/tasks.js");})();
23451
+ const { failTask: failTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
22052
23452
  const resolvedId = resolveId(task_id);
22053
- const task = updateTask2(resolvedId, {
22054
- status: "pending",
22055
- retry_count: (getTask(resolvedId)?.retry_count ?? 0) + 1,
22056
- back_on_board: false,
22057
- metadata: reason ? { failure_reason: reason } : {}
22058
- }, version);
22059
- return { content: [{ type: "text", text: `Task ${task_id.slice(0, 8)} marked failed. Retry count: ${task.retry_count}` }] };
23453
+ versionFor(resolvedId, version);
23454
+ const result = failTask2(resolvedId, agent_id, reason);
23455
+ return { content: [{ type: "text", text: `Task ${task_id.slice(0, 8)} marked failed. Retry count: ${result.task.retry_count}` }] };
22060
23456
  } catch (e) {
22061
23457
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
22062
23458
  }
@@ -22065,12 +23461,12 @@ function registerTaskWorkflowTools(server, ctx) {
22065
23461
  if (shouldRegisterTool("get_my_tasks")) {
22066
23462
  server.tool("get_my_tasks", "Get tasks assigned to the calling agent. Supports focus mode scoping.", {
22067
23463
  agent_id: exports_external.string().optional().describe("Agent ID (defaults to context agent)"),
22068
- status: exports_external.enum(["pending", "in_progress", "completed", "cancelled"]).optional().describe("Filter by status"),
23464
+ status: exports_external.enum(["pending", "in_progress", "completed", "failed", "cancelled"]).optional().describe("Filter by status"),
22069
23465
  project_id: exports_external.string().optional().describe("Filter by project (respects focus mode)"),
22070
23466
  limit: exports_external.number().optional().describe("Max results (default: 50)")
22071
23467
  }, async ({ agent_id, status, project_id, limit }) => {
22072
23468
  try {
22073
- const { listTasks: listTasks3 } = (()=>{throw new Error("Cannot require module "+"../db/tasks.js");})();
23469
+ const { listTasks: listTasks3 } = (init_tasks(), __toCommonJS(exports_tasks));
22074
23470
  const focus = ctx.getAgentFocus(agent_id || "");
22075
23471
  const effectiveAgentId = focus ? focus.agent_id : agent_id || "";
22076
23472
  const effectiveProjectId = focus?.project_id || project_id;
@@ -22090,6 +23486,122 @@ function registerTaskWorkflowTools(server, ctx) {
22090
23486
  }
22091
23487
  });
22092
23488
  }
23489
+ if (shouldRegisterTool("get_next_task")) {
23490
+ server.tool("get_next_task", "Get the best available pending task without claiming it.", {
23491
+ agent_id: exports_external.string().optional().describe("Agent ID or name for assignment affinity"),
23492
+ project_id: exports_external.string().optional().describe("Filter by project"),
23493
+ task_list_id: exports_external.string().optional().describe("Filter by task list"),
23494
+ plan_id: exports_external.string().optional().describe("Filter by plan"),
23495
+ tags: exports_external.array(exports_external.string()).optional().describe("Filter by tags")
23496
+ }, async ({ agent_id, project_id, task_list_id, plan_id, tags }) => {
23497
+ try {
23498
+ const { getNextTask: getNextTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
23499
+ const filters = {};
23500
+ if (project_id)
23501
+ filters.project_id = resolveId(project_id, "projects");
23502
+ if (task_list_id)
23503
+ filters.task_list_id = resolveId(task_list_id, "task_lists");
23504
+ if (plan_id)
23505
+ filters.plan_id = resolveId(plan_id, "plans");
23506
+ if (tags)
23507
+ filters.tags = tags;
23508
+ const task = getNextTask2(agent_id, filters);
23509
+ return { content: [{ type: "text", text: task ? formatTask(task) : "No available task." }] };
23510
+ } catch (e) {
23511
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
23512
+ }
23513
+ });
23514
+ }
23515
+ if (shouldRegisterTool("claim_next_task")) {
23516
+ server.tool("claim_next_task", "Atomically claim and start the best available pending task for an agent.", {
23517
+ agent_id: exports_external.string().describe("Agent ID or name claiming the task"),
23518
+ project_id: exports_external.string().optional().describe("Filter by project"),
23519
+ task_list_id: exports_external.string().optional().describe("Filter by task list"),
23520
+ plan_id: exports_external.string().optional().describe("Filter by plan"),
23521
+ tags: exports_external.array(exports_external.string()).optional().describe("Filter by tags")
23522
+ }, async ({ agent_id, project_id, task_list_id, plan_id, tags }) => {
23523
+ try {
23524
+ const { claimNextTask: claimNextTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
23525
+ const filters = {};
23526
+ if (project_id)
23527
+ filters.project_id = resolveId(project_id, "projects");
23528
+ if (task_list_id)
23529
+ filters.task_list_id = resolveId(task_list_id, "task_lists");
23530
+ if (plan_id)
23531
+ filters.plan_id = resolveId(plan_id, "plans");
23532
+ if (tags)
23533
+ filters.tags = tags;
23534
+ const task = claimNextTask2(agent_id, filters);
23535
+ return { content: [{ type: "text", text: task ? formatTask(task) : "No available task to claim." }] };
23536
+ } catch (e) {
23537
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
23538
+ }
23539
+ });
23540
+ }
23541
+ if (shouldRegisterTool("get_tasks_changed_since")) {
23542
+ server.tool("get_tasks_changed_since", "List tasks changed since an ISO timestamp.", {
23543
+ since: exports_external.string().describe("ISO timestamp"),
23544
+ project_id: exports_external.string().optional().describe("Filter by project"),
23545
+ task_list_id: exports_external.string().optional().describe("Filter by task list"),
23546
+ limit: exports_external.number().optional().describe("Maximum tasks to return")
23547
+ }, async ({ since, project_id, task_list_id, limit }) => {
23548
+ try {
23549
+ const { getTasksChangedSince: getTasksChangedSince2 } = (init_tasks(), __toCommonJS(exports_tasks));
23550
+ const filters = {};
23551
+ if (project_id)
23552
+ filters.project_id = resolveId(project_id, "projects");
23553
+ if (task_list_id)
23554
+ filters.task_list_id = resolveId(task_list_id, "task_lists");
23555
+ const tasks = getTasksChangedSince2(since, filters).slice(0, limit || 50);
23556
+ if (tasks.length === 0)
23557
+ return { content: [{ type: "text", text: "No changed tasks." }] };
23558
+ return { content: [{ type: "text", text: tasks.map(formatTask).join(`
23559
+ `) }] };
23560
+ } catch (e) {
23561
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
23562
+ }
23563
+ });
23564
+ }
23565
+ function registerContextTool(name, description) {
23566
+ if (!shouldRegisterTool(name))
23567
+ return;
23568
+ server.tool(name, description, {
23569
+ agent_id: exports_external.string().optional().describe("Agent ID or name"),
23570
+ project_id: exports_external.string().optional().describe("Filter by project"),
23571
+ task_list_id: exports_external.string().optional().describe("Filter by task list"),
23572
+ explain_blocked: exports_external.boolean().optional().describe("Include blocked task details")
23573
+ }, async ({ agent_id, project_id, task_list_id, explain_blocked }) => {
23574
+ try {
23575
+ const { getStatus: getStatus2, getNextTask: getNextTask2, getOverdueTasks: getOverdueTasks2 } = (init_tasks(), __toCommonJS(exports_tasks));
23576
+ const { getLatestHandoff: getLatestHandoff2 } = (init_handoffs(), __toCommonJS(exports_handoffs));
23577
+ const filters = {};
23578
+ if (project_id)
23579
+ filters.project_id = resolveId(project_id, "projects");
23580
+ if (task_list_id)
23581
+ filters.task_list_id = resolveId(task_list_id, "task_lists");
23582
+ const status = getStatus2(filters, agent_id, { explain_blocked });
23583
+ const next_task = getNextTask2(agent_id, filters);
23584
+ const overdue = getOverdueTasks2(filters.project_id);
23585
+ const latest_handoff = getLatestHandoff2(agent_id, filters.project_id);
23586
+ return {
23587
+ content: [{
23588
+ type: "text",
23589
+ text: JSON.stringify({
23590
+ status,
23591
+ next_task,
23592
+ overdue_count: overdue.length,
23593
+ latest_handoff,
23594
+ as_of: new Date().toISOString()
23595
+ }, null, 2)
23596
+ }]
23597
+ };
23598
+ } catch (e) {
23599
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
23600
+ }
23601
+ });
23602
+ }
23603
+ registerContextTool("get_context", "Get session start context: queue status, next task, overdue count, and latest handoff.");
23604
+ registerContextTool("bootstrap", "Bootstrap an agent session with queue context and the next available task.");
22093
23605
  if (shouldRegisterTool("get_org_chart")) {
22094
23606
  server.tool("get_org_chart", "Get the global org chart (agent hierarchy + titles).", {
22095
23607
  format: exports_external.enum(["text", "json"]).optional().describe("Output format (default: text)")
@@ -22110,7 +23622,7 @@ function registerTaskWorkflowTools(server, ctx) {
22110
23622
  }).join(`
22111
23623
  `);
22112
23624
  };
22113
- const { getOrgChart: getOrgChart2 } = (()=>{throw new Error("Cannot require module "+"../db/agents.js");})();
23625
+ const { getOrgChart: getOrgChart2 } = (init_agents(), __toCommonJS(exports_agents));
22114
23626
  const tree = getOrgChart2();
22115
23627
  if (format === "json") {
22116
23628
  return { content: [{ type: "text", text: JSON.stringify(tree, null, 2) }] };
@@ -22130,10 +23642,10 @@ function registerTaskWorkflowTools(server, ctx) {
22130
23642
  reports_to: exports_external.string().describe("Manager agent ID or name")
22131
23643
  }, async ({ agent_id, reports_to }) => {
22132
23644
  try {
22133
- const { setReportsTo } = (()=>{throw new Error("Cannot require module "+"../db/agents.js");})();
23645
+ const { updateAgent: updateAgent2 } = (init_agents(), __toCommonJS(exports_agents));
22134
23646
  const resolvedAgent = resolveId(agent_id, "agents");
22135
23647
  const resolvedManager = resolveId(reports_to, "agents");
22136
- setReportsTo(resolvedAgent, resolvedManager);
23648
+ updateAgent2(resolvedAgent, { reports_to: resolvedManager });
22137
23649
  return { content: [{ type: "text", text: `${agent_id} now reports to ${reports_to}` }] };
22138
23650
  } catch (e) {
22139
23651
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
@@ -22151,9 +23663,9 @@ function registerTaskAutoTools(server, ctx) {
22151
23663
  project_id: exports_external.string().optional().describe("Scope to a project")
22152
23664
  }, async ({ days = 7, project_id }) => {
22153
23665
  try {
22154
- const { archiveCompletedTasks } = (init_tasks(), __toCommonJS(exports_tasks));
23666
+ const { archiveCompletedTasks: archiveCompletedTasks2 } = (init_tasks(), __toCommonJS(exports_tasks));
22155
23667
  const resolvedProjectId = project_id ? resolveId(project_id, "projects") : undefined;
22156
- const count = archiveCompletedTasks(days, resolvedProjectId);
23668
+ const count = archiveCompletedTasks2(days, resolvedProjectId);
22157
23669
  return { content: [{ type: "text", text: `Archived ${count} completed task(s) older than ${days} days.` }] };
22158
23670
  } catch (e) {
22159
23671
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
@@ -22179,9 +23691,9 @@ function registerTaskAutoTools(server, ctx) {
22179
23691
  limit: exports_external.number().optional().describe("Max results (default: 50)")
22180
23692
  }, async ({ project_id, limit }) => {
22181
23693
  try {
22182
- const { getArchivedTasks } = (init_tasks(), __toCommonJS(exports_tasks));
23694
+ const { getArchivedTasks: getArchivedTasks2 } = (init_tasks(), __toCommonJS(exports_tasks));
22183
23695
  const resolvedProjectId = project_id ? resolveId(project_id, "projects") : undefined;
22184
- const tasks = getArchivedTasks({ project_id: resolvedProjectId, limit: limit || 50 });
23696
+ const tasks = getArchivedTasks2({ project_id: resolvedProjectId, limit: limit || 50 });
22185
23697
  if (tasks.length === 0)
22186
23698
  return { content: [{ type: "text", text: "No archived tasks." }] };
22187
23699
  const lines = tasks.map((t) => `${t.short_id || t.id.slice(0, 8)} ${t.title} archived ${t.archived_at}`);
@@ -22197,14 +23709,13 @@ function registerTaskAutoTools(server, ctx) {
22197
23709
  task_id: exports_external.string().describe("Task ID")
22198
23710
  }, async ({ task_id }) => {
22199
23711
  try {
22200
- const { autoAssign } = (init_agents(), __toCommonJS(exports_agents));
22201
23712
  const resolvedId = resolveId(task_id);
22202
- const assignment = autoAssign(resolvedId);
22203
- if (!assignment)
23713
+ const { autoAssignTask: autoAssignTask2 } = (init_auto_assign(), __toCommonJS(exports_auto_assign));
23714
+ const assignment = await autoAssignTask2(resolvedId);
23715
+ if (!assignment.assigned_to)
22204
23716
  return { content: [{ type: "text", text: "No suitable agent found for this task." }] };
22205
- const { updateTask: updateTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
22206
- updateTask2(resolvedId, { assigned_to: assignment.agent_id });
22207
- return { content: [{ type: "text", text: `Task ${task_id.slice(0, 8)} assigned to ${assignment.agent_name} (score: ${assignment.score.toFixed(2)})` }] };
23717
+ const reason = assignment.reason ? ` \u2014 ${assignment.reason}` : "";
23718
+ return { content: [{ type: "text", text: `Task ${task_id.slice(0, 8)} assigned to ${assignment.agent_name || assignment.assigned_to} via ${assignment.method}${reason}` }] };
22208
23719
  } catch (e) {
22209
23720
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
22210
23721
  }
@@ -22215,10 +23726,23 @@ function registerTaskAutoTools(server, ctx) {
22215
23726
  agent_id: exports_external.string().optional().describe("Agent ID (defaults to context agent)")
22216
23727
  }, async ({ agent_id }) => {
22217
23728
  try {
22218
- const { getAgentWorkload } = (init_agents(), __toCommonJS(exports_agents));
23729
+ const { listTasks: listTasks3, getBlockedTasks: getBlockedTasks2 } = (init_tasks(), __toCommonJS(exports_tasks));
22219
23730
  const focus = ctx.getAgentFocus(agent_id || "");
22220
23731
  const effectiveAgentId = focus ? focus.agent_id : agent_id || "";
22221
- const workload = getAgentWorkload(effectiveAgentId);
23732
+ if (!effectiveAgentId) {
23733
+ return { content: [{ type: "text", text: "No agent_id provided and no agent focus is active." }], isError: true };
23734
+ }
23735
+ const assigned = listTasks3({ assigned_to: effectiveAgentId, limit: 500 }, undefined);
23736
+ const now2 = Date.now();
23737
+ const dueSoonCutoff = now2 + 24 * 60 * 60 * 1000;
23738
+ const blocked = getBlockedTasks2().filter((t) => t.assigned_to === effectiveAgentId);
23739
+ const workload = {
23740
+ in_progress: assigned.filter((t) => t.status === "in_progress").length,
23741
+ pending: assigned.filter((t) => t.status === "pending").length,
23742
+ completed_recent: assigned.filter((t) => t.status === "completed" && t.completed_at && now2 - new Date(t.completed_at).getTime() <= 7 * 24 * 60 * 60 * 1000).length,
23743
+ 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,
23744
+ blocked: blocked.length
23745
+ };
22222
23746
  const lines = [
22223
23747
  `Agent: ${effectiveAgentId}`,
22224
23748
  `In Progress: ${workload.in_progress}`,
@@ -22240,10 +23764,29 @@ function registerTaskAutoTools(server, ctx) {
22240
23764
  max_per_agent: exports_external.number().optional().describe("Max tasks per agent (default: 5)")
22241
23765
  }, async ({ project_id, max_per_agent }) => {
22242
23766
  try {
22243
- const { rebalanceWorkload } = (init_agents(), __toCommonJS(exports_agents));
23767
+ const { listAgents: listAgents2 } = (init_agents(), __toCommonJS(exports_agents));
23768
+ const { listTasks: listTasks3, updateTask: updateTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
22244
23769
  const resolvedProjectId = project_id ? resolveId(project_id, "projects") : undefined;
22245
- const result = rebalanceWorkload({ project_id: resolvedProjectId, max_per_agent: max_per_agent || 5 });
22246
- return { content: [{ type: "text", text: `Rebalanced: moved ${result.moved} task(s), ${result.skipped} skipped.` }] };
23770
+ const limit = max_per_agent || 5;
23771
+ const agents = listAgents2().filter((agent) => agent.status === "active");
23772
+ if (agents.length === 0)
23773
+ return { content: [{ type: "text", text: "No active agents available for rebalancing." }] };
23774
+ const activeTasks = listTasks3({ project_id: resolvedProjectId, status: ["pending", "in_progress"], limit: 1000 }, undefined);
23775
+ const load = new Map(agents.map((agent) => [agent.id, activeTasks.filter((t) => t.assigned_to === agent.id).length]));
23776
+ let moved = 0;
23777
+ let skipped = 0;
23778
+ for (const task of activeTasks.filter((t) => t.status === "pending" && t.assigned_to && (load.get(t.assigned_to) ?? 0) > limit)) {
23779
+ const target = agents.filter((agent) => agent.id !== task.assigned_to).sort((a, b) => (load.get(a.id) ?? 0) - (load.get(b.id) ?? 0))[0];
23780
+ if (!target || (load.get(target.id) ?? 0) >= limit) {
23781
+ skipped++;
23782
+ continue;
23783
+ }
23784
+ updateTask2(task.id, { assigned_to: target.id, version: task.version });
23785
+ load.set(task.assigned_to, (load.get(task.assigned_to) ?? 1) - 1);
23786
+ load.set(target.id, (load.get(target.id) ?? 0) + 1);
23787
+ moved++;
23788
+ }
23789
+ return { content: [{ type: "text", text: `Rebalanced: moved ${moved} task(s), ${skipped} skipped.` }] };
22247
23790
  } catch (e) {
22248
23791
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
22249
23792
  }
@@ -22256,13 +23799,13 @@ function registerTaskAutoTools(server, ctx) {
22256
23799
  agent_id: exports_external.string().optional().describe("Filter by assignee")
22257
23800
  }, async ({ hours = 24, project_id, agent_id }) => {
22258
23801
  try {
22259
- const { notifyUpcomingDeadlines } = (init_tasks(), __toCommonJS(exports_tasks));
23802
+ const { notifyUpcomingDeadlines: notifyUpcomingDeadlines2 } = (init_tasks(), __toCommonJS(exports_tasks));
22260
23803
  const resolvedProjectId = project_id ? resolveId(project_id, "projects") : undefined;
22261
23804
  const resolvedAgentId = agent_id ? resolveId(agent_id, "agents") : undefined;
22262
- const tasks = notifyUpcomingDeadlines({ hours, project_id: resolvedProjectId, agent_id: resolvedAgentId });
23805
+ const tasks = notifyUpcomingDeadlines2({ hours, project_id: resolvedProjectId, agent_id: resolvedAgentId });
22263
23806
  if (tasks.length === 0)
22264
23807
  return { content: [{ type: "text", text: "No deadlines approaching." }] };
22265
- const lines = tasks.map((t) => `${t.short_id || t.id.slice(0, 8)} ${t.title} due ${t.deadline}`);
23808
+ const lines = tasks.map((t) => `${t.short_id || t.id.slice(0, 8)} ${t.title} due ${t.due_at}`);
22266
23809
  return { content: [{ type: "text", text: `${tasks.length} task(s) due within ${hours}h:
22267
23810
  ${lines.join(`
22268
23811
  `)}` }] };
@@ -22296,9 +23839,9 @@ ${lines.join(`
22296
23839
  project_id: exports_external.string().optional().describe("Filter by project")
22297
23840
  }, async ({ project_id }) => {
22298
23841
  try {
22299
- const { getBlockedTasks } = (init_tasks(), __toCommonJS(exports_tasks));
23842
+ const { getBlockedTasks: getBlockedTasks2 } = (init_tasks(), __toCommonJS(exports_tasks));
22300
23843
  const resolvedProjectId = project_id ? resolveId(project_id, "projects") : undefined;
22301
- const tasks = getBlockedTasks(resolvedProjectId);
23844
+ const tasks = getBlockedTasks2(resolvedProjectId);
22302
23845
  if (tasks.length === 0)
22303
23846
  return { content: [{ type: "text", text: "No blocked tasks." }] };
22304
23847
  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(", ")}`);
@@ -22315,9 +23858,9 @@ ${lines.join(`
22315
23858
  project_id: exports_external.string().optional().describe("Filter by project")
22316
23859
  }, async ({ project_id }) => {
22317
23860
  try {
22318
- const { getBlockingTasks } = (init_tasks(), __toCommonJS(exports_tasks));
23861
+ const { getBlockingTasks: getBlockingTasks2 } = (init_tasks(), __toCommonJS(exports_tasks));
22319
23862
  const resolvedProjectId = project_id ? resolveId(project_id, "projects") : undefined;
22320
- const tasks = getBlockingTasks(resolvedProjectId);
23863
+ const tasks = getBlockingTasks2(resolvedProjectId);
22321
23864
  if (tasks.length === 0)
22322
23865
  return { content: [{ type: "text", text: "No tasks blocking others." }] };
22323
23866
  const lines = tasks.map((t) => `${t.short_id || t.id.slice(0, 8)} [${t.status}] ${t.title} \u2014 blocking ${t.blocking_count} task(s)`);
@@ -22332,20 +23875,18 @@ ${lines.join(`
22332
23875
  if (shouldRegisterTool("get_health")) {
22333
23876
  server.tool("get_health", "Get system health: task counts by status, active agents, project summary.", async () => {
22334
23877
  try {
22335
- const { listTasks: listTasks3 } = (init_tasks(), __toCommonJS(exports_tasks));
22336
- const { listProjects: listProjects2 } = (init_tasks(), __toCommonJS(exports_tasks));
23878
+ const { countTasks: countTasks2 } = (init_tasks(), __toCommonJS(exports_tasks));
23879
+ const { listProjects: listProjects2 } = (init_projects(), __toCommonJS(exports_projects));
22337
23880
  const { listAgents: listAgents2 } = (init_agents(), __toCommonJS(exports_agents));
22338
- const [pending, inProgress, completed, cancelled] = await Promise.all([
22339
- Promise.resolve(listTasks3({ status: "pending", limit: 1 }, undefined)),
22340
- Promise.resolve(listTasks3({ status: "in_progress", limit: 1 }, undefined)),
22341
- Promise.resolve(listTasks3({ status: "completed", limit: 1 }, undefined)),
22342
- Promise.resolve(listTasks3({ status: "cancelled", limit: 1 }, undefined))
22343
- ]);
22344
- const projects = listProjects2({ limit: 100 });
22345
- const agents = listAgents2({ limit: 100 });
23881
+ const pending = countTasks2({ status: "pending" });
23882
+ const inProgress = countTasks2({ status: "in_progress" });
23883
+ const completed = countTasks2({ status: "completed" });
23884
+ const cancelled = countTasks2({ status: "cancelled" });
23885
+ const projects = listProjects2();
23886
+ const agents = listAgents2();
22346
23887
  const lines = [
22347
23888
  `=== System Health ===`,
22348
- `Tasks: ${pending.total} pending | ${inProgress.total} in progress | ${completed.total} completed | ${cancelled.total} cancelled`,
23889
+ `Tasks: ${pending} pending | ${inProgress} in progress | ${completed} completed | ${cancelled} cancelled`,
22349
23890
  `Projects: ${projects.length} total`,
22350
23891
  `Agents: ${agents.length} registered`
22351
23892
  ];
@@ -22362,17 +23903,31 @@ ${lines.join(`
22362
23903
  function registerTaskAdvTools(server, ctx) {
22363
23904
  const { shouldRegisterTool, resolveId, formatError, formatTask, formatTaskDetail } = ctx;
22364
23905
  if (shouldRegisterTool("get_status")) {
22365
- server.tool("get_status", "Get a task's current status including blockers, dependencies, comments, and assignee.", {
22366
- task_id: exports_external.string().describe("Task ID")
22367
- }, async ({ task_id }) => {
23906
+ server.tool("get_status", "Get queue status summary, or pass task_id for a task's detailed status.", {
23907
+ task_id: exports_external.string().optional().describe("Task ID for task-specific status"),
23908
+ project_id: exports_external.string().optional().describe("Filter summary by project"),
23909
+ task_list_id: exports_external.string().optional().describe("Filter summary by task list"),
23910
+ agent_id: exports_external.string().optional().describe("Agent for next-task affinity"),
23911
+ explain_blocked: exports_external.boolean().optional().describe("Include blocked task explanations in summary")
23912
+ }, async ({ task_id, project_id, task_list_id, agent_id, explain_blocked }) => {
22368
23913
  try {
23914
+ if (!task_id) {
23915
+ const { getStatus: getStatus2 } = (init_tasks(), __toCommonJS(exports_tasks));
23916
+ const filters = {};
23917
+ if (project_id)
23918
+ filters.project_id = resolveId(project_id, "projects");
23919
+ if (task_list_id)
23920
+ filters.task_list_id = resolveId(task_list_id, "task_lists");
23921
+ const status = getStatus2(filters, agent_id, { explain_blocked });
23922
+ return { content: [{ type: "text", text: JSON.stringify(status, null, 2) }] };
23923
+ }
22369
23924
  const resolvedId = resolveId(task_id);
22370
- const { getTask: getTask4 } = (init_tasks(), __toCommonJS(exports_tasks));
22371
- const task = getTask4(resolvedId);
23925
+ const { getTask: getTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
23926
+ const task = getTask2(resolvedId);
22372
23927
  if (!task)
22373
23928
  throw new Error(`Task not found: ${task_id}`);
22374
23929
  const { getTaskDependencies: getTaskDependencies3 } = (init_tasks(), __toCommonJS(exports_tasks));
22375
- const { listComments: listComments2 } = (init_tasks(), __toCommonJS(exports_tasks));
23930
+ const { listComments: listComments2 } = (init_comments(), __toCommonJS(exports_comments));
22376
23931
  const { listTaskFiles: listTaskFiles2 } = (init_task_files(), __toCommonJS(exports_task_files));
22377
23932
  const [deps, comments, files] = await Promise.all([
22378
23933
  Promise.resolve(getTaskDependencies3(resolvedId, "both")),
@@ -22382,14 +23937,14 @@ function registerTaskAdvTools(server, ctx) {
22382
23937
  const lines = [
22383
23938
  `Status: ${task.status} | Priority: ${task.priority}`,
22384
23939
  task.assigned_to ? `Assigned: ${task.assigned_to}` : "Unassigned",
22385
- task.deadline ? `Deadline: ${task.deadline}` : null,
23940
+ task.due_at ? `Due: ${task.due_at}` : null,
22386
23941
  task.confidence != null ? `Confidence: ${task.confidence}` : null,
22387
23942
  deps.length > 0 ? `
22388
23943
  Dependencies (${deps.length}):` : null,
22389
23944
  ...deps.map((d) => ` [${d.direction}] ${d.task_id.slice(0, 8)} (${d.status})`),
22390
23945
  comments.length > 0 ? `
22391
23946
  Comments (${comments.length}):` : null,
22392
- ...comments.map((c) => ` [${c.author || "?"}] ${c.created_at?.slice(0, 16)}: ${c.body.slice(0, 80)}`),
23947
+ ...comments.map((c) => ` [${c.agent_id || "?"}] ${c.created_at?.slice(0, 16)}: ${c.content.slice(0, 80)}`),
22393
23948
  files.length > 0 ? `
22394
23949
  Files (${files.length}):` : null,
22395
23950
  ...files.map((f) => ` ${f.status} ${f.path}`)
@@ -22407,22 +23962,22 @@ Files (${files.length}):` : null,
22407
23962
  }, async ({ task_id }) => {
22408
23963
  try {
22409
23964
  const resolvedId = resolveId(task_id);
22410
- const { getTask: getTask4 } = (init_tasks(), __toCommonJS(exports_tasks));
22411
- const task = getTask4(resolvedId);
23965
+ const { getTask: getTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
23966
+ const task = getTask2(resolvedId);
22412
23967
  if (!task)
22413
23968
  throw new Error(`Task not found: ${task_id}`);
22414
23969
  const { getTaskDependencies: getTaskDependencies3 } = (init_tasks(), __toCommonJS(exports_tasks));
22415
23970
  const { getTaskRelationships: getTaskRelationships2 } = (init_task_relationships(), __toCommonJS(exports_task_relationships));
22416
- const { listComments: listComments2 } = (init_tasks(), __toCommonJS(exports_tasks));
23971
+ const { listComments: listComments2 } = (init_comments(), __toCommonJS(exports_comments));
22417
23972
  const { listTaskFiles: listTaskFiles2 } = (init_task_files(), __toCommonJS(exports_task_files));
22418
- const { getTaskCommits } = (init_tasks(), __toCommonJS(exports_tasks));
23973
+ const { getTaskCommits: getTaskCommits2 } = (init_task_commits(), __toCommonJS(exports_task_commits));
22419
23974
  const { getTaskWatchers: getTaskWatchers2 } = (init_tasks(), __toCommonJS(exports_tasks));
22420
23975
  const [deps, rels, comments, files, commits, watchers] = await Promise.all([
22421
23976
  Promise.resolve(getTaskDependencies3(resolvedId, "both")),
22422
23977
  Promise.resolve(getTaskRelationships2(resolvedId)),
22423
23978
  Promise.resolve(listComments2(resolvedId)),
22424
23979
  Promise.resolve(listTaskFiles2(resolvedId)),
22425
- Promise.resolve(getTaskCommits(resolvedId)),
23980
+ Promise.resolve(getTaskCommits2(resolvedId)),
22426
23981
  Promise.resolve(getTaskWatchers2(resolvedId))
22427
23982
  ]);
22428
23983
  const lines = [
@@ -22436,7 +23991,7 @@ Files (${files.length}):` : null,
22436
23991
  task.tags?.length ? `Tags: ${task.tags.join(", ")}` : null,
22437
23992
  task.created_at ? `Created: ${task.created_at}` : null,
22438
23993
  task.updated_at ? `Updated: ${task.updated_at}` : null,
22439
- task.deadline ? `Deadline: ${task.deadline}` : null,
23994
+ task.due_at ? `Due: ${task.due_at}` : null,
22440
23995
  task.completed_at ? `Completed: ${task.completed_at}` : null,
22441
23996
  deps.length > 0 ? `
22442
23997
  --- Dependencies (${deps.length}) ---` : null,
@@ -22446,7 +24001,7 @@ Files (${files.length}):` : null,
22446
24001
  ...rels.map((r) => ` ${r.source_task_id.slice(0, 8)} --[${r.relationship_type}]--> ${r.target_task_id.slice(0, 8)}`),
22447
24002
  comments.length > 0 ? `
22448
24003
  --- Comments (${comments.length}) ---` : null,
22449
- ...comments.map((c) => ` [${c.author || "?"}] ${c.created_at?.slice(0, 16)}: ${c.body.slice(0, 120)}`),
24004
+ ...comments.map((c) => ` [${c.agent_id || "?"}] ${c.created_at?.slice(0, 16)}: ${c.content.slice(0, 120)}`),
22450
24005
  files.length > 0 ? `
22451
24006
  --- Files (${files.length}) ---` : null,
22452
24007
  ...files.map((f) => ` [${f.status}] ${f.path}`),
@@ -22495,8 +24050,8 @@ ${JSON.stringify(task.metadata, null, 2)}` : null
22495
24050
  limit: 20
22496
24051
  }, undefined);
22497
24052
  const completedYesterday = completed.filter((t) => t.completed_at && t.completed_at.startsWith(yesterdayStr));
22498
- const { getBlockedTasks } = (init_tasks(), __toCommonJS(exports_tasks));
22499
- const blocked = getBlockedTasks(effectiveProjectId ? resolveId(effectiveProjectId, "projects") : undefined).filter((t) => t.assigned_to === effectiveAgentId);
24053
+ const { getBlockedTasks: getBlockedTasks2 } = (init_tasks(), __toCommonJS(exports_tasks));
24054
+ const blocked = getBlockedTasks2(effectiveProjectId ? resolveId(effectiveProjectId, "projects") : undefined).filter((t) => t.assigned_to === effectiveAgentId);
22500
24055
  const lines = [
22501
24056
  `Standup for ${effectiveAgentId} (${effectiveProjectId ? `project: ${effectiveProjectId.slice(0, 8)}` : "all projects"})`,
22502
24057
  inProgress.length > 0 ? `
@@ -22525,11 +24080,11 @@ No blocked tasks.`,
22525
24080
  agent_id: exports_external.string().optional().describe("Agent claiming (defaults to context)")
22526
24081
  }, async ({ task_id, agent_id }) => {
22527
24082
  try {
22528
- const { updateTask: updateTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
24083
+ const { startTask: startTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
22529
24084
  const resolvedId = resolveId(task_id);
22530
24085
  const focus = ctx.getAgentFocus(agent_id || "");
22531
- const effectiveAgent = focus ? focus.agent_id : agent_id || "";
22532
- const task = updateTask2(resolvedId, { status: "in_progress", assigned_to: effectiveAgent });
24086
+ const effectiveAgent = focus ? focus.agent_id : agent_id || "mcp";
24087
+ const task = startTask2(resolvedId, effectiveAgent);
22533
24088
  return { content: [{ type: "text", text: formatTask(task) }] };
22534
24089
  } catch (e) {
22535
24090
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
@@ -22541,211 +24096,95 @@ No blocked tasks.`,
22541
24096
  task_id: exports_external.string().describe("Task ID")
22542
24097
  }, async ({ task_id }) => {
22543
24098
  try {
22544
- const { updateTask: updateTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
22545
- const task = updateTask2(resolveId(task_id), { status: "pending", assigned_to: null });
22546
- return { content: [{ type: "text", text: formatTask(task) }] };
22547
- } catch (e) {
22548
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
22549
- }
22550
- });
22551
- }
22552
- if (shouldRegisterTool("extend_task")) {
22553
- server.tool("extend_task", "Add more time to a task's estimate.", {
22554
- task_id: exports_external.string().describe("Task ID"),
22555
- minutes: exports_external.number().describe("Additional minutes to add to estimate")
22556
- }, async ({ task_id, minutes }) => {
22557
- try {
22558
- const { getTask: getTask4, updateTask: updateTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
24099
+ const { getTask: getTask2, updateTask: updateTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
22559
24100
  const resolvedId = resolveId(task_id);
22560
- const task = getTask4(resolvedId);
22561
- if (!task)
24101
+ const current = getTask2(resolvedId);
24102
+ if (!current)
22562
24103
  throw new Error(`Task not found: ${task_id}`);
22563
- const currentEstimate = task.estimated_minutes || 0;
22564
- const updated = updateTask2(resolvedId, { estimated_minutes: currentEstimate + minutes });
22565
- return { content: [{ type: "text", text: `Estimate updated: ${currentEstimate} \u2192 ${updated.estimated_minutes} min (+${minutes})` }] };
22566
- } catch (e) {
22567
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
22568
- }
22569
- });
22570
- }
22571
- if (shouldRegisterTool("add_comment")) {
22572
- server.tool("add_comment", "Alias for create_comment.", {
22573
- task_id: exports_external.string().describe("Task ID"),
22574
- body: exports_external.string().describe("Comment body"),
22575
- author: exports_external.string().optional().describe("Author agent ID or name")
22576
- }, async ({ task_id, body, author }) => {
22577
- try {
22578
- const { createComment } = (init_tasks(), __toCommonJS(exports_tasks));
22579
- const resolvedId = resolveId(task_id);
22580
- const resolvedAuthor = author ? resolveId(author, "agents") : undefined;
22581
- createComment({ task_id: resolvedId, body, author: resolvedAuthor });
22582
- return { content: [{ type: "text", text: `Comment added to ${task_id.slice(0, 8)}` }] };
22583
- } catch (e) {
22584
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
22585
- }
22586
- });
22587
- }
22588
- if (shouldRegisterTool("get_comments")) {
22589
- server.tool("get_comments", "Alias for list_comments.", {
22590
- task_id: exports_external.string().describe("Task ID")
22591
- }, async ({ task_id }) => {
22592
- try {
22593
- const { listComments: listComments2 } = (init_tasks(), __toCommonJS(exports_tasks));
22594
- const comments = listComments2(resolveId(task_id));
22595
- if (comments.length === 0)
22596
- return { content: [{ type: "text", text: "No comments." }] };
22597
- const lines = comments.map((c) => `[${c.author || "?"}] ${c.created_at?.slice(0, 16)}: ${c.body}`);
22598
- return { content: [{ type: "text", text: lines.join(`
22599
-
22600
- `) }] };
22601
- } catch (e) {
22602
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
22603
- }
22604
- });
22605
- }
22606
- if (shouldRegisterTool("list_my_tasks")) {
22607
- server.tool("list_my_tasks", "Alias for get_my_tasks.", {
22608
- agent_id: exports_external.string().optional().describe("Agent ID (defaults to context agent)"),
22609
- status: exports_external.enum(["pending", "in_progress", "completed", "cancelled"]).optional(),
22610
- project_id: exports_external.string().optional().describe("Filter by project"),
22611
- limit: exports_external.number().optional()
22612
- }, async ({ agent_id, status, project_id, limit }) => {
22613
- try {
22614
- const { listTasks: listTasks3 } = (init_tasks(), __toCommonJS(exports_tasks));
22615
- const focus = ctx.getAgentFocus(agent_id || "");
22616
- const effectiveAgentId = focus ? focus.agent_id : agent_id || "";
22617
- const effectiveProjectId = focus?.project_id || project_id;
22618
- const tasks = listTasks3({
22619
- assigned_to: effectiveAgentId,
22620
- status,
22621
- project_id: effectiveProjectId ? resolveId(effectiveProjectId, "projects") : undefined,
22622
- limit: limit || 50
22623
- }, undefined);
22624
- if (tasks.length === 0)
22625
- return { content: [{ type: "text", text: "No tasks found." }] };
22626
- const lines = tasks.map(formatTask);
22627
- return { content: [{ type: "text", text: lines.join(`
22628
- `) }] };
22629
- } catch (e) {
22630
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
22631
- }
22632
- });
22633
- }
22634
- if (shouldRegisterTool("list_agents")) {
22635
- server.tool("list_agents", "List all registered agents.", {
22636
- project_id: exports_external.string().optional().describe("Filter by project"),
22637
- role: exports_external.string().optional().describe("Filter by global role"),
22638
- capabilities: exports_external.array(exports_external.string()).optional().describe("Filter by capabilities")
22639
- }, async ({ project_id, role, capabilities }) => {
22640
- try {
22641
- const { listAgents: listAgents2 } = (init_agents(), __toCommonJS(exports_agents));
22642
- const resolved = {};
22643
- if (project_id)
22644
- resolved.project_id = resolveId(project_id, "projects");
22645
- if (role)
22646
- resolved.role = role;
22647
- if (capabilities)
22648
- resolved.capabilities = capabilities;
22649
- const agents = listAgents2(resolved);
22650
- if (agents.length === 0)
22651
- return { content: [{ type: "text", text: "No agents found." }] };
22652
- const lines = agents.map((a) => `\u25CF ${a.name} [${a.role || "no role"}]${a.capabilities?.length ? ` caps:[${a.capabilities.join(",")}]` : ""}`);
22653
- return { content: [{ type: "text", text: lines.join(`
22654
- `) }] };
22655
- } catch (e) {
22656
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
22657
- }
22658
- });
22659
- }
22660
- if (shouldRegisterTool("get_agent")) {
22661
- server.tool("get_agent", "Get full details for an agent.", {
22662
- agent_id: exports_external.string().describe("Agent ID or name")
22663
- }, async ({ agent_id }) => {
22664
- try {
22665
- const { getAgentByName: getAgentByName2, getAgent: getAgent2 } = (init_agents(), __toCommonJS(exports_agents));
22666
- const resolvedId = resolveId(agent_id, "agents");
22667
- const agent = agent_id.includes("@") || !resolvedId.startsWith("agent_") ? getAgentByName2(agent_id) : getAgent2(resolvedId);
22668
- if (!agent)
22669
- return { content: [{ type: "text", text: `Agent not found: ${agent_id}` }], isError: true };
22670
- const lines = [
22671
- `ID: ${agent.id}`,
22672
- `Name: ${agent.name}`,
22673
- agent.email ? `Email: ${agent.email}` : null,
22674
- agent.title ? `Title: ${agent.title}` : null,
22675
- agent.role ? `Role: ${agent.role}` : null,
22676
- agent.capabilities?.length ? `Capabilities: ${agent.capabilities.join(", ")}` : null,
22677
- agent.last_seen_at ? `Last Seen: ${agent.last_seen_at}` : null
22678
- ].filter(Boolean);
22679
- return { content: [{ type: "text", text: lines.join(`
22680
- `) }] };
24104
+ const task = updateTask2(resolvedId, { status: "pending", assigned_to: null, version: current.version });
24105
+ return { content: [{ type: "text", text: formatTask(task) }] };
22681
24106
  } catch (e) {
22682
24107
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
22683
24108
  }
22684
- });
22685
- }
22686
- if (shouldRegisterTool("update_agent")) {
22687
- server.tool("update_agent", "Update an agent's fields.", {
22688
- agent_id: exports_external.string().describe("Agent ID or name"),
22689
- name: exports_external.string().optional(),
22690
- email: exports_external.string().optional(),
22691
- title: exports_external.string().optional(),
22692
- role: exports_external.string().optional(),
22693
- capabilities: exports_external.array(exports_external.string()).optional()
22694
- }, async ({ agent_id, ...updates }) => {
24109
+ });
24110
+ }
24111
+ if (shouldRegisterTool("extend_task")) {
24112
+ server.tool("extend_task", "Add more time to a task's estimate.", {
24113
+ task_id: exports_external.string().describe("Task ID"),
24114
+ minutes: exports_external.number().describe("Additional minutes to add to estimate")
24115
+ }, async ({ task_id, minutes }) => {
22695
24116
  try {
22696
- const { getAgentByName: getAgentByName2, updateAgent: updateAgent2 } = (init_agents(), __toCommonJS(exports_agents));
22697
- const resolvedId = resolveId(agent_id, "agents");
22698
- const agent = agent_id.includes("@") || !resolvedId.startsWith("agent_") ? getAgentByName2(agent_id) : { id: resolvedId };
22699
- updateAgent2(agent.id, updates);
22700
- return { content: [{ type: "text", text: `Agent ${agent.id.slice(0, 8)} updated.` }] };
24117
+ const { getTask: getTask2, updateTask: updateTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
24118
+ const resolvedId = resolveId(task_id);
24119
+ const task = getTask2(resolvedId);
24120
+ if (!task)
24121
+ throw new Error(`Task not found: ${task_id}`);
24122
+ const currentEstimate = task.estimated_minutes || 0;
24123
+ const updated = updateTask2(resolvedId, { estimated_minutes: currentEstimate + minutes, version: task.version });
24124
+ return { content: [{ type: "text", text: `Estimate updated: ${currentEstimate} \u2192 ${updated.estimated_minutes} min (+${minutes})` }] };
22701
24125
  } catch (e) {
22702
24126
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
22703
24127
  }
22704
24128
  });
22705
24129
  }
22706
- if (shouldRegisterTool("delete_agent")) {
22707
- server.tool("delete_agent", "Deregister an agent.", {
22708
- agent_id: exports_external.string().describe("Agent ID or name")
22709
- }, async ({ agent_id }) => {
24130
+ if (shouldRegisterTool("add_comment")) {
24131
+ server.tool("add_comment", "Alias for create_comment.", {
24132
+ task_id: exports_external.string().describe("Task ID"),
24133
+ body: exports_external.string().describe("Comment body"),
24134
+ author: exports_external.string().optional().describe("Author agent ID or name")
24135
+ }, async ({ task_id, body, author }) => {
22710
24136
  try {
22711
- const { getAgentByName: getAgentByName2, deleteAgent: deleteAgent2 } = (init_agents(), __toCommonJS(exports_agents));
22712
- const resolvedId = resolveId(agent_id, "agents");
22713
- const agent = agent_id.includes("@") || !resolvedId.startsWith("agent_") ? getAgentByName2(agent_id) : { id: resolvedId };
22714
- if (!agent)
22715
- return { content: [{ type: "text", text: `Agent not found: ${agent_id}` }], isError: true };
22716
- deleteAgent2(agent.id);
22717
- return { content: [{ type: "text", text: `Agent ${agent_id} deleted.` }] };
24137
+ const { addComment: addComment2 } = (init_comments(), __toCommonJS(exports_comments));
24138
+ const resolvedId = resolveId(task_id);
24139
+ const resolvedAuthor = author ? resolveId(author, "agents") : undefined;
24140
+ addComment2({ task_id: resolvedId, content: body, agent_id: resolvedAuthor });
24141
+ return { content: [{ type: "text", text: `Comment added to ${task_id.slice(0, 8)}` }] };
22718
24142
  } catch (e) {
22719
24143
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
22720
24144
  }
22721
24145
  });
22722
24146
  }
22723
- if (shouldRegisterTool("register_agent")) {
22724
- server.tool("register_agent", "Register a new agent.", {
22725
- name: exports_external.string().describe("Agent name"),
22726
- email: exports_external.string().optional(),
22727
- title: exports_external.string().optional(),
22728
- role: exports_external.string().optional(),
22729
- capabilities: exports_external.array(exports_external.string()).optional()
22730
- }, async ({ name, email, title, role, capabilities }) => {
24147
+ if (shouldRegisterTool("get_comments")) {
24148
+ server.tool("get_comments", "Alias for list_comments.", {
24149
+ task_id: exports_external.string().describe("Task ID")
24150
+ }, async ({ task_id }) => {
22731
24151
  try {
22732
- const { registerAgent: registerAgent2 } = (init_agents(), __toCommonJS(exports_agents));
22733
- const agent = registerAgent2({ name, email, title, role, capabilities });
22734
- return { content: [{ type: "text", text: `Agent registered: ${agent.name} (${agent.id})` }] };
24152
+ const { listComments: listComments2 } = (init_comments(), __toCommonJS(exports_comments));
24153
+ const comments = listComments2(resolveId(task_id));
24154
+ if (comments.length === 0)
24155
+ return { content: [{ type: "text", text: "No comments." }] };
24156
+ const lines = comments.map((c) => `[${c.agent_id || "?"}] ${c.created_at?.slice(0, 16)}: ${c.content}`);
24157
+ return { content: [{ type: "text", text: lines.join(`
24158
+
24159
+ `) }] };
22735
24160
  } catch (e) {
22736
24161
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
22737
24162
  }
22738
24163
  });
22739
24164
  }
22740
- if (shouldRegisterTool("heartbeat")) {
22741
- server.tool("heartbeat", "Agent heartbeat \u2014 updates last_seen_at timestamp.", {
22742
- agent_id: exports_external.string().describe("Agent ID"),
22743
- status: exports_external.string().optional().describe("Current status message")
22744
- }, async ({ agent_id, status }) => {
24165
+ if (shouldRegisterTool("list_my_tasks")) {
24166
+ server.tool("list_my_tasks", "Alias for get_my_tasks.", {
24167
+ agent_id: exports_external.string().optional().describe("Agent ID (defaults to context agent)"),
24168
+ status: exports_external.enum(["pending", "in_progress", "completed", "failed", "cancelled"]).optional(),
24169
+ project_id: exports_external.string().optional().describe("Filter by project"),
24170
+ limit: exports_external.number().optional()
24171
+ }, async ({ agent_id, status, project_id, limit }) => {
22745
24172
  try {
22746
- const { heartbeat } = (init_agents(), __toCommonJS(exports_agents));
22747
- heartbeat(resolveId(agent_id, "agents"), status);
22748
- return { content: [{ type: "text", text: "Heartbeat received." }] };
24173
+ const { listTasks: listTasks3 } = (init_tasks(), __toCommonJS(exports_tasks));
24174
+ const focus = ctx.getAgentFocus(agent_id || "");
24175
+ const effectiveAgentId = focus ? focus.agent_id : agent_id || "";
24176
+ const effectiveProjectId = focus?.project_id || project_id;
24177
+ const tasks = listTasks3({
24178
+ assigned_to: effectiveAgentId,
24179
+ status,
24180
+ project_id: effectiveProjectId ? resolveId(effectiveProjectId, "projects") : undefined,
24181
+ limit: limit || 50
24182
+ }, undefined);
24183
+ if (tasks.length === 0)
24184
+ return { content: [{ type: "text", text: "No tasks found." }] };
24185
+ const lines = tasks.map(formatTask);
24186
+ return { content: [{ type: "text", text: lines.join(`
24187
+ `) }] };
22749
24188
  } catch (e) {
22750
24189
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
22751
24190
  }
@@ -22792,6 +24231,11 @@ function registerTaskMetaTools(server, ctx) {
22792
24231
  prioritize_task: "prioritize_task \u2014 Set priority. Params: task_id, priority, version",
22793
24232
  search_tasks: "search_tasks \u2014 Full-text search. Params: query, project_id, status, limit",
22794
24233
  get_my_tasks: "get_my_tasks \u2014 Get tasks for calling agent. Params: agent_id, status, project_id, limit",
24234
+ 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",
24235
+ 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",
24236
+ get_tasks_changed_since: "get_tasks_changed_since \u2014 List tasks changed since an ISO timestamp. Params: since, project_id, task_list_id, limit",
24237
+ get_context: "get_context \u2014 Get session start context. Params: agent_id, project_id, task_list_id, explain_blocked",
24238
+ bootstrap: "bootstrap \u2014 Bootstrap an agent session with queue context. Params: agent_id, project_id, task_list_id, explain_blocked",
22795
24239
  standup: "standup \u2014 Get standup report. Params: agent_id, project_id",
22796
24240
  patrol_tasks: "patrol_tasks \u2014 Scan for task issues. Params: stuck_minutes, confidence_threshold, project_id",
22797
24241
  get_review_queue: "get_review_queue \u2014 Get tasks needing review. Params: project_id, limit",
@@ -22849,9 +24293,20 @@ function registerTaskMetaTools(server, ctx) {
22849
24293
  get_health: "get_health \u2014 Get system health stats",
22850
24294
  approve_task: "approve_task \u2014 Approve a task. Params: task_id, approved_by, notes, version",
22851
24295
  fail_task: "fail_task \u2014 Mark task failed. Params: task_id, reason, agent_id, version",
24296
+ register_agent: "register_agent \u2014 Register an agent. Params: name, description, role, title, capabilities, session_id, working_dir, force",
24297
+ list_agents: "list_agents \u2014 List registered agents. Params: include_archived",
24298
+ get_agent: "get_agent \u2014 Get agent details. Params: agent_id, id, name",
24299
+ update_agent: "update_agent \u2014 Update an agent. Params: agent_id, id, name, description, role, title, level, capabilities, permissions, metadata",
24300
+ delete_agent: "delete_agent \u2014 Archive an agent. Params: agent_id, id, name",
24301
+ unarchive_agent: "unarchive_agent \u2014 Restore an archived agent. Params: agent_id, id, name",
24302
+ heartbeat: "heartbeat \u2014 Update an agent heartbeat. Params: agent_id",
24303
+ release_agent: "release_agent \u2014 Release an agent session/name. Params: agent_id, session_id",
24304
+ set_focus: "set_focus \u2014 Focus an agent on a project. Params: agent_id, project_id, task_list_id",
24305
+ get_focus: "get_focus \u2014 Get current agent focus. Params: agent_id",
24306
+ unfocus: "unfocus \u2014 Clear agent focus. Params: agent_id",
24307
+ suggest_agent_name: "suggest_agent_name \u2014 Suggest available agent names. Params: working_dir",
22852
24308
  get_org_chart: "get_org_chart \u2014 Get global org chart. Params: format",
22853
24309
  set_reports_to: "set_reports_to \u2014 Set org hierarchy. Params: agent_id, reports_to",
22854
- sync: "sync \u2014 Sync from external source. Params: source, source_id, project_id, options",
22855
24310
  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",
22856
24311
  migrate_pg: "migrate_pg \u2014 Apply PostgreSQL migrations. Params: connection_string"
22857
24312
  };
@@ -22870,39 +24325,7 @@ function registerTaskMetaTools(server, ctx) {
22870
24325
  init_tasks();
22871
24326
  init_projects();
22872
24327
  init_agents();
22873
-
22874
- // src/db/task-commits.ts
22875
- init_database();
22876
- function rowToCommit(row) {
22877
- return {
22878
- ...row,
22879
- files_changed: row.files_changed ? JSON.parse(row.files_changed) : null
22880
- };
22881
- }
22882
- function linkTaskToCommit(input, db) {
22883
- const d = db || getDatabase();
22884
- const existing = d.query("SELECT * FROM task_commits WHERE task_id = ? AND sha = ?").get(input.task_id, input.sha);
22885
- if (existing) {
22886
- d.run("UPDATE task_commits SET message = COALESCE(?, message), author = COALESCE(?, author), files_changed = COALESCE(?, files_changed), committed_at = COALESCE(?, committed_at) WHERE id = ?", [input.message ?? null, input.author ?? null, input.files_changed ? JSON.stringify(input.files_changed) : null, input.committed_at ?? null, existing.id]);
22887
- return rowToCommit(d.query("SELECT * FROM task_commits WHERE id = ?").get(existing.id));
22888
- }
22889
- const id = uuid();
22890
- d.run("INSERT INTO task_commits (id, task_id, sha, message, author, files_changed, committed_at, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?)", [id, input.task_id, input.sha, input.message ?? null, input.author ?? null, input.files_changed ? JSON.stringify(input.files_changed) : null, input.committed_at ?? null, now()]);
22891
- return rowToCommit(d.query("SELECT * FROM task_commits WHERE id = ?").get(id));
22892
- }
22893
- function getTaskCommits(taskId, db) {
22894
- const d = db || getDatabase();
22895
- return d.query("SELECT * FROM task_commits WHERE task_id = ? ORDER BY committed_at DESC, created_at DESC").all(taskId).map(rowToCommit);
22896
- }
22897
- function findTaskByCommit(sha, db) {
22898
- const d = db || getDatabase();
22899
- const row = d.query("SELECT * FROM task_commits WHERE sha = ? OR sha LIKE ? LIMIT 1").get(sha, `${sha}%`);
22900
- if (!row)
22901
- return null;
22902
- return { task_id: row.task_id, commit: rowToCommit(row) };
22903
- }
22904
-
22905
- // src/mcp/tools/task-resources.ts
24328
+ init_task_commits();
22906
24329
  function registerTaskResources(server, ctx) {
22907
24330
  const { shouldRegisterTool, resolveId, formatError } = ctx;
22908
24331
  server.resource("tasks", "todos://tasks", { description: "All active tasks", mimeType: "application/json" }, async () => {
@@ -22927,7 +24350,7 @@ function registerTaskResources(server, ctx) {
22927
24350
  note: exports_external.string().optional().describe("Note about why this file is linked")
22928
24351
  }, async ({ task_id, path, paths: multiplePaths, status, agent_id, note }) => {
22929
24352
  try {
22930
- const { addTaskFile: addTaskFile2, bulkAddTaskFiles: bulkAddTaskFiles2, detectFileConflicts: detectFileConflicts2 } = (()=>{throw new Error("Cannot require module "+"../db/task-files.js");})();
24353
+ const { addTaskFile: addTaskFile2, bulkAddTaskFiles: bulkAddTaskFiles2, detectFileConflicts: detectFileConflicts2 } = (init_task_files(), __toCommonJS(exports_task_files));
22931
24354
  const resolvedId = resolveId(task_id);
22932
24355
  let addedFiles;
22933
24356
  if (multiplePaths && multiplePaths.length > 0) {
@@ -22971,7 +24394,7 @@ function registerTaskResources(server, ctx) {
22971
24394
  if (shouldRegisterTool("list_task_files")) {
22972
24395
  server.tool("list_task_files", "List all files linked to a task.", { task_id: exports_external.string().describe("Task ID") }, async ({ task_id }) => {
22973
24396
  try {
22974
- const { listTaskFiles: listTaskFiles2 } = (()=>{throw new Error("Cannot require module "+"../db/task-files.js");})();
24397
+ const { listTaskFiles: listTaskFiles2 } = (init_task_files(), __toCommonJS(exports_task_files));
22975
24398
  const resolvedId = resolveId(task_id);
22976
24399
  const files = listTaskFiles2(resolvedId);
22977
24400
  if (files.length === 0)
@@ -22988,7 +24411,7 @@ ${lines.join(`
22988
24411
  if (shouldRegisterTool("find_tasks_by_file")) {
22989
24412
  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_external.string().describe("File path to search for") }, async ({ path }) => {
22990
24413
  try {
22991
- const { findTasksByFile: findTasksByFile2 } = (()=>{throw new Error("Cannot require module "+"../db/task-files.js");})();
24414
+ const { findTasksByFile: findTasksByFile2 } = (init_task_files(), __toCommonJS(exports_task_files));
22992
24415
  const files = findTasksByFile2(path);
22993
24416
  if (files.length === 0)
22994
24417
  return { content: [{ type: "text", text: `No tasks linked to ${path}` }] };
@@ -23008,7 +24431,7 @@ ${lines.join(`
23008
24431
  min_edits: exports_external.number().optional().describe("Minimum edit count to include (default: 1)")
23009
24432
  }, async ({ limit, project_id, min_edits }) => {
23010
24433
  try {
23011
- const { getFileHeatMap: getFileHeatMap2 } = (()=>{throw new Error("Cannot require module "+"../db/task-files.js");})();
24434
+ const { getFileHeatMap: getFileHeatMap2 } = (init_task_files(), __toCommonJS(exports_task_files));
23012
24435
  const resolvedProjectId = project_id ? resolveId(project_id, "projects") : undefined;
23013
24436
  const results = getFileHeatMap2({ limit, project_id: resolvedProjectId, min_edits });
23014
24437
  return { content: [{ type: "text", text: JSON.stringify(results, null, 2) }] };
@@ -23022,7 +24445,7 @@ ${lines.join(`
23022
24445
  paths: exports_external.array(exports_external.string()).describe("Array of file paths to check")
23023
24446
  }, async ({ paths }) => {
23024
24447
  try {
23025
- const { bulkFindTasksByFiles: bulkFindTasksByFiles2 } = (()=>{throw new Error("Cannot require module "+"../db/task-files.js");})();
24448
+ const { bulkFindTasksByFiles: bulkFindTasksByFiles2 } = (init_task_files(), __toCommonJS(exports_task_files));
23026
24449
  const results = bulkFindTasksByFiles2(paths);
23027
24450
  return { content: [{ type: "text", text: JSON.stringify(results, null, 2) }] };
23028
24451
  } catch (e) {
@@ -23035,11 +24458,11 @@ ${lines.join(`
23035
24458
  project_id: exports_external.string().optional().describe("Filter by project")
23036
24459
  }, async ({ project_id }) => {
23037
24460
  try {
23038
- const { listActiveFiles: listActiveFiles2 } = (()=>{throw new Error("Cannot require module "+"../db/task-files.js");})();
24461
+ const { listActiveFiles: listActiveFiles2 } = (init_task_files(), __toCommonJS(exports_task_files));
23039
24462
  let files = listActiveFiles2();
23040
24463
  if (project_id) {
23041
24464
  const pid = resolveId(project_id, "projects");
23042
- const db = (()=>{throw new Error("Cannot require module "+"../db/database.js");})().getDatabase();
24465
+ const db = (init_database(), __toCommonJS(exports_database)).getDatabase();
23043
24466
  files = db.query(`
23044
24467
  SELECT
23045
24468
  tf.path,
@@ -23121,8 +24544,8 @@ ${lines.join(`
23121
24544
  ttl_seconds: exports_external.number().optional().describe("Lock TTL in seconds (default: 1800 = 30 min)")
23122
24545
  }, async ({ path, agent_id, task_id, ttl_seconds }) => {
23123
24546
  try {
23124
- const { lockFile } = (()=>{throw new Error("Cannot require module "+"../db/file-locks.js");})();
23125
- const lock = lockFile({ path, agent_id, task_id, ttl_seconds });
24547
+ const { lockFile: lockFile2 } = (init_file_locks(), __toCommonJS(exports_file_locks));
24548
+ const lock = lockFile2({ path, agent_id, task_id, ttl_seconds });
23126
24549
  return { content: [{ type: "text", text: JSON.stringify(lock, null, 2) }] };
23127
24550
  } catch (e) {
23128
24551
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
@@ -23135,8 +24558,8 @@ ${lines.join(`
23135
24558
  agent_id: exports_external.string().describe("Agent releasing the lock (must be the lock holder)")
23136
24559
  }, async ({ path, agent_id }) => {
23137
24560
  try {
23138
- const { unlockFile } = (()=>{throw new Error("Cannot require module "+"../db/file-locks.js");})();
23139
- const released = unlockFile(path, agent_id);
24561
+ const { unlockFile: unlockFile2 } = (init_file_locks(), __toCommonJS(exports_file_locks));
24562
+ const released = unlockFile2(path, agent_id);
23140
24563
  return { content: [{ type: "text", text: JSON.stringify({ released, path }) }] };
23141
24564
  } catch (e) {
23142
24565
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
@@ -23148,8 +24571,8 @@ ${lines.join(`
23148
24571
  path: exports_external.string().describe("File path to check")
23149
24572
  }, async ({ path }) => {
23150
24573
  try {
23151
- const { checkFileLock } = (()=>{throw new Error("Cannot require module "+"../db/file-locks.js");})();
23152
- const lock = checkFileLock(path);
24574
+ const { checkFileLock: checkFileLock2 } = (init_file_locks(), __toCommonJS(exports_file_locks));
24575
+ const lock = checkFileLock2(path);
23153
24576
  if (!lock)
23154
24577
  return { content: [{ type: "text", text: JSON.stringify({ path, locked: false }) }] };
23155
24578
  return { content: [{ type: "text", text: JSON.stringify({ path, locked: true, ...lock }) }] };
@@ -23163,8 +24586,8 @@ ${lines.join(`
23163
24586
  agent_id: exports_external.string().optional().describe("Filter locks by agent")
23164
24587
  }, async ({ agent_id }) => {
23165
24588
  try {
23166
- const { listFileLocks } = (()=>{throw new Error("Cannot require module "+"../db/file-locks.js");})();
23167
- const locks = listFileLocks(agent_id);
24589
+ const { listFileLocks: listFileLocks2 } = (init_file_locks(), __toCommonJS(exports_file_locks));
24590
+ const locks = listFileLocks2(agent_id);
23168
24591
  return { content: [{ type: "text", text: JSON.stringify(locks, null, 2) }] };
23169
24592
  } catch (e) {
23170
24593
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
@@ -23187,8 +24610,8 @@ function registerTaskRelTools(server, ctx) {
23187
24610
  next_steps: exports_external.array(exports_external.string()).optional().describe("Recommended next actions")
23188
24611
  }, async ({ agent_id, project_id, summary, completed, in_progress, blockers, next_steps }) => {
23189
24612
  try {
23190
- const { createHandoff } = (()=>{throw new Error("Cannot require module "+"../db/handoffs.js");})();
23191
- const handoff = createHandoff({
24613
+ const { createHandoff: createHandoff2 } = (init_handoffs(), __toCommonJS(exports_handoffs));
24614
+ const handoff = createHandoff2({
23192
24615
  agent_id,
23193
24616
  project_id: project_id ? resolveId(project_id, "projects") : undefined,
23194
24617
  summary,
@@ -23209,8 +24632,8 @@ function registerTaskRelTools(server, ctx) {
23209
24632
  project_id: exports_external.string().optional().describe("Filter by project")
23210
24633
  }, async ({ agent_id, project_id }) => {
23211
24634
  try {
23212
- const { getLatestHandoff } = (()=>{throw new Error("Cannot require module "+"../db/handoffs.js");})();
23213
- const handoff = getLatestHandoff(agent_id, project_id ? resolveId(project_id, "projects") : undefined);
24635
+ const { getLatestHandoff: getLatestHandoff2 } = (init_handoffs(), __toCommonJS(exports_handoffs));
24636
+ const handoff = getLatestHandoff2(agent_id, project_id ? resolveId(project_id, "projects") : undefined);
23214
24637
  if (!handoff)
23215
24638
  return { content: [{ type: "text", text: "No handoffs found." }] };
23216
24639
  const lines = [
@@ -23240,7 +24663,7 @@ function registerTaskRelTools(server, ctx) {
23240
24663
  created_by: exports_external.string().optional().describe("Agent ID who created this relationship")
23241
24664
  }, async ({ source_task_id, target_task_id, relationship_type, created_by }) => {
23242
24665
  try {
23243
- const { addTaskRelationship: addTaskRelationship2 } = (()=>{throw new Error("Cannot require module "+"../db/task-relationships.js");})();
24666
+ const { addTaskRelationship: addTaskRelationship2 } = (init_task_relationships(), __toCommonJS(exports_task_relationships));
23244
24667
  const rel = addTaskRelationship2({
23245
24668
  source_task_id: resolveId(source_task_id),
23246
24669
  target_task_id: resolveId(target_task_id),
@@ -23261,7 +24684,7 @@ function registerTaskRelTools(server, ctx) {
23261
24684
  relationship_type: exports_external.enum(["related_to", "conflicts_with", "similar_to", "duplicates", "supersedes", "modifies_same_file"]).optional()
23262
24685
  }, async ({ id, source_task_id, target_task_id, relationship_type }) => {
23263
24686
  try {
23264
- const { removeTaskRelationship: removeTaskRelationship2, removeTaskRelationshipByPair: removeTaskRelationshipByPair2 } = (()=>{throw new Error("Cannot require module "+"../db/task-relationships.js");})();
24687
+ const { removeTaskRelationship: removeTaskRelationship2, removeTaskRelationshipByPair: removeTaskRelationshipByPair2 } = (init_task_relationships(), __toCommonJS(exports_task_relationships));
23265
24688
  let removed = false;
23266
24689
  if (id) {
23267
24690
  removed = removeTaskRelationship2(id);
@@ -23282,7 +24705,7 @@ function registerTaskRelTools(server, ctx) {
23282
24705
  relationship_type: exports_external.enum(["related_to", "conflicts_with", "similar_to", "duplicates", "supersedes", "modifies_same_file"]).optional()
23283
24706
  }, async ({ task_id, relationship_type }) => {
23284
24707
  try {
23285
- const { getTaskRelationships: getTaskRelationships2 } = (()=>{throw new Error("Cannot require module "+"../db/task-relationships.js");})();
24708
+ const { getTaskRelationships: getTaskRelationships2 } = (init_task_relationships(), __toCommonJS(exports_task_relationships));
23286
24709
  const rels = getTaskRelationships2(resolveId(task_id), relationship_type);
23287
24710
  if (rels.length === 0)
23288
24711
  return { content: [{ type: "text", text: "No relationships found." }] };
@@ -23299,7 +24722,7 @@ function registerTaskRelTools(server, ctx) {
23299
24722
  task_id: exports_external.string().describe("Task ID to detect file relationships for")
23300
24723
  }, async ({ task_id }) => {
23301
24724
  try {
23302
- const { autoDetectFileRelationships: autoDetectFileRelationships2 } = (()=>{throw new Error("Cannot require module "+"../db/task-relationships.js");})();
24725
+ const { autoDetectFileRelationships: autoDetectFileRelationships2 } = (init_task_relationships(), __toCommonJS(exports_task_relationships));
23303
24726
  const created = autoDetectFileRelationships2(resolveId(task_id));
23304
24727
  return { content: [{ type: "text", text: created.length > 0 ? `Created ${created.length} file relationship(s).` : "No file overlaps detected." }] };
23305
24728
  } catch (e) {
@@ -23310,8 +24733,8 @@ function registerTaskRelTools(server, ctx) {
23310
24733
  if (shouldRegisterTool("sync_kg")) {
23311
24734
  server.tool("sync_kg", "Sync all existing relationships into the knowledge graph edges table. Idempotent.", {}, async () => {
23312
24735
  try {
23313
- const { syncKgEdges } = (()=>{throw new Error("Cannot require module "+"../db/kg.js");})();
23314
- const result = syncKgEdges();
24736
+ const { syncKgEdges: syncKgEdges2 } = (init_kg(), __toCommonJS(exports_kg));
24737
+ const result = syncKgEdges2();
23315
24738
  return { content: [{ type: "text", text: `Knowledge graph synced: ${result.synced} edge(s) processed.` }] };
23316
24739
  } catch (e) {
23317
24740
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
@@ -23327,8 +24750,8 @@ function registerTaskRelTools(server, ctx) {
23327
24750
  limit: exports_external.number().optional().describe("Max results")
23328
24751
  }, async ({ entity_id, relation_type, entity_type, direction, limit }) => {
23329
24752
  try {
23330
- const { getRelated } = (()=>{throw new Error("Cannot require module "+"../db/kg.js");})();
23331
- const edges = getRelated(entity_id, { relation_type, entity_type, direction, limit });
24753
+ const { getRelated: getRelated2 } = (init_kg(), __toCommonJS(exports_kg));
24754
+ const edges = getRelated2(entity_id, { relation_type, entity_type, direction, limit });
23332
24755
  if (edges.length === 0)
23333
24756
  return { content: [{ type: "text", text: "No related entities found." }] };
23334
24757
  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})`);
@@ -23347,8 +24770,8 @@ function registerTaskRelTools(server, ctx) {
23347
24770
  relation_types: exports_external.array(exports_external.string()).optional().describe("Filter by relation types")
23348
24771
  }, async ({ source_id, target_id, max_depth, relation_types }) => {
23349
24772
  try {
23350
- const { findPath } = (()=>{throw new Error("Cannot require module "+"../db/kg.js");})();
23351
- const paths = findPath(source_id, target_id, { max_depth, relation_types });
24773
+ const { findPath: findPath2 } = (init_kg(), __toCommonJS(exports_kg));
24774
+ const paths = findPath2(source_id, target_id, { max_depth, relation_types });
23352
24775
  if (paths.length === 0)
23353
24776
  return { content: [{ type: "text", text: "No path found." }] };
23354
24777
  const lines = paths.map((path, i) => {
@@ -23372,8 +24795,8 @@ function registerTaskRelTools(server, ctx) {
23372
24795
  relation_types: exports_external.array(exports_external.string()).optional().describe("Filter by relation types")
23373
24796
  }, async ({ entity_id, max_depth, relation_types }) => {
23374
24797
  try {
23375
- const { getImpactAnalysis } = (()=>{throw new Error("Cannot require module "+"../db/kg.js");})();
23376
- const impact = getImpactAnalysis(entity_id, { max_depth, relation_types });
24798
+ const { getImpactAnalysis: getImpactAnalysis2 } = (init_kg(), __toCommonJS(exports_kg));
24799
+ const impact = getImpactAnalysis2(entity_id, { max_depth, relation_types });
23377
24800
  if (impact.length === 0)
23378
24801
  return { content: [{ type: "text", text: "No downstream impact detected." }] };
23379
24802
  const byDepth = new Map;
@@ -23403,8 +24826,8 @@ Depth ${depth}:`);
23403
24826
  limit: exports_external.number().optional().describe("Max results (default: 20)")
23404
24827
  }, async ({ project_id, limit }) => {
23405
24828
  try {
23406
- const { getCriticalPath } = (()=>{throw new Error("Cannot require module "+"../db/kg.js");})();
23407
- const result = getCriticalPath({ project_id: project_id ? resolveId(project_id, "projects") : undefined, limit });
24829
+ const { getCriticalPath: getCriticalPath2 } = (init_kg(), __toCommonJS(exports_kg));
24830
+ const result = getCriticalPath2({ project_id: project_id ? resolveId(project_id, "projects") : undefined, limit });
23408
24831
  if (result.length === 0)
23409
24832
  return { content: [{ type: "text", text: "No critical path data. Run sync_kg first to populate the knowledge graph." }] };
23410
24833
  const lines = result.map((r, i) => `${i + 1}. ${r.task_id.slice(0, 8)} blocks ${r.blocking_count} task(s), max depth ${r.depth}`);
@@ -23424,13 +24847,13 @@ ${lines.join(`
23424
24847
  is_lead: exports_external.coerce.boolean().optional().describe("Whether this agent is the project lead for this role")
23425
24848
  }, async ({ project_id, agent_name, role, is_lead }) => {
23426
24849
  try {
23427
- const { setProjectAgentRole } = (()=>{throw new Error("Cannot require module "+"../db/project-agent-roles.js");})();
23428
- const { getAgentByName: getAgentByName2 } = (()=>{throw new Error("Cannot require module "+"../db/agents.js");})();
24850
+ const { setProjectAgentRole: setProjectAgentRole2 } = (init_project_agent_roles(), __toCommonJS(exports_project_agent_roles));
24851
+ const { getAgentByName: getAgentByName2 } = (init_agents(), __toCommonJS(exports_agents));
23429
24852
  const agent = getAgentByName2(agent_name);
23430
24853
  if (!agent)
23431
24854
  return { content: [{ type: "text", text: `Agent not found: ${agent_name}` }], isError: true };
23432
24855
  const pid = resolveId(project_id, "projects");
23433
- const result = setProjectAgentRole(pid, agent.id, role, is_lead ?? false);
24856
+ const result = setProjectAgentRole2(pid, agent.id, role, is_lead ?? false);
23434
24857
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
23435
24858
  } catch (e) {
23436
24859
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
@@ -23460,9 +24883,9 @@ ${lines.join(`
23460
24883
  }).join(`
23461
24884
  `);
23462
24885
  };
23463
- const { getProjectOrgChart } = (()=>{throw new Error("Cannot require module "+"../db/project-agent-roles.js");})();
24886
+ const { getProjectOrgChart: getProjectOrgChart2 } = (init_project_agent_roles(), __toCommonJS(exports_project_agent_roles));
23464
24887
  const pid = resolveId(project_id, "projects");
23465
- const tree = getProjectOrgChart(pid, { filter_to_project });
24888
+ const tree = getProjectOrgChart2(pid, { filter_to_project });
23466
24889
  if (format === "json") {
23467
24890
  return { content: [{ type: "text", text: JSON.stringify(tree, null, 2) }] };
23468
24891
  }
@@ -23480,9 +24903,9 @@ ${lines.join(`
23480
24903
  project_id: exports_external.string().describe("Project ID")
23481
24904
  }, async ({ project_id }) => {
23482
24905
  try {
23483
- const { listProjectAgentRoles } = (()=>{throw new Error("Cannot require module "+"../db/project-agent-roles.js");})();
24906
+ const { listProjectAgentRoles: listProjectAgentRoles2 } = (init_project_agent_roles(), __toCommonJS(exports_project_agent_roles));
23484
24907
  const pid = resolveId(project_id, "projects");
23485
- const roles = listProjectAgentRoles(pid);
24908
+ const roles = listProjectAgentRoles2(pid);
23486
24909
  return { content: [{ type: "text", text: JSON.stringify(roles, null, 2) }] };
23487
24910
  } catch (e) {
23488
24911
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
@@ -23496,7 +24919,7 @@ ${lines.join(`
23496
24919
  limit: exports_external.number().optional().describe("Max results")
23497
24920
  }, async ({ capabilities, min_score, limit }) => {
23498
24921
  try {
23499
- const { getCapableAgents: getCapableAgents2 } = (()=>{throw new Error("Cannot require module "+"../db/agents.js");})();
24922
+ const { getCapableAgents: getCapableAgents2 } = (init_agents(), __toCommonJS(exports_agents));
23500
24923
  const results = getCapableAgents2(capabilities, { min_score, limit });
23501
24924
  if (results.length === 0)
23502
24925
  return { content: [{ type: "text", text: "No agents match the given capabilities." }] };
@@ -23515,8 +24938,8 @@ ${lines.join(`
23515
24938
  project_id: exports_external.string().optional().describe("Filter by project")
23516
24939
  }, async ({ stuck_minutes, confidence_threshold, project_id }) => {
23517
24940
  try {
23518
- const { patrolTasks } = (()=>{throw new Error("Cannot require module "+"../db/patrol.js");})();
23519
- const result = patrolTasks({
24941
+ const { patrolTasks: patrolTasks2 } = (init_patrol(), __toCommonJS(exports_patrol));
24942
+ const result = patrolTasks2({
23520
24943
  stuck_minutes,
23521
24944
  confidence_threshold,
23522
24945
  project_id: project_id ? resolveId(project_id, "projects") : undefined
@@ -23542,8 +24965,8 @@ ${lines.join(`
23542
24965
  limit: exports_external.number().optional().describe("Max results (default: all)")
23543
24966
  }, async ({ project_id, limit }) => {
23544
24967
  try {
23545
- const { getReviewQueue } = (()=>{throw new Error("Cannot require module "+"../db/patrol.js");})();
23546
- const tasks = getReviewQueue({
24968
+ const { getReviewQueue: getReviewQueue2 } = (init_patrol(), __toCommonJS(exports_patrol));
24969
+ const tasks = getReviewQueue2({
23547
24970
  project_id: project_id ? resolveId(project_id, "projects") : undefined,
23548
24971
  limit
23549
24972
  });
@@ -23569,8 +24992,8 @@ ${lines.join(`
23569
24992
  reviewer_id: exports_external.string().optional().describe("Agent ID of reviewer")
23570
24993
  }, async ({ task_id, score, reviewer_id }) => {
23571
24994
  try {
23572
- const { scoreTask } = (()=>{throw new Error("Cannot require module "+"../db/agent-metrics.js");})();
23573
- scoreTask(resolveId(task_id), score, reviewer_id);
24995
+ const { scoreTask: scoreTask2 } = (init_agent_metrics(), __toCommonJS(exports_agent_metrics));
24996
+ scoreTask2(resolveId(task_id), score, reviewer_id);
23574
24997
  return { content: [{ type: "text", text: `Task ${task_id.slice(0, 8)} scored: ${score}` }] };
23575
24998
  } catch (e) {
23576
24999
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
@@ -23587,7 +25010,7 @@ ${lines.join(`
23587
25010
  notes: exports_external.string().optional().describe("Notes about what was done")
23588
25011
  }, async ({ task_id, minutes, agent_id, started_at, ended_at, notes }) => {
23589
25012
  try {
23590
- const { logTime: logTime2 } = (()=>{throw new Error("Cannot require module "+"../db/tasks.js");})();
25013
+ const { logTime: logTime2 } = (init_tasks(), __toCommonJS(exports_tasks));
23591
25014
  logTime2({ task_id: resolveId(task_id), minutes, agent_id, started_at, ended_at, notes });
23592
25015
  return { content: [{ type: "text", text: `Logged ${minutes} min on task ${task_id.slice(0, 8)}` }] };
23593
25016
  } catch (e) {
@@ -23602,7 +25025,7 @@ ${lines.join(`
23602
25025
  since: exports_external.string().optional().describe("ISO date \u2014 only tasks completed after this date")
23603
25026
  }, async ({ project_id, agent_id, since }) => {
23604
25027
  try {
23605
- const { getTimeReport: getTimeReport2 } = (()=>{throw new Error("Cannot require module "+"../db/tasks.js");})();
25028
+ const { getTimeReport: getTimeReport2 } = (init_tasks(), __toCommonJS(exports_tasks));
23606
25029
  const report = getTimeReport2({ project_id: project_id ? resolveId(project_id, "projects") : undefined, agent_id, since });
23607
25030
  if (report.length === 0)
23608
25031
  return { content: [{ type: "text", text: "No completed tasks found." }] };
@@ -23626,7 +25049,7 @@ ${lines.join(`
23626
25049
  agent_id: exports_external.string().optional().describe("Agent subscribing (defaults to context agent)")
23627
25050
  }, async ({ task_id, agent_id }) => {
23628
25051
  try {
23629
- const { watchTask: watchTask2 } = (()=>{throw new Error("Cannot require module "+"../db/tasks.js");})();
25052
+ const { watchTask: watchTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
23630
25053
  watchTask2(resolveId(task_id), agent_id || "");
23631
25054
  return { content: [{ type: "text", text: `Now watching task ${task_id.slice(0, 8)}` }] };
23632
25055
  } catch (e) {
@@ -23640,7 +25063,7 @@ ${lines.join(`
23640
25063
  agent_id: exports_external.string().optional().describe("Agent unsubscribing (defaults to context agent)")
23641
25064
  }, async ({ task_id, agent_id }) => {
23642
25065
  try {
23643
- const { unwatchTask: unwatchTask2 } = (()=>{throw new Error("Cannot require module "+"../db/tasks.js");})();
25066
+ const { unwatchTask: unwatchTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
23644
25067
  unwatchTask2(resolveId(task_id), agent_id || "");
23645
25068
  return { content: [{ type: "text", text: `Stopped watching task ${task_id.slice(0, 8)}` }] };
23646
25069
  } catch (e) {
@@ -23653,7 +25076,7 @@ ${lines.join(`
23653
25076
  task_id: exports_external.string().describe("Task ID")
23654
25077
  }, async ({ task_id }) => {
23655
25078
  try {
23656
- const { getTaskWatchers: getTaskWatchers2 } = (()=>{throw new Error("Cannot require module "+"../db/tasks.js");})();
25079
+ const { getTaskWatchers: getTaskWatchers2 } = (init_tasks(), __toCommonJS(exports_tasks));
23657
25080
  const watchers = getTaskWatchers2(resolveId(task_id));
23658
25081
  if (watchers.length === 0)
23659
25082
  return { content: [{ type: "text", text: "No watchers." }] };
@@ -23672,15 +25095,15 @@ ${lines.join(`
23672
25095
  agent_id: exports_external.string().optional().describe("Filter by assignee")
23673
25096
  }, async ({ project_id, plan_id, task_list_id, since, agent_id }) => {
23674
25097
  try {
23675
- const { listTasks: listTasks3 } = (()=>{throw new Error("Cannot require module "+"../db/tasks.js");})();
23676
- const { patrolTasks } = (()=>{throw new Error("Cannot require module "+"../db/patrol.js");})();
25098
+ const { listTasks: listTasks3 } = (init_tasks(), __toCommonJS(exports_tasks));
25099
+ const { patrolTasks: patrolTasks2 } = (init_patrol(), __toCommonJS(exports_patrol));
23677
25100
  const completed = listTasks3({ status: "completed", project_id, plan_id, task_list_id, assigned_to: agent_id, limit: 500 }, undefined);
23678
25101
  const filtered = since ? completed.filter((t) => t.completed_at && t.completed_at >= since) : completed;
23679
25102
  const total = filtered.length;
23680
25103
  const lowConf = filtered.filter((t) => t.confidence != null && t.confidence < 0.7).length;
23681
25104
  const withEstimate = filtered.filter((t) => t.estimated_minutes != null && t.actual_minutes != null);
23682
25105
  const avgDiff = withEstimate.length > 0 ? withEstimate.reduce((acc, t) => acc + (t.actual_minutes - t.estimated_minutes), 0) / withEstimate.length : 0;
23683
- const patrolResult = patrolTasks({ project_id: project_id ? resolveId(project_id, "projects") : undefined });
25106
+ const patrolResult = patrolTasks2({ project_id: project_id ? resolveId(project_id, "projects") : undefined });
23684
25107
  const stuck = patrolResult.issues.filter((i) => i.type === "stuck").length;
23685
25108
  const lines = [
23686
25109
  `Retro (${total} completed tasks${since ? ` since ${since}` : ""})`,
@@ -23701,7 +25124,7 @@ ${lines.join(`
23701
25124
  limit: exports_external.number().optional().describe("Max results (default: 20)")
23702
25125
  }, async ({ project_id, limit }) => {
23703
25126
  try {
23704
- const { listTasks: listTasks3 } = (()=>{throw new Error("Cannot require module "+"../db/tasks.js");})();
25127
+ const { listTasks: listTasks3 } = (init_tasks(), __toCommonJS(exports_tasks));
23705
25128
  const tasks = listTasks3({ status: "pending", project_id, assigned_to: "", limit: limit || 20 }, undefined);
23706
25129
  if (tasks.length === 0)
23707
25130
  return { content: [{ type: "text", text: "Inbox is empty." }] };
@@ -23720,8 +25143,8 @@ ${lines.join(`
23720
25143
  project_id: exports_external.string().optional().describe("Filter by project")
23721
25144
  }, async ({ agent_id, project_id }) => {
23722
25145
  try {
23723
- const { getAgentMetrics } = (()=>{throw new Error("Cannot require module "+"../db/agent-metrics.js");})();
23724
- const metrics = getAgentMetrics(agent_id, {
25146
+ const { getAgentMetrics: getAgentMetrics2 } = (init_agent_metrics(), __toCommonJS(exports_agent_metrics));
25147
+ const metrics = getAgentMetrics2(agent_id, {
23725
25148
  project_id: project_id ? resolveId(project_id, "projects") : undefined
23726
25149
  });
23727
25150
  if (!metrics)
@@ -23748,8 +25171,8 @@ ${lines.join(`
23748
25171
  limit: exports_external.number().optional().describe("Max entries (default: 20)")
23749
25172
  }, async ({ project_id, limit }) => {
23750
25173
  try {
23751
- const { getLeaderboard } = (()=>{throw new Error("Cannot require module "+"../db/agent-metrics.js");})();
23752
- const entries = getLeaderboard({
25174
+ const { getLeaderboard: getLeaderboard2 } = (init_agent_metrics(), __toCommonJS(exports_agent_metrics));
25175
+ const entries = getLeaderboard2({
23753
25176
  project_id: project_id ? resolveId(project_id, "projects") : undefined,
23754
25177
  limit
23755
25178
  });
@@ -23999,15 +25422,373 @@ ${lines.join(`
23999
25422
  }
24000
25423
  }
24001
25424
 
25425
+ // src/mcp/tools/agents.ts
25426
+ init_agents();
25427
+ init_config2();
25428
+ init_database();
25429
+ function registerAgentTools(server, { shouldRegisterTool, resolveId, formatError, agentFocusMap, getAgentFocus }) {
25430
+ if (shouldRegisterTool("set_focus")) {
25431
+ server.tool("set_focus", "Focus this agent on a project. All list/search/status tools will default to this project.", {
25432
+ agent_id: exports_external.string().describe("Agent ID or name"),
25433
+ project_id: exports_external.string().optional().describe("Project to focus on. Omit to clear."),
25434
+ task_list_id: exports_external.string().optional().describe("Task list to focus on")
25435
+ }, async ({ agent_id, project_id, task_list_id }) => {
25436
+ try {
25437
+ const resolvedProject = project_id ? resolveId(project_id, "projects") : undefined;
25438
+ const focus = { agent_id, project_id: resolvedProject, task_list_id };
25439
+ agentFocusMap.set(agent_id, focus);
25440
+ try {
25441
+ const agent = getAgentByName(agent_id) || getAgent(agent_id);
25442
+ if (agent) {
25443
+ const db = getDatabase();
25444
+ db.run("UPDATE agents SET active_project_id = ? WHERE id = ?", [resolvedProject || null, agent.id]);
25445
+ }
25446
+ } catch {}
25447
+ const projectName = resolvedProject ? ` (${resolvedProject.slice(0, 8)})` : "";
25448
+ return { content: [{ type: "text", text: `Focused on project${projectName}. Read tools will default to this scope. Pass explicit project_id to override.` }] };
25449
+ } catch (e) {
25450
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
25451
+ }
25452
+ });
25453
+ }
25454
+ if (shouldRegisterTool("get_focus")) {
25455
+ server.tool("get_focus", "Get the current focus for an agent.", { agent_id: exports_external.string().describe("Agent ID or name") }, async ({ agent_id }) => {
25456
+ const focus = getAgentFocus(agent_id);
25457
+ if (!focus?.project_id) {
25458
+ return { content: [{ type: "text", text: "No focus set. Showing all projects." }] };
25459
+ }
25460
+ return { content: [{ type: "text", text: `Focused on project: ${focus.project_id}${focus.task_list_id ? `, task list: ${focus.task_list_id}` : ""}` }] };
25461
+ });
25462
+ }
25463
+ if (shouldRegisterTool("unfocus")) {
25464
+ server.tool("unfocus", "Clear focus \u2014 show all projects and tasks.", { agent_id: exports_external.string().describe("Agent ID or name") }, async ({ agent_id }) => {
25465
+ agentFocusMap.delete(agent_id);
25466
+ try {
25467
+ const agent = getAgentByName(agent_id) || getAgent(agent_id);
25468
+ if (agent) {
25469
+ const db = getDatabase();
25470
+ db.run("UPDATE agents SET active_project_id = NULL WHERE id = ?", [agent.id]);
25471
+ }
25472
+ } catch {}
25473
+ return { content: [{ type: "text", text: "Focus cleared. Showing all projects." }] };
25474
+ });
25475
+ }
25476
+ if (shouldRegisterTool("register_agent")) {
25477
+ server.tool("register_agent", "Register an agent. Any name is allowed \u2014 the configured pool is advisory, not enforced. Returns a conflict error if the name is held by a recently-active agent.", {
25478
+ name: exports_external.string().describe("Agent name \u2014 any name is allowed. Use suggest_agent_name to see pool suggestions and avoid conflicts."),
25479
+ description: exports_external.string().optional(),
25480
+ role: exports_external.string().optional(),
25481
+ title: exports_external.string().optional(),
25482
+ level: exports_external.string().optional(),
25483
+ permissions: exports_external.array(exports_external.string()).optional(),
25484
+ capabilities: exports_external.array(exports_external.string()).optional().describe("Agent capabilities/skills for task routing (e.g. ['typescript', 'testing', 'devops'])"),
25485
+ session_id: exports_external.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."),
25486
+ working_dir: exports_external.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"),
25487
+ force: exports_external.boolean().optional().describe("Force takeover of an active agent's name. Use with caution \u2014 only when you know the previous session is dead.")
25488
+ }, async ({ name, description, role, title, level, permissions, capabilities, session_id, working_dir, force }) => {
25489
+ try {
25490
+ const pool = getAgentPoolForProject(working_dir);
25491
+ const result = registerAgent({ name, description, role, title, level, permissions, capabilities, session_id, working_dir, force, pool: pool || undefined });
25492
+ if (isAgentConflict(result)) {
25493
+ const suggestLine = result.suggestions && result.suggestions.length > 0 ? `
25494
+ Available names: ${result.suggestions.join(", ")}` : "";
25495
+ const hint = `CONFLICT: ${result.message}${suggestLine}`;
25496
+ return {
25497
+ content: [{ type: "text", text: hint }],
25498
+ isError: true
25499
+ };
25500
+ }
25501
+ const agent = result;
25502
+ const poolLine = pool ? `
25503
+ Pool: [${pool.join(", ")}]` : "";
25504
+ return {
25505
+ content: [{
25506
+ type: "text",
25507
+ text: `Agent registered:
25508
+ ID: ${agent.id}
25509
+ Name: ${agent.name}${agent.description ? `
25510
+ Description: ${agent.description}` : ""}
25511
+ Session: ${agent.session_id ?? "unbound"}${poolLine}
25512
+ Created: ${agent.created_at}
25513
+ Last seen: ${agent.last_seen_at}`
25514
+ }]
25515
+ };
25516
+ } catch (e) {
25517
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
25518
+ }
25519
+ });
25520
+ }
25521
+ if (shouldRegisterTool("suggest_agent_name")) {
25522
+ server.tool("suggest_agent_name", "Get available agent names for a project. Shows configured pool, active agents, and suggestions. If no pool is configured, any name is allowed.", {
25523
+ working_dir: exports_external.string().optional().describe("Your working directory \u2014 used to look up the project's allowed name pool from config")
25524
+ }, async ({ working_dir }) => {
25525
+ try {
25526
+ const pool = getAgentPoolForProject(working_dir);
25527
+ const cutoff = new Date(Date.now() - 30 * 60 * 1000).toISOString();
25528
+ const allActive = listAgents().filter((a) => a.last_seen_at > cutoff);
25529
+ if (!pool) {
25530
+ const lines2 = [
25531
+ "No agent pool configured \u2014 any name is allowed.",
25532
+ 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.",
25533
+ `
25534
+ To restrict names, configure agent_pool or project_pools in ~/.hasna/todos/config.json`
25535
+ ];
25536
+ return { content: [{ type: "text", text: lines2.join(`
25537
+ `) }] };
25538
+ }
25539
+ const available = getAvailableNamesFromPool(pool, getDatabase());
25540
+ const activeInPool = allActive.filter((a) => pool.map((n) => n.toLowerCase()).includes(a.name));
25541
+ const lines = [
25542
+ `Project pool: ${pool.join(", ")}`,
25543
+ `Available now (${available.length}): ${available.length > 0 ? available.join(", ") : "none \u2014 all names in use"}`,
25544
+ 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",
25545
+ available.length > 0 ? `
25546
+ Suggested: ${available[0]}` : `
25547
+ No names available. Wait for an active agent to go stale (30min timeout).`
25548
+ ];
25549
+ return { content: [{ type: "text", text: lines.join(`
25550
+ `) }] };
25551
+ } catch (e) {
25552
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
25553
+ }
25554
+ });
25555
+ }
25556
+ if (shouldRegisterTool("list_agents")) {
25557
+ server.tool("list_agents", "List all registered agents. By default shows only active agents \u2014 set include_archived to see archived ones too.", {
25558
+ include_archived: exports_external.boolean().optional().describe("Include archived agents in the list (default: false)")
25559
+ }, async ({ include_archived }) => {
25560
+ try {
25561
+ const agents = listAgents({ include_archived: include_archived ?? false });
25562
+ if (agents.length === 0) {
25563
+ return { content: [{ type: "text", text: "No agents registered." }] };
25564
+ }
25565
+ const text = agents.map((a) => {
25566
+ const statusTag = a.status === "archived" ? " [archived]" : "";
25567
+ return `${a.id} | ${a.name}${statusTag}${a.description ? ` - ${a.description}` : ""} (last seen: ${a.last_seen_at})`;
25568
+ }).join(`
25569
+ `);
25570
+ return { content: [{ type: "text", text: `${agents.length} agent(s):
25571
+ ${text}` }] };
25572
+ } catch (e) {
25573
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
25574
+ }
25575
+ });
25576
+ }
25577
+ if (shouldRegisterTool("get_agent")) {
25578
+ server.tool("get_agent", "Get agent details by ID or name. Provide one of id or name.", {
25579
+ agent_id: exports_external.string().optional(),
25580
+ id: exports_external.string().optional(),
25581
+ name: exports_external.string().optional()
25582
+ }, async ({ agent_id, id, name }) => {
25583
+ try {
25584
+ const identifier = agent_id || id || name;
25585
+ if (!identifier) {
25586
+ return { content: [{ type: "text", text: "Provide agent_id, id, or name." }], isError: true };
25587
+ }
25588
+ const agent = getAgent(identifier) || getAgentByName(identifier);
25589
+ if (!agent) {
25590
+ return { content: [{ type: "text", text: `Agent not found: ${identifier}` }], isError: true };
25591
+ }
25592
+ const parts = [
25593
+ `ID: ${agent.id}`,
25594
+ `Name: ${agent.name}`
25595
+ ];
25596
+ if (agent.description)
25597
+ parts.push(`Description: ${agent.description}`);
25598
+ if (Object.keys(agent.metadata).length > 0)
25599
+ parts.push(`Metadata: ${JSON.stringify(agent.metadata)}`);
25600
+ parts.push(`Created: ${agent.created_at}`);
25601
+ parts.push(`Last seen: ${agent.last_seen_at}`);
25602
+ return { content: [{ type: "text", text: parts.join(`
25603
+ `) }] };
25604
+ } catch (e) {
25605
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
25606
+ }
25607
+ });
25608
+ }
25609
+ if (shouldRegisterTool("rename_agent")) {
25610
+ server.tool("rename_agent", "Rename an agent. Resolve by id or current name.", {
25611
+ id: exports_external.string().optional(),
25612
+ name: exports_external.string().optional(),
25613
+ new_name: exports_external.string()
25614
+ }, async ({ id, name, new_name }) => {
25615
+ try {
25616
+ if (!id && !name) {
25617
+ return { content: [{ type: "text", text: "Provide either id or name." }], isError: true };
25618
+ }
25619
+ const agent = id ? getAgent(id) : getAgentByName(name);
25620
+ if (!agent) {
25621
+ return { content: [{ type: "text", text: `Agent not found: ${id || name}` }], isError: true };
25622
+ }
25623
+ const oldName = agent.name;
25624
+ const updated = updateAgent(agent.id, { name: new_name });
25625
+ const db = getDatabase();
25626
+ const tasksResult = db.run("UPDATE tasks SET assigned_to = ? WHERE assigned_to = ?", [new_name, oldName]);
25627
+ const taskNote = tasksResult.changes > 0 ? `
25628
+ Updated assigned_to on ${tasksResult.changes} task(s).` : "";
25629
+ return {
25630
+ content: [{
25631
+ type: "text",
25632
+ text: `Agent renamed: ${oldName} -> ${updated.name}
25633
+ ID: ${updated.id}${taskNote}`
25634
+ }]
25635
+ };
25636
+ } catch (e) {
25637
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
25638
+ }
25639
+ });
25640
+ }
25641
+ if (shouldRegisterTool("update_agent")) {
25642
+ server.tool("update_agent", "Update an agent's description, role, title, or other metadata. Resolve by id or name.", {
25643
+ agent_id: exports_external.string().optional(),
25644
+ id: exports_external.string().optional(),
25645
+ name: exports_external.string().optional(),
25646
+ description: exports_external.string().optional(),
25647
+ role: exports_external.string().optional(),
25648
+ title: exports_external.string().optional(),
25649
+ level: exports_external.string().optional(),
25650
+ capabilities: exports_external.array(exports_external.string()).optional(),
25651
+ permissions: exports_external.array(exports_external.string()).optional(),
25652
+ metadata: exports_external.record(exports_external.unknown()).optional()
25653
+ }, async ({ agent_id, id, name, ...updates }) => {
25654
+ try {
25655
+ const identifier = agent_id || id || name;
25656
+ if (!identifier) {
25657
+ return { content: [{ type: "text", text: "Provide agent_id, id, or name." }], isError: true };
25658
+ }
25659
+ const agent = getAgent(identifier) || getAgentByName(identifier);
25660
+ if (!agent) {
25661
+ return { content: [{ type: "text", text: `Agent not found: ${identifier}` }], isError: true };
25662
+ }
25663
+ const updated = updateAgent(agent.id, updates);
25664
+ return { content: [{ type: "text", text: `Agent updated: ${updated.name} (${updated.id.slice(0, 8)})` }] };
25665
+ } catch (e) {
25666
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
25667
+ }
25668
+ });
25669
+ }
25670
+ if (shouldRegisterTool("delete_agent")) {
25671
+ 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.", {
25672
+ agent_id: exports_external.string().optional(),
25673
+ id: exports_external.string().optional(),
25674
+ name: exports_external.string().optional()
25675
+ }, async ({ agent_id, id, name }) => {
25676
+ try {
25677
+ const identifier = agent_id || id || name;
25678
+ if (!identifier) {
25679
+ return { content: [{ type: "text", text: "Provide agent_id, id, or name." }], isError: true };
25680
+ }
25681
+ const agent = getAgent(identifier) || getAgentByName(identifier);
25682
+ if (!agent) {
25683
+ return { content: [{ type: "text", text: `Agent not found: ${identifier}` }], isError: true };
25684
+ }
25685
+ const archived = archiveAgent(agent.id);
25686
+ return {
25687
+ content: [{
25688
+ type: "text",
25689
+ text: archived ? `Agent archived: ${agent.name} (${agent.id}). Use unarchive_agent to restore.` : `Failed to archive agent: ${agent.name}`
25690
+ }],
25691
+ isError: !archived
25692
+ };
25693
+ } catch (e) {
25694
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
25695
+ }
25696
+ });
25697
+ }
25698
+ if (shouldRegisterTool("unarchive_agent")) {
25699
+ server.tool("unarchive_agent", "Restore an archived agent back to active status. Resolve by id or name.", {
25700
+ agent_id: exports_external.string().optional(),
25701
+ id: exports_external.string().optional(),
25702
+ name: exports_external.string().optional()
25703
+ }, async ({ agent_id, id, name }) => {
25704
+ try {
25705
+ const identifier = agent_id || id || name;
25706
+ if (!identifier) {
25707
+ return { content: [{ type: "text", text: "Provide agent_id, id, or name." }], isError: true };
25708
+ }
25709
+ const agent = getAgent(identifier) || getAgentByName(identifier);
25710
+ if (!agent) {
25711
+ return { content: [{ type: "text", text: `Agent not found: ${identifier}` }], isError: true };
25712
+ }
25713
+ if (agent.status === "active") {
25714
+ return { content: [{ type: "text", text: `Agent ${agent.name} is already active.` }] };
25715
+ }
25716
+ const restored = unarchiveAgent(agent.id);
25717
+ return {
25718
+ content: [{
25719
+ type: "text",
25720
+ text: restored ? `Agent restored: ${agent.name} (${agent.id}) is now active.` : `Failed to restore agent: ${agent.name}`
25721
+ }],
25722
+ isError: !restored
25723
+ };
25724
+ } catch (e) {
25725
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
25726
+ }
25727
+ });
25728
+ }
25729
+ if (shouldRegisterTool("heartbeat")) {
25730
+ 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.", {
25731
+ agent_id: exports_external.string().describe("Your agent ID or name.")
25732
+ }, async ({ agent_id }) => {
25733
+ try {
25734
+ const agent = getAgent(agent_id) || getAgentByName(agent_id);
25735
+ if (!agent) {
25736
+ return { content: [{ type: "text", text: `Agent not found: ${agent_id}` }], isError: true };
25737
+ }
25738
+ updateAgentActivity(agent.id);
25739
+ return {
25740
+ content: [{
25741
+ type: "text",
25742
+ text: `Heartbeat: ${agent.name} (${agent.id}) \u2014 last_seen_at updated to ${new Date().toISOString()}`
25743
+ }]
25744
+ };
25745
+ } catch (e) {
25746
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
25747
+ }
25748
+ });
25749
+ }
25750
+ if (shouldRegisterTool("release_agent")) {
25751
+ 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.", {
25752
+ agent_id: exports_external.string().describe("Your agent ID or name."),
25753
+ session_id: exports_external.string().optional().describe("Your session ID \u2014 if provided, release only succeeds if it matches (prevents other sessions from releasing your agent).")
25754
+ }, async ({ agent_id, session_id }) => {
25755
+ try {
25756
+ const agent = getAgent(agent_id) || getAgentByName(agent_id);
25757
+ if (!agent) {
25758
+ return { content: [{ type: "text", text: `Agent not found: ${agent_id}` }], isError: true };
25759
+ }
25760
+ const released = releaseAgent(agent.id, session_id);
25761
+ if (!released) {
25762
+ return { content: [{ type: "text", text: `Release denied: session_id does not match agent's current session.` }], isError: true };
25763
+ }
25764
+ return {
25765
+ content: [{
25766
+ type: "text",
25767
+ text: `Agent released: ${agent.name} (${agent.id}) \u2014 session cleared, name is now available.`
25768
+ }]
25769
+ };
25770
+ } catch (e) {
25771
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
25772
+ }
25773
+ });
25774
+ }
25775
+ }
25776
+
24002
25777
  // src/mcp/index.ts
24003
25778
  function getMcpVersion() {
24004
25779
  try {
24005
- const __dir = dirname3(fileURLToPath(import.meta.url));
24006
- const pkgPath = join10(__dir, "..", "package.json");
24007
- return JSON.parse(readFileSync6(pkgPath, "utf-8")).version || "0.0.0";
25780
+ let dir = dirname3(fileURLToPath(import.meta.url));
25781
+ for (let i = 0;i < 4; i++) {
25782
+ const pkgPath = join10(dir, "package.json");
25783
+ if (existsSync10(pkgPath)) {
25784
+ return JSON.parse(readFileSync6(pkgPath, "utf-8")).version || "0.0.0";
25785
+ }
25786
+ dir = dirname3(dir);
25787
+ }
24008
25788
  } catch {
24009
25789
  return "0.0.0";
24010
25790
  }
25791
+ return "0.0.0";
24011
25792
  }
24012
25793
  var server = new McpServer({
24013
25794
  name: "todos",
@@ -24026,6 +25807,7 @@ var MINIMAL_TOOLS = new Set([
24026
25807
  "get_next_task",
24027
25808
  "bootstrap",
24028
25809
  "get_tasks_changed_since",
25810
+ "get_health",
24029
25811
  "heartbeat",
24030
25812
  "release_agent"
24031
25813
  ]);
@@ -24204,6 +25986,7 @@ registerTaskMetaTools(server, toolContext);
24204
25986
  registerTaskResources(server, toolContext);
24205
25987
  registerTaskRelTools(server, toolContext);
24206
25988
  registerCodeTools(server, toolContext);
25989
+ registerAgentTools(server, { ...toolContext, agentFocusMap });
24207
25990
  registerMachineTools(server, { shouldRegisterTool, formatError });
24208
25991
  registerDispatchTools(server, { shouldRegisterTool, resolveId, formatError });
24209
25992
  registerCloudSyncTools(server, { shouldRegisterTool, formatError });