@hasna/todos 0.11.33 → 0.11.35

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/README.md +12 -0
  2. package/dist/cli/commands/agent-commands.d.ts.map +1 -1
  3. package/dist/cli/commands/api-key-commands.d.ts +3 -0
  4. package/dist/cli/commands/api-key-commands.d.ts.map +1 -0
  5. package/dist/cli/commands/config-serve-commands.d.ts.map +1 -1
  6. package/dist/cli/index.js +2343 -607
  7. package/dist/db/agent-names.d.ts +23 -0
  8. package/dist/db/agent-names.d.ts.map +1 -0
  9. package/dist/db/agents.d.ts +2 -0
  10. package/dist/db/agents.d.ts.map +1 -1
  11. package/dist/db/api-keys.d.ts +28 -0
  12. package/dist/db/api-keys.d.ts.map +1 -0
  13. package/dist/db/comments.d.ts +3 -0
  14. package/dist/db/comments.d.ts.map +1 -1
  15. package/dist/db/migrations.d.ts.map +1 -1
  16. package/dist/db/schema.d.ts.map +1 -1
  17. package/dist/db/task-crud.d.ts.map +1 -1
  18. package/dist/db/task-lifecycle.d.ts +1 -0
  19. package/dist/db/task-lifecycle.d.ts.map +1 -1
  20. package/dist/db/task-relations.d.ts +24 -0
  21. package/dist/db/task-relations.d.ts.map +1 -1
  22. package/dist/db/tasks.d.ts +1 -1
  23. package/dist/db/tasks.d.ts.map +1 -1
  24. package/dist/index.d.ts +3 -1
  25. package/dist/index.d.ts.map +1 -1
  26. package/dist/index.js +400 -13
  27. package/dist/mcp/index.d.ts.map +1 -1
  28. package/dist/mcp/index.js +2585 -536
  29. package/dist/mcp/tools/agents.d.ts.map +1 -1
  30. package/dist/mcp/tools/task-adv-tools.d.ts.map +1 -1
  31. package/dist/mcp/tools/task-auto-tools.d.ts.map +1 -1
  32. package/dist/mcp/tools/task-crud.d.ts.map +1 -1
  33. package/dist/mcp/tools/task-meta-tools.d.ts.map +1 -1
  34. package/dist/mcp/tools/task-project-tools.d.ts.map +1 -1
  35. package/dist/mcp/tools/task-workflow-tools.d.ts.map +1 -1
  36. package/dist/server/index.js +352 -42
  37. package/dist/server/routes.d.ts.map +1 -1
  38. package/dist/server/serve.d.ts.map +1 -1
  39. package/dist/types/index.d.ts +21 -0
  40. package/dist/types/index.d.ts.map +1 -1
  41. package/package.json +1 -1
package/dist/mcp/index.js CHANGED
@@ -810,6 +810,42 @@ 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);
833
+ `,
834
+ `
835
+ CREATE TABLE IF NOT EXISTS api_keys (
836
+ id TEXT PRIMARY KEY,
837
+ name TEXT NOT NULL,
838
+ key_hash TEXT NOT NULL UNIQUE,
839
+ prefix TEXT NOT NULL UNIQUE,
840
+ permissions TEXT NOT NULL DEFAULT '["*"]',
841
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
842
+ last_used_at TEXT,
843
+ expires_at TEXT,
844
+ revoked_at TEXT
845
+ );
846
+ CREATE INDEX IF NOT EXISTS idx_api_keys_prefix ON api_keys(prefix);
847
+ CREATE INDEX IF NOT EXISTS idx_api_keys_active ON api_keys(revoked_at, expires_at);
848
+ INSERT OR IGNORE INTO _migrations (id) VALUES (50);
813
849
  `
814
850
  ];
815
851
  });
@@ -1208,6 +1244,20 @@ function ensureSchema(db) {
1208
1244
  ensureIndex("CREATE INDEX IF NOT EXISTS idx_cycles_dates ON cycles(start_date, end_date)");
1209
1245
  ensureColumn("tasks", "cycle_id", "TEXT REFERENCES cycles(id) ON DELETE SET NULL");
1210
1246
  ensureIndex("CREATE INDEX IF NOT EXISTS idx_tasks_cycle ON tasks(cycle_id) WHERE cycle_id IS NOT NULL");
1247
+ ensureTable("api_keys", `
1248
+ CREATE TABLE api_keys (
1249
+ id TEXT PRIMARY KEY,
1250
+ name TEXT NOT NULL,
1251
+ key_hash TEXT NOT NULL UNIQUE,
1252
+ prefix TEXT NOT NULL UNIQUE,
1253
+ permissions TEXT NOT NULL DEFAULT '["*"]',
1254
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
1255
+ last_used_at TEXT,
1256
+ expires_at TEXT,
1257
+ revoked_at TEXT
1258
+ )`);
1259
+ ensureIndex("CREATE INDEX IF NOT EXISTS idx_api_keys_prefix ON api_keys(prefix)");
1260
+ ensureIndex("CREATE INDEX IF NOT EXISTS idx_api_keys_active ON api_keys(revoked_at, expires_at)");
1211
1261
  }
1212
1262
  function backfillTaskTags(db) {
1213
1263
  try {
@@ -12635,14 +12685,224 @@ See https://www.postgresql.org/docs/current/libpq-ssl.html for libpq SSL mode de
12635
12685
  init_adapter();
12636
12686
  });
12637
12687
 
12688
+ // src/db/agent-names.ts
12689
+ function normalizeAgentNameInput(name) {
12690
+ return name.trim().toLowerCase();
12691
+ }
12692
+ function hasGeneratedNumericSuffix(name) {
12693
+ return NUMERIC_SUFFIX_RE.test(normalizeAgentNameInput(name));
12694
+ }
12695
+ function isGenericAgentName(name) {
12696
+ const normalized = normalizeAgentNameInput(name);
12697
+ if (RESERVED_GENERIC_NAMES.has(normalized))
12698
+ return true;
12699
+ for (const generic of RESERVED_GENERIC_NAMES) {
12700
+ if (normalized === `${generic}s`)
12701
+ return true;
12702
+ if (normalized.match(new RegExp(`^${generic}\\d+$`)))
12703
+ return true;
12704
+ if (normalized.match(new RegExp(`^${generic}[-_]\\d+$`)))
12705
+ return true;
12706
+ }
12707
+ return false;
12708
+ }
12709
+ function isBlockedAgentName(name) {
12710
+ const normalized = normalizeAgentNameInput(name);
12711
+ return isGenericAgentName(normalized) || hasGeneratedNumericSuffix(normalized) || !ONE_WORD_NAME_RE.test(normalized);
12712
+ }
12713
+ function suggestAgentNames(existingNames = []) {
12714
+ const existing = new Set([...existingNames].map(normalizeAgentNameInput));
12715
+ return PREFERRED_AGENT_NAMES.filter((name) => !existing.has(name));
12716
+ }
12717
+ function validateAgentName(name, existingNames = []) {
12718
+ const normalized = normalizeAgentNameInput(name);
12719
+ const suggestions = suggestAgentNames(existingNames).slice(0, 5);
12720
+ if (!normalized) {
12721
+ throw new InvalidAgentNameError(name, "choose a real one-word name instead of an empty value", suggestions);
12722
+ }
12723
+ if (/\s/.test(normalized)) {
12724
+ throw new InvalidAgentNameError(name, "use a single word, preferably a Roman or Greek name", suggestions);
12725
+ }
12726
+ if (normalized.length < 3) {
12727
+ throw new InvalidAgentNameError(name, "use a more distinctive name with at least three characters", suggestions);
12728
+ }
12729
+ if (isGenericAgentName(normalized)) {
12730
+ throw new InvalidAgentNameError(name, "generic names like agent, agent-1, assistant, or worker-2 are reserved", suggestions);
12731
+ }
12732
+ if (hasGeneratedNumericSuffix(normalized)) {
12733
+ throw new InvalidAgentNameError(name, "numbered suffix names are not allowed; pick a distinct human-readable name", suggestions);
12734
+ }
12735
+ if (!ONE_WORD_NAME_RE.test(normalized)) {
12736
+ throw new InvalidAgentNameError(name, "use one word made of letters only, preferably a Roman or Greek name", suggestions);
12737
+ }
12738
+ return normalized;
12739
+ }
12740
+ function tableHasColumn(db, table, column) {
12741
+ try {
12742
+ return db.query(`PRAGMA table_info(${table})`).all().some((row) => row.name === column);
12743
+ } catch {
12744
+ return false;
12745
+ }
12746
+ }
12747
+ function updateReferences(db, oldName, newName) {
12748
+ const refs = [
12749
+ ["tasks", "assigned_to"],
12750
+ ["tasks", "agent_id"],
12751
+ ["tasks", "locked_by"],
12752
+ ["tasks", "assigned_by"],
12753
+ ["plans", "agent_id"],
12754
+ ["sessions", "agent_id"],
12755
+ ["task_comments", "agent_id"],
12756
+ ["task_history", "agent_id"],
12757
+ ["webhooks", "agent_id"],
12758
+ ["task_files", "agent_id"],
12759
+ ["task_time_logs", "agent_id"],
12760
+ ["task_watchers", "agent_id"],
12761
+ ["task_checkpoints", "agent_id"],
12762
+ ["task_heartbeats", "agent_id"],
12763
+ ["project_agent_roles", "agent_id"]
12764
+ ];
12765
+ let changed = 0;
12766
+ for (const [table, column] of refs) {
12767
+ if (!tableHasColumn(db, table, column))
12768
+ continue;
12769
+ try {
12770
+ changed += db.run(`UPDATE ${table} SET ${column} = ? WHERE LOWER(${column}) = ?`, [newName, oldName]).changes;
12771
+ } catch {}
12772
+ }
12773
+ return changed;
12774
+ }
12775
+ function normalizeGeneratedAgentNames(db) {
12776
+ const rows = db.query("SELECT * FROM agents ORDER BY created_at, id").all();
12777
+ const existing = new Set(rows.map((agent) => normalizeAgentNameInput(agent.name)));
12778
+ const renamed = [];
12779
+ for (const agent of rows) {
12780
+ const oldName = normalizeAgentNameInput(agent.name);
12781
+ if (!isBlockedAgentName(oldName))
12782
+ continue;
12783
+ const candidates = suggestAgentNames(existing);
12784
+ const replacement = candidates[0];
12785
+ if (!replacement) {
12786
+ throw new Error("No safe agent names are available for normalization");
12787
+ }
12788
+ existing.delete(oldName);
12789
+ existing.add(replacement);
12790
+ db.run("UPDATE agents SET name = ?, last_seen_at = ? WHERE id = ?", [replacement, now(), agent.id]);
12791
+ const referenceUpdates = updateReferences(db, oldName, replacement);
12792
+ renamed.push({
12793
+ id: agent.id,
12794
+ old_name: oldName,
12795
+ new_name: replacement,
12796
+ reference_updates: referenceUpdates
12797
+ });
12798
+ }
12799
+ return renamed;
12800
+ }
12801
+ var InvalidAgentNameError, ROMAN_AGENT_NAMES, GREEK_AGENT_NAMES, NICE_AGENT_NAMES, PREFERRED_AGENT_NAMES, RESERVED_GENERIC_NAMES, NUMERIC_SUFFIX_RE, ONE_WORD_NAME_RE;
12802
+ var init_agent_names = __esm(() => {
12803
+ init_database();
12804
+ InvalidAgentNameError = class InvalidAgentNameError extends Error {
12805
+ suggestions;
12806
+ constructor(name, reason, suggestions = []) {
12807
+ super(`Invalid agent name "${name}": ${reason}${suggestions.length > 0 ? `. Try: ${suggestions.join(", ")}` : ""}`);
12808
+ this.name = "InvalidAgentNameError";
12809
+ this.suggestions = suggestions;
12810
+ }
12811
+ };
12812
+ ROMAN_AGENT_NAMES = [
12813
+ "caesar",
12814
+ "augustus",
12815
+ "marcus",
12816
+ "brutus",
12817
+ "cicero",
12818
+ "cato",
12819
+ "nero",
12820
+ "claudius",
12821
+ "tiberius",
12822
+ "hadrian",
12823
+ "trajan",
12824
+ "vespasian",
12825
+ "domitian",
12826
+ "caligula",
12827
+ "commodus",
12828
+ "livia",
12829
+ "julia",
12830
+ "octavia",
12831
+ "claudia",
12832
+ "agrippina",
12833
+ "cornelia",
12834
+ "valeria",
12835
+ "fulvia",
12836
+ "hortensia",
12837
+ "fabia"
12838
+ ];
12839
+ GREEK_AGENT_NAMES = [
12840
+ "athena",
12841
+ "apollo",
12842
+ "artemis",
12843
+ "hera",
12844
+ "iris",
12845
+ "hector",
12846
+ "achilles",
12847
+ "odysseus",
12848
+ "theseus",
12849
+ "pericles",
12850
+ "solon",
12851
+ "sophia",
12852
+ "thalia",
12853
+ "calliope",
12854
+ "clio",
12855
+ "phoebe",
12856
+ "daphne",
12857
+ "leonidas",
12858
+ "andromeda",
12859
+ "cassander"
12860
+ ];
12861
+ NICE_AGENT_NAMES = [
12862
+ "atlas",
12863
+ "aurora",
12864
+ "ember",
12865
+ "nova",
12866
+ "orion",
12867
+ "rhea",
12868
+ "selene",
12869
+ "sirius",
12870
+ "vesper",
12871
+ "zephyr"
12872
+ ];
12873
+ PREFERRED_AGENT_NAMES = [
12874
+ ...ROMAN_AGENT_NAMES,
12875
+ ...GREEK_AGENT_NAMES,
12876
+ ...NICE_AGENT_NAMES
12877
+ ];
12878
+ RESERVED_GENERIC_NAMES = new Set([
12879
+ "agent",
12880
+ "agents",
12881
+ "ai",
12882
+ "assistant",
12883
+ "bot",
12884
+ "coder",
12885
+ "default",
12886
+ "helper",
12887
+ "model",
12888
+ "system",
12889
+ "user",
12890
+ "worker"
12891
+ ]);
12892
+ NUMERIC_SUFFIX_RE = /[-_]\d+$/;
12893
+ ONE_WORD_NAME_RE = /^[a-z]+$/;
12894
+ });
12895
+
12638
12896
  // src/db/agents.ts
12639
12897
  var exports_agents = {};
12640
12898
  __export(exports_agents, {
12641
12899
  updateAgentActivity: () => updateAgentActivity,
12642
12900
  updateAgent: () => updateAgent,
12643
12901
  unarchiveAgent: () => unarchiveAgent,
12902
+ suggestAgentNames: () => suggestAgentNames,
12644
12903
  releaseAgent: () => releaseAgent,
12645
12904
  registerAgent: () => registerAgent,
12905
+ normalizeGeneratedAgentNames: () => normalizeGeneratedAgentNames,
12646
12906
  matchCapabilities: () => matchCapabilities,
12647
12907
  listAgents: () => listAgents,
12648
12908
  isAgentConflict: () => isAgentConflict,
@@ -12654,7 +12914,8 @@ __export(exports_agents, {
12654
12914
  getAgent: () => getAgent,
12655
12915
  deleteAgent: () => deleteAgent,
12656
12916
  autoReleaseStaleAgents: () => autoReleaseStaleAgents,
12657
- archiveAgent: () => archiveAgent
12917
+ archiveAgent: () => archiveAgent,
12918
+ InvalidAgentNameError: () => InvalidAgentNameError
12658
12919
  });
12659
12920
  function getActiveWindowMs() {
12660
12921
  const env = process.env["TODOS_AGENT_TIMEOUT_MS"];
@@ -12677,7 +12938,7 @@ function getAvailableNamesFromPool(pool, db) {
12677
12938
  autoReleaseStaleAgents(db);
12678
12939
  const cutoff = new Date(Date.now() - getActiveWindowMs()).toISOString();
12679
12940
  const activeNames = new Set(db.query("SELECT name FROM agents WHERE status = 'active' AND last_seen_at > ?").all(cutoff).map((r) => r.name.toLowerCase()));
12680
- return pool.filter((name) => !activeNames.has(name.toLowerCase()));
12941
+ return pool.map(normalizeAgentNameInput).filter((name) => !activeNames.has(name));
12681
12942
  }
12682
12943
  function shortUuid() {
12683
12944
  return crypto.randomUUID().slice(0, 8);
@@ -12693,7 +12954,8 @@ function rowToAgent(row) {
12693
12954
  }
12694
12955
  function registerAgent(input, db) {
12695
12956
  const d = db || getDatabase();
12696
- const normalizedName = input.name.trim().toLowerCase();
12957
+ const existingNames = d.query("SELECT name FROM agents").all().map((row) => row.name);
12958
+ const normalizedName = validateAgentName(input.name, existingNames);
12697
12959
  const existing = getAgentByName(normalizedName, d);
12698
12960
  if (existing) {
12699
12961
  const lastSeenMs = new Date(existing.last_seen_at).getTime();
@@ -12826,7 +13088,8 @@ function updateAgent(id, input, db) {
12826
13088
  const sets = ["last_seen_at = ?"];
12827
13089
  const params = [now()];
12828
13090
  if (input.name !== undefined) {
12829
- const newName = input.name.trim().toLowerCase();
13091
+ const existingNames = d.query("SELECT name FROM agents WHERE id != ?").all(id).map((row) => row.name);
13092
+ const newName = validateAgentName(input.name, existingNames);
12830
13093
  const holder = getAgentByName(newName, d);
12831
13094
  if (holder && holder.id !== id) {
12832
13095
  const lastSeenMs = new Date(holder.last_seen_at).getTime();
@@ -12937,6 +13200,7 @@ function getCapableAgents(capabilities, opts, db) {
12937
13200
  }
12938
13201
  var init_agents = __esm(() => {
12939
13202
  init_database();
13203
+ init_agent_names();
12940
13204
  });
12941
13205
 
12942
13206
  // src/types/index.ts
@@ -13203,6 +13467,23 @@ function loadConfig() {
13203
13467
  cached = config;
13204
13468
  return cached;
13205
13469
  }
13470
+ function getAgentPoolForProject(workingDir) {
13471
+ const config = loadConfig();
13472
+ if (workingDir && config.project_pools) {
13473
+ let bestKey = null;
13474
+ let bestLen = 0;
13475
+ for (const key of Object.keys(config.project_pools)) {
13476
+ if (workingDir.startsWith(key) && key.length > bestLen) {
13477
+ bestKey = key;
13478
+ bestLen = key.length;
13479
+ }
13480
+ }
13481
+ if (bestKey && config.project_pools[bestKey]) {
13482
+ return config.project_pools[bestKey];
13483
+ }
13484
+ }
13485
+ return config.agent_pool || null;
13486
+ }
13206
13487
  function getCompletionGuardConfig(projectPath) {
13207
13488
  const config = loadConfig();
13208
13489
  const global2 = { ...GUARD_DEFAULTS, ...config.completion_guard };
@@ -13224,6 +13505,27 @@ var init_config2 = __esm(() => {
13224
13505
  });
13225
13506
 
13226
13507
  // src/db/projects.ts
13508
+ var exports_projects = {};
13509
+ __export(exports_projects, {
13510
+ updateProject: () => updateProject,
13511
+ slugify: () => slugify,
13512
+ setMachineLocalPath: () => setMachineLocalPath,
13513
+ renameProject: () => renameProject,
13514
+ removeProjectSource: () => removeProjectSource,
13515
+ removeMachineLocalPath: () => removeMachineLocalPath,
13516
+ nextTaskShortId: () => nextTaskShortId,
13517
+ listProjects: () => listProjects,
13518
+ listProjectSources: () => listProjectSources,
13519
+ listMachineLocalPaths: () => listMachineLocalPaths,
13520
+ getProjectWithSources: () => getProjectWithSources,
13521
+ getProjectByPath: () => getProjectByPath,
13522
+ getProject: () => getProject,
13523
+ getMachineLocalPath: () => getMachineLocalPath,
13524
+ ensureProject: () => ensureProject,
13525
+ deleteProject: () => deleteProject,
13526
+ createProject: () => createProject,
13527
+ addProjectSource: () => addProjectSource
13528
+ });
13227
13529
  function slugify(name) {
13228
13530
  return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
13229
13531
  }
@@ -13262,6 +13564,18 @@ function getProject(id, db) {
13262
13564
  const row = d.query("SELECT * FROM projects WHERE id = ?").get(id);
13263
13565
  return row;
13264
13566
  }
13567
+ function getProjectByPath(path, db) {
13568
+ const d = db || getDatabase();
13569
+ try {
13570
+ const machineId = getMachineId(d);
13571
+ const machineRow = d.query(`SELECT p.* FROM projects p
13572
+ JOIN project_machine_paths pmp ON pmp.project_id = p.id
13573
+ WHERE pmp.machine_id = ? AND pmp.path = ?`).get(machineId, path);
13574
+ if (machineRow)
13575
+ return machineRow;
13576
+ } catch {}
13577
+ return d.query("SELECT * FROM projects WHERE path = ?").get(path);
13578
+ }
13265
13579
  function listProjects(db) {
13266
13580
  const d = db || getDatabase();
13267
13581
  return d.query("SELECT * FROM projects ORDER BY name").all();
@@ -13293,11 +13607,141 @@ function updateProject(id, input, db) {
13293
13607
  d.run(`UPDATE projects SET ${sets.join(", ")} WHERE id = ?`, params);
13294
13608
  return getProject(id, d);
13295
13609
  }
13610
+ function renameProject(id, input, db) {
13611
+ const d = db || getDatabase();
13612
+ const project = getProject(id, d);
13613
+ if (!project)
13614
+ throw new ProjectNotFoundError(id);
13615
+ let taskListsUpdated = 0;
13616
+ const ts = now();
13617
+ if (input.new_slug !== undefined) {
13618
+ const normalised = input.new_slug.toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-|-$/g, "");
13619
+ if (!normalised)
13620
+ throw new Error("Invalid slug \u2014 must be non-empty kebab-case");
13621
+ const conflict = d.query("SELECT id FROM projects WHERE task_list_id = ? AND id != ?").get(normalised, id);
13622
+ if (conflict)
13623
+ throw new Error(`Slug "${normalised}" is already used by another project`);
13624
+ const oldSlug = project.task_list_id;
13625
+ d.run("UPDATE projects SET task_list_id = ?, updated_at = ? WHERE id = ?", [normalised, ts, id]);
13626
+ if (oldSlug) {
13627
+ 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]);
13628
+ taskListsUpdated = result.changes;
13629
+ }
13630
+ }
13631
+ if (input.name !== undefined) {
13632
+ d.run("UPDATE projects SET name = ?, updated_at = ? WHERE id = ?", [input.name, ts, id]);
13633
+ }
13634
+ return { project: getProject(id, d), task_lists_updated: taskListsUpdated };
13635
+ }
13296
13636
  function deleteProject(id, db) {
13297
13637
  const d = db || getDatabase();
13298
13638
  const result = d.run("DELETE FROM projects WHERE id = ?", [id]);
13299
13639
  return result.changes > 0;
13300
13640
  }
13641
+ function rowToSource(row) {
13642
+ return {
13643
+ ...row,
13644
+ metadata: row.metadata ? JSON.parse(row.metadata) : {}
13645
+ };
13646
+ }
13647
+ function addProjectSource(input, db) {
13648
+ const d = db || getDatabase();
13649
+ const id = uuid();
13650
+ const timestamp = now();
13651
+ d.run(`INSERT INTO project_sources (id, project_id, type, name, uri, description, metadata, created_at, updated_at)
13652
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
13653
+ id,
13654
+ input.project_id,
13655
+ input.type,
13656
+ input.name,
13657
+ input.uri,
13658
+ input.description || null,
13659
+ JSON.stringify(input.metadata || {}),
13660
+ timestamp,
13661
+ timestamp
13662
+ ]);
13663
+ return rowToSource(d.query("SELECT * FROM project_sources WHERE id = ?").get(id));
13664
+ }
13665
+ function removeProjectSource(id, db) {
13666
+ const d = db || getDatabase();
13667
+ const result = d.run("DELETE FROM project_sources WHERE id = ?", [id]);
13668
+ return result.changes > 0;
13669
+ }
13670
+ function listProjectSources(projectId, db) {
13671
+ const d = db || getDatabase();
13672
+ const rows = d.query("SELECT * FROM project_sources WHERE project_id = ? ORDER BY name").all(projectId);
13673
+ return rows.map(rowToSource);
13674
+ }
13675
+ function getProjectWithSources(id, db) {
13676
+ const d = db || getDatabase();
13677
+ const project = getProject(id, d);
13678
+ if (!project)
13679
+ return null;
13680
+ project.sources = listProjectSources(id, d);
13681
+ return project;
13682
+ }
13683
+ function nextTaskShortId(projectId, db) {
13684
+ const d = db || getDatabase();
13685
+ const project = getProject(projectId, d);
13686
+ if (!project || !project.task_prefix)
13687
+ return null;
13688
+ d.run("UPDATE projects SET task_counter = task_counter + 1, updated_at = ? WHERE id = ?", [now(), projectId]);
13689
+ const updated = getProject(projectId, d);
13690
+ const padded = String(updated.task_counter).padStart(5, "0");
13691
+ return `${updated.task_prefix}-${padded}`;
13692
+ }
13693
+ function ensureProject(name, path, db) {
13694
+ const d = db || getDatabase();
13695
+ const existing = getProjectByPath(path, d);
13696
+ if (existing) {
13697
+ if (!existing.task_prefix) {
13698
+ const prefix = generatePrefix(existing.name, d);
13699
+ d.run("UPDATE projects SET task_prefix = ?, updated_at = ? WHERE id = ?", [prefix, now(), existing.id]);
13700
+ return getProject(existing.id, d);
13701
+ }
13702
+ setMachineLocalPath(existing.id, path, d);
13703
+ return existing;
13704
+ }
13705
+ const project = createProject({ name, path }, d);
13706
+ setMachineLocalPath(project.id, path, d);
13707
+ return project;
13708
+ }
13709
+ function setMachineLocalPath(projectId, path, db) {
13710
+ const d = db || getDatabase();
13711
+ const machineId = getMachineId(d);
13712
+ const ts = now();
13713
+ const existing = d.query("SELECT * FROM project_machine_paths WHERE project_id = ? AND machine_id = ?").get(projectId, machineId);
13714
+ if (existing) {
13715
+ if (existing.path !== path) {
13716
+ d.run("UPDATE project_machine_paths SET path = ?, updated_at = ? WHERE id = ?", [path, ts, existing.id]);
13717
+ }
13718
+ return d.query("SELECT * FROM project_machine_paths WHERE id = ?").get(existing.id);
13719
+ }
13720
+ const id = uuid();
13721
+ d.run("INSERT INTO project_machine_paths (id, project_id, machine_id, path, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?)", [id, projectId, machineId, path, ts, ts]);
13722
+ return d.query("SELECT * FROM project_machine_paths WHERE id = ?").get(id);
13723
+ }
13724
+ function getMachineLocalPath(projectId, db) {
13725
+ const d = db || getDatabase();
13726
+ try {
13727
+ const machineId = getMachineId(d);
13728
+ const row = d.query("SELECT path FROM project_machine_paths WHERE project_id = ? AND machine_id = ?").get(projectId, machineId);
13729
+ if (row)
13730
+ return row.path;
13731
+ } catch {}
13732
+ const project = getProject(projectId, d);
13733
+ return project?.path ?? null;
13734
+ }
13735
+ function listMachineLocalPaths(projectId, db) {
13736
+ const d = db || getDatabase();
13737
+ return d.query("SELECT * FROM project_machine_paths WHERE project_id = ? ORDER BY machine_id").all(projectId);
13738
+ }
13739
+ function removeMachineLocalPath(projectId, machineId, db) {
13740
+ const d = db || getDatabase();
13741
+ const mid = machineId ?? getMachineId(d);
13742
+ const result = d.run("DELETE FROM project_machine_paths WHERE project_id = ? AND machine_id = ?", [projectId, mid]);
13743
+ return result.changes > 0;
13744
+ }
13301
13745
  var init_projects = __esm(() => {
13302
13746
  init_types2();
13303
13747
  init_database();
@@ -13558,8 +14002,8 @@ function createTask(input, db) {
13558
14002
  let id = uuid();
13559
14003
  for (let attempt = 0;attempt < 3; attempt++) {
13560
14004
  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, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
14005
+ 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)
14006
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
13563
14007
  id,
13564
14008
  null,
13565
14009
  input.project_id || null,
@@ -13581,6 +14025,10 @@ function createTask(input, db) {
13581
14025
  timestamp,
13582
14026
  input.due_at || null,
13583
14027
  input.estimated_minutes || null,
14028
+ input.confidence ?? null,
14029
+ input.retry_count ?? 0,
14030
+ input.max_retries ?? 3,
14031
+ input.retry_after ?? null,
13584
14032
  input.requires_approval ? 1 : 0,
13585
14033
  null,
13586
14034
  null,
@@ -13605,11 +14053,11 @@ function createTask(input, db) {
13605
14053
  if (tags.length > 0) {
13606
14054
  insertTaskTags(id, tags, d);
13607
14055
  }
13608
- const task = getTask2(id, d);
14056
+ const task = getTask(id, d);
13609
14057
  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
14058
  return task;
13611
14059
  }
13612
- function getTask2(id, db) {
14060
+ function getTask(id, db) {
13613
14061
  const d = db || getDatabase();
13614
14062
  const row = d.query("SELECT * FROM tasks WHERE id = ?").get(id);
13615
14063
  if (!row)
@@ -13618,7 +14066,7 @@ function getTask2(id, db) {
13618
14066
  }
13619
14067
  function getTaskWithRelations(id, db) {
13620
14068
  const d = db || getDatabase();
13621
- const task = getTask2(id, d);
14069
+ const task = getTask(id, d);
13622
14070
  if (!task)
13623
14071
  return null;
13624
14072
  const subtaskRows = d.query("SELECT * FROM tasks WHERE parent_id = ? ORDER BY created_at").all(id);
@@ -13632,7 +14080,7 @@ function getTaskWithRelations(id, db) {
13632
14080
  WHERE td.depends_on = ?`).all(id);
13633
14081
  const blocked_by = blockedByRows.map(rowToTask);
13634
14082
  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;
14083
+ const parent = task.parent_id ? getTask(task.parent_id, d) : null;
13636
14084
  const checklist = getChecklist(id, d);
13637
14085
  return {
13638
14086
  ...task,
@@ -13819,14 +14267,16 @@ function countTasks(filter = {}, db) {
13819
14267
  }
13820
14268
  function updateTask(id, input, db) {
13821
14269
  const d = db || getDatabase();
13822
- const task = getTask2(id, d);
14270
+ const task = getTask(id, d);
13823
14271
  if (!task)
13824
14272
  throw new TaskNotFoundError(id);
13825
14273
  if (task.version !== input.version) {
13826
14274
  throw new VersionConflictError(id, input.version, task.version);
13827
14275
  }
14276
+ const timestamp = now();
14277
+ const completionTimestamp = input.completed_at ?? timestamp;
13828
14278
  const sets = ["version = version + 1", "updated_at = ?"];
13829
- const params = [now()];
14279
+ const params = [timestamp];
13830
14280
  if (input.title !== undefined) {
13831
14281
  sets.push("title = ?");
13832
14282
  params.push(input.title);
@@ -13843,13 +14293,17 @@ function updateTask(id, input, db) {
13843
14293
  params.push(input.status);
13844
14294
  if (input.status === "completed") {
13845
14295
  sets.push("completed_at = ?");
13846
- params.push(now());
14296
+ params.push(completionTimestamp);
13847
14297
  }
13848
14298
  }
13849
14299
  if (input.priority !== undefined) {
13850
14300
  sets.push("priority = ?");
13851
14301
  params.push(input.priority);
13852
14302
  }
14303
+ if (input.project_id !== undefined) {
14304
+ sets.push("project_id = ?");
14305
+ params.push(input.project_id);
14306
+ }
13853
14307
  if (input.assigned_to !== undefined) {
13854
14308
  sets.push("assigned_to = ?");
13855
14309
  params.push(input.assigned_to);
@@ -13878,6 +14332,30 @@ function updateTask(id, input, db) {
13878
14332
  sets.push("estimated_minutes = ?");
13879
14333
  params.push(input.estimated_minutes);
13880
14334
  }
14335
+ if (input.actual_minutes !== undefined) {
14336
+ sets.push("actual_minutes = ?");
14337
+ params.push(input.actual_minutes);
14338
+ }
14339
+ if (input.completed_at !== undefined && input.status !== "completed") {
14340
+ sets.push("completed_at = ?");
14341
+ params.push(input.completed_at);
14342
+ }
14343
+ if (input.confidence !== undefined) {
14344
+ sets.push("confidence = ?");
14345
+ params.push(input.confidence);
14346
+ }
14347
+ if (input.retry_count !== undefined) {
14348
+ sets.push("retry_count = ?");
14349
+ params.push(input.retry_count);
14350
+ }
14351
+ if (input.max_retries !== undefined) {
14352
+ sets.push("max_retries = ?");
14353
+ params.push(input.max_retries);
14354
+ }
14355
+ if (input.retry_after !== undefined) {
14356
+ sets.push("retry_after = ?");
14357
+ params.push(input.retry_after);
14358
+ }
13881
14359
  if (input.requires_approval !== undefined) {
13882
14360
  sets.push("requires_approval = ?");
13883
14361
  params.push(input.requires_approval ? 1 : 0);
@@ -13899,7 +14377,7 @@ function updateTask(id, input, db) {
13899
14377
  params.push(id, input.version);
13900
14378
  const result = d.run(`UPDATE tasks SET ${sets.join(", ")} WHERE id = ? AND version = ?`, params);
13901
14379
  if (result.changes === 0) {
13902
- const current = getTask2(id, d);
14380
+ const current = getTask(id, d);
13903
14381
  throw new VersionConflictError(id, input.version, current?.version ?? -1);
13904
14382
  }
13905
14383
  if (input.tags !== undefined) {
@@ -13928,11 +14406,16 @@ function updateTask(id, input, db) {
13928
14406
  tags: input.tags ?? task.tags,
13929
14407
  metadata: input.metadata ?? task.metadata,
13930
14408
  version: task.version + 1,
13931
- updated_at: now(),
13932
- completed_at: input.status === "completed" ? now() : task.completed_at,
14409
+ updated_at: timestamp,
14410
+ completed_at: input.status === "completed" ? completionTimestamp : input.completed_at !== undefined ? input.completed_at : task.completed_at,
14411
+ actual_minutes: input.actual_minutes ?? task.actual_minutes,
14412
+ confidence: input.confidence !== undefined ? input.confidence : task.confidence,
14413
+ retry_count: input.retry_count ?? task.retry_count,
14414
+ max_retries: input.max_retries ?? task.max_retries,
14415
+ retry_after: input.retry_after !== undefined ? input.retry_after : task.retry_after,
13933
14416
  requires_approval: input.requires_approval !== undefined ? input.requires_approval : task.requires_approval,
13934
14417
  approved_by: input.approved_by ?? task.approved_by,
13935
- approved_at: input.approved_by ? now() : task.approved_at
14418
+ approved_at: input.approved_by ? timestamp : task.approved_at
13936
14419
  };
13937
14420
  }
13938
14421
  function deleteTask(id, db) {
@@ -14085,9 +14568,9 @@ var init_templates = __esm(() => {
14085
14568
  // src/db/task-graph.ts
14086
14569
  function addDependency(taskId, dependsOn, db) {
14087
14570
  const d = db || getDatabase();
14088
- if (!getTask2(taskId, d))
14571
+ if (!getTask(taskId, d))
14089
14572
  throw new TaskNotFoundError(taskId);
14090
- if (!getTask2(dependsOn, d))
14573
+ if (!getTask(dependsOn, d))
14091
14574
  throw new TaskNotFoundError(dependsOn);
14092
14575
  if (wouldCreateCycle(taskId, dependsOn, d)) {
14093
14576
  throw new DependencyCycleError(taskId, dependsOn);
@@ -14109,7 +14592,7 @@ function getTaskDependents(taskId, db) {
14109
14592
  }
14110
14593
  function cloneTask(taskId, overrides, db) {
14111
14594
  const d = db || getDatabase();
14112
- const source = getTask2(taskId, d);
14595
+ const source = getTask(taskId, d);
14113
14596
  if (!source)
14114
14597
  throw new TaskNotFoundError(taskId);
14115
14598
  const input = {
@@ -14132,13 +14615,13 @@ function cloneTask(taskId, overrides, db) {
14132
14615
  }
14133
14616
  function getTaskGraph(taskId, direction = "both", db) {
14134
14617
  const d = db || getDatabase();
14135
- const task = getTask2(taskId, d);
14618
+ const task = getTask(taskId, d);
14136
14619
  if (!task)
14137
14620
  throw new TaskNotFoundError(taskId);
14138
14621
  function toNode(t) {
14139
14622
  const deps = getTaskDependencies(t.id, d);
14140
14623
  const hasUnfinishedDeps = deps.some((dep) => {
14141
- const depTask = getTask2(dep.depends_on, d);
14624
+ const depTask = getTask(dep.depends_on, d);
14142
14625
  return depTask && depTask.status !== "completed";
14143
14626
  });
14144
14627
  return { id: t.id, short_id: t.short_id, title: t.title, status: t.status, priority: t.priority, is_blocked: hasUnfinishedDeps };
@@ -14149,7 +14632,7 @@ function getTaskGraph(taskId, direction = "both", db) {
14149
14632
  visited.add(id);
14150
14633
  const deps = d.query("SELECT depends_on FROM task_dependencies WHERE task_id = ?").all(id);
14151
14634
  return deps.map((dep) => {
14152
- const depTask = getTask2(dep.depends_on, d);
14635
+ const depTask = getTask(dep.depends_on, d);
14153
14636
  if (!depTask)
14154
14637
  return null;
14155
14638
  return { task: toNode(depTask), depends_on: buildUp(dep.depends_on, visited), blocks: [] };
@@ -14161,7 +14644,7 @@ function getTaskGraph(taskId, direction = "both", db) {
14161
14644
  visited.add(id);
14162
14645
  const dependents = d.query("SELECT task_id FROM task_dependencies WHERE depends_on = ?").all(id);
14163
14646
  return dependents.map((dep) => {
14164
- const depTask = getTask2(dep.task_id, d);
14647
+ const depTask = getTask(dep.task_id, d);
14165
14648
  if (!depTask)
14166
14649
  return null;
14167
14650
  return { task: toNode(depTask), depends_on: [], blocks: buildDown(dep.task_id, visited) };
@@ -14174,7 +14657,7 @@ function getTaskGraph(taskId, direction = "both", db) {
14174
14657
  }
14175
14658
  function moveTask(taskId, target, db) {
14176
14659
  const d = db || getDatabase();
14177
- const task = getTask2(taskId, d);
14660
+ const task = getTask(taskId, d);
14178
14661
  if (!task)
14179
14662
  throw new TaskNotFoundError(taskId);
14180
14663
  const sets = ["updated_at = ?", "version = version + 1"];
@@ -14193,7 +14676,7 @@ function moveTask(taskId, target, db) {
14193
14676
  }
14194
14677
  params.push(taskId);
14195
14678
  d.run(`UPDATE tasks SET ${sets.join(", ")} WHERE id = ?`, params);
14196
- return getTask2(taskId, d);
14679
+ return getTask(taskId, d);
14197
14680
  }
14198
14681
  function wouldCreateCycle(taskId, dependsOn, db) {
14199
14682
  const visited = new Set;
@@ -14226,7 +14709,7 @@ function getBlockingDeps(id, db) {
14226
14709
  return [];
14227
14710
  const blocking = [];
14228
14711
  for (const dep of deps) {
14229
- const task = getTask2(dep.depends_on, d);
14712
+ const task = getTask(dep.depends_on, d);
14230
14713
  if (task && task.status !== "completed")
14231
14714
  blocking.push(task);
14232
14715
  }
@@ -14234,7 +14717,7 @@ function getBlockingDeps(id, db) {
14234
14717
  }
14235
14718
  function startTask(id, agentId, db) {
14236
14719
  const d = db || getDatabase();
14237
- const task = getTask2(id, d);
14720
+ const task = getTask(id, d);
14238
14721
  if (!task)
14239
14722
  throw new TaskNotFoundError(id);
14240
14723
  const blocking = getBlockingDeps(id, d);
@@ -14257,7 +14740,7 @@ function startTask(id, agentId, db) {
14257
14740
  }
14258
14741
  function completeTask(id, agentId, db, options) {
14259
14742
  const d = db || getDatabase();
14260
- const task = getTask2(id, d);
14743
+ const task = getTask(id, d);
14261
14744
  if (!task)
14262
14745
  throw new TaskNotFoundError(id);
14263
14746
  if (agentId && task.locked_by && task.locked_by !== agentId && !isLockExpired(task.locked_at)) {
@@ -14273,14 +14756,14 @@ function completeTask(id, agentId, db, options) {
14273
14756
  completionMeta._completion = { confidence: options.confidence };
14274
14757
  }
14275
14758
  const hasMeta = Object.keys(completionMeta).length > 0;
14276
- const timestamp = now();
14759
+ const timestamp = options?.completed_at || now();
14277
14760
  const confidence = options?.confidence !== undefined ? options.confidence : null;
14278
14761
  const tx = d.transaction(() => {
14279
14762
  if (hasMeta) {
14280
14763
  const meta2 = { ...task.metadata, ...completionMeta };
14281
14764
  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
14765
  if (metaResult.changes === 0) {
14283
- const current = getTask2(id, d);
14766
+ const current = getTask(id, d);
14284
14767
  throw new VersionConflictError(id, task.version, current?.version ?? -1);
14285
14768
  }
14286
14769
  }
@@ -14337,7 +14820,7 @@ function completeTask(id, agentId, db, options) {
14337
14820
  }
14338
14821
  function lockTask(id, agentId, db) {
14339
14822
  const d = db || getDatabase();
14340
- const task = getTask2(id, d);
14823
+ const task = getTask(id, d);
14341
14824
  if (!task)
14342
14825
  throw new TaskNotFoundError(id);
14343
14826
  if (task.locked_by === agentId && !isLockExpired(task.locked_at)) {
@@ -14348,7 +14831,7 @@ function lockTask(id, agentId, db) {
14348
14831
  const result = d.run(`UPDATE tasks SET locked_by = ?, locked_at = ?, version = version + 1, updated_at = ?
14349
14832
  WHERE id = ? AND (locked_by IS NULL OR locked_by = ? OR locked_at < ?)`, [agentId, timestamp, timestamp, id, agentId, cutoff]);
14350
14833
  if (result.changes === 0) {
14351
- const current = getTask2(id, d);
14834
+ const current = getTask(id, d);
14352
14835
  if (!current)
14353
14836
  throw new TaskNotFoundError(id);
14354
14837
  if (current.locked_by && !isLockExpired(current.locked_at)) {
@@ -14364,7 +14847,7 @@ function lockTask(id, agentId, db) {
14364
14847
  }
14365
14848
  function unlockTask(id, agentId, db) {
14366
14849
  const d = db || getDatabase();
14367
- const task = getTask2(id, d);
14850
+ const task = getTask(id, d);
14368
14851
  if (!task)
14369
14852
  throw new TaskNotFoundError(id);
14370
14853
  if (agentId && task.locked_by && task.locked_by !== agentId) {
@@ -14465,7 +14948,7 @@ function getTasksChangedSince(since, filters, db) {
14465
14948
  }
14466
14949
  function failTask(id, agentId, reason, options, db) {
14467
14950
  const d = db || getDatabase();
14468
- const task = getTask2(id, d);
14951
+ const task = getTask(id, d);
14469
14952
  if (!task)
14470
14953
  throw new TaskNotFoundError(id);
14471
14954
  const meta = {
@@ -14664,7 +15147,7 @@ function getStatus(filters, agentId, options, db) {
14664
15147
  }
14665
15148
  function decomposeTasks(parentId, subtasks, options, db) {
14666
15149
  const d = db || getDatabase();
14667
- const parent = getTask2(parentId, d);
15150
+ const parent = getTask(parentId, d);
14668
15151
  if (!parent)
14669
15152
  throw new TaskNotFoundError(parentId);
14670
15153
  const created = [];
@@ -14695,7 +15178,7 @@ function decomposeTasks(parentId, subtasks, options, db) {
14695
15178
  function setTaskStatus(id, status, _agentId, db) {
14696
15179
  const d = db || getDatabase();
14697
15180
  for (let attempt = 0;attempt < 3; attempt++) {
14698
- const task = getTask2(id, d);
15181
+ const task = getTask(id, d);
14699
15182
  if (!task)
14700
15183
  throw new TaskNotFoundError(id);
14701
15184
  if (task.status === status)
@@ -14713,7 +15196,7 @@ function setTaskStatus(id, status, _agentId, db) {
14713
15196
  function setTaskPriority(id, priority, _agentId, db) {
14714
15197
  const d = db || getDatabase();
14715
15198
  for (let attempt = 0;attempt < 3; attempt++) {
14716
- const task = getTask2(id, d);
15199
+ const task = getTask(id, d);
14717
15200
  if (!task)
14718
15201
  throw new TaskNotFoundError(id);
14719
15202
  if (task.priority === priority)
@@ -14821,7 +15304,7 @@ function bulkUpdateTasks(taskIds, updates, db) {
14821
15304
  const tx = d.transaction(() => {
14822
15305
  for (const id of taskIds) {
14823
15306
  try {
14824
- const task = getTask2(id, d);
15307
+ const task = getTask(id, d);
14825
15308
  if (!task) {
14826
15309
  failed.push({ id, error: "Task not found" });
14827
15310
  continue;
@@ -14836,6 +15319,30 @@ function bulkUpdateTasks(taskIds, updates, db) {
14836
15319
  tx();
14837
15320
  return { updated, failed };
14838
15321
  }
15322
+ function bulkDeleteTasks(taskIds, force = false, db) {
15323
+ const d = db || getDatabase();
15324
+ let deleted = 0;
15325
+ let skipped = 0;
15326
+ const failed = [];
15327
+ const tx = d.transaction(() => {
15328
+ for (const id of taskIds) {
15329
+ try {
15330
+ const childCount = d.query("SELECT COUNT(*) as count FROM tasks WHERE parent_id = ?").get(id);
15331
+ if (!force && childCount.count > 0) {
15332
+ skipped++;
15333
+ continue;
15334
+ }
15335
+ const result = d.run("DELETE FROM tasks WHERE id = ?", [id]);
15336
+ if (result.changes > 0)
15337
+ deleted++;
15338
+ } catch (e) {
15339
+ failed.push({ id, error: e instanceof Error ? e.message : String(e) });
15340
+ }
15341
+ }
15342
+ });
15343
+ tx();
15344
+ return { deleted, skipped, failed };
15345
+ }
14839
15346
  function archiveTasks(options, db) {
14840
15347
  const d = db || getDatabase();
14841
15348
  const conditions = ["archived_at IS NULL"];
@@ -14860,10 +15367,33 @@ function archiveTasks(options, db) {
14860
15367
  const result = d.run(`UPDATE tasks SET archived_at = ? WHERE ${conditions.join(" AND ")}`, [ts, ...params]);
14861
15368
  return { archived: result.changes };
14862
15369
  }
15370
+ function archiveCompletedTasks(days = 7, projectId, db) {
15371
+ return archiveTasks({
15372
+ project_id: projectId,
15373
+ older_than_days: days,
15374
+ status: ["completed"]
15375
+ }, db).archived;
15376
+ }
15377
+ function getArchivedTasks(opts = {}, db) {
15378
+ const d = db || getDatabase();
15379
+ const conditions = ["archived_at IS NOT NULL"];
15380
+ const params = [];
15381
+ if (opts.project_id) {
15382
+ conditions.push("project_id = ?");
15383
+ params.push(opts.project_id);
15384
+ }
15385
+ let limitClause = "";
15386
+ if (opts.limit) {
15387
+ limitClause = " LIMIT ?";
15388
+ params.push(opts.limit);
15389
+ }
15390
+ const rows = d.query(`SELECT * FROM tasks WHERE ${conditions.join(" AND ")} ORDER BY archived_at DESC${limitClause}`).all(...params);
15391
+ return rows.map(rowToTask);
15392
+ }
14863
15393
  function unarchiveTask(id, db) {
14864
15394
  const d = db || getDatabase();
14865
15395
  d.run("UPDATE tasks SET archived_at = NULL WHERE id = ?", [id]);
14866
- return getTask2(id, d);
15396
+ return getTask(id, d);
14867
15397
  }
14868
15398
  function getOverdueTasks(projectId, db) {
14869
15399
  const d = db || getDatabase();
@@ -14878,6 +15408,80 @@ function getOverdueTasks(projectId, db) {
14878
15408
  const rows = d.query(query).all(...params);
14879
15409
  return rows.map(rowToTask);
14880
15410
  }
15411
+ function notifyUpcomingDeadlines(opts = {}, db) {
15412
+ const d = db || getDatabase();
15413
+ const hours = opts.hours ?? 24;
15414
+ const start = new Date().toISOString();
15415
+ const end = new Date(Date.now() + hours * 60 * 60 * 1000).toISOString();
15416
+ const conditions = [
15417
+ "archived_at IS NULL",
15418
+ "due_at IS NOT NULL",
15419
+ "due_at >= ?",
15420
+ "due_at <= ?",
15421
+ "status NOT IN ('completed', 'cancelled', 'failed')"
15422
+ ];
15423
+ const params = [start, end];
15424
+ if (opts.project_id) {
15425
+ conditions.push("project_id = ?");
15426
+ params.push(opts.project_id);
15427
+ }
15428
+ if (opts.agent_id) {
15429
+ conditions.push("assigned_to = ?");
15430
+ params.push(opts.agent_id);
15431
+ }
15432
+ const rows = d.query(`SELECT * FROM tasks WHERE ${conditions.join(" AND ")} ORDER BY due_at ASC`).all(...params);
15433
+ return rows.map(rowToTask);
15434
+ }
15435
+ function getBlockedTasks(projectId, db) {
15436
+ const d = db || getDatabase();
15437
+ const params = [];
15438
+ let projectClause = "";
15439
+ if (projectId) {
15440
+ projectClause = "AND t.project_id = ?";
15441
+ params.push(projectId);
15442
+ }
15443
+ const rows = d.query(`
15444
+ SELECT t.*, GROUP_CONCAT(dep.id) AS blocked_by_ids
15445
+ FROM tasks t
15446
+ JOIN task_dependencies td ON td.task_id = t.id
15447
+ JOIN tasks dep ON dep.id = td.depends_on
15448
+ WHERE t.archived_at IS NULL
15449
+ AND t.status NOT IN ('completed', 'cancelled', 'failed')
15450
+ AND dep.status NOT IN ('completed', 'cancelled')
15451
+ ${projectClause}
15452
+ GROUP BY t.id
15453
+ ORDER BY t.priority DESC, t.created_at DESC
15454
+ `).all(...params);
15455
+ return rows.map((row) => ({
15456
+ ...rowToTask(row),
15457
+ blocked_by: row.blocked_by_ids ? row.blocked_by_ids.split(",") : []
15458
+ }));
15459
+ }
15460
+ function getBlockingTasks(projectId, db) {
15461
+ const d = db || getDatabase();
15462
+ const params = [];
15463
+ let projectClause = "";
15464
+ if (projectId) {
15465
+ projectClause = "AND blocker.project_id = ?";
15466
+ params.push(projectId);
15467
+ }
15468
+ const rows = d.query(`
15469
+ SELECT blocker.*, COUNT(DISTINCT blocked.id) AS blocking_count
15470
+ FROM tasks blocker
15471
+ JOIN task_dependencies td ON td.depends_on = blocker.id
15472
+ JOIN tasks blocked ON blocked.id = td.task_id
15473
+ WHERE blocker.archived_at IS NULL
15474
+ AND blocker.status NOT IN ('completed', 'cancelled', 'failed')
15475
+ AND blocked.status NOT IN ('completed', 'cancelled', 'failed')
15476
+ ${projectClause}
15477
+ GROUP BY blocker.id
15478
+ ORDER BY blocking_count DESC, blocker.created_at DESC
15479
+ `).all(...params);
15480
+ return rows.map((row) => ({
15481
+ ...rowToTask(row),
15482
+ blocking_count: row.blocking_count
15483
+ }));
15484
+ }
14881
15485
  function logTime(input, db) {
14882
15486
  const d = db || getDatabase();
14883
15487
  const id = uuid();
@@ -14958,6 +15562,7 @@ __export(exports_tasks, {
14958
15562
  removeDependency: () => removeDependency,
14959
15563
  redistributeStaleTasks: () => redistributeStaleTasks,
14960
15564
  notifyWatchers: () => notifyWatchers,
15565
+ notifyUpcomingDeadlines: () => notifyUpcomingDeadlines,
14961
15566
  moveTask: () => moveTask,
14962
15567
  logTime: () => logTime,
14963
15568
  logCost: () => logCost,
@@ -14973,12 +15578,15 @@ __export(exports_tasks, {
14973
15578
  getTaskGraph: () => getTaskGraph,
14974
15579
  getTaskDependents: () => getTaskDependents,
14975
15580
  getTaskDependencies: () => getTaskDependencies,
14976
- getTask: () => getTask2,
15581
+ getTask: () => getTask,
14977
15582
  getStatus: () => getStatus,
14978
15583
  getStaleTasks: () => getStaleTasks,
14979
15584
  getOverdueTasks: () => getOverdueTasks,
14980
15585
  getNextTask: () => getNextTask,
15586
+ getBlockingTasks: () => getBlockingTasks,
14981
15587
  getBlockingDeps: () => getBlockingDeps,
15588
+ getBlockedTasks: () => getBlockedTasks,
15589
+ getArchivedTasks: () => getArchivedTasks,
14982
15590
  getActiveWork: () => getActiveWork,
14983
15591
  failTask: () => failTask,
14984
15592
  deleteTask: () => deleteTask,
@@ -14990,8 +15598,10 @@ __export(exports_tasks, {
14990
15598
  claimOrSteal: () => claimOrSteal,
14991
15599
  claimNextTask: () => claimNextTask,
14992
15600
  bulkUpdateTasks: () => bulkUpdateTasks,
15601
+ bulkDeleteTasks: () => bulkDeleteTasks,
14993
15602
  bulkCreateTasks: () => bulkCreateTasks,
14994
15603
  archiveTasks: () => archiveTasks,
15604
+ archiveCompletedTasks: () => archiveCompletedTasks,
14995
15605
  addDependency: () => addDependency
14996
15606
  });
14997
15607
  var init_tasks = __esm(() => {
@@ -15169,54 +15779,459 @@ var init_dispatch = __esm(() => {
15169
15779
  init_dispatch_formatter();
15170
15780
  });
15171
15781
 
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
15782
+ // src/db/comments.ts
15783
+ var exports_comments = {};
15784
+ __export(exports_comments, {
15785
+ updateComment: () => updateComment,
15786
+ logProgress: () => logProgress,
15787
+ listComments: () => listComments,
15788
+ getComment: () => getComment,
15789
+ deleteComment: () => deleteComment,
15790
+ addComment: () => addComment
15186
15791
  });
15187
- function addTaskFile(input, db) {
15792
+ function addComment(input, db) {
15188
15793
  const d = db || getDatabase();
15794
+ if (!getTask(input.task_id, d)) {
15795
+ throw new TaskNotFoundError(input.task_id);
15796
+ }
15189
15797
  const id = uuid();
15190
15798
  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);
15799
+ d.run(`INSERT INTO task_comments (id, task_id, agent_id, session_id, content, type, progress_pct, created_at)
15800
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, [
15801
+ id,
15802
+ input.task_id,
15803
+ input.agent_id || null,
15804
+ input.session_id || null,
15805
+ input.content,
15806
+ input.type || "comment",
15807
+ input.progress_pct ?? null,
15808
+ timestamp
15809
+ ]);
15810
+ return getComment(id, d);
15199
15811
  }
15200
- function getTaskFile(id, db) {
15201
- const d = db || getDatabase();
15202
- return d.query("SELECT * FROM task_files WHERE id = ?").get(id);
15812
+ function logProgress(taskId, message, pct, agentId, db) {
15813
+ return addComment({ task_id: taskId, content: message, type: "progress", progress_pct: pct, agent_id: agentId }, db);
15203
15814
  }
15204
- function listTaskFiles(taskId, db) {
15815
+ function getComment(id, db) {
15205
15816
  const d = db || getDatabase();
15206
- return d.query("SELECT * FROM task_files WHERE task_id = ? ORDER BY path").all(taskId);
15817
+ return d.query("SELECT * FROM task_comments WHERE id = ?").get(id);
15207
15818
  }
15208
- function findTasksByFile(path, db) {
15819
+ function listComments(taskId, db) {
15209
15820
  const d = db || getDatabase();
15210
- return d.query("SELECT * FROM task_files WHERE path = ? AND status != 'removed' ORDER BY updated_at DESC").all(path);
15821
+ return d.query("SELECT * FROM task_comments WHERE task_id = ? ORDER BY created_at").all(taskId);
15211
15822
  }
15212
- function updateTaskFileStatus(taskId, path, status, agentId, db) {
15823
+ function updateComment(id, input, db) {
15213
15824
  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;
15825
+ d.run("UPDATE task_comments SET content = ? WHERE id = ?", [input.content, id]);
15826
+ const comment = getComment(id, d);
15827
+ if (!comment) {
15828
+ throw new Error(`Comment not found: ${id}`);
15829
+ }
15830
+ return comment;
15218
15831
  }
15219
- function removeTaskFile(taskId, path, db) {
15832
+ function deleteComment(id, db) {
15833
+ const d = db || getDatabase();
15834
+ const result = d.run("DELETE FROM task_comments WHERE id = ?", [id]);
15835
+ return result.changes > 0;
15836
+ }
15837
+ var init_comments = __esm(() => {
15838
+ init_types2();
15839
+ init_database();
15840
+ init_tasks();
15841
+ });
15842
+
15843
+ // src/lib/search.ts
15844
+ var exports_search = {};
15845
+ __export(exports_search, {
15846
+ searchTasks: () => searchTasks
15847
+ });
15848
+ function rowToTask2(row) {
15849
+ return {
15850
+ ...row,
15851
+ tags: JSON.parse(row.tags || "[]"),
15852
+ metadata: JSON.parse(row.metadata || "{}"),
15853
+ status: row.status,
15854
+ priority: row.priority,
15855
+ requires_approval: Boolean(row.requires_approval)
15856
+ };
15857
+ }
15858
+ function hasFts(db) {
15859
+ try {
15860
+ const result = db.query("SELECT 1 FROM sqlite_master WHERE type='table' AND name='tasks_fts'").get();
15861
+ return result !== null;
15862
+ } catch {
15863
+ return false;
15864
+ }
15865
+ }
15866
+ function escapeFtsQuery(q) {
15867
+ return q.replace(/["*^()]/g, " ").trim().split(/\s+/).filter(Boolean).map((token) => `"${token}"*`).join(" ");
15868
+ }
15869
+ function searchTasks(options, projectId, taskListId, db) {
15870
+ const opts = typeof options === "string" ? { query: options || undefined, project_id: projectId, task_list_id: taskListId } : options;
15871
+ const d = db || getDatabase();
15872
+ clearExpiredLocks(d);
15873
+ const params = [];
15874
+ let sql;
15875
+ const raw = opts.query?.trim() ?? "";
15876
+ const q = raw === "*" ? "" : raw;
15877
+ if (hasFts(d) && q) {
15878
+ const ftsQuery = escapeFtsQuery(q);
15879
+ sql = `SELECT t.* FROM tasks t
15880
+ INNER JOIN tasks_fts fts ON fts.rowid = t.rowid
15881
+ WHERE tasks_fts MATCH ?`;
15882
+ params.push(ftsQuery);
15883
+ } else if (q) {
15884
+ const pattern = `%${q}%`;
15885
+ 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 ?))`;
15886
+ params.push(pattern, pattern, pattern);
15887
+ } else {
15888
+ sql = `SELECT * FROM tasks t WHERE 1=1`;
15889
+ }
15890
+ if (opts.project_id) {
15891
+ sql += " AND t.project_id = ?";
15892
+ params.push(opts.project_id);
15893
+ }
15894
+ if (opts.task_list_id) {
15895
+ sql += " AND t.task_list_id = ?";
15896
+ params.push(opts.task_list_id);
15897
+ }
15898
+ if (opts.status) {
15899
+ if (Array.isArray(opts.status)) {
15900
+ sql += ` AND t.status IN (${opts.status.map(() => "?").join(",")})`;
15901
+ params.push(...opts.status);
15902
+ } else {
15903
+ sql += " AND t.status = ?";
15904
+ params.push(opts.status);
15905
+ }
15906
+ }
15907
+ if (opts.priority) {
15908
+ if (Array.isArray(opts.priority)) {
15909
+ sql += ` AND t.priority IN (${opts.priority.map(() => "?").join(",")})`;
15910
+ params.push(...opts.priority);
15911
+ } else {
15912
+ sql += " AND t.priority = ?";
15913
+ params.push(opts.priority);
15914
+ }
15915
+ }
15916
+ if (opts.assigned_to) {
15917
+ sql += " AND t.assigned_to = ?";
15918
+ params.push(opts.assigned_to);
15919
+ }
15920
+ if (opts.agent_id) {
15921
+ sql += " AND t.agent_id = ?";
15922
+ params.push(opts.agent_id);
15923
+ }
15924
+ if (opts.created_after) {
15925
+ sql += " AND t.created_at > ?";
15926
+ params.push(opts.created_after);
15927
+ }
15928
+ if (opts.updated_after) {
15929
+ sql += " AND t.updated_at > ?";
15930
+ params.push(opts.updated_after);
15931
+ }
15932
+ if (opts.has_dependencies === true) {
15933
+ sql += " AND t.id IN (SELECT task_id FROM task_dependencies)";
15934
+ } else if (opts.has_dependencies === false) {
15935
+ sql += " AND t.id NOT IN (SELECT task_id FROM task_dependencies)";
15936
+ }
15937
+ if (opts.is_blocked === true) {
15938
+ 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')";
15939
+ } else if (opts.is_blocked === false) {
15940
+ 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')";
15941
+ }
15942
+ if (hasFts(d) && q) {
15943
+ sql += ` ORDER BY bm25(tasks_fts),
15944
+ CASE t.priority WHEN 'critical' THEN 0 WHEN 'high' THEN 1 WHEN 'medium' THEN 2 WHEN 'low' THEN 3 END,
15945
+ t.created_at DESC`;
15946
+ } else {
15947
+ sql += ` ORDER BY
15948
+ CASE t.priority WHEN 'critical' THEN 0 WHEN 'high' THEN 1 WHEN 'medium' THEN 2 WHEN 'low' THEN 3 END,
15949
+ t.created_at DESC`;
15950
+ }
15951
+ const rows = d.query(sql).all(...params);
15952
+ return rows.map(rowToTask2);
15953
+ }
15954
+ var init_search = __esm(() => {
15955
+ init_database();
15956
+ });
15957
+
15958
+ // src/db/handoffs.ts
15959
+ var exports_handoffs = {};
15960
+ __export(exports_handoffs, {
15961
+ listHandoffs: () => listHandoffs,
15962
+ getLatestHandoff: () => getLatestHandoff,
15963
+ createHandoff: () => createHandoff
15964
+ });
15965
+ function createHandoff(input, db) {
15966
+ const d = db || getDatabase();
15967
+ const id = uuid();
15968
+ const timestamp = now();
15969
+ d.run(`INSERT INTO handoffs (id, agent_id, project_id, summary, completed, in_progress, blockers, next_steps, created_at)
15970
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
15971
+ id,
15972
+ input.agent_id || null,
15973
+ input.project_id || null,
15974
+ input.summary,
15975
+ input.completed ? JSON.stringify(input.completed) : null,
15976
+ input.in_progress ? JSON.stringify(input.in_progress) : null,
15977
+ input.blockers ? JSON.stringify(input.blockers) : null,
15978
+ input.next_steps ? JSON.stringify(input.next_steps) : null,
15979
+ timestamp
15980
+ ]);
15981
+ return {
15982
+ id,
15983
+ agent_id: input.agent_id || null,
15984
+ project_id: input.project_id || null,
15985
+ summary: input.summary,
15986
+ completed: input.completed || null,
15987
+ in_progress: input.in_progress || null,
15988
+ blockers: input.blockers || null,
15989
+ next_steps: input.next_steps || null,
15990
+ created_at: timestamp
15991
+ };
15992
+ }
15993
+ function rowToHandoff(row) {
15994
+ return {
15995
+ ...row,
15996
+ completed: row.completed ? JSON.parse(row.completed) : null,
15997
+ in_progress: row.in_progress ? JSON.parse(row.in_progress) : null,
15998
+ blockers: row.blockers ? JSON.parse(row.blockers) : null,
15999
+ next_steps: row.next_steps ? JSON.parse(row.next_steps) : null
16000
+ };
16001
+ }
16002
+ function listHandoffs(projectId, limit = 10, db) {
16003
+ const d = db || getDatabase();
16004
+ if (projectId) {
16005
+ return d.query("SELECT * FROM handoffs WHERE project_id = ? ORDER BY rowid DESC LIMIT ?").all(projectId, limit).map(rowToHandoff);
16006
+ }
16007
+ return d.query("SELECT * FROM handoffs ORDER BY rowid DESC LIMIT ?").all(limit).map(rowToHandoff);
16008
+ }
16009
+ function getLatestHandoff(agentId, projectId, db) {
16010
+ const d = db || getDatabase();
16011
+ let query = "SELECT * FROM handoffs WHERE 1=1";
16012
+ const params = [];
16013
+ if (agentId) {
16014
+ query += " AND agent_id = ?";
16015
+ params.push(agentId);
16016
+ }
16017
+ if (projectId) {
16018
+ query += " AND project_id = ?";
16019
+ params.push(projectId);
16020
+ }
16021
+ query += " ORDER BY rowid DESC LIMIT 1";
16022
+ const row = d.query(query).get(...params);
16023
+ return row ? rowToHandoff(row) : null;
16024
+ }
16025
+ var init_handoffs = __esm(() => {
16026
+ init_database();
16027
+ });
16028
+
16029
+ // src/lib/auto-assign.ts
16030
+ var exports_auto_assign = {};
16031
+ __export(exports_auto_assign, {
16032
+ findBestAgent: () => findBestAgent,
16033
+ autoAssignTask: () => autoAssignTask
16034
+ });
16035
+ function findBestAgent(_task, db) {
16036
+ const d = db || getDatabase();
16037
+ const agents = listAgents(d).filter((a) => (a.role || "agent") === "agent");
16038
+ if (agents.length === 0)
16039
+ return null;
16040
+ const inProgressTasks = listTasks({ status: "in_progress" }, d);
16041
+ const idToName = new Map;
16042
+ const load = new Map;
16043
+ for (const a of agents) {
16044
+ idToName.set(a.id, a.name);
16045
+ load.set(a.id, 0);
16046
+ }
16047
+ for (const t of inProgressTasks) {
16048
+ const agentId = t.assigned_to || t.agent_id;
16049
+ if (agentId && load.has(agentId)) {
16050
+ load.set(agentId, (load.get(agentId) || 0) + 1);
16051
+ }
16052
+ }
16053
+ let bestAgent = agents[0].name;
16054
+ let bestLoad = load.get(agents[0].id) ?? 0;
16055
+ for (const a of agents) {
16056
+ const l = load.get(a.id) ?? 0;
16057
+ if (l < bestLoad) {
16058
+ bestAgent = a.name;
16059
+ bestLoad = l;
16060
+ }
16061
+ }
16062
+ return bestAgent;
16063
+ }
16064
+ function getAgentWorkloads(d) {
16065
+ 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();
16066
+ return new Map(rows.map((r) => [r.assigned_to, r.count]));
16067
+ }
16068
+ function buildPrompt(task, agents) {
16069
+ const agentList = agents.map((a) => `- ${a.name} (role: ${a.role}, caps: [${a.capabilities.join(", ")}], active_tasks: ${a.in_progress_tasks})`).join(`
16070
+ `);
16071
+ return `You are a task routing assistant. Given a task and available agents, choose the SINGLE best agent.
16072
+
16073
+ TASK:
16074
+ Title: ${task.title}
16075
+ Priority: ${task.priority}
16076
+ Tags: ${task.tags.join(", ") || "none"}
16077
+ Description: ${task.description?.slice(0, 300) || "none"}
16078
+
16079
+ AVAILABLE AGENTS:
16080
+ ${agentList}
16081
+
16082
+ Rules:
16083
+ - Match task tags/content to agent capabilities
16084
+ - Prefer agents with fewer active tasks
16085
+ - Prefer agents whose role fits the task (lead for critical, developer for features, qa for testing)
16086
+ - If no clear match, pick the agent with fewest active tasks
16087
+
16088
+ Respond with ONLY a JSON object: {"agent_name": "<name>", "reason": "<one sentence>"}`;
16089
+ }
16090
+ async function callCerebras(prompt, apiKey) {
16091
+ try {
16092
+ const resp = await fetch(CEREBRAS_API_URL, {
16093
+ method: "POST",
16094
+ headers: { "Content-Type": "application/json", Authorization: `Bearer ${apiKey}` },
16095
+ body: JSON.stringify({
16096
+ model: CEREBRAS_MODEL,
16097
+ messages: [{ role: "user", content: prompt }],
16098
+ max_tokens: 150,
16099
+ temperature: 0
16100
+ }),
16101
+ signal: AbortSignal.timeout(1e4)
16102
+ });
16103
+ if (!resp.ok)
16104
+ return null;
16105
+ const data = await resp.json();
16106
+ const content = data?.choices?.[0]?.message?.content?.trim();
16107
+ if (!content)
16108
+ return null;
16109
+ const match = content.match(/\{[^}]+\}/s);
16110
+ if (!match)
16111
+ return null;
16112
+ return JSON.parse(match[0]);
16113
+ } catch {
16114
+ return null;
16115
+ }
16116
+ }
16117
+ async function autoAssignTask(taskId, db) {
16118
+ const d = db || getDatabase();
16119
+ const task = getTask(taskId, d);
16120
+ if (!task)
16121
+ throw new Error(`Task ${taskId} not found`);
16122
+ const agents = listAgents(d).filter((a) => a.status === "active");
16123
+ if (agents.length === 0) {
16124
+ return { task_id: taskId, assigned_to: null, agent_name: null, method: "no_agents" };
16125
+ }
16126
+ const workloads = getAgentWorkloads(d);
16127
+ const apiKey = process.env["CEREBRAS_API_KEY"];
16128
+ let selectedAgent = null;
16129
+ let method = "capability_match";
16130
+ let reason;
16131
+ if (apiKey) {
16132
+ const agentData = agents.map((a) => ({
16133
+ id: a.id,
16134
+ name: a.name,
16135
+ role: a.role || "agent",
16136
+ capabilities: a.capabilities || [],
16137
+ in_progress_tasks: workloads.get(a.id) ?? 0
16138
+ }));
16139
+ const result = await callCerebras(buildPrompt({
16140
+ title: task.title,
16141
+ description: task.description,
16142
+ priority: task.priority,
16143
+ tags: task.tags || []
16144
+ }, agentData), apiKey);
16145
+ if (result?.agent_name) {
16146
+ selectedAgent = agents.find((a) => a.name === result.agent_name) ?? null;
16147
+ if (selectedAgent) {
16148
+ method = "cerebras";
16149
+ reason = result.reason;
16150
+ }
16151
+ }
16152
+ }
16153
+ if (!selectedAgent) {
16154
+ const taskTags = task.tags || [];
16155
+ const capable = getCapableAgents(taskTags, { min_score: 0, limit: 10 }, d);
16156
+ if (capable.length > 0) {
16157
+ const sorted = capable.sort((a, b) => {
16158
+ if (b.score !== a.score)
16159
+ return b.score - a.score;
16160
+ return (workloads.get(a.agent.id) ?? 0) - (workloads.get(b.agent.id) ?? 0);
16161
+ });
16162
+ selectedAgent = sorted[0].agent;
16163
+ reason = `Capability match (score: ${sorted[0].score.toFixed(2)})`;
16164
+ } else {
16165
+ selectedAgent = agents.slice().sort((a, b) => (workloads.get(a.id) ?? 0) - (workloads.get(b.id) ?? 0))[0];
16166
+ reason = `Least busy agent (${workloads.get(selectedAgent.id) ?? 0} active tasks)`;
16167
+ }
16168
+ }
16169
+ if (selectedAgent) {
16170
+ updateTask(taskId, { assigned_to: selectedAgent.id, version: task.version }, d);
16171
+ }
16172
+ return {
16173
+ task_id: taskId,
16174
+ assigned_to: selectedAgent?.id ?? null,
16175
+ agent_name: selectedAgent?.name ?? null,
16176
+ method,
16177
+ reason
16178
+ };
16179
+ }
16180
+ var CEREBRAS_API_URL = "https://api.cerebras.ai/v1/chat/completions", CEREBRAS_MODEL = "llama-3.3-70b";
16181
+ var init_auto_assign = __esm(() => {
16182
+ init_database();
16183
+ init_tasks();
16184
+ init_agents();
16185
+ });
16186
+
16187
+ // src/db/task-files.ts
16188
+ var exports_task_files = {};
16189
+ __export(exports_task_files, {
16190
+ updateTaskFileStatus: () => updateTaskFileStatus,
16191
+ removeTaskFile: () => removeTaskFile,
16192
+ listTaskFiles: () => listTaskFiles,
16193
+ listActiveFiles: () => listActiveFiles,
16194
+ getTaskFile: () => getTaskFile,
16195
+ getFileHeatMap: () => getFileHeatMap,
16196
+ findTasksByFile: () => findTasksByFile,
16197
+ detectFileConflicts: () => detectFileConflicts,
16198
+ bulkFindTasksByFiles: () => bulkFindTasksByFiles,
16199
+ bulkAddTaskFiles: () => bulkAddTaskFiles,
16200
+ addTaskFile: () => addTaskFile
16201
+ });
16202
+ function addTaskFile(input, db) {
16203
+ const d = db || getDatabase();
16204
+ const id = uuid();
16205
+ const timestamp = now();
16206
+ const existing = d.query("SELECT id FROM task_files WHERE task_id = ? AND path = ?").get(input.task_id, input.path);
16207
+ if (existing) {
16208
+ 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]);
16209
+ return getTaskFile(existing.id, d);
16210
+ }
16211
+ d.run(`INSERT INTO task_files (id, task_id, path, status, agent_id, note, created_at, updated_at)
16212
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, [id, input.task_id, input.path, input.status || "active", input.agent_id || null, input.note || null, timestamp, timestamp]);
16213
+ return getTaskFile(id, d);
16214
+ }
16215
+ function getTaskFile(id, db) {
16216
+ const d = db || getDatabase();
16217
+ return d.query("SELECT * FROM task_files WHERE id = ?").get(id);
16218
+ }
16219
+ function listTaskFiles(taskId, db) {
16220
+ const d = db || getDatabase();
16221
+ return d.query("SELECT * FROM task_files WHERE task_id = ? ORDER BY path").all(taskId);
16222
+ }
16223
+ function findTasksByFile(path, db) {
16224
+ const d = db || getDatabase();
16225
+ return d.query("SELECT * FROM task_files WHERE path = ? AND status != 'removed' ORDER BY updated_at DESC").all(path);
16226
+ }
16227
+ function updateTaskFileStatus(taskId, path, status, agentId, db) {
16228
+ const d = db || getDatabase();
16229
+ const timestamp = now();
16230
+ 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]);
16231
+ const row = d.query("SELECT * FROM task_files WHERE task_id = ? AND path = ?").get(taskId, path);
16232
+ return row;
16233
+ }
16234
+ function removeTaskFile(taskId, path, db) {
15220
16235
  const d = db || getDatabase();
15221
16236
  const result = d.run("DELETE FROM task_files WHERE task_id = ? AND path = ?", [taskId, path]);
15222
16237
  return result.changes > 0;
@@ -15451,14 +16466,653 @@ function autoDetectFileRelationships(taskId, db) {
15451
16466
  var RELATIONSHIP_TYPES;
15452
16467
  var init_task_relationships = __esm(() => {
15453
16468
  init_database();
15454
- RELATIONSHIP_TYPES = [
15455
- "related_to",
15456
- "conflicts_with",
15457
- "similar_to",
15458
- "duplicates",
15459
- "supersedes",
15460
- "modifies_same_file"
15461
- ];
16469
+ RELATIONSHIP_TYPES = [
16470
+ "related_to",
16471
+ "conflicts_with",
16472
+ "similar_to",
16473
+ "duplicates",
16474
+ "supersedes",
16475
+ "modifies_same_file"
16476
+ ];
16477
+ });
16478
+
16479
+ // src/db/task-commits.ts
16480
+ var exports_task_commits = {};
16481
+ __export(exports_task_commits, {
16482
+ unlinkTaskCommit: () => unlinkTaskCommit,
16483
+ linkTaskToCommit: () => linkTaskToCommit,
16484
+ getTaskCommits: () => getTaskCommits,
16485
+ findTaskByCommit: () => findTaskByCommit
16486
+ });
16487
+ function rowToCommit(row) {
16488
+ return {
16489
+ ...row,
16490
+ files_changed: row.files_changed ? JSON.parse(row.files_changed) : null
16491
+ };
16492
+ }
16493
+ function linkTaskToCommit(input, db) {
16494
+ const d = db || getDatabase();
16495
+ const existing = d.query("SELECT * FROM task_commits WHERE task_id = ? AND sha = ?").get(input.task_id, input.sha);
16496
+ if (existing) {
16497
+ 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]);
16498
+ return rowToCommit(d.query("SELECT * FROM task_commits WHERE id = ?").get(existing.id));
16499
+ }
16500
+ const id = uuid();
16501
+ 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()]);
16502
+ return rowToCommit(d.query("SELECT * FROM task_commits WHERE id = ?").get(id));
16503
+ }
16504
+ function getTaskCommits(taskId, db) {
16505
+ const d = db || getDatabase();
16506
+ return d.query("SELECT * FROM task_commits WHERE task_id = ? ORDER BY committed_at DESC, created_at DESC").all(taskId).map(rowToCommit);
16507
+ }
16508
+ function findTaskByCommit(sha, db) {
16509
+ const d = db || getDatabase();
16510
+ const row = d.query("SELECT * FROM task_commits WHERE sha = ? OR sha LIKE ? LIMIT 1").get(sha, `${sha}%`);
16511
+ if (!row)
16512
+ return null;
16513
+ return { task_id: row.task_id, commit: rowToCommit(row) };
16514
+ }
16515
+ function unlinkTaskCommit(taskId, sha, db) {
16516
+ const d = db || getDatabase();
16517
+ return d.run("DELETE FROM task_commits WHERE task_id = ? AND (sha = ? OR sha LIKE ?)", [taskId, sha, `${sha}%`]).changes > 0;
16518
+ }
16519
+ var init_task_commits = __esm(() => {
16520
+ init_database();
16521
+ });
16522
+
16523
+ // src/db/file-locks.ts
16524
+ var exports_file_locks = {};
16525
+ __export(exports_file_locks, {
16526
+ unlockFile: () => unlockFile,
16527
+ lockFile: () => lockFile,
16528
+ listFileLocks: () => listFileLocks,
16529
+ forceUnlockFile: () => forceUnlockFile,
16530
+ cleanExpiredFileLocks: () => cleanExpiredFileLocks,
16531
+ checkFileLock: () => checkFileLock,
16532
+ FILE_LOCK_DEFAULT_TTL_SECONDS: () => FILE_LOCK_DEFAULT_TTL_SECONDS
16533
+ });
16534
+ function expiresAt(ttlSeconds) {
16535
+ return new Date(Date.now() + ttlSeconds * 1000).toISOString();
16536
+ }
16537
+ function cleanExpiredFileLocks(db) {
16538
+ const d = db || getDatabase();
16539
+ const result = d.run("DELETE FROM file_locks WHERE expires_at <= ?", [now()]);
16540
+ return result.changes;
16541
+ }
16542
+ function lockFile(input, db) {
16543
+ const d = db || getDatabase();
16544
+ const ttl = input.ttl_seconds ?? FILE_LOCK_DEFAULT_TTL_SECONDS;
16545
+ const expiry = expiresAt(ttl);
16546
+ const timestamp = now();
16547
+ cleanExpiredFileLocks(d);
16548
+ const existing = d.query("SELECT * FROM file_locks WHERE path = ?").get(input.path);
16549
+ if (existing) {
16550
+ if (existing.agent_id === input.agent_id) {
16551
+ d.run("UPDATE file_locks SET expires_at = ?, task_id = COALESCE(?, task_id) WHERE id = ?", [expiry, input.task_id ?? null, existing.id]);
16552
+ return d.query("SELECT * FROM file_locks WHERE id = ?").get(existing.id);
16553
+ }
16554
+ throw new LockError(input.path, existing.agent_id);
16555
+ }
16556
+ const id = uuid();
16557
+ 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]);
16558
+ return d.query("SELECT * FROM file_locks WHERE id = ?").get(id);
16559
+ }
16560
+ function unlockFile(path, agentId, db) {
16561
+ const d = db || getDatabase();
16562
+ cleanExpiredFileLocks(d);
16563
+ const result = d.run("DELETE FROM file_locks WHERE path = ? AND agent_id = ?", [path, agentId]);
16564
+ return result.changes > 0;
16565
+ }
16566
+ function checkFileLock(path, db) {
16567
+ const d = db || getDatabase();
16568
+ cleanExpiredFileLocks(d);
16569
+ return d.query("SELECT * FROM file_locks WHERE path = ?").get(path);
16570
+ }
16571
+ function listFileLocks(agentId, db) {
16572
+ const d = db || getDatabase();
16573
+ cleanExpiredFileLocks(d);
16574
+ if (agentId) {
16575
+ return d.query("SELECT * FROM file_locks WHERE agent_id = ? ORDER BY created_at DESC").all(agentId);
16576
+ }
16577
+ return d.query("SELECT * FROM file_locks ORDER BY created_at DESC").all();
16578
+ }
16579
+ function forceUnlockFile(path, db) {
16580
+ const d = db || getDatabase();
16581
+ const result = d.run("DELETE FROM file_locks WHERE path = ?", [path]);
16582
+ return result.changes > 0;
16583
+ }
16584
+ var FILE_LOCK_DEFAULT_TTL_SECONDS;
16585
+ var init_file_locks = __esm(() => {
16586
+ init_database();
16587
+ init_types2();
16588
+ FILE_LOCK_DEFAULT_TTL_SECONDS = 30 * 60;
16589
+ });
16590
+
16591
+ // src/db/kg.ts
16592
+ var exports_kg = {};
16593
+ __export(exports_kg, {
16594
+ syncKgEdges: () => syncKgEdges,
16595
+ removeKgEdges: () => removeKgEdges,
16596
+ getRelated: () => getRelated,
16597
+ getImpactAnalysis: () => getImpactAnalysis,
16598
+ getCriticalPath: () => getCriticalPath,
16599
+ findPath: () => findPath,
16600
+ addKgEdge: () => addKgEdge
16601
+ });
16602
+ function rowToEdge(row) {
16603
+ return {
16604
+ ...row,
16605
+ metadata: JSON.parse(row.metadata || "{}")
16606
+ };
16607
+ }
16608
+ function syncKgEdges(db) {
16609
+ const d = db || getDatabase();
16610
+ let synced = 0;
16611
+ const tx = d.transaction(() => {
16612
+ const deps = d.query("SELECT task_id, depends_on FROM task_dependencies").all();
16613
+ for (const dep of deps) {
16614
+ synced += upsertEdge(d, dep.task_id, "task", dep.depends_on, "task", "depends_on");
16615
+ }
16616
+ const assignments = d.query("SELECT id, assigned_to FROM tasks WHERE assigned_to IS NOT NULL").all();
16617
+ for (const a of assignments) {
16618
+ synced += upsertEdge(d, a.id, "task", a.assigned_to, "agent", "assigned_to");
16619
+ }
16620
+ const agents = d.query("SELECT id, reports_to FROM agents WHERE reports_to IS NOT NULL").all();
16621
+ for (const a of agents) {
16622
+ synced += upsertEdge(d, a.id, "agent", a.reports_to, "agent", "reports_to");
16623
+ }
16624
+ const files = d.query("SELECT task_id, path FROM task_files WHERE status != 'removed'").all();
16625
+ for (const f of files) {
16626
+ synced += upsertEdge(d, f.task_id, "task", f.path, "file", "references_file");
16627
+ }
16628
+ const taskProjects = d.query("SELECT id, project_id FROM tasks WHERE project_id IS NOT NULL").all();
16629
+ for (const tp of taskProjects) {
16630
+ synced += upsertEdge(d, tp.id, "task", tp.project_id, "project", "in_project");
16631
+ }
16632
+ const taskPlans = d.query("SELECT id, plan_id FROM tasks WHERE plan_id IS NOT NULL").all();
16633
+ for (const tp of taskPlans) {
16634
+ synced += upsertEdge(d, tp.id, "task", tp.plan_id, "plan", "in_plan");
16635
+ }
16636
+ try {
16637
+ const rels = d.query("SELECT source_task_id, target_task_id, relationship_type FROM task_relationships").all();
16638
+ for (const r of rels) {
16639
+ synced += upsertEdge(d, r.source_task_id, "task", r.target_task_id, "task", r.relationship_type);
16640
+ }
16641
+ } catch {}
16642
+ });
16643
+ tx();
16644
+ return { synced };
16645
+ }
16646
+ function upsertEdge(d, sourceId, sourceType, targetId, targetType, relationType, weight = 1) {
16647
+ try {
16648
+ d.run(`INSERT OR IGNORE INTO kg_edges (id, source_id, source_type, target_id, target_type, relation_type, weight, metadata, created_at)
16649
+ VALUES (?, ?, ?, ?, ?, ?, ?, '{}', ?)`, [uuid(), sourceId, sourceType, targetId, targetType, relationType, weight, now()]);
16650
+ return 1;
16651
+ } catch {
16652
+ return 0;
16653
+ }
16654
+ }
16655
+ function getRelated(entityId, opts, db) {
16656
+ const d = db || getDatabase();
16657
+ const direction = opts?.direction || "both";
16658
+ const conditions = [];
16659
+ const params = [];
16660
+ if (direction === "outgoing" || direction === "both") {
16661
+ conditions.push("source_id = ?");
16662
+ params.push(entityId);
16663
+ }
16664
+ if (direction === "incoming" || direction === "both") {
16665
+ conditions.push("target_id = ?");
16666
+ params.push(entityId);
16667
+ }
16668
+ let sql = `SELECT * FROM kg_edges WHERE (${conditions.join(" OR ")})`;
16669
+ if (opts?.relation_type) {
16670
+ sql += " AND relation_type = ?";
16671
+ params.push(opts.relation_type);
16672
+ }
16673
+ if (opts?.entity_type) {
16674
+ sql += " AND (source_type = ? OR target_type = ?)";
16675
+ params.push(opts.entity_type, opts.entity_type);
16676
+ }
16677
+ sql += " ORDER BY weight DESC, created_at DESC";
16678
+ if (opts?.limit) {
16679
+ sql += " LIMIT ?";
16680
+ params.push(opts.limit);
16681
+ }
16682
+ return d.query(sql).all(...params).map(rowToEdge);
16683
+ }
16684
+ function findPath(sourceId, targetId, opts, db) {
16685
+ const d = db || getDatabase();
16686
+ const maxDepth = opts?.max_depth || 5;
16687
+ const visited = new Set;
16688
+ const queue = [{ id: sourceId, path: [] }];
16689
+ const results = [];
16690
+ visited.add(sourceId);
16691
+ while (queue.length > 0) {
16692
+ const current = queue.shift();
16693
+ if (current.path.length >= maxDepth)
16694
+ continue;
16695
+ let sql = "SELECT * FROM kg_edges WHERE source_id = ?";
16696
+ const params = [current.id];
16697
+ if (opts?.relation_types && opts.relation_types.length > 0) {
16698
+ const placeholders = opts.relation_types.map(() => "?").join(",");
16699
+ sql += ` AND relation_type IN (${placeholders})`;
16700
+ params.push(...opts.relation_types);
16701
+ }
16702
+ const edges = d.query(sql).all(...params).map(rowToEdge);
16703
+ for (const edge of edges) {
16704
+ const nextId = edge.target_id;
16705
+ const newPath = [...current.path, edge];
16706
+ if (nextId === targetId) {
16707
+ results.push(newPath);
16708
+ if (results.length >= 3)
16709
+ return results;
16710
+ continue;
16711
+ }
16712
+ if (!visited.has(nextId)) {
16713
+ visited.add(nextId);
16714
+ queue.push({ id: nextId, path: newPath });
16715
+ }
16716
+ }
16717
+ }
16718
+ return results;
16719
+ }
16720
+ function getImpactAnalysis(entityId, opts, db) {
16721
+ const d = db || getDatabase();
16722
+ const maxDepth = opts?.max_depth || 3;
16723
+ const results = [];
16724
+ const visited = new Set;
16725
+ visited.add(entityId);
16726
+ const queue = [{ id: entityId, depth: 0 }];
16727
+ while (queue.length > 0) {
16728
+ const current = queue.shift();
16729
+ if (current.depth >= maxDepth)
16730
+ continue;
16731
+ let sql = "SELECT * FROM kg_edges WHERE source_id = ?";
16732
+ const params = [current.id];
16733
+ if (opts?.relation_types && opts.relation_types.length > 0) {
16734
+ const placeholders = opts.relation_types.map(() => "?").join(",");
16735
+ sql += ` AND relation_type IN (${placeholders})`;
16736
+ params.push(...opts.relation_types);
16737
+ }
16738
+ const edges = d.query(sql).all(...params).map(rowToEdge);
16739
+ for (const edge of edges) {
16740
+ if (!visited.has(edge.target_id)) {
16741
+ visited.add(edge.target_id);
16742
+ results.push({
16743
+ entity_id: edge.target_id,
16744
+ entity_type: edge.target_type,
16745
+ depth: current.depth + 1,
16746
+ relation: edge.relation_type
16747
+ });
16748
+ queue.push({ id: edge.target_id, depth: current.depth + 1 });
16749
+ }
16750
+ }
16751
+ }
16752
+ return results;
16753
+ }
16754
+ function getCriticalPath(opts, db) {
16755
+ const d = db || getDatabase();
16756
+ let sql = `SELECT source_id, target_id FROM kg_edges WHERE relation_type = 'depends_on'`;
16757
+ const params = [];
16758
+ if (opts?.project_id) {
16759
+ sql += ` AND source_id IN (SELECT id FROM tasks WHERE project_id = ?)`;
16760
+ params.push(opts.project_id);
16761
+ }
16762
+ const edges = d.query(sql).all(...params);
16763
+ const blocks = new Map;
16764
+ for (const e of edges) {
16765
+ if (!blocks.has(e.target_id))
16766
+ blocks.set(e.target_id, new Set);
16767
+ blocks.get(e.target_id).add(e.source_id);
16768
+ }
16769
+ const results = [];
16770
+ for (const [taskId] of blocks) {
16771
+ const visited = new Set;
16772
+ const q = [taskId];
16773
+ let maxDepth = 0;
16774
+ let depth = 0;
16775
+ let levelSize = q.length;
16776
+ while (q.length > 0) {
16777
+ const node = q.shift();
16778
+ levelSize--;
16779
+ const downstream = blocks.get(node);
16780
+ if (downstream) {
16781
+ for (const d2 of downstream) {
16782
+ if (!visited.has(d2)) {
16783
+ visited.add(d2);
16784
+ q.push(d2);
16785
+ }
16786
+ }
16787
+ }
16788
+ if (levelSize === 0) {
16789
+ depth++;
16790
+ maxDepth = Math.max(maxDepth, depth);
16791
+ levelSize = q.length;
16792
+ }
16793
+ }
16794
+ if (visited.size > 0) {
16795
+ results.push({ task_id: taskId, blocking_count: visited.size, depth: maxDepth });
16796
+ }
16797
+ }
16798
+ results.sort((a, b) => b.blocking_count - a.blocking_count);
16799
+ return results.slice(0, opts?.limit || 20);
16800
+ }
16801
+ function addKgEdge(sourceId, sourceType, targetId, targetType, relationType, weight = 1, metadata, db) {
16802
+ const d = db || getDatabase();
16803
+ const id = uuid();
16804
+ const timestamp = now();
16805
+ d.run(`INSERT OR IGNORE INTO kg_edges (id, source_id, source_type, target_id, target_type, relation_type, weight, metadata, created_at)
16806
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`, [id, sourceId, sourceType, targetId, targetType, relationType, weight, JSON.stringify(metadata || {}), timestamp]);
16807
+ return { id, source_id: sourceId, source_type: sourceType, target_id: targetId, target_type: targetType, relation_type: relationType, weight, metadata: metadata || {}, created_at: timestamp };
16808
+ }
16809
+ function removeKgEdges(sourceId, targetId, relationType, db) {
16810
+ const d = db || getDatabase();
16811
+ if (relationType) {
16812
+ return d.run("DELETE FROM kg_edges WHERE source_id = ? AND target_id = ? AND relation_type = ?", [sourceId, targetId, relationType]).changes;
16813
+ }
16814
+ return d.run("DELETE FROM kg_edges WHERE source_id = ? AND target_id = ?", [sourceId, targetId]).changes;
16815
+ }
16816
+ var init_kg = __esm(() => {
16817
+ init_database();
16818
+ });
16819
+
16820
+ // src/db/project-agent-roles.ts
16821
+ var exports_project_agent_roles = {};
16822
+ __export(exports_project_agent_roles, {
16823
+ setProjectAgentRole: () => setProjectAgentRole,
16824
+ removeProjectAgentRole: () => removeProjectAgentRole,
16825
+ listProjectAgentRoles: () => listProjectAgentRoles,
16826
+ getProjectOrgChart: () => getProjectOrgChart,
16827
+ getAgentProjectRoles: () => getAgentProjectRoles
16828
+ });
16829
+ function rowToRole(row) {
16830
+ return { ...row, is_lead: row.is_lead === 1 };
16831
+ }
16832
+ function setProjectAgentRole(projectId, agentId, role, isLead = false, db) {
16833
+ const d = db || getDatabase();
16834
+ const existing = d.query("SELECT * FROM project_agent_roles WHERE project_id = ? AND agent_id = ? AND role = ?").get(projectId, agentId, role);
16835
+ if (existing) {
16836
+ d.run("UPDATE project_agent_roles SET is_lead = ? WHERE id = ?", [isLead ? 1 : 0, existing.id]);
16837
+ return rowToRole(d.query("SELECT * FROM project_agent_roles WHERE id = ?").get(existing.id));
16838
+ }
16839
+ const id = uuid();
16840
+ 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()]);
16841
+ return rowToRole(d.query("SELECT * FROM project_agent_roles WHERE id = ?").get(id));
16842
+ }
16843
+ function removeProjectAgentRole(projectId, agentId, role, db) {
16844
+ const d = db || getDatabase();
16845
+ if (role) {
16846
+ return d.run("DELETE FROM project_agent_roles WHERE project_id = ? AND agent_id = ? AND role = ?", [projectId, agentId, role]).changes;
16847
+ }
16848
+ return d.run("DELETE FROM project_agent_roles WHERE project_id = ? AND agent_id = ?", [projectId, agentId]).changes;
16849
+ }
16850
+ function listProjectAgentRoles(projectId, db) {
16851
+ const d = db || getDatabase();
16852
+ return d.query("SELECT * FROM project_agent_roles WHERE project_id = ? ORDER BY role, created_at").all(projectId).map(rowToRole);
16853
+ }
16854
+ function getAgentProjectRoles(agentId, db) {
16855
+ const d = db || getDatabase();
16856
+ return d.query("SELECT * FROM project_agent_roles WHERE agent_id = ? ORDER BY project_id, role").all(agentId).map(rowToRole);
16857
+ }
16858
+ function getProjectOrgChart(projectId, opts, db) {
16859
+ const d = db || getDatabase();
16860
+ const globalTree = getOrgChart(d);
16861
+ const projectRoles = listProjectAgentRoles(projectId, d);
16862
+ const rolesByAgent = new Map;
16863
+ for (const pr of projectRoles) {
16864
+ if (!rolesByAgent.has(pr.agent_id))
16865
+ rolesByAgent.set(pr.agent_id, { roles: [], isLead: false });
16866
+ const entry = rolesByAgent.get(pr.agent_id);
16867
+ entry.roles.push(pr.role);
16868
+ if (pr.is_lead)
16869
+ entry.isLead = true;
16870
+ }
16871
+ function augmentTree(nodes) {
16872
+ return nodes.map((n) => {
16873
+ const override = rolesByAgent.get(n.agent.id);
16874
+ return {
16875
+ ...n,
16876
+ reports: augmentTree(n.reports),
16877
+ project_roles: override?.roles ?? [],
16878
+ is_project_lead: override?.isLead ?? false
16879
+ };
16880
+ }).filter((n) => {
16881
+ if (!opts?.filter_to_project)
16882
+ return true;
16883
+ const hasRole = n.project_roles.length > 0;
16884
+ const hasDescendant = n.reports.length > 0;
16885
+ return hasRole || hasDescendant;
16886
+ });
16887
+ }
16888
+ return augmentTree(globalTree);
16889
+ }
16890
+ var init_project_agent_roles = __esm(() => {
16891
+ init_database();
16892
+ init_agents();
16893
+ });
16894
+
16895
+ // src/db/patrol.ts
16896
+ var exports_patrol = {};
16897
+ __export(exports_patrol, {
16898
+ patrolTasks: () => patrolTasks,
16899
+ getReviewQueue: () => getReviewQueue
16900
+ });
16901
+ function rowToTask3(row) {
16902
+ return {
16903
+ ...row,
16904
+ status: row.status,
16905
+ priority: row.priority,
16906
+ tags: JSON.parse(row.tags || "[]"),
16907
+ metadata: JSON.parse(row.metadata || "{}"),
16908
+ requires_approval: Boolean(row.requires_approval)
16909
+ };
16910
+ }
16911
+ function patrolTasks(opts, db) {
16912
+ const d = db || getDatabase();
16913
+ const stuckMinutes = opts?.stuck_minutes || 60;
16914
+ const confidenceThreshold = opts?.confidence_threshold || 0.5;
16915
+ const issues = [];
16916
+ let projectFilter = "";
16917
+ const projectParams = [];
16918
+ if (opts?.project_id) {
16919
+ projectFilter = " AND project_id = ?";
16920
+ projectParams.push(opts.project_id);
16921
+ }
16922
+ const stuckCutoff = new Date(Date.now() - stuckMinutes * 60 * 1000).toISOString();
16923
+ const stuckRows = d.query(`SELECT * FROM tasks WHERE status = 'in_progress' AND updated_at < ?${projectFilter} ORDER BY updated_at ASC`).all(stuckCutoff, ...projectParams);
16924
+ for (const row of stuckRows) {
16925
+ const task = rowToTask3(row);
16926
+ const minutesStuck = Math.round((Date.now() - new Date(task.updated_at).getTime()) / 60000);
16927
+ issues.push({
16928
+ type: "stuck",
16929
+ task_id: task.id,
16930
+ task_title: task.title,
16931
+ severity: minutesStuck > 480 ? "critical" : minutesStuck > 120 ? "high" : "medium",
16932
+ detail: `In progress for ${minutesStuck} minutes without update`
16933
+ });
16934
+ }
16935
+ 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);
16936
+ for (const row of lowConfRows) {
16937
+ const task = rowToTask3(row);
16938
+ issues.push({
16939
+ type: "low_confidence",
16940
+ task_id: task.id,
16941
+ task_title: task.title,
16942
+ severity: (task.confidence ?? 0) < 0.3 ? "high" : "medium",
16943
+ detail: `Completed with confidence ${task.confidence} (threshold: ${confidenceThreshold})`
16944
+ });
16945
+ }
16946
+ 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();
16947
+ for (const row of orphanedRows) {
16948
+ const task = rowToTask3(row);
16949
+ issues.push({
16950
+ type: "orphaned",
16951
+ task_id: task.id,
16952
+ task_title: task.title,
16953
+ severity: "low",
16954
+ detail: "Pending task with no project, no agent, and no assignee"
16955
+ });
16956
+ }
16957
+ 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);
16958
+ for (const row of needsReviewRows) {
16959
+ const task = rowToTask3(row);
16960
+ issues.push({
16961
+ type: "needs_review",
16962
+ task_id: task.id,
16963
+ task_title: task.title,
16964
+ severity: "medium",
16965
+ detail: "Completed but requires approval \u2014 not yet reviewed"
16966
+ });
16967
+ }
16968
+ const pendingWithDeps = d.query(`SELECT t.* FROM tasks t
16969
+ WHERE t.status = 'pending'${projectFilter}
16970
+ AND t.id IN (SELECT task_id FROM task_dependencies)`).all(...projectParams);
16971
+ for (const row of pendingWithDeps) {
16972
+ const deps = d.query(`SELECT d.depends_on, t.status FROM task_dependencies d
16973
+ JOIN tasks t ON t.id = d.depends_on
16974
+ WHERE d.task_id = ?`).all(row.id);
16975
+ if (deps.length > 0 && deps.every((dep) => dep.status === "failed" || dep.status === "cancelled")) {
16976
+ const task = rowToTask3(row);
16977
+ issues.push({
16978
+ type: "zombie_blocked",
16979
+ task_id: task.id,
16980
+ task_title: task.title,
16981
+ severity: "high",
16982
+ detail: `Blocked by ${deps.length} task(s) that are all failed/cancelled \u2014 will never unblock`
16983
+ });
16984
+ }
16985
+ }
16986
+ const severityOrder = { critical: 0, high: 1, medium: 2, low: 3 };
16987
+ issues.sort((a, b) => severityOrder[a.severity] - severityOrder[b.severity]);
16988
+ return {
16989
+ issues,
16990
+ total_issues: issues.length,
16991
+ scanned_at: new Date().toISOString()
16992
+ };
16993
+ }
16994
+ function getReviewQueue(opts, db) {
16995
+ const d = db || getDatabase();
16996
+ let sql = `SELECT * FROM tasks WHERE status = 'completed' AND (
16997
+ (requires_approval = 1 AND approved_by IS NULL)
16998
+ OR (confidence IS NOT NULL AND confidence < 0.5)
16999
+ )`;
17000
+ const params = [];
17001
+ if (opts?.project_id) {
17002
+ sql += " AND project_id = ?";
17003
+ params.push(opts.project_id);
17004
+ }
17005
+ sql += " ORDER BY CASE WHEN requires_approval = 1 AND approved_by IS NULL THEN 0 ELSE 1 END, confidence ASC";
17006
+ if (opts?.limit) {
17007
+ sql += " LIMIT ?";
17008
+ params.push(opts.limit);
17009
+ }
17010
+ return d.query(sql).all(...params).map(rowToTask3);
17011
+ }
17012
+ var init_patrol = __esm(() => {
17013
+ init_database();
17014
+ });
17015
+
17016
+ // src/db/agent-metrics.ts
17017
+ var exports_agent_metrics = {};
17018
+ __export(exports_agent_metrics, {
17019
+ scoreTask: () => scoreTask,
17020
+ getLeaderboard: () => getLeaderboard,
17021
+ getAgentMetrics: () => getAgentMetrics
17022
+ });
17023
+ function getAgentMetrics(agentId, opts, db) {
17024
+ const d = db || getDatabase();
17025
+ const agent = d.query("SELECT id, name FROM agents WHERE id = ? OR LOWER(name) = LOWER(?)").get(agentId, agentId);
17026
+ if (!agent)
17027
+ return null;
17028
+ let projectFilter = "";
17029
+ const params = [agent.id, agent.id];
17030
+ if (opts?.project_id) {
17031
+ projectFilter = " AND project_id = ?";
17032
+ params.push(opts.project_id);
17033
+ }
17034
+ const completed = d.query(`SELECT COUNT(*) as count FROM tasks WHERE (agent_id = ? OR assigned_to = ?) AND status = 'completed'${projectFilter}`).get(...params).count;
17035
+ const failed = d.query(`SELECT COUNT(*) as count FROM tasks WHERE (agent_id = ? OR assigned_to = ?) AND status = 'failed'${projectFilter}`).get(...params).count;
17036
+ const inProgress = d.query(`SELECT COUNT(*) as count FROM tasks WHERE (agent_id = ? OR assigned_to = ?) AND status = 'in_progress'${projectFilter}`).get(...params).count;
17037
+ const total = completed + failed;
17038
+ const completionRate = total > 0 ? completed / total : 0;
17039
+ const avgTime = d.query(`SELECT AVG(
17040
+ (julianday(completed_at) - julianday(created_at)) * 24 * 60
17041
+ ) as avg_minutes
17042
+ FROM tasks
17043
+ WHERE (agent_id = ? OR assigned_to = ?) AND status = 'completed' AND completed_at IS NOT NULL${projectFilter}`).get(...params);
17044
+ const avgConf = d.query(`SELECT AVG(confidence) as avg_confidence
17045
+ FROM tasks
17046
+ WHERE (agent_id = ? OR assigned_to = ?) AND status = 'completed' AND confidence IS NOT NULL${projectFilter}`).get(...params);
17047
+ const reviewTasks = d.query(`SELECT metadata FROM tasks
17048
+ WHERE (agent_id = ? OR assigned_to = ?) AND status = 'completed'${projectFilter}
17049
+ AND metadata LIKE '%_review_score%'`).all(...params);
17050
+ let reviewScoreAvg = null;
17051
+ if (reviewTasks.length > 0) {
17052
+ let total2 = 0;
17053
+ let count = 0;
17054
+ for (const row of reviewTasks) {
17055
+ try {
17056
+ const meta = JSON.parse(row.metadata);
17057
+ if (typeof meta._review_score === "number") {
17058
+ total2 += meta._review_score;
17059
+ count++;
17060
+ }
17061
+ } catch {}
17062
+ }
17063
+ if (count > 0)
17064
+ reviewScoreAvg = total2 / count;
17065
+ }
17066
+ const speedScore = avgTime?.avg_minutes != null ? Math.max(0, 1 - avgTime.avg_minutes / (60 * 24)) : 0.5;
17067
+ const confidenceScore = avgConf?.avg_confidence ?? 0.5;
17068
+ const volumeScore = Math.min(1, completed / 50);
17069
+ const compositeScore = completionRate * 0.3 + speedScore * 0.2 + confidenceScore * 0.3 + volumeScore * 0.2;
17070
+ return {
17071
+ agent_id: agent.id,
17072
+ agent_name: agent.name,
17073
+ tasks_completed: completed,
17074
+ tasks_failed: failed,
17075
+ tasks_in_progress: inProgress,
17076
+ completion_rate: Math.round(completionRate * 1000) / 1000,
17077
+ avg_completion_minutes: avgTime?.avg_minutes != null ? Math.round(avgTime.avg_minutes * 10) / 10 : null,
17078
+ avg_confidence: avgConf?.avg_confidence != null ? Math.round(avgConf.avg_confidence * 1000) / 1000 : null,
17079
+ review_score_avg: reviewScoreAvg != null ? Math.round(reviewScoreAvg * 1000) / 1000 : null,
17080
+ composite_score: Math.round(compositeScore * 1000) / 1000
17081
+ };
17082
+ }
17083
+ function getLeaderboard(opts, db) {
17084
+ const d = db || getDatabase();
17085
+ const agents = d.query("SELECT id FROM agents ORDER BY name").all();
17086
+ const entries = [];
17087
+ for (const agent of agents) {
17088
+ const metrics = getAgentMetrics(agent.id, { project_id: opts?.project_id }, d);
17089
+ if (metrics && (metrics.tasks_completed > 0 || metrics.tasks_failed > 0 || metrics.tasks_in_progress > 0)) {
17090
+ entries.push(metrics);
17091
+ }
17092
+ }
17093
+ entries.sort((a, b) => b.composite_score - a.composite_score);
17094
+ const limit = opts?.limit || 20;
17095
+ return entries.slice(0, limit).map((entry, idx) => ({
17096
+ ...entry,
17097
+ rank: idx + 1
17098
+ }));
17099
+ }
17100
+ function scoreTask(taskId, score, reviewerId, db) {
17101
+ const d = db || getDatabase();
17102
+ if (score < 0 || score > 1)
17103
+ throw new Error("Score must be between 0 and 1");
17104
+ const task = d.query("SELECT metadata FROM tasks WHERE id = ?").get(taskId);
17105
+ if (!task)
17106
+ throw new Error(`Task not found: ${taskId}`);
17107
+ const metadata = JSON.parse(task.metadata || "{}");
17108
+ metadata._review_score = score;
17109
+ if (reviewerId)
17110
+ metadata._reviewed_by = reviewerId;
17111
+ metadata._reviewed_at = now();
17112
+ d.run("UPDATE tasks SET metadata = ?, updated_at = ? WHERE id = ?", [JSON.stringify(metadata), now(), taskId]);
17113
+ }
17114
+ var init_agent_metrics = __esm(() => {
17115
+ init_database();
15462
17116
  });
15463
17117
 
15464
17118
  // src/lib/extract.ts
@@ -20707,7 +22361,7 @@ async function logError(message, opts) {
20707
22361
 
20708
22362
  // src/mcp/index.ts
20709
22363
  init_types2();
20710
- import { readFileSync as readFileSync6 } from "fs";
22364
+ import { existsSync as existsSync10, readFileSync as readFileSync6 } from "fs";
20711
22365
  import { join as join10, dirname as dirname3 } from "path";
20712
22366
  import { fileURLToPath } from "url";
20713
22367
 
@@ -20950,14 +22604,24 @@ ${lines.join(`
20950
22604
 
20951
22605
  // src/mcp/tools/task-crud.ts
20952
22606
  init_tasks();
22607
+ init_types2();
20953
22608
  function registerTaskCrudTools(server, ctx) {
20954
22609
  const { shouldRegisterTool, resolveId, formatError, formatTask } = ctx;
22610
+ function versionFor(taskId, version) {
22611
+ const current = getTask(taskId);
22612
+ if (!current)
22613
+ throw new TaskNotFoundError(taskId);
22614
+ if (version !== undefined && current.version !== version) {
22615
+ throw new VersionConflictError(taskId, version, current.version);
22616
+ }
22617
+ return current.version;
22618
+ }
20955
22619
  if (shouldRegisterTool("create_task")) {
20956
22620
  server.tool("create_task", "Create a new task in a project. Pass short_id=null to auto-generate.", {
20957
22621
  title: exports_external.string().describe("Task title"),
20958
22622
  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)"),
22623
+ status: exports_external.enum(["pending", "in_progress", "completed", "failed", "cancelled"]).optional().describe("Initial status (default: pending)"),
22624
+ priority: exports_external.enum(["low", "medium", "high", "critical"]).optional().describe("Priority (default: medium)"),
20961
22625
  project_id: exports_external.string().optional().describe("Project ID"),
20962
22626
  task_list_id: exports_external.string().optional().describe("Task list ID"),
20963
22627
  assigned_to: exports_external.string().optional().describe("Agent ID or name to assign to"),
@@ -20987,9 +22651,9 @@ function registerTaskCrudTools(server, ctx) {
20987
22651
  if (confidence !== undefined)
20988
22652
  resolved.confidence = confidence;
20989
22653
  if (retry_count !== undefined)
20990
- resolved.retry_count = retry_count;
22654
+ resolved.max_retries = retry_count;
20991
22655
  if (deadline)
20992
- resolved.deadline = deadline;
22656
+ resolved.due_at = deadline;
20993
22657
  const task = createTask(resolved);
20994
22658
  return { content: [{ type: "text", text: formatTask(task) }] };
20995
22659
  } catch (e) {
@@ -20999,8 +22663,8 @@ function registerTaskCrudTools(server, ctx) {
20999
22663
  }
21000
22664
  if (shouldRegisterTool("list_tasks")) {
21001
22665
  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"),
22666
+ 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"),
22667
+ 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
22668
  project_id: exports_external.string().optional().describe("Filter by project"),
21005
22669
  task_list_id: exports_external.string().optional().describe("Filter by task list"),
21006
22670
  assigned_to: exports_external.string().optional().describe("Filter by assignee (agent ID or name, empty string = unassigned)"),
@@ -21035,9 +22699,9 @@ function registerTaskCrudTools(server, ctx) {
21035
22699
  }, async ({ task_id }) => {
21036
22700
  try {
21037
22701
  const resolvedId = resolveId(task_id);
21038
- const task = getTask2(resolvedId);
22702
+ const task = getTask(resolvedId);
21039
22703
  if (!task)
21040
- throw new NotFoundError(`Task not found: ${task_id}`);
22704
+ throw new TaskNotFoundError(task_id);
21041
22705
  const focus = ctx.getAgentFocus(task.assigned_to || "");
21042
22706
  const lines = [
21043
22707
  `ID: ${task.id}`,
@@ -21051,7 +22715,7 @@ function registerTaskCrudTools(server, ctx) {
21051
22715
  task.estimated_minutes != null ? `Estimate: ${task.estimated_minutes} min` : null,
21052
22716
  task.actual_minutes != null ? `Actual: ${task.actual_minutes} min` : null,
21053
22717
  task.confidence != null ? `Confidence: ${task.confidence}` : null,
21054
- task.deadline ? `Deadline: ${task.deadline}` : null,
22718
+ task.due_at ? `Due: ${task.due_at}` : null,
21055
22719
  task.completed_at ? `Completed: ${task.completed_at}` : null,
21056
22720
  focus ? `Focus: agent=${focus.agent_id} project=${focus.project_id || "(global)"}` : null,
21057
22721
  task.created_at ? `Created: ${task.created_at}` : null,
@@ -21073,8 +22737,8 @@ ${task.description}` : null
21073
22737
  task_id: exports_external.string().describe("Task ID"),
21074
22738
  title: exports_external.string().optional(),
21075
22739
  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(),
22740
+ status: exports_external.enum(["pending", "in_progress", "completed", "failed", "cancelled"]).optional(),
22741
+ priority: exports_external.enum(["low", "medium", "high", "critical"]).optional(),
21078
22742
  assigned_to: exports_external.string().nullable().optional().describe("Agent ID or name, null to unassign"),
21079
22743
  project_id: exports_external.string().nullable().optional(),
21080
22744
  task_list_id: exports_external.string().nullable().optional(),
@@ -21103,9 +22767,15 @@ ${task.description}` : null
21103
22767
  resolved.task_list_id = resolveId(resolved.task_list_id, "task_lists");
21104
22768
  if (resolved.depends_on && Array.isArray(resolved.depends_on))
21105
22769
  resolved.depends_on = resolved.depends_on.map(resolveId);
21106
- if (resolved.estimate !== undefined)
22770
+ if (resolved.estimate !== undefined) {
21107
22771
  resolved.estimated_minutes = resolved.estimate;
21108
- const task = updateTask(resolvedId, resolved, version);
22772
+ delete resolved.estimate;
22773
+ }
22774
+ if (resolved.deadline !== undefined) {
22775
+ resolved.due_at = resolved.deadline;
22776
+ delete resolved.deadline;
22777
+ }
22778
+ const task = updateTask(resolvedId, { ...resolved, version: versionFor(resolvedId, version) });
21109
22779
  return { content: [{ type: "text", text: formatTask(task) }] };
21110
22780
  } catch (e) {
21111
22781
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
@@ -21202,48 +22872,23 @@ function deletePlan(id, db) {
21202
22872
  return result.changes > 0;
21203
22873
  }
21204
22874
 
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
22875
  // src/mcp/tools/task-project-tools.ts
22876
+ init_comments();
21244
22877
  init_types2();
21245
22878
  function registerTaskProjectTools(server, ctx) {
21246
22879
  const { shouldRegisterTool, resolveId, formatError, formatTask } = ctx;
22880
+ function versionFor(taskId, version) {
22881
+ const current = getTask(taskId);
22882
+ if (!current)
22883
+ throw new TaskNotFoundError(taskId);
22884
+ if (version !== undefined && current.version !== version) {
22885
+ throw new VersionConflictError(taskId, version, current.version);
22886
+ }
22887
+ return current.version;
22888
+ }
22889
+ function updateWithOptionalVersion(taskId, updates, version) {
22890
+ return updateTask(taskId, { ...updates, version: versionFor(taskId, version) });
22891
+ }
21247
22892
  if (shouldRegisterTool("start_task")) {
21248
22893
  server.tool("start_task", "Mark a task as in_progress. Uses optimistic locking via version if provided.", {
21249
22894
  task_id: exports_external.string().describe("Task ID"),
@@ -21251,7 +22896,12 @@ function registerTaskProjectTools(server, ctx) {
21251
22896
  }, async ({ task_id, version }) => {
21252
22897
  try {
21253
22898
  const resolvedId = resolveId(task_id);
21254
- const task = updateTask(resolvedId, { status: "in_progress" }, version);
22899
+ if (version !== undefined)
22900
+ versionFor(resolvedId, version);
22901
+ const current = getTask(resolvedId);
22902
+ if (!current)
22903
+ throw new TaskNotFoundError(resolvedId);
22904
+ const task = startTask(resolvedId, current.assigned_to || current.agent_id || "mcp");
21255
22905
  return { content: [{ type: "text", text: formatTask(task) }] };
21256
22906
  } catch (e) {
21257
22907
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
@@ -21267,7 +22917,12 @@ function registerTaskProjectTools(server, ctx) {
21267
22917
  }, async ({ task_id, confidence, completed_at, version }) => {
21268
22918
  try {
21269
22919
  const resolvedId = resolveId(task_id);
21270
- const task = updateTask(resolvedId, { status: "completed", confidence, completed_at }, version);
22920
+ if (version !== undefined)
22921
+ versionFor(resolvedId, version);
22922
+ const current = getTask(resolvedId);
22923
+ if (!current)
22924
+ throw new TaskNotFoundError(resolvedId);
22925
+ const task = completeTask(resolvedId, current.assigned_to || current.agent_id || undefined, undefined, { confidence, completed_at });
21271
22926
  return { content: [{ type: "text", text: formatTask(task) }] };
21272
22927
  } catch (e) {
21273
22928
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
@@ -21281,7 +22936,7 @@ function registerTaskProjectTools(server, ctx) {
21281
22936
  }, async ({ task_id, version }) => {
21282
22937
  try {
21283
22938
  const resolvedId = resolveId(task_id);
21284
- const task = updateTask(resolvedId, { status: "cancelled" }, version);
22939
+ const task = version === undefined ? setTaskStatus(resolvedId, "cancelled") : updateWithOptionalVersion(resolvedId, { status: "cancelled" }, version);
21285
22940
  return { content: [{ type: "text", text: formatTask(task) }] };
21286
22941
  } catch (e) {
21287
22942
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
@@ -21297,7 +22952,7 @@ function registerTaskProjectTools(server, ctx) {
21297
22952
  try {
21298
22953
  const resolvedId = resolveId(task_id);
21299
22954
  const resolvedAssignee = resolveId(new_assignee, "agents");
21300
- const task = updateTask(resolvedId, { assigned_to: resolvedAssignee }, version);
22955
+ const task = updateWithOptionalVersion(resolvedId, { assigned_to: resolvedAssignee }, version);
21301
22956
  return { content: [{ type: "text", text: formatTask(task) }] };
21302
22957
  } catch (e) {
21303
22958
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
@@ -21312,7 +22967,7 @@ function registerTaskProjectTools(server, ctx) {
21312
22967
  }, async ({ task_id, deadline, version }) => {
21313
22968
  try {
21314
22969
  const resolvedId = resolveId(task_id);
21315
- const task = updateTask(resolvedId, { deadline }, version);
22970
+ const task = updateWithOptionalVersion(resolvedId, { due_at: deadline }, version);
21316
22971
  return { content: [{ type: "text", text: formatTask(task) }] };
21317
22972
  } catch (e) {
21318
22973
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
@@ -21322,12 +22977,12 @@ function registerTaskProjectTools(server, ctx) {
21322
22977
  if (shouldRegisterTool("prioritize_task")) {
21323
22978
  server.tool("prioritize_task", "Set a task's priority.", {
21324
22979
  task_id: exports_external.string().describe("Task ID"),
21325
- priority: exports_external.enum(["low", "medium", "high", "urgent"]).describe("New priority"),
22980
+ priority: exports_external.enum(["low", "medium", "high", "critical"]).describe("New priority"),
21326
22981
  version: exports_external.number().optional().describe("Expected version for optimistic locking")
21327
22982
  }, async ({ task_id, priority, version }) => {
21328
22983
  try {
21329
22984
  const resolvedId = resolveId(task_id);
21330
- const task = updateTask(resolvedId, { priority }, version);
22985
+ const task = version === undefined ? setTaskPriority(resolvedId, priority) : updateWithOptionalVersion(resolvedId, { priority }, version);
21331
22986
  return { content: [{ type: "text", text: formatTask(task) }] };
21332
22987
  } catch (e) {
21333
22988
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
@@ -21383,18 +23038,18 @@ function registerTaskProjectTools(server, ctx) {
21383
23038
  if (shouldRegisterTool("bulk_update_tasks")) {
21384
23039
  server.tool("bulk_update_tasks", "Update multiple tasks at once. All tasks must pass the dependency check.", {
21385
23040
  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(),
23041
+ status: exports_external.enum(["pending", "in_progress", "completed", "failed", "cancelled"]).optional(),
23042
+ priority: exports_external.enum(["low", "medium", "high", "critical"]).optional(),
21388
23043
  assigned_to: exports_external.string().nullable().optional().describe("Agent ID or name, null to unassign")
21389
23044
  }, async ({ task_ids, status, priority, assigned_to }) => {
21390
23045
  try {
21391
- const { bulkUpdateTasks: bulkUpdateTasks2 } = (()=>{throw new Error("Cannot require module "+"../db/tasks.js");})();
23046
+ const { bulkUpdateTasks: bulkUpdateTasks2 } = (init_tasks(), __toCommonJS(exports_tasks));
21392
23047
  const resolved = task_ids.map(resolveId);
21393
23048
  let resolvedAssignee = assigned_to;
21394
23049
  if (resolvedAssignee && typeof resolvedAssignee === "string")
21395
23050
  resolvedAssignee = resolveId(resolvedAssignee, "agents");
21396
23051
  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).` }] };
23052
+ return { content: [{ type: "text", text: `${result.updated} task(s) updated, ${result.failed.length} failed.` }] };
21398
23053
  } catch (e) {
21399
23054
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
21400
23055
  }
@@ -21405,8 +23060,8 @@ function registerTaskProjectTools(server, ctx) {
21405
23060
  tasks: exports_external.array(exports_external.object({
21406
23061
  title: exports_external.string(),
21407
23062
  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(),
23063
+ status: exports_external.enum(["pending", "in_progress", "completed", "failed", "cancelled"]).optional(),
23064
+ priority: exports_external.enum(["low", "medium", "high", "critical"]).optional(),
21410
23065
  project_id: exports_external.string().optional(),
21411
23066
  task_list_id: exports_external.string().optional(),
21412
23067
  assigned_to: exports_external.string().optional(),
@@ -21417,7 +23072,7 @@ function registerTaskProjectTools(server, ctx) {
21417
23072
  })).describe("Array of task objects")
21418
23073
  }, async ({ tasks }) => {
21419
23074
  try {
21420
- const { bulkCreateTasks: bulkCreateTasks2 } = (()=>{throw new Error("Cannot require module "+"../db/tasks.js");})();
23075
+ const { bulkCreateTasks: bulkCreateTasks2 } = (init_tasks(), __toCommonJS(exports_tasks));
21421
23076
  const resolved = tasks.map((t) => {
21422
23077
  const r = { ...t };
21423
23078
  if (r.project_id)
@@ -21431,7 +23086,7 @@ function registerTaskProjectTools(server, ctx) {
21431
23086
  return r;
21432
23087
  });
21433
23088
  const result = bulkCreateTasks2(resolved);
21434
- return { content: [{ type: "text", text: `${result.created} task(s) created, ${result.skipped} skipped (duplicate short_id).` }] };
23089
+ return { content: [{ type: "text", text: `${result.created.length} task(s) created.` }] };
21435
23090
  } catch (e) {
21436
23091
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
21437
23092
  }
@@ -21443,9 +23098,9 @@ function registerTaskProjectTools(server, ctx) {
21443
23098
  force: exports_external.boolean().optional().describe("Skip child check for all tasks (dangerous)")
21444
23099
  }, async ({ task_ids, force }) => {
21445
23100
  try {
21446
- const { bulkDeleteTasks } = (()=>{throw new Error("Cannot require module "+"../db/tasks.js");})();
23101
+ const { bulkDeleteTasks: bulkDeleteTasks2 } = (init_tasks(), __toCommonJS(exports_tasks));
21447
23102
  const resolved = task_ids.map(resolveId);
21448
- const result = bulkDeleteTasks(resolved, force);
23103
+ const result = bulkDeleteTasks2(resolved, force);
21449
23104
  return { content: [{ type: "text", text: `${result.deleted} task(s) deleted, ${result.skipped} skipped (has children).` }] };
21450
23105
  } catch (e) {
21451
23106
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
@@ -21494,7 +23149,7 @@ function registerTaskProjectTools(server, ctx) {
21494
23149
  const project = getProject(resolvedId);
21495
23150
  if (!project)
21496
23151
  throw new TaskNotFoundError(`Project not found: ${project_id}`);
21497
- const { listTasks: listTasks3 } = (()=>{throw new Error("Cannot require module "+"../db/tasks.js");})();
23152
+ const { listTasks: listTasks3 } = (init_tasks(), __toCommonJS(exports_tasks));
21498
23153
  const tasks = listTasks3({ project_id: resolvedId, limit: 100 }, undefined);
21499
23154
  const lines = [
21500
23155
  `ID: ${project.id}`,
@@ -21595,7 +23250,7 @@ function registerTaskProjectTools(server, ctx) {
21595
23250
  throw new TaskNotFoundError(`Task list not found: ${task_list_id}`);
21596
23251
  let tasks = [];
21597
23252
  if (include_tasks) {
21598
- const { listTasks: listTasks3 } = (()=>{throw new Error("Cannot require module "+"../db/tasks.js");})();
23253
+ const { listTasks: listTasks3 } = (init_tasks(), __toCommonJS(exports_tasks));
21599
23254
  tasks = listTasks3({ task_list_id: resolvedId, limit: 200 }, undefined);
21600
23255
  }
21601
23256
  const lines = [
@@ -21695,7 +23350,7 @@ Tasks:` : null,
21695
23350
  throw new TaskNotFoundError(`Plan not found: ${plan_id}`);
21696
23351
  let tasks = [];
21697
23352
  if (include_tasks) {
21698
- const { listTasks: listTasks3 } = (()=>{throw new Error("Cannot require module "+"../db/tasks.js");})();
23353
+ const { listTasks: listTasks3 } = (init_tasks(), __toCommonJS(exports_tasks));
21699
23354
  tasks = listTasks3({ plan_id: resolvedId, limit: 200 }, undefined);
21700
23355
  }
21701
23356
  const lines = [
@@ -21784,7 +23439,7 @@ Tasks:` : null,
21784
23439
  const tag = getTag(tag_id);
21785
23440
  if (!tag)
21786
23441
  throw new TaskNotFoundError(`Tag not found: ${tag_id}`);
21787
- const { listTasks: listTasks3 } = (()=>{throw new Error("Cannot require module "+"../db/tasks.js");})();
23442
+ const { listTasks: listTasks3 } = (init_tasks(), __toCommonJS(exports_tasks));
21788
23443
  const tasks = listTasks3({ tags: [tag.name], limit: 100 }, undefined);
21789
23444
  const lines = [
21790
23445
  `Tag: ${tag.name}${tag.color ? ` (${tag.color})` : ""}`,
@@ -21863,7 +23518,7 @@ Tasks:` : null,
21863
23518
  const label = getLabel(label_id);
21864
23519
  if (!label)
21865
23520
  throw new TaskNotFoundError(`Label not found: ${label_id}`);
21866
- const { listTasks: listTasks3 } = (()=>{throw new Error("Cannot require module "+"../db/tasks.js");})();
23521
+ const { listTasks: listTasks3 } = (init_tasks(), __toCommonJS(exports_tasks));
21867
23522
  const tasks = listTasks3({ tags: [label.name], limit: 100 }, undefined);
21868
23523
  const lines = [
21869
23524
  `Label: ${label.name}${label.color ? ` (${label.color})` : ""}`,
@@ -21914,7 +23569,7 @@ Tasks:` : null,
21914
23569
  try {
21915
23570
  const resolvedId = resolveId(task_id);
21916
23571
  const resolvedAuthor = author ? resolveId(author, "agents") : undefined;
21917
- const comment = addComment({ task_id: resolvedId, body, author: resolvedAuthor });
23572
+ const comment = addComment({ task_id: resolvedId, content: body, agent_id: resolvedAuthor });
21918
23573
  return { content: [{ type: "text", text: `Comment added to ${task_id.slice(0, 8)}: ${body.slice(0, 50)}${body.length > 50 ? "..." : ""}` }] };
21919
23574
  } catch (e) {
21920
23575
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
@@ -21930,8 +23585,8 @@ Tasks:` : null,
21930
23585
  const comments = listComments(resolvedId);
21931
23586
  if (comments.length === 0)
21932
23587
  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}`);
23588
+ const lines = comments.map((c) => `[${c.agent_id || "unknown"}] ${c.created_at?.slice(0, 16)}:
23589
+ ${c.content}`);
21935
23590
  return { content: [{ type: "text", text: lines.join(`
21936
23591
 
21937
23592
  `) }] };
@@ -21946,7 +23601,7 @@ Tasks:` : null,
21946
23601
  body: exports_external.string().describe("New comment body")
21947
23602
  }, async ({ comment_id, body }) => {
21948
23603
  try {
21949
- const comment = updateComment(comment_id, { body });
23604
+ const comment = updateComment(comment_id, { content: body });
21950
23605
  return { content: [{ type: "text", text: `Comment ${comment_id.slice(0, 8)} updated.` }] };
21951
23606
  } catch (e) {
21952
23607
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
@@ -21969,17 +23624,17 @@ Tasks:` : null,
21969
23624
  server.tool("search_tasks", "Full-text search across task titles and descriptions.", {
21970
23625
  query: exports_external.string().describe("Search query"),
21971
23626
  project_id: exports_external.string().optional().describe("Filter by project"),
21972
- status: exports_external.enum(["pending", "in_progress", "completed", "cancelled"]).optional(),
23627
+ status: exports_external.enum(["pending", "in_progress", "completed", "failed", "cancelled"]).optional(),
21973
23628
  limit: exports_external.number().optional().describe("Max results (default: 20)")
21974
23629
  }, async ({ query, project_id, status, limit }) => {
21975
23630
  try {
21976
- const { searchTasks } = (()=>{throw new Error("Cannot require module "+"../db/tasks.js");})();
23631
+ const { searchTasks: searchTasks2 } = (init_search(), __toCommonJS(exports_search));
21977
23632
  const resolved = { query, limit };
21978
23633
  if (project_id)
21979
23634
  resolved.project_id = resolveId(project_id, "projects");
21980
23635
  if (status)
21981
23636
  resolved.status = status;
21982
- const results = searchTasks(resolved);
23637
+ const results = searchTasks2(resolved);
21983
23638
  if (results.length === 0)
21984
23639
  return { content: [{ type: "text", text: `No results for: ${query}` }] };
21985
23640
  const lines = results.map((t) => `${t.short_id || t.id.slice(0, 8)} [${t.status}] ${t.title}`);
@@ -21991,34 +23646,22 @@ ${lines.join(`
21991
23646
  }
21992
23647
  });
21993
23648
  }
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
23649
  }
22018
23650
 
22019
23651
  // src/mcp/tools/task-workflow-tools.ts
23652
+ init_types2();
22020
23653
  function registerTaskWorkflowTools(server, ctx) {
22021
23654
  const { shouldRegisterTool, resolveId, formatError, formatTask } = ctx;
23655
+ function versionFor(taskId, version) {
23656
+ const { getTask: getTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
23657
+ const current = getTask2(taskId);
23658
+ if (!current)
23659
+ throw new TaskNotFoundError(taskId);
23660
+ if (version !== undefined && current.version !== version) {
23661
+ throw new VersionConflictError(taskId, version, current.version);
23662
+ }
23663
+ return current.version;
23664
+ }
22022
23665
  if (shouldRegisterTool("approve_task")) {
22023
23666
  server.tool("approve_task", "Approve a task that requires_approval. Records who approved it.", {
22024
23667
  task_id: exports_external.string().describe("Task ID"),
@@ -22027,13 +23670,14 @@ function registerTaskWorkflowTools(server, ctx) {
22027
23670
  version: exports_external.number().optional().describe("Expected version for optimistic locking")
22028
23671
  }, async ({ task_id, approved_by, notes, version }) => {
22029
23672
  try {
22030
- const { updateTask: updateTask2 } = (()=>{throw new Error("Cannot require module "+"../db/tasks.js");})();
23673
+ const { updateTask: updateTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
22031
23674
  const resolvedId = resolveId(task_id);
22032
23675
  const resolvedApprover = approved_by ? resolveId(approved_by, "agents") : undefined;
22033
23676
  const task = updateTask2(resolvedId, {
22034
23677
  approved_by: resolvedApprover,
22035
- metadata: notes ? { approval_notes: notes } : {}
22036
- }, version);
23678
+ metadata: notes ? { approval_notes: notes } : {},
23679
+ version: versionFor(resolvedId, version)
23680
+ });
22037
23681
  return { content: [{ type: "text", text: `Task ${task_id.slice(0, 8)} approved by ${resolvedApprover || "unknown"}` }] };
22038
23682
  } catch (e) {
22039
23683
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
@@ -22048,15 +23692,11 @@ function registerTaskWorkflowTools(server, ctx) {
22048
23692
  version: exports_external.number().optional().describe("Expected version for optimistic locking")
22049
23693
  }, async ({ task_id, reason, agent_id, version }) => {
22050
23694
  try {
22051
- const { updateTask: updateTask2 } = (()=>{throw new Error("Cannot require module "+"../db/tasks.js");})();
23695
+ const { failTask: failTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
22052
23696
  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}` }] };
23697
+ versionFor(resolvedId, version);
23698
+ const result = failTask2(resolvedId, agent_id, reason);
23699
+ return { content: [{ type: "text", text: `Task ${task_id.slice(0, 8)} marked failed. Retry count: ${result.task.retry_count}` }] };
22060
23700
  } catch (e) {
22061
23701
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
22062
23702
  }
@@ -22065,12 +23705,12 @@ function registerTaskWorkflowTools(server, ctx) {
22065
23705
  if (shouldRegisterTool("get_my_tasks")) {
22066
23706
  server.tool("get_my_tasks", "Get tasks assigned to the calling agent. Supports focus mode scoping.", {
22067
23707
  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"),
23708
+ status: exports_external.enum(["pending", "in_progress", "completed", "failed", "cancelled"]).optional().describe("Filter by status"),
22069
23709
  project_id: exports_external.string().optional().describe("Filter by project (respects focus mode)"),
22070
23710
  limit: exports_external.number().optional().describe("Max results (default: 50)")
22071
23711
  }, async ({ agent_id, status, project_id, limit }) => {
22072
23712
  try {
22073
- const { listTasks: listTasks3 } = (()=>{throw new Error("Cannot require module "+"../db/tasks.js");})();
23713
+ const { listTasks: listTasks3 } = (init_tasks(), __toCommonJS(exports_tasks));
22074
23714
  const focus = ctx.getAgentFocus(agent_id || "");
22075
23715
  const effectiveAgentId = focus ? focus.agent_id : agent_id || "";
22076
23716
  const effectiveProjectId = focus?.project_id || project_id;
@@ -22090,6 +23730,122 @@ function registerTaskWorkflowTools(server, ctx) {
22090
23730
  }
22091
23731
  });
22092
23732
  }
23733
+ if (shouldRegisterTool("get_next_task")) {
23734
+ server.tool("get_next_task", "Get the best available pending task without claiming it.", {
23735
+ agent_id: exports_external.string().optional().describe("Agent ID or name for assignment affinity"),
23736
+ project_id: exports_external.string().optional().describe("Filter by project"),
23737
+ task_list_id: exports_external.string().optional().describe("Filter by task list"),
23738
+ plan_id: exports_external.string().optional().describe("Filter by plan"),
23739
+ tags: exports_external.array(exports_external.string()).optional().describe("Filter by tags")
23740
+ }, async ({ agent_id, project_id, task_list_id, plan_id, tags }) => {
23741
+ try {
23742
+ const { getNextTask: getNextTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
23743
+ const filters = {};
23744
+ if (project_id)
23745
+ filters.project_id = resolveId(project_id, "projects");
23746
+ if (task_list_id)
23747
+ filters.task_list_id = resolveId(task_list_id, "task_lists");
23748
+ if (plan_id)
23749
+ filters.plan_id = resolveId(plan_id, "plans");
23750
+ if (tags)
23751
+ filters.tags = tags;
23752
+ const task = getNextTask2(agent_id, filters);
23753
+ return { content: [{ type: "text", text: task ? formatTask(task) : "No available task." }] };
23754
+ } catch (e) {
23755
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
23756
+ }
23757
+ });
23758
+ }
23759
+ if (shouldRegisterTool("claim_next_task")) {
23760
+ server.tool("claim_next_task", "Atomically claim and start the best available pending task for an agent.", {
23761
+ agent_id: exports_external.string().describe("Agent ID or name claiming the task"),
23762
+ project_id: exports_external.string().optional().describe("Filter by project"),
23763
+ task_list_id: exports_external.string().optional().describe("Filter by task list"),
23764
+ plan_id: exports_external.string().optional().describe("Filter by plan"),
23765
+ tags: exports_external.array(exports_external.string()).optional().describe("Filter by tags")
23766
+ }, async ({ agent_id, project_id, task_list_id, plan_id, tags }) => {
23767
+ try {
23768
+ const { claimNextTask: claimNextTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
23769
+ const filters = {};
23770
+ if (project_id)
23771
+ filters.project_id = resolveId(project_id, "projects");
23772
+ if (task_list_id)
23773
+ filters.task_list_id = resolveId(task_list_id, "task_lists");
23774
+ if (plan_id)
23775
+ filters.plan_id = resolveId(plan_id, "plans");
23776
+ if (tags)
23777
+ filters.tags = tags;
23778
+ const task = claimNextTask2(agent_id, filters);
23779
+ return { content: [{ type: "text", text: task ? formatTask(task) : "No available task to claim." }] };
23780
+ } catch (e) {
23781
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
23782
+ }
23783
+ });
23784
+ }
23785
+ if (shouldRegisterTool("get_tasks_changed_since")) {
23786
+ server.tool("get_tasks_changed_since", "List tasks changed since an ISO timestamp.", {
23787
+ since: exports_external.string().describe("ISO timestamp"),
23788
+ project_id: exports_external.string().optional().describe("Filter by project"),
23789
+ task_list_id: exports_external.string().optional().describe("Filter by task list"),
23790
+ limit: exports_external.number().optional().describe("Maximum tasks to return")
23791
+ }, async ({ since, project_id, task_list_id, limit }) => {
23792
+ try {
23793
+ const { getTasksChangedSince: getTasksChangedSince2 } = (init_tasks(), __toCommonJS(exports_tasks));
23794
+ const filters = {};
23795
+ if (project_id)
23796
+ filters.project_id = resolveId(project_id, "projects");
23797
+ if (task_list_id)
23798
+ filters.task_list_id = resolveId(task_list_id, "task_lists");
23799
+ const tasks = getTasksChangedSince2(since, filters).slice(0, limit || 50);
23800
+ if (tasks.length === 0)
23801
+ return { content: [{ type: "text", text: "No changed tasks." }] };
23802
+ return { content: [{ type: "text", text: tasks.map(formatTask).join(`
23803
+ `) }] };
23804
+ } catch (e) {
23805
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
23806
+ }
23807
+ });
23808
+ }
23809
+ function registerContextTool(name, description) {
23810
+ if (!shouldRegisterTool(name))
23811
+ return;
23812
+ server.tool(name, description, {
23813
+ agent_id: exports_external.string().optional().describe("Agent ID or name"),
23814
+ project_id: exports_external.string().optional().describe("Filter by project"),
23815
+ task_list_id: exports_external.string().optional().describe("Filter by task list"),
23816
+ explain_blocked: exports_external.boolean().optional().describe("Include blocked task details")
23817
+ }, async ({ agent_id, project_id, task_list_id, explain_blocked }) => {
23818
+ try {
23819
+ const { getStatus: getStatus2, getNextTask: getNextTask2, getOverdueTasks: getOverdueTasks2 } = (init_tasks(), __toCommonJS(exports_tasks));
23820
+ const { getLatestHandoff: getLatestHandoff2 } = (init_handoffs(), __toCommonJS(exports_handoffs));
23821
+ const filters = {};
23822
+ if (project_id)
23823
+ filters.project_id = resolveId(project_id, "projects");
23824
+ if (task_list_id)
23825
+ filters.task_list_id = resolveId(task_list_id, "task_lists");
23826
+ const status = getStatus2(filters, agent_id, { explain_blocked });
23827
+ const next_task = getNextTask2(agent_id, filters);
23828
+ const overdue = getOverdueTasks2(filters.project_id);
23829
+ const latest_handoff = getLatestHandoff2(agent_id, filters.project_id);
23830
+ return {
23831
+ content: [{
23832
+ type: "text",
23833
+ text: JSON.stringify({
23834
+ status,
23835
+ next_task,
23836
+ overdue_count: overdue.length,
23837
+ latest_handoff,
23838
+ as_of: new Date().toISOString()
23839
+ }, null, 2)
23840
+ }]
23841
+ };
23842
+ } catch (e) {
23843
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
23844
+ }
23845
+ });
23846
+ }
23847
+ registerContextTool("get_context", "Get session start context: queue status, next task, overdue count, and latest handoff.");
23848
+ registerContextTool("bootstrap", "Bootstrap an agent session with queue context and the next available task.");
22093
23849
  if (shouldRegisterTool("get_org_chart")) {
22094
23850
  server.tool("get_org_chart", "Get the global org chart (agent hierarchy + titles).", {
22095
23851
  format: exports_external.enum(["text", "json"]).optional().describe("Output format (default: text)")
@@ -22110,7 +23866,7 @@ function registerTaskWorkflowTools(server, ctx) {
22110
23866
  }).join(`
22111
23867
  `);
22112
23868
  };
22113
- const { getOrgChart: getOrgChart2 } = (()=>{throw new Error("Cannot require module "+"../db/agents.js");})();
23869
+ const { getOrgChart: getOrgChart2 } = (init_agents(), __toCommonJS(exports_agents));
22114
23870
  const tree = getOrgChart2();
22115
23871
  if (format === "json") {
22116
23872
  return { content: [{ type: "text", text: JSON.stringify(tree, null, 2) }] };
@@ -22130,10 +23886,10 @@ function registerTaskWorkflowTools(server, ctx) {
22130
23886
  reports_to: exports_external.string().describe("Manager agent ID or name")
22131
23887
  }, async ({ agent_id, reports_to }) => {
22132
23888
  try {
22133
- const { setReportsTo } = (()=>{throw new Error("Cannot require module "+"../db/agents.js");})();
23889
+ const { updateAgent: updateAgent2 } = (init_agents(), __toCommonJS(exports_agents));
22134
23890
  const resolvedAgent = resolveId(agent_id, "agents");
22135
23891
  const resolvedManager = resolveId(reports_to, "agents");
22136
- setReportsTo(resolvedAgent, resolvedManager);
23892
+ updateAgent2(resolvedAgent, { reports_to: resolvedManager });
22137
23893
  return { content: [{ type: "text", text: `${agent_id} now reports to ${reports_to}` }] };
22138
23894
  } catch (e) {
22139
23895
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
@@ -22151,9 +23907,9 @@ function registerTaskAutoTools(server, ctx) {
22151
23907
  project_id: exports_external.string().optional().describe("Scope to a project")
22152
23908
  }, async ({ days = 7, project_id }) => {
22153
23909
  try {
22154
- const { archiveCompletedTasks } = (init_tasks(), __toCommonJS(exports_tasks));
23910
+ const { archiveCompletedTasks: archiveCompletedTasks2 } = (init_tasks(), __toCommonJS(exports_tasks));
22155
23911
  const resolvedProjectId = project_id ? resolveId(project_id, "projects") : undefined;
22156
- const count = archiveCompletedTasks(days, resolvedProjectId);
23912
+ const count = archiveCompletedTasks2(days, resolvedProjectId);
22157
23913
  return { content: [{ type: "text", text: `Archived ${count} completed task(s) older than ${days} days.` }] };
22158
23914
  } catch (e) {
22159
23915
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
@@ -22179,9 +23935,9 @@ function registerTaskAutoTools(server, ctx) {
22179
23935
  limit: exports_external.number().optional().describe("Max results (default: 50)")
22180
23936
  }, async ({ project_id, limit }) => {
22181
23937
  try {
22182
- const { getArchivedTasks } = (init_tasks(), __toCommonJS(exports_tasks));
23938
+ const { getArchivedTasks: getArchivedTasks2 } = (init_tasks(), __toCommonJS(exports_tasks));
22183
23939
  const resolvedProjectId = project_id ? resolveId(project_id, "projects") : undefined;
22184
- const tasks = getArchivedTasks({ project_id: resolvedProjectId, limit: limit || 50 });
23940
+ const tasks = getArchivedTasks2({ project_id: resolvedProjectId, limit: limit || 50 });
22185
23941
  if (tasks.length === 0)
22186
23942
  return { content: [{ type: "text", text: "No archived tasks." }] };
22187
23943
  const lines = tasks.map((t) => `${t.short_id || t.id.slice(0, 8)} ${t.title} archived ${t.archived_at}`);
@@ -22197,14 +23953,13 @@ function registerTaskAutoTools(server, ctx) {
22197
23953
  task_id: exports_external.string().describe("Task ID")
22198
23954
  }, async ({ task_id }) => {
22199
23955
  try {
22200
- const { autoAssign } = (init_agents(), __toCommonJS(exports_agents));
22201
23956
  const resolvedId = resolveId(task_id);
22202
- const assignment = autoAssign(resolvedId);
22203
- if (!assignment)
23957
+ const { autoAssignTask: autoAssignTask2 } = (init_auto_assign(), __toCommonJS(exports_auto_assign));
23958
+ const assignment = await autoAssignTask2(resolvedId);
23959
+ if (!assignment.assigned_to)
22204
23960
  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)})` }] };
23961
+ const reason = assignment.reason ? ` \u2014 ${assignment.reason}` : "";
23962
+ return { content: [{ type: "text", text: `Task ${task_id.slice(0, 8)} assigned to ${assignment.agent_name || assignment.assigned_to} via ${assignment.method}${reason}` }] };
22208
23963
  } catch (e) {
22209
23964
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
22210
23965
  }
@@ -22215,10 +23970,23 @@ function registerTaskAutoTools(server, ctx) {
22215
23970
  agent_id: exports_external.string().optional().describe("Agent ID (defaults to context agent)")
22216
23971
  }, async ({ agent_id }) => {
22217
23972
  try {
22218
- const { getAgentWorkload } = (init_agents(), __toCommonJS(exports_agents));
23973
+ const { listTasks: listTasks3, getBlockedTasks: getBlockedTasks2 } = (init_tasks(), __toCommonJS(exports_tasks));
22219
23974
  const focus = ctx.getAgentFocus(agent_id || "");
22220
23975
  const effectiveAgentId = focus ? focus.agent_id : agent_id || "";
22221
- const workload = getAgentWorkload(effectiveAgentId);
23976
+ if (!effectiveAgentId) {
23977
+ return { content: [{ type: "text", text: "No agent_id provided and no agent focus is active." }], isError: true };
23978
+ }
23979
+ const assigned = listTasks3({ assigned_to: effectiveAgentId, limit: 500 }, undefined);
23980
+ const now2 = Date.now();
23981
+ const dueSoonCutoff = now2 + 24 * 60 * 60 * 1000;
23982
+ const blocked = getBlockedTasks2().filter((t) => t.assigned_to === effectiveAgentId);
23983
+ const workload = {
23984
+ in_progress: assigned.filter((t) => t.status === "in_progress").length,
23985
+ pending: assigned.filter((t) => t.status === "pending").length,
23986
+ completed_recent: assigned.filter((t) => t.status === "completed" && t.completed_at && now2 - new Date(t.completed_at).getTime() <= 7 * 24 * 60 * 60 * 1000).length,
23987
+ 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,
23988
+ blocked: blocked.length
23989
+ };
22222
23990
  const lines = [
22223
23991
  `Agent: ${effectiveAgentId}`,
22224
23992
  `In Progress: ${workload.in_progress}`,
@@ -22240,10 +24008,29 @@ function registerTaskAutoTools(server, ctx) {
22240
24008
  max_per_agent: exports_external.number().optional().describe("Max tasks per agent (default: 5)")
22241
24009
  }, async ({ project_id, max_per_agent }) => {
22242
24010
  try {
22243
- const { rebalanceWorkload } = (init_agents(), __toCommonJS(exports_agents));
24011
+ const { listAgents: listAgents2 } = (init_agents(), __toCommonJS(exports_agents));
24012
+ const { listTasks: listTasks3, updateTask: updateTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
22244
24013
  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.` }] };
24014
+ const limit = max_per_agent || 5;
24015
+ const agents = listAgents2().filter((agent) => agent.status === "active");
24016
+ if (agents.length === 0)
24017
+ return { content: [{ type: "text", text: "No active agents available for rebalancing." }] };
24018
+ const activeTasks = listTasks3({ project_id: resolvedProjectId, status: ["pending", "in_progress"], limit: 1000 }, undefined);
24019
+ const load = new Map(agents.map((agent) => [agent.id, activeTasks.filter((t) => t.assigned_to === agent.id).length]));
24020
+ let moved = 0;
24021
+ let skipped = 0;
24022
+ for (const task of activeTasks.filter((t) => t.status === "pending" && t.assigned_to && (load.get(t.assigned_to) ?? 0) > limit)) {
24023
+ const target = agents.filter((agent) => agent.id !== task.assigned_to).sort((a, b) => (load.get(a.id) ?? 0) - (load.get(b.id) ?? 0))[0];
24024
+ if (!target || (load.get(target.id) ?? 0) >= limit) {
24025
+ skipped++;
24026
+ continue;
24027
+ }
24028
+ updateTask2(task.id, { assigned_to: target.id, version: task.version });
24029
+ load.set(task.assigned_to, (load.get(task.assigned_to) ?? 1) - 1);
24030
+ load.set(target.id, (load.get(target.id) ?? 0) + 1);
24031
+ moved++;
24032
+ }
24033
+ return { content: [{ type: "text", text: `Rebalanced: moved ${moved} task(s), ${skipped} skipped.` }] };
22247
24034
  } catch (e) {
22248
24035
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
22249
24036
  }
@@ -22256,13 +24043,13 @@ function registerTaskAutoTools(server, ctx) {
22256
24043
  agent_id: exports_external.string().optional().describe("Filter by assignee")
22257
24044
  }, async ({ hours = 24, project_id, agent_id }) => {
22258
24045
  try {
22259
- const { notifyUpcomingDeadlines } = (init_tasks(), __toCommonJS(exports_tasks));
24046
+ const { notifyUpcomingDeadlines: notifyUpcomingDeadlines2 } = (init_tasks(), __toCommonJS(exports_tasks));
22260
24047
  const resolvedProjectId = project_id ? resolveId(project_id, "projects") : undefined;
22261
24048
  const resolvedAgentId = agent_id ? resolveId(agent_id, "agents") : undefined;
22262
- const tasks = notifyUpcomingDeadlines({ hours, project_id: resolvedProjectId, agent_id: resolvedAgentId });
24049
+ const tasks = notifyUpcomingDeadlines2({ hours, project_id: resolvedProjectId, agent_id: resolvedAgentId });
22263
24050
  if (tasks.length === 0)
22264
24051
  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}`);
24052
+ const lines = tasks.map((t) => `${t.short_id || t.id.slice(0, 8)} ${t.title} due ${t.due_at}`);
22266
24053
  return { content: [{ type: "text", text: `${tasks.length} task(s) due within ${hours}h:
22267
24054
  ${lines.join(`
22268
24055
  `)}` }] };
@@ -22296,9 +24083,9 @@ ${lines.join(`
22296
24083
  project_id: exports_external.string().optional().describe("Filter by project")
22297
24084
  }, async ({ project_id }) => {
22298
24085
  try {
22299
- const { getBlockedTasks } = (init_tasks(), __toCommonJS(exports_tasks));
24086
+ const { getBlockedTasks: getBlockedTasks2 } = (init_tasks(), __toCommonJS(exports_tasks));
22300
24087
  const resolvedProjectId = project_id ? resolveId(project_id, "projects") : undefined;
22301
- const tasks = getBlockedTasks(resolvedProjectId);
24088
+ const tasks = getBlockedTasks2(resolvedProjectId);
22302
24089
  if (tasks.length === 0)
22303
24090
  return { content: [{ type: "text", text: "No blocked tasks." }] };
22304
24091
  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 +24102,9 @@ ${lines.join(`
22315
24102
  project_id: exports_external.string().optional().describe("Filter by project")
22316
24103
  }, async ({ project_id }) => {
22317
24104
  try {
22318
- const { getBlockingTasks } = (init_tasks(), __toCommonJS(exports_tasks));
24105
+ const { getBlockingTasks: getBlockingTasks2 } = (init_tasks(), __toCommonJS(exports_tasks));
22319
24106
  const resolvedProjectId = project_id ? resolveId(project_id, "projects") : undefined;
22320
- const tasks = getBlockingTasks(resolvedProjectId);
24107
+ const tasks = getBlockingTasks2(resolvedProjectId);
22321
24108
  if (tasks.length === 0)
22322
24109
  return { content: [{ type: "text", text: "No tasks blocking others." }] };
22323
24110
  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 +24119,18 @@ ${lines.join(`
22332
24119
  if (shouldRegisterTool("get_health")) {
22333
24120
  server.tool("get_health", "Get system health: task counts by status, active agents, project summary.", async () => {
22334
24121
  try {
22335
- const { listTasks: listTasks3 } = (init_tasks(), __toCommonJS(exports_tasks));
22336
- const { listProjects: listProjects2 } = (init_tasks(), __toCommonJS(exports_tasks));
24122
+ const { countTasks: countTasks2 } = (init_tasks(), __toCommonJS(exports_tasks));
24123
+ const { listProjects: listProjects2 } = (init_projects(), __toCommonJS(exports_projects));
22337
24124
  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 });
24125
+ const pending = countTasks2({ status: "pending" });
24126
+ const inProgress = countTasks2({ status: "in_progress" });
24127
+ const completed = countTasks2({ status: "completed" });
24128
+ const cancelled = countTasks2({ status: "cancelled" });
24129
+ const projects = listProjects2();
24130
+ const agents = listAgents2();
22346
24131
  const lines = [
22347
24132
  `=== System Health ===`,
22348
- `Tasks: ${pending.total} pending | ${inProgress.total} in progress | ${completed.total} completed | ${cancelled.total} cancelled`,
24133
+ `Tasks: ${pending} pending | ${inProgress} in progress | ${completed} completed | ${cancelled} cancelled`,
22349
24134
  `Projects: ${projects.length} total`,
22350
24135
  `Agents: ${agents.length} registered`
22351
24136
  ];
@@ -22362,17 +24147,31 @@ ${lines.join(`
22362
24147
  function registerTaskAdvTools(server, ctx) {
22363
24148
  const { shouldRegisterTool, resolveId, formatError, formatTask, formatTaskDetail } = ctx;
22364
24149
  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 }) => {
24150
+ server.tool("get_status", "Get queue status summary, or pass task_id for a task's detailed status.", {
24151
+ task_id: exports_external.string().optional().describe("Task ID for task-specific status"),
24152
+ project_id: exports_external.string().optional().describe("Filter summary by project"),
24153
+ task_list_id: exports_external.string().optional().describe("Filter summary by task list"),
24154
+ agent_id: exports_external.string().optional().describe("Agent for next-task affinity"),
24155
+ explain_blocked: exports_external.boolean().optional().describe("Include blocked task explanations in summary")
24156
+ }, async ({ task_id, project_id, task_list_id, agent_id, explain_blocked }) => {
22368
24157
  try {
24158
+ if (!task_id) {
24159
+ const { getStatus: getStatus2 } = (init_tasks(), __toCommonJS(exports_tasks));
24160
+ const filters = {};
24161
+ if (project_id)
24162
+ filters.project_id = resolveId(project_id, "projects");
24163
+ if (task_list_id)
24164
+ filters.task_list_id = resolveId(task_list_id, "task_lists");
24165
+ const status = getStatus2(filters, agent_id, { explain_blocked });
24166
+ return { content: [{ type: "text", text: JSON.stringify(status, null, 2) }] };
24167
+ }
22369
24168
  const resolvedId = resolveId(task_id);
22370
- const { getTask: getTask4 } = (init_tasks(), __toCommonJS(exports_tasks));
22371
- const task = getTask4(resolvedId);
24169
+ const { getTask: getTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
24170
+ const task = getTask2(resolvedId);
22372
24171
  if (!task)
22373
24172
  throw new Error(`Task not found: ${task_id}`);
22374
24173
  const { getTaskDependencies: getTaskDependencies3 } = (init_tasks(), __toCommonJS(exports_tasks));
22375
- const { listComments: listComments2 } = (init_tasks(), __toCommonJS(exports_tasks));
24174
+ const { listComments: listComments2 } = (init_comments(), __toCommonJS(exports_comments));
22376
24175
  const { listTaskFiles: listTaskFiles2 } = (init_task_files(), __toCommonJS(exports_task_files));
22377
24176
  const [deps, comments, files] = await Promise.all([
22378
24177
  Promise.resolve(getTaskDependencies3(resolvedId, "both")),
@@ -22382,14 +24181,14 @@ function registerTaskAdvTools(server, ctx) {
22382
24181
  const lines = [
22383
24182
  `Status: ${task.status} | Priority: ${task.priority}`,
22384
24183
  task.assigned_to ? `Assigned: ${task.assigned_to}` : "Unassigned",
22385
- task.deadline ? `Deadline: ${task.deadline}` : null,
24184
+ task.due_at ? `Due: ${task.due_at}` : null,
22386
24185
  task.confidence != null ? `Confidence: ${task.confidence}` : null,
22387
24186
  deps.length > 0 ? `
22388
24187
  Dependencies (${deps.length}):` : null,
22389
24188
  ...deps.map((d) => ` [${d.direction}] ${d.task_id.slice(0, 8)} (${d.status})`),
22390
24189
  comments.length > 0 ? `
22391
24190
  Comments (${comments.length}):` : null,
22392
- ...comments.map((c) => ` [${c.author || "?"}] ${c.created_at?.slice(0, 16)}: ${c.body.slice(0, 80)}`),
24191
+ ...comments.map((c) => ` [${c.agent_id || "?"}] ${c.created_at?.slice(0, 16)}: ${c.content.slice(0, 80)}`),
22393
24192
  files.length > 0 ? `
22394
24193
  Files (${files.length}):` : null,
22395
24194
  ...files.map((f) => ` ${f.status} ${f.path}`)
@@ -22407,22 +24206,22 @@ Files (${files.length}):` : null,
22407
24206
  }, async ({ task_id }) => {
22408
24207
  try {
22409
24208
  const resolvedId = resolveId(task_id);
22410
- const { getTask: getTask4 } = (init_tasks(), __toCommonJS(exports_tasks));
22411
- const task = getTask4(resolvedId);
24209
+ const { getTask: getTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
24210
+ const task = getTask2(resolvedId);
22412
24211
  if (!task)
22413
24212
  throw new Error(`Task not found: ${task_id}`);
22414
24213
  const { getTaskDependencies: getTaskDependencies3 } = (init_tasks(), __toCommonJS(exports_tasks));
22415
24214
  const { getTaskRelationships: getTaskRelationships2 } = (init_task_relationships(), __toCommonJS(exports_task_relationships));
22416
- const { listComments: listComments2 } = (init_tasks(), __toCommonJS(exports_tasks));
24215
+ const { listComments: listComments2 } = (init_comments(), __toCommonJS(exports_comments));
22417
24216
  const { listTaskFiles: listTaskFiles2 } = (init_task_files(), __toCommonJS(exports_task_files));
22418
- const { getTaskCommits } = (init_tasks(), __toCommonJS(exports_tasks));
24217
+ const { getTaskCommits: getTaskCommits2 } = (init_task_commits(), __toCommonJS(exports_task_commits));
22419
24218
  const { getTaskWatchers: getTaskWatchers2 } = (init_tasks(), __toCommonJS(exports_tasks));
22420
24219
  const [deps, rels, comments, files, commits, watchers] = await Promise.all([
22421
24220
  Promise.resolve(getTaskDependencies3(resolvedId, "both")),
22422
24221
  Promise.resolve(getTaskRelationships2(resolvedId)),
22423
24222
  Promise.resolve(listComments2(resolvedId)),
22424
24223
  Promise.resolve(listTaskFiles2(resolvedId)),
22425
- Promise.resolve(getTaskCommits(resolvedId)),
24224
+ Promise.resolve(getTaskCommits2(resolvedId)),
22426
24225
  Promise.resolve(getTaskWatchers2(resolvedId))
22427
24226
  ]);
22428
24227
  const lines = [
@@ -22436,7 +24235,7 @@ Files (${files.length}):` : null,
22436
24235
  task.tags?.length ? `Tags: ${task.tags.join(", ")}` : null,
22437
24236
  task.created_at ? `Created: ${task.created_at}` : null,
22438
24237
  task.updated_at ? `Updated: ${task.updated_at}` : null,
22439
- task.deadline ? `Deadline: ${task.deadline}` : null,
24238
+ task.due_at ? `Due: ${task.due_at}` : null,
22440
24239
  task.completed_at ? `Completed: ${task.completed_at}` : null,
22441
24240
  deps.length > 0 ? `
22442
24241
  --- Dependencies (${deps.length}) ---` : null,
@@ -22446,7 +24245,7 @@ Files (${files.length}):` : null,
22446
24245
  ...rels.map((r) => ` ${r.source_task_id.slice(0, 8)} --[${r.relationship_type}]--> ${r.target_task_id.slice(0, 8)}`),
22447
24246
  comments.length > 0 ? `
22448
24247
  --- Comments (${comments.length}) ---` : null,
22449
- ...comments.map((c) => ` [${c.author || "?"}] ${c.created_at?.slice(0, 16)}: ${c.body.slice(0, 120)}`),
24248
+ ...comments.map((c) => ` [${c.agent_id || "?"}] ${c.created_at?.slice(0, 16)}: ${c.content.slice(0, 120)}`),
22450
24249
  files.length > 0 ? `
22451
24250
  --- Files (${files.length}) ---` : null,
22452
24251
  ...files.map((f) => ` [${f.status}] ${f.path}`),
@@ -22495,8 +24294,8 @@ ${JSON.stringify(task.metadata, null, 2)}` : null
22495
24294
  limit: 20
22496
24295
  }, undefined);
22497
24296
  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);
24297
+ const { getBlockedTasks: getBlockedTasks2 } = (init_tasks(), __toCommonJS(exports_tasks));
24298
+ const blocked = getBlockedTasks2(effectiveProjectId ? resolveId(effectiveProjectId, "projects") : undefined).filter((t) => t.assigned_to === effectiveAgentId);
22500
24299
  const lines = [
22501
24300
  `Standup for ${effectiveAgentId} (${effectiveProjectId ? `project: ${effectiveProjectId.slice(0, 8)}` : "all projects"})`,
22502
24301
  inProgress.length > 0 ? `
@@ -22525,11 +24324,11 @@ No blocked tasks.`,
22525
24324
  agent_id: exports_external.string().optional().describe("Agent claiming (defaults to context)")
22526
24325
  }, async ({ task_id, agent_id }) => {
22527
24326
  try {
22528
- const { updateTask: updateTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
24327
+ const { startTask: startTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
22529
24328
  const resolvedId = resolveId(task_id);
22530
24329
  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 });
24330
+ const effectiveAgent = focus ? focus.agent_id : agent_id || "mcp";
24331
+ const task = startTask2(resolvedId, effectiveAgent);
22533
24332
  return { content: [{ type: "text", text: formatTask(task) }] };
22534
24333
  } catch (e) {
22535
24334
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
@@ -22541,211 +24340,95 @@ No blocked tasks.`,
22541
24340
  task_id: exports_external.string().describe("Task ID")
22542
24341
  }, async ({ task_id }) => {
22543
24342
  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));
24343
+ const { getTask: getTask2, updateTask: updateTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
22559
24344
  const resolvedId = resolveId(task_id);
22560
- const task = getTask4(resolvedId);
22561
- if (!task)
24345
+ const current = getTask2(resolvedId);
24346
+ if (!current)
22562
24347
  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
- `) }] };
24348
+ const task = updateTask2(resolvedId, { status: "pending", assigned_to: null, version: current.version });
24349
+ return { content: [{ type: "text", text: formatTask(task) }] };
22681
24350
  } catch (e) {
22682
24351
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
22683
24352
  }
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 }) => {
24353
+ });
24354
+ }
24355
+ if (shouldRegisterTool("extend_task")) {
24356
+ server.tool("extend_task", "Add more time to a task's estimate.", {
24357
+ task_id: exports_external.string().describe("Task ID"),
24358
+ minutes: exports_external.number().describe("Additional minutes to add to estimate")
24359
+ }, async ({ task_id, minutes }) => {
22695
24360
  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.` }] };
24361
+ const { getTask: getTask2, updateTask: updateTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
24362
+ const resolvedId = resolveId(task_id);
24363
+ const task = getTask2(resolvedId);
24364
+ if (!task)
24365
+ throw new Error(`Task not found: ${task_id}`);
24366
+ const currentEstimate = task.estimated_minutes || 0;
24367
+ const updated = updateTask2(resolvedId, { estimated_minutes: currentEstimate + minutes, version: task.version });
24368
+ return { content: [{ type: "text", text: `Estimate updated: ${currentEstimate} \u2192 ${updated.estimated_minutes} min (+${minutes})` }] };
22701
24369
  } catch (e) {
22702
24370
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
22703
24371
  }
22704
24372
  });
22705
24373
  }
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 }) => {
24374
+ if (shouldRegisterTool("add_comment")) {
24375
+ server.tool("add_comment", "Alias for create_comment.", {
24376
+ task_id: exports_external.string().describe("Task ID"),
24377
+ body: exports_external.string().describe("Comment body"),
24378
+ author: exports_external.string().optional().describe("Author agent ID or name")
24379
+ }, async ({ task_id, body, author }) => {
22710
24380
  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.` }] };
24381
+ const { addComment: addComment2 } = (init_comments(), __toCommonJS(exports_comments));
24382
+ const resolvedId = resolveId(task_id);
24383
+ const resolvedAuthor = author ? resolveId(author, "agents") : undefined;
24384
+ addComment2({ task_id: resolvedId, content: body, agent_id: resolvedAuthor });
24385
+ return { content: [{ type: "text", text: `Comment added to ${task_id.slice(0, 8)}` }] };
22718
24386
  } catch (e) {
22719
24387
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
22720
24388
  }
22721
24389
  });
22722
24390
  }
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 }) => {
24391
+ if (shouldRegisterTool("get_comments")) {
24392
+ server.tool("get_comments", "Alias for list_comments.", {
24393
+ task_id: exports_external.string().describe("Task ID")
24394
+ }, async ({ task_id }) => {
22731
24395
  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})` }] };
24396
+ const { listComments: listComments2 } = (init_comments(), __toCommonJS(exports_comments));
24397
+ const comments = listComments2(resolveId(task_id));
24398
+ if (comments.length === 0)
24399
+ return { content: [{ type: "text", text: "No comments." }] };
24400
+ const lines = comments.map((c) => `[${c.agent_id || "?"}] ${c.created_at?.slice(0, 16)}: ${c.content}`);
24401
+ return { content: [{ type: "text", text: lines.join(`
24402
+
24403
+ `) }] };
22735
24404
  } catch (e) {
22736
24405
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
22737
24406
  }
22738
24407
  });
22739
24408
  }
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 }) => {
24409
+ if (shouldRegisterTool("list_my_tasks")) {
24410
+ server.tool("list_my_tasks", "Alias for get_my_tasks.", {
24411
+ agent_id: exports_external.string().optional().describe("Agent ID (defaults to context agent)"),
24412
+ status: exports_external.enum(["pending", "in_progress", "completed", "failed", "cancelled"]).optional(),
24413
+ project_id: exports_external.string().optional().describe("Filter by project"),
24414
+ limit: exports_external.number().optional()
24415
+ }, async ({ agent_id, status, project_id, limit }) => {
22745
24416
  try {
22746
- const { heartbeat } = (init_agents(), __toCommonJS(exports_agents));
22747
- heartbeat(resolveId(agent_id, "agents"), status);
22748
- return { content: [{ type: "text", text: "Heartbeat received." }] };
24417
+ const { listTasks: listTasks3 } = (init_tasks(), __toCommonJS(exports_tasks));
24418
+ const focus = ctx.getAgentFocus(agent_id || "");
24419
+ const effectiveAgentId = focus ? focus.agent_id : agent_id || "";
24420
+ const effectiveProjectId = focus?.project_id || project_id;
24421
+ const tasks = listTasks3({
24422
+ assigned_to: effectiveAgentId,
24423
+ status,
24424
+ project_id: effectiveProjectId ? resolveId(effectiveProjectId, "projects") : undefined,
24425
+ limit: limit || 50
24426
+ }, undefined);
24427
+ if (tasks.length === 0)
24428
+ return { content: [{ type: "text", text: "No tasks found." }] };
24429
+ const lines = tasks.map(formatTask);
24430
+ return { content: [{ type: "text", text: lines.join(`
24431
+ `) }] };
22749
24432
  } catch (e) {
22750
24433
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
22751
24434
  }
@@ -22792,6 +24475,11 @@ function registerTaskMetaTools(server, ctx) {
22792
24475
  prioritize_task: "prioritize_task \u2014 Set priority. Params: task_id, priority, version",
22793
24476
  search_tasks: "search_tasks \u2014 Full-text search. Params: query, project_id, status, limit",
22794
24477
  get_my_tasks: "get_my_tasks \u2014 Get tasks for calling agent. Params: agent_id, status, project_id, limit",
24478
+ 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",
24479
+ 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",
24480
+ get_tasks_changed_since: "get_tasks_changed_since \u2014 List tasks changed since an ISO timestamp. Params: since, project_id, task_list_id, limit",
24481
+ get_context: "get_context \u2014 Get session start context. Params: agent_id, project_id, task_list_id, explain_blocked",
24482
+ bootstrap: "bootstrap \u2014 Bootstrap an agent session with queue context. Params: agent_id, project_id, task_list_id, explain_blocked",
22795
24483
  standup: "standup \u2014 Get standup report. Params: agent_id, project_id",
22796
24484
  patrol_tasks: "patrol_tasks \u2014 Scan for task issues. Params: stuck_minutes, confidence_threshold, project_id",
22797
24485
  get_review_queue: "get_review_queue \u2014 Get tasks needing review. Params: project_id, limit",
@@ -22849,9 +24537,20 @@ function registerTaskMetaTools(server, ctx) {
22849
24537
  get_health: "get_health \u2014 Get system health stats",
22850
24538
  approve_task: "approve_task \u2014 Approve a task. Params: task_id, approved_by, notes, version",
22851
24539
  fail_task: "fail_task \u2014 Mark task failed. Params: task_id, reason, agent_id, version",
24540
+ register_agent: "register_agent \u2014 Register an agent. Params: name, description, role, title, capabilities, session_id, working_dir, force",
24541
+ list_agents: "list_agents \u2014 List registered agents. Params: include_archived",
24542
+ get_agent: "get_agent \u2014 Get agent details. Params: agent_id, id, name",
24543
+ update_agent: "update_agent \u2014 Update an agent. Params: agent_id, id, name, description, role, title, level, capabilities, permissions, metadata",
24544
+ delete_agent: "delete_agent \u2014 Archive an agent. Params: agent_id, id, name",
24545
+ unarchive_agent: "unarchive_agent \u2014 Restore an archived agent. Params: agent_id, id, name",
24546
+ heartbeat: "heartbeat \u2014 Update an agent heartbeat. Params: agent_id",
24547
+ release_agent: "release_agent \u2014 Release an agent session/name. Params: agent_id, session_id",
24548
+ set_focus: "set_focus \u2014 Focus an agent on a project. Params: agent_id, project_id, task_list_id",
24549
+ get_focus: "get_focus \u2014 Get current agent focus. Params: agent_id",
24550
+ unfocus: "unfocus \u2014 Clear agent focus. Params: agent_id",
24551
+ suggest_agent_name: "suggest_agent_name \u2014 Suggest available agent names. Params: working_dir",
22852
24552
  get_org_chart: "get_org_chart \u2014 Get global org chart. Params: format",
22853
24553
  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
24554
  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
24555
  migrate_pg: "migrate_pg \u2014 Apply PostgreSQL migrations. Params: connection_string"
22857
24556
  };
@@ -22870,39 +24569,7 @@ function registerTaskMetaTools(server, ctx) {
22870
24569
  init_tasks();
22871
24570
  init_projects();
22872
24571
  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
24572
+ init_task_commits();
22906
24573
  function registerTaskResources(server, ctx) {
22907
24574
  const { shouldRegisterTool, resolveId, formatError } = ctx;
22908
24575
  server.resource("tasks", "todos://tasks", { description: "All active tasks", mimeType: "application/json" }, async () => {
@@ -22927,7 +24594,7 @@ function registerTaskResources(server, ctx) {
22927
24594
  note: exports_external.string().optional().describe("Note about why this file is linked")
22928
24595
  }, async ({ task_id, path, paths: multiplePaths, status, agent_id, note }) => {
22929
24596
  try {
22930
- const { addTaskFile: addTaskFile2, bulkAddTaskFiles: bulkAddTaskFiles2, detectFileConflicts: detectFileConflicts2 } = (()=>{throw new Error("Cannot require module "+"../db/task-files.js");})();
24597
+ const { addTaskFile: addTaskFile2, bulkAddTaskFiles: bulkAddTaskFiles2, detectFileConflicts: detectFileConflicts2 } = (init_task_files(), __toCommonJS(exports_task_files));
22931
24598
  const resolvedId = resolveId(task_id);
22932
24599
  let addedFiles;
22933
24600
  if (multiplePaths && multiplePaths.length > 0) {
@@ -22971,7 +24638,7 @@ function registerTaskResources(server, ctx) {
22971
24638
  if (shouldRegisterTool("list_task_files")) {
22972
24639
  server.tool("list_task_files", "List all files linked to a task.", { task_id: exports_external.string().describe("Task ID") }, async ({ task_id }) => {
22973
24640
  try {
22974
- const { listTaskFiles: listTaskFiles2 } = (()=>{throw new Error("Cannot require module "+"../db/task-files.js");})();
24641
+ const { listTaskFiles: listTaskFiles2 } = (init_task_files(), __toCommonJS(exports_task_files));
22975
24642
  const resolvedId = resolveId(task_id);
22976
24643
  const files = listTaskFiles2(resolvedId);
22977
24644
  if (files.length === 0)
@@ -22988,7 +24655,7 @@ ${lines.join(`
22988
24655
  if (shouldRegisterTool("find_tasks_by_file")) {
22989
24656
  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
24657
  try {
22991
- const { findTasksByFile: findTasksByFile2 } = (()=>{throw new Error("Cannot require module "+"../db/task-files.js");})();
24658
+ const { findTasksByFile: findTasksByFile2 } = (init_task_files(), __toCommonJS(exports_task_files));
22992
24659
  const files = findTasksByFile2(path);
22993
24660
  if (files.length === 0)
22994
24661
  return { content: [{ type: "text", text: `No tasks linked to ${path}` }] };
@@ -23008,7 +24675,7 @@ ${lines.join(`
23008
24675
  min_edits: exports_external.number().optional().describe("Minimum edit count to include (default: 1)")
23009
24676
  }, async ({ limit, project_id, min_edits }) => {
23010
24677
  try {
23011
- const { getFileHeatMap: getFileHeatMap2 } = (()=>{throw new Error("Cannot require module "+"../db/task-files.js");})();
24678
+ const { getFileHeatMap: getFileHeatMap2 } = (init_task_files(), __toCommonJS(exports_task_files));
23012
24679
  const resolvedProjectId = project_id ? resolveId(project_id, "projects") : undefined;
23013
24680
  const results = getFileHeatMap2({ limit, project_id: resolvedProjectId, min_edits });
23014
24681
  return { content: [{ type: "text", text: JSON.stringify(results, null, 2) }] };
@@ -23022,7 +24689,7 @@ ${lines.join(`
23022
24689
  paths: exports_external.array(exports_external.string()).describe("Array of file paths to check")
23023
24690
  }, async ({ paths }) => {
23024
24691
  try {
23025
- const { bulkFindTasksByFiles: bulkFindTasksByFiles2 } = (()=>{throw new Error("Cannot require module "+"../db/task-files.js");})();
24692
+ const { bulkFindTasksByFiles: bulkFindTasksByFiles2 } = (init_task_files(), __toCommonJS(exports_task_files));
23026
24693
  const results = bulkFindTasksByFiles2(paths);
23027
24694
  return { content: [{ type: "text", text: JSON.stringify(results, null, 2) }] };
23028
24695
  } catch (e) {
@@ -23035,11 +24702,11 @@ ${lines.join(`
23035
24702
  project_id: exports_external.string().optional().describe("Filter by project")
23036
24703
  }, async ({ project_id }) => {
23037
24704
  try {
23038
- const { listActiveFiles: listActiveFiles2 } = (()=>{throw new Error("Cannot require module "+"../db/task-files.js");})();
24705
+ const { listActiveFiles: listActiveFiles2 } = (init_task_files(), __toCommonJS(exports_task_files));
23039
24706
  let files = listActiveFiles2();
23040
24707
  if (project_id) {
23041
24708
  const pid = resolveId(project_id, "projects");
23042
- const db = (()=>{throw new Error("Cannot require module "+"../db/database.js");})().getDatabase();
24709
+ const db = (init_database(), __toCommonJS(exports_database)).getDatabase();
23043
24710
  files = db.query(`
23044
24711
  SELECT
23045
24712
  tf.path,
@@ -23121,8 +24788,8 @@ ${lines.join(`
23121
24788
  ttl_seconds: exports_external.number().optional().describe("Lock TTL in seconds (default: 1800 = 30 min)")
23122
24789
  }, async ({ path, agent_id, task_id, ttl_seconds }) => {
23123
24790
  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 });
24791
+ const { lockFile: lockFile2 } = (init_file_locks(), __toCommonJS(exports_file_locks));
24792
+ const lock = lockFile2({ path, agent_id, task_id, ttl_seconds });
23126
24793
  return { content: [{ type: "text", text: JSON.stringify(lock, null, 2) }] };
23127
24794
  } catch (e) {
23128
24795
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
@@ -23135,8 +24802,8 @@ ${lines.join(`
23135
24802
  agent_id: exports_external.string().describe("Agent releasing the lock (must be the lock holder)")
23136
24803
  }, async ({ path, agent_id }) => {
23137
24804
  try {
23138
- const { unlockFile } = (()=>{throw new Error("Cannot require module "+"../db/file-locks.js");})();
23139
- const released = unlockFile(path, agent_id);
24805
+ const { unlockFile: unlockFile2 } = (init_file_locks(), __toCommonJS(exports_file_locks));
24806
+ const released = unlockFile2(path, agent_id);
23140
24807
  return { content: [{ type: "text", text: JSON.stringify({ released, path }) }] };
23141
24808
  } catch (e) {
23142
24809
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
@@ -23148,8 +24815,8 @@ ${lines.join(`
23148
24815
  path: exports_external.string().describe("File path to check")
23149
24816
  }, async ({ path }) => {
23150
24817
  try {
23151
- const { checkFileLock } = (()=>{throw new Error("Cannot require module "+"../db/file-locks.js");})();
23152
- const lock = checkFileLock(path);
24818
+ const { checkFileLock: checkFileLock2 } = (init_file_locks(), __toCommonJS(exports_file_locks));
24819
+ const lock = checkFileLock2(path);
23153
24820
  if (!lock)
23154
24821
  return { content: [{ type: "text", text: JSON.stringify({ path, locked: false }) }] };
23155
24822
  return { content: [{ type: "text", text: JSON.stringify({ path, locked: true, ...lock }) }] };
@@ -23163,8 +24830,8 @@ ${lines.join(`
23163
24830
  agent_id: exports_external.string().optional().describe("Filter locks by agent")
23164
24831
  }, async ({ agent_id }) => {
23165
24832
  try {
23166
- const { listFileLocks } = (()=>{throw new Error("Cannot require module "+"../db/file-locks.js");})();
23167
- const locks = listFileLocks(agent_id);
24833
+ const { listFileLocks: listFileLocks2 } = (init_file_locks(), __toCommonJS(exports_file_locks));
24834
+ const locks = listFileLocks2(agent_id);
23168
24835
  return { content: [{ type: "text", text: JSON.stringify(locks, null, 2) }] };
23169
24836
  } catch (e) {
23170
24837
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
@@ -23187,8 +24854,8 @@ function registerTaskRelTools(server, ctx) {
23187
24854
  next_steps: exports_external.array(exports_external.string()).optional().describe("Recommended next actions")
23188
24855
  }, async ({ agent_id, project_id, summary, completed, in_progress, blockers, next_steps }) => {
23189
24856
  try {
23190
- const { createHandoff } = (()=>{throw new Error("Cannot require module "+"../db/handoffs.js");})();
23191
- const handoff = createHandoff({
24857
+ const { createHandoff: createHandoff2 } = (init_handoffs(), __toCommonJS(exports_handoffs));
24858
+ const handoff = createHandoff2({
23192
24859
  agent_id,
23193
24860
  project_id: project_id ? resolveId(project_id, "projects") : undefined,
23194
24861
  summary,
@@ -23209,8 +24876,8 @@ function registerTaskRelTools(server, ctx) {
23209
24876
  project_id: exports_external.string().optional().describe("Filter by project")
23210
24877
  }, async ({ agent_id, project_id }) => {
23211
24878
  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);
24879
+ const { getLatestHandoff: getLatestHandoff2 } = (init_handoffs(), __toCommonJS(exports_handoffs));
24880
+ const handoff = getLatestHandoff2(agent_id, project_id ? resolveId(project_id, "projects") : undefined);
23214
24881
  if (!handoff)
23215
24882
  return { content: [{ type: "text", text: "No handoffs found." }] };
23216
24883
  const lines = [
@@ -23240,7 +24907,7 @@ function registerTaskRelTools(server, ctx) {
23240
24907
  created_by: exports_external.string().optional().describe("Agent ID who created this relationship")
23241
24908
  }, async ({ source_task_id, target_task_id, relationship_type, created_by }) => {
23242
24909
  try {
23243
- const { addTaskRelationship: addTaskRelationship2 } = (()=>{throw new Error("Cannot require module "+"../db/task-relationships.js");})();
24910
+ const { addTaskRelationship: addTaskRelationship2 } = (init_task_relationships(), __toCommonJS(exports_task_relationships));
23244
24911
  const rel = addTaskRelationship2({
23245
24912
  source_task_id: resolveId(source_task_id),
23246
24913
  target_task_id: resolveId(target_task_id),
@@ -23261,7 +24928,7 @@ function registerTaskRelTools(server, ctx) {
23261
24928
  relationship_type: exports_external.enum(["related_to", "conflicts_with", "similar_to", "duplicates", "supersedes", "modifies_same_file"]).optional()
23262
24929
  }, async ({ id, source_task_id, target_task_id, relationship_type }) => {
23263
24930
  try {
23264
- const { removeTaskRelationship: removeTaskRelationship2, removeTaskRelationshipByPair: removeTaskRelationshipByPair2 } = (()=>{throw new Error("Cannot require module "+"../db/task-relationships.js");})();
24931
+ const { removeTaskRelationship: removeTaskRelationship2, removeTaskRelationshipByPair: removeTaskRelationshipByPair2 } = (init_task_relationships(), __toCommonJS(exports_task_relationships));
23265
24932
  let removed = false;
23266
24933
  if (id) {
23267
24934
  removed = removeTaskRelationship2(id);
@@ -23282,7 +24949,7 @@ function registerTaskRelTools(server, ctx) {
23282
24949
  relationship_type: exports_external.enum(["related_to", "conflicts_with", "similar_to", "duplicates", "supersedes", "modifies_same_file"]).optional()
23283
24950
  }, async ({ task_id, relationship_type }) => {
23284
24951
  try {
23285
- const { getTaskRelationships: getTaskRelationships2 } = (()=>{throw new Error("Cannot require module "+"../db/task-relationships.js");})();
24952
+ const { getTaskRelationships: getTaskRelationships2 } = (init_task_relationships(), __toCommonJS(exports_task_relationships));
23286
24953
  const rels = getTaskRelationships2(resolveId(task_id), relationship_type);
23287
24954
  if (rels.length === 0)
23288
24955
  return { content: [{ type: "text", text: "No relationships found." }] };
@@ -23299,7 +24966,7 @@ function registerTaskRelTools(server, ctx) {
23299
24966
  task_id: exports_external.string().describe("Task ID to detect file relationships for")
23300
24967
  }, async ({ task_id }) => {
23301
24968
  try {
23302
- const { autoDetectFileRelationships: autoDetectFileRelationships2 } = (()=>{throw new Error("Cannot require module "+"../db/task-relationships.js");})();
24969
+ const { autoDetectFileRelationships: autoDetectFileRelationships2 } = (init_task_relationships(), __toCommonJS(exports_task_relationships));
23303
24970
  const created = autoDetectFileRelationships2(resolveId(task_id));
23304
24971
  return { content: [{ type: "text", text: created.length > 0 ? `Created ${created.length} file relationship(s).` : "No file overlaps detected." }] };
23305
24972
  } catch (e) {
@@ -23310,8 +24977,8 @@ function registerTaskRelTools(server, ctx) {
23310
24977
  if (shouldRegisterTool("sync_kg")) {
23311
24978
  server.tool("sync_kg", "Sync all existing relationships into the knowledge graph edges table. Idempotent.", {}, async () => {
23312
24979
  try {
23313
- const { syncKgEdges } = (()=>{throw new Error("Cannot require module "+"../db/kg.js");})();
23314
- const result = syncKgEdges();
24980
+ const { syncKgEdges: syncKgEdges2 } = (init_kg(), __toCommonJS(exports_kg));
24981
+ const result = syncKgEdges2();
23315
24982
  return { content: [{ type: "text", text: `Knowledge graph synced: ${result.synced} edge(s) processed.` }] };
23316
24983
  } catch (e) {
23317
24984
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
@@ -23327,8 +24994,8 @@ function registerTaskRelTools(server, ctx) {
23327
24994
  limit: exports_external.number().optional().describe("Max results")
23328
24995
  }, async ({ entity_id, relation_type, entity_type, direction, limit }) => {
23329
24996
  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 });
24997
+ const { getRelated: getRelated2 } = (init_kg(), __toCommonJS(exports_kg));
24998
+ const edges = getRelated2(entity_id, { relation_type, entity_type, direction, limit });
23332
24999
  if (edges.length === 0)
23333
25000
  return { content: [{ type: "text", text: "No related entities found." }] };
23334
25001
  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 +25014,8 @@ function registerTaskRelTools(server, ctx) {
23347
25014
  relation_types: exports_external.array(exports_external.string()).optional().describe("Filter by relation types")
23348
25015
  }, async ({ source_id, target_id, max_depth, relation_types }) => {
23349
25016
  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 });
25017
+ const { findPath: findPath2 } = (init_kg(), __toCommonJS(exports_kg));
25018
+ const paths = findPath2(source_id, target_id, { max_depth, relation_types });
23352
25019
  if (paths.length === 0)
23353
25020
  return { content: [{ type: "text", text: "No path found." }] };
23354
25021
  const lines = paths.map((path, i) => {
@@ -23372,8 +25039,8 @@ function registerTaskRelTools(server, ctx) {
23372
25039
  relation_types: exports_external.array(exports_external.string()).optional().describe("Filter by relation types")
23373
25040
  }, async ({ entity_id, max_depth, relation_types }) => {
23374
25041
  try {
23375
- const { getImpactAnalysis } = (()=>{throw new Error("Cannot require module "+"../db/kg.js");})();
23376
- const impact = getImpactAnalysis(entity_id, { max_depth, relation_types });
25042
+ const { getImpactAnalysis: getImpactAnalysis2 } = (init_kg(), __toCommonJS(exports_kg));
25043
+ const impact = getImpactAnalysis2(entity_id, { max_depth, relation_types });
23377
25044
  if (impact.length === 0)
23378
25045
  return { content: [{ type: "text", text: "No downstream impact detected." }] };
23379
25046
  const byDepth = new Map;
@@ -23403,8 +25070,8 @@ Depth ${depth}:`);
23403
25070
  limit: exports_external.number().optional().describe("Max results (default: 20)")
23404
25071
  }, async ({ project_id, limit }) => {
23405
25072
  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 });
25073
+ const { getCriticalPath: getCriticalPath2 } = (init_kg(), __toCommonJS(exports_kg));
25074
+ const result = getCriticalPath2({ project_id: project_id ? resolveId(project_id, "projects") : undefined, limit });
23408
25075
  if (result.length === 0)
23409
25076
  return { content: [{ type: "text", text: "No critical path data. Run sync_kg first to populate the knowledge graph." }] };
23410
25077
  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 +25091,13 @@ ${lines.join(`
23424
25091
  is_lead: exports_external.coerce.boolean().optional().describe("Whether this agent is the project lead for this role")
23425
25092
  }, async ({ project_id, agent_name, role, is_lead }) => {
23426
25093
  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");})();
25094
+ const { setProjectAgentRole: setProjectAgentRole2 } = (init_project_agent_roles(), __toCommonJS(exports_project_agent_roles));
25095
+ const { getAgentByName: getAgentByName2 } = (init_agents(), __toCommonJS(exports_agents));
23429
25096
  const agent = getAgentByName2(agent_name);
23430
25097
  if (!agent)
23431
25098
  return { content: [{ type: "text", text: `Agent not found: ${agent_name}` }], isError: true };
23432
25099
  const pid = resolveId(project_id, "projects");
23433
- const result = setProjectAgentRole(pid, agent.id, role, is_lead ?? false);
25100
+ const result = setProjectAgentRole2(pid, agent.id, role, is_lead ?? false);
23434
25101
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
23435
25102
  } catch (e) {
23436
25103
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
@@ -23460,9 +25127,9 @@ ${lines.join(`
23460
25127
  }).join(`
23461
25128
  `);
23462
25129
  };
23463
- const { getProjectOrgChart } = (()=>{throw new Error("Cannot require module "+"../db/project-agent-roles.js");})();
25130
+ const { getProjectOrgChart: getProjectOrgChart2 } = (init_project_agent_roles(), __toCommonJS(exports_project_agent_roles));
23464
25131
  const pid = resolveId(project_id, "projects");
23465
- const tree = getProjectOrgChart(pid, { filter_to_project });
25132
+ const tree = getProjectOrgChart2(pid, { filter_to_project });
23466
25133
  if (format === "json") {
23467
25134
  return { content: [{ type: "text", text: JSON.stringify(tree, null, 2) }] };
23468
25135
  }
@@ -23480,9 +25147,9 @@ ${lines.join(`
23480
25147
  project_id: exports_external.string().describe("Project ID")
23481
25148
  }, async ({ project_id }) => {
23482
25149
  try {
23483
- const { listProjectAgentRoles } = (()=>{throw new Error("Cannot require module "+"../db/project-agent-roles.js");})();
25150
+ const { listProjectAgentRoles: listProjectAgentRoles2 } = (init_project_agent_roles(), __toCommonJS(exports_project_agent_roles));
23484
25151
  const pid = resolveId(project_id, "projects");
23485
- const roles = listProjectAgentRoles(pid);
25152
+ const roles = listProjectAgentRoles2(pid);
23486
25153
  return { content: [{ type: "text", text: JSON.stringify(roles, null, 2) }] };
23487
25154
  } catch (e) {
23488
25155
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
@@ -23496,7 +25163,7 @@ ${lines.join(`
23496
25163
  limit: exports_external.number().optional().describe("Max results")
23497
25164
  }, async ({ capabilities, min_score, limit }) => {
23498
25165
  try {
23499
- const { getCapableAgents: getCapableAgents2 } = (()=>{throw new Error("Cannot require module "+"../db/agents.js");})();
25166
+ const { getCapableAgents: getCapableAgents2 } = (init_agents(), __toCommonJS(exports_agents));
23500
25167
  const results = getCapableAgents2(capabilities, { min_score, limit });
23501
25168
  if (results.length === 0)
23502
25169
  return { content: [{ type: "text", text: "No agents match the given capabilities." }] };
@@ -23515,8 +25182,8 @@ ${lines.join(`
23515
25182
  project_id: exports_external.string().optional().describe("Filter by project")
23516
25183
  }, async ({ stuck_minutes, confidence_threshold, project_id }) => {
23517
25184
  try {
23518
- const { patrolTasks } = (()=>{throw new Error("Cannot require module "+"../db/patrol.js");})();
23519
- const result = patrolTasks({
25185
+ const { patrolTasks: patrolTasks2 } = (init_patrol(), __toCommonJS(exports_patrol));
25186
+ const result = patrolTasks2({
23520
25187
  stuck_minutes,
23521
25188
  confidence_threshold,
23522
25189
  project_id: project_id ? resolveId(project_id, "projects") : undefined
@@ -23542,8 +25209,8 @@ ${lines.join(`
23542
25209
  limit: exports_external.number().optional().describe("Max results (default: all)")
23543
25210
  }, async ({ project_id, limit }) => {
23544
25211
  try {
23545
- const { getReviewQueue } = (()=>{throw new Error("Cannot require module "+"../db/patrol.js");})();
23546
- const tasks = getReviewQueue({
25212
+ const { getReviewQueue: getReviewQueue2 } = (init_patrol(), __toCommonJS(exports_patrol));
25213
+ const tasks = getReviewQueue2({
23547
25214
  project_id: project_id ? resolveId(project_id, "projects") : undefined,
23548
25215
  limit
23549
25216
  });
@@ -23569,8 +25236,8 @@ ${lines.join(`
23569
25236
  reviewer_id: exports_external.string().optional().describe("Agent ID of reviewer")
23570
25237
  }, async ({ task_id, score, reviewer_id }) => {
23571
25238
  try {
23572
- const { scoreTask } = (()=>{throw new Error("Cannot require module "+"../db/agent-metrics.js");})();
23573
- scoreTask(resolveId(task_id), score, reviewer_id);
25239
+ const { scoreTask: scoreTask2 } = (init_agent_metrics(), __toCommonJS(exports_agent_metrics));
25240
+ scoreTask2(resolveId(task_id), score, reviewer_id);
23574
25241
  return { content: [{ type: "text", text: `Task ${task_id.slice(0, 8)} scored: ${score}` }] };
23575
25242
  } catch (e) {
23576
25243
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
@@ -23587,7 +25254,7 @@ ${lines.join(`
23587
25254
  notes: exports_external.string().optional().describe("Notes about what was done")
23588
25255
  }, async ({ task_id, minutes, agent_id, started_at, ended_at, notes }) => {
23589
25256
  try {
23590
- const { logTime: logTime2 } = (()=>{throw new Error("Cannot require module "+"../db/tasks.js");})();
25257
+ const { logTime: logTime2 } = (init_tasks(), __toCommonJS(exports_tasks));
23591
25258
  logTime2({ task_id: resolveId(task_id), minutes, agent_id, started_at, ended_at, notes });
23592
25259
  return { content: [{ type: "text", text: `Logged ${minutes} min on task ${task_id.slice(0, 8)}` }] };
23593
25260
  } catch (e) {
@@ -23602,7 +25269,7 @@ ${lines.join(`
23602
25269
  since: exports_external.string().optional().describe("ISO date \u2014 only tasks completed after this date")
23603
25270
  }, async ({ project_id, agent_id, since }) => {
23604
25271
  try {
23605
- const { getTimeReport: getTimeReport2 } = (()=>{throw new Error("Cannot require module "+"../db/tasks.js");})();
25272
+ const { getTimeReport: getTimeReport2 } = (init_tasks(), __toCommonJS(exports_tasks));
23606
25273
  const report = getTimeReport2({ project_id: project_id ? resolveId(project_id, "projects") : undefined, agent_id, since });
23607
25274
  if (report.length === 0)
23608
25275
  return { content: [{ type: "text", text: "No completed tasks found." }] };
@@ -23626,7 +25293,7 @@ ${lines.join(`
23626
25293
  agent_id: exports_external.string().optional().describe("Agent subscribing (defaults to context agent)")
23627
25294
  }, async ({ task_id, agent_id }) => {
23628
25295
  try {
23629
- const { watchTask: watchTask2 } = (()=>{throw new Error("Cannot require module "+"../db/tasks.js");})();
25296
+ const { watchTask: watchTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
23630
25297
  watchTask2(resolveId(task_id), agent_id || "");
23631
25298
  return { content: [{ type: "text", text: `Now watching task ${task_id.slice(0, 8)}` }] };
23632
25299
  } catch (e) {
@@ -23640,7 +25307,7 @@ ${lines.join(`
23640
25307
  agent_id: exports_external.string().optional().describe("Agent unsubscribing (defaults to context agent)")
23641
25308
  }, async ({ task_id, agent_id }) => {
23642
25309
  try {
23643
- const { unwatchTask: unwatchTask2 } = (()=>{throw new Error("Cannot require module "+"../db/tasks.js");})();
25310
+ const { unwatchTask: unwatchTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
23644
25311
  unwatchTask2(resolveId(task_id), agent_id || "");
23645
25312
  return { content: [{ type: "text", text: `Stopped watching task ${task_id.slice(0, 8)}` }] };
23646
25313
  } catch (e) {
@@ -23653,7 +25320,7 @@ ${lines.join(`
23653
25320
  task_id: exports_external.string().describe("Task ID")
23654
25321
  }, async ({ task_id }) => {
23655
25322
  try {
23656
- const { getTaskWatchers: getTaskWatchers2 } = (()=>{throw new Error("Cannot require module "+"../db/tasks.js");})();
25323
+ const { getTaskWatchers: getTaskWatchers2 } = (init_tasks(), __toCommonJS(exports_tasks));
23657
25324
  const watchers = getTaskWatchers2(resolveId(task_id));
23658
25325
  if (watchers.length === 0)
23659
25326
  return { content: [{ type: "text", text: "No watchers." }] };
@@ -23672,15 +25339,15 @@ ${lines.join(`
23672
25339
  agent_id: exports_external.string().optional().describe("Filter by assignee")
23673
25340
  }, async ({ project_id, plan_id, task_list_id, since, agent_id }) => {
23674
25341
  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");})();
25342
+ const { listTasks: listTasks3 } = (init_tasks(), __toCommonJS(exports_tasks));
25343
+ const { patrolTasks: patrolTasks2 } = (init_patrol(), __toCommonJS(exports_patrol));
23677
25344
  const completed = listTasks3({ status: "completed", project_id, plan_id, task_list_id, assigned_to: agent_id, limit: 500 }, undefined);
23678
25345
  const filtered = since ? completed.filter((t) => t.completed_at && t.completed_at >= since) : completed;
23679
25346
  const total = filtered.length;
23680
25347
  const lowConf = filtered.filter((t) => t.confidence != null && t.confidence < 0.7).length;
23681
25348
  const withEstimate = filtered.filter((t) => t.estimated_minutes != null && t.actual_minutes != null);
23682
25349
  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 });
25350
+ const patrolResult = patrolTasks2({ project_id: project_id ? resolveId(project_id, "projects") : undefined });
23684
25351
  const stuck = patrolResult.issues.filter((i) => i.type === "stuck").length;
23685
25352
  const lines = [
23686
25353
  `Retro (${total} completed tasks${since ? ` since ${since}` : ""})`,
@@ -23701,7 +25368,7 @@ ${lines.join(`
23701
25368
  limit: exports_external.number().optional().describe("Max results (default: 20)")
23702
25369
  }, async ({ project_id, limit }) => {
23703
25370
  try {
23704
- const { listTasks: listTasks3 } = (()=>{throw new Error("Cannot require module "+"../db/tasks.js");})();
25371
+ const { listTasks: listTasks3 } = (init_tasks(), __toCommonJS(exports_tasks));
23705
25372
  const tasks = listTasks3({ status: "pending", project_id, assigned_to: "", limit: limit || 20 }, undefined);
23706
25373
  if (tasks.length === 0)
23707
25374
  return { content: [{ type: "text", text: "Inbox is empty." }] };
@@ -23720,8 +25387,8 @@ ${lines.join(`
23720
25387
  project_id: exports_external.string().optional().describe("Filter by project")
23721
25388
  }, async ({ agent_id, project_id }) => {
23722
25389
  try {
23723
- const { getAgentMetrics } = (()=>{throw new Error("Cannot require module "+"../db/agent-metrics.js");})();
23724
- const metrics = getAgentMetrics(agent_id, {
25390
+ const { getAgentMetrics: getAgentMetrics2 } = (init_agent_metrics(), __toCommonJS(exports_agent_metrics));
25391
+ const metrics = getAgentMetrics2(agent_id, {
23725
25392
  project_id: project_id ? resolveId(project_id, "projects") : undefined
23726
25393
  });
23727
25394
  if (!metrics)
@@ -23748,8 +25415,8 @@ ${lines.join(`
23748
25415
  limit: exports_external.number().optional().describe("Max entries (default: 20)")
23749
25416
  }, async ({ project_id, limit }) => {
23750
25417
  try {
23751
- const { getLeaderboard } = (()=>{throw new Error("Cannot require module "+"../db/agent-metrics.js");})();
23752
- const entries = getLeaderboard({
25418
+ const { getLeaderboard: getLeaderboard2 } = (init_agent_metrics(), __toCommonJS(exports_agent_metrics));
25419
+ const entries = getLeaderboard2({
23753
25420
  project_id: project_id ? resolveId(project_id, "projects") : undefined,
23754
25421
  limit
23755
25422
  });
@@ -23999,15 +25666,395 @@ ${lines.join(`
23999
25666
  }
24000
25667
  }
24001
25668
 
25669
+ // src/mcp/tools/agents.ts
25670
+ init_agents();
25671
+ init_config2();
25672
+ init_database();
25673
+ function registerAgentTools(server, { shouldRegisterTool, resolveId, formatError, agentFocusMap, getAgentFocus }) {
25674
+ if (shouldRegisterTool("set_focus")) {
25675
+ server.tool("set_focus", "Focus this agent on a project. All list/search/status tools will default to this project.", {
25676
+ agent_id: exports_external.string().describe("Agent ID or name"),
25677
+ project_id: exports_external.string().optional().describe("Project to focus on. Omit to clear."),
25678
+ task_list_id: exports_external.string().optional().describe("Task list to focus on")
25679
+ }, async ({ agent_id, project_id, task_list_id }) => {
25680
+ try {
25681
+ const resolvedProject = project_id ? resolveId(project_id, "projects") : undefined;
25682
+ const focus = { agent_id, project_id: resolvedProject, task_list_id };
25683
+ agentFocusMap.set(agent_id, focus);
25684
+ try {
25685
+ const agent = getAgentByName(agent_id) || getAgent(agent_id);
25686
+ if (agent) {
25687
+ const db = getDatabase();
25688
+ db.run("UPDATE agents SET active_project_id = ? WHERE id = ?", [resolvedProject || null, agent.id]);
25689
+ }
25690
+ } catch {}
25691
+ const projectName = resolvedProject ? ` (${resolvedProject.slice(0, 8)})` : "";
25692
+ return { content: [{ type: "text", text: `Focused on project${projectName}. Read tools will default to this scope. Pass explicit project_id to override.` }] };
25693
+ } catch (e) {
25694
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
25695
+ }
25696
+ });
25697
+ }
25698
+ if (shouldRegisterTool("get_focus")) {
25699
+ server.tool("get_focus", "Get the current focus for an agent.", { agent_id: exports_external.string().describe("Agent ID or name") }, async ({ agent_id }) => {
25700
+ const focus = getAgentFocus(agent_id);
25701
+ if (!focus?.project_id) {
25702
+ return { content: [{ type: "text", text: "No focus set. Showing all projects." }] };
25703
+ }
25704
+ return { content: [{ type: "text", text: `Focused on project: ${focus.project_id}${focus.task_list_id ? `, task list: ${focus.task_list_id}` : ""}` }] };
25705
+ });
25706
+ }
25707
+ if (shouldRegisterTool("unfocus")) {
25708
+ server.tool("unfocus", "Clear focus \u2014 show all projects and tasks.", { agent_id: exports_external.string().describe("Agent ID or name") }, async ({ agent_id }) => {
25709
+ agentFocusMap.delete(agent_id);
25710
+ try {
25711
+ const agent = getAgentByName(agent_id) || getAgent(agent_id);
25712
+ if (agent) {
25713
+ const db = getDatabase();
25714
+ db.run("UPDATE agents SET active_project_id = NULL WHERE id = ?", [agent.id]);
25715
+ }
25716
+ } catch {}
25717
+ return { content: [{ type: "text", text: "Focus cleared. Showing all projects." }] };
25718
+ });
25719
+ }
25720
+ if (shouldRegisterTool("register_agent")) {
25721
+ server.tool("register_agent", "Register an agent with a distinctive one-word name. Generic/generated names like agent, agent-1, assistant, or worker-2 are rejected. Prefer Roman or Greek names from suggest_agent_name.", {
25722
+ name: exports_external.string().describe("Distinctive one-word agent name. Use suggest_agent_name first; avoid agent, agent-1, and other numbered generated names."),
25723
+ description: exports_external.string().optional(),
25724
+ role: exports_external.string().optional(),
25725
+ title: exports_external.string().optional(),
25726
+ level: exports_external.string().optional(),
25727
+ permissions: exports_external.array(exports_external.string()).optional(),
25728
+ capabilities: exports_external.array(exports_external.string()).optional().describe("Agent capabilities/skills for task routing (e.g. ['typescript', 'testing', 'devops'])"),
25729
+ 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."),
25730
+ 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"),
25731
+ 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.")
25732
+ }, async ({ name, description, role, title, level, permissions, capabilities, session_id, working_dir, force }) => {
25733
+ try {
25734
+ const pool = getAgentPoolForProject(working_dir);
25735
+ const result = registerAgent({ name, description, role, title, level, permissions, capabilities, session_id, working_dir, force, pool: pool || undefined });
25736
+ if (isAgentConflict(result)) {
25737
+ const suggestLine = result.suggestions && result.suggestions.length > 0 ? `
25738
+ Available names: ${result.suggestions.join(", ")}` : "";
25739
+ const hint = `CONFLICT: ${result.message}${suggestLine}`;
25740
+ return {
25741
+ content: [{ type: "text", text: hint }],
25742
+ isError: true
25743
+ };
25744
+ }
25745
+ const agent = result;
25746
+ const poolLine = pool ? `
25747
+ Pool: [${pool.join(", ")}]` : "";
25748
+ return {
25749
+ content: [{
25750
+ type: "text",
25751
+ text: `Agent registered:
25752
+ ID: ${agent.id}
25753
+ Name: ${agent.name}${agent.description ? `
25754
+ Description: ${agent.description}` : ""}
25755
+ Session: ${agent.session_id ?? "unbound"}${poolLine}
25756
+ Created: ${agent.created_at}
25757
+ Last seen: ${agent.last_seen_at}`
25758
+ }]
25759
+ };
25760
+ } catch (e) {
25761
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
25762
+ }
25763
+ });
25764
+ }
25765
+ if (shouldRegisterTool("suggest_agent_name")) {
25766
+ server.tool("suggest_agent_name", "Get available Roman/Greek-style agent names for a project. Use these instead of generic generated names.", {
25767
+ working_dir: exports_external.string().optional().describe("Your working directory \u2014 used to look up the project's allowed name pool from config")
25768
+ }, async ({ working_dir }) => {
25769
+ try {
25770
+ const pool = getAgentPoolForProject(working_dir);
25771
+ const cutoff = new Date(Date.now() - 30 * 60 * 1000).toISOString();
25772
+ const allActive = listAgents().filter((a) => a.last_seen_at > cutoff);
25773
+ if (!pool) {
25774
+ const suggestions = getAvailableNamesFromPool([
25775
+ "caesar",
25776
+ "augustus",
25777
+ "marcus",
25778
+ "brutus",
25779
+ "cicero",
25780
+ "cato",
25781
+ "nero",
25782
+ "claudius",
25783
+ "tiberius",
25784
+ "hadrian",
25785
+ "athena",
25786
+ "apollo",
25787
+ "artemis",
25788
+ "iris",
25789
+ "hector",
25790
+ "sophia",
25791
+ "thalia",
25792
+ "phoebe",
25793
+ "daphne"
25794
+ ], getDatabase());
25795
+ const lines2 = [
25796
+ "No project pool configured. Use a distinctive one-word name; generic generated names are blocked.",
25797
+ `Suggested names: ${suggestions.slice(0, 8).join(", ")}`,
25798
+ 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.",
25799
+ `
25800
+ To restrict names, configure agent_pool or project_pools in ~/.hasna/todos/config.json`
25801
+ ];
25802
+ return { content: [{ type: "text", text: lines2.join(`
25803
+ `) }] };
25804
+ }
25805
+ const available = getAvailableNamesFromPool(pool, getDatabase());
25806
+ const activeInPool = allActive.filter((a) => pool.map((n) => n.toLowerCase()).includes(a.name));
25807
+ const lines = [
25808
+ `Project pool: ${pool.join(", ")}`,
25809
+ `Available now (${available.length}): ${available.length > 0 ? available.join(", ") : "none \u2014 all names in use"}`,
25810
+ 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",
25811
+ available.length > 0 ? `
25812
+ Suggested: ${available[0]}` : `
25813
+ No names available. Wait for an active agent to go stale (30min timeout).`
25814
+ ];
25815
+ return { content: [{ type: "text", text: lines.join(`
25816
+ `) }] };
25817
+ } catch (e) {
25818
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
25819
+ }
25820
+ });
25821
+ }
25822
+ if (shouldRegisterTool("list_agents")) {
25823
+ server.tool("list_agents", "List all registered agents. By default shows only active agents \u2014 set include_archived to see archived ones too.", {
25824
+ include_archived: exports_external.boolean().optional().describe("Include archived agents in the list (default: false)")
25825
+ }, async ({ include_archived }) => {
25826
+ try {
25827
+ const agents = listAgents({ include_archived: include_archived ?? false });
25828
+ if (agents.length === 0) {
25829
+ return { content: [{ type: "text", text: "No agents registered." }] };
25830
+ }
25831
+ const text = agents.map((a) => {
25832
+ const statusTag = a.status === "archived" ? " [archived]" : "";
25833
+ return `${a.id} | ${a.name}${statusTag}${a.description ? ` - ${a.description}` : ""} (last seen: ${a.last_seen_at})`;
25834
+ }).join(`
25835
+ `);
25836
+ return { content: [{ type: "text", text: `${agents.length} agent(s):
25837
+ ${text}` }] };
25838
+ } catch (e) {
25839
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
25840
+ }
25841
+ });
25842
+ }
25843
+ if (shouldRegisterTool("get_agent")) {
25844
+ server.tool("get_agent", "Get agent details by ID or name. Provide one of id or name.", {
25845
+ agent_id: exports_external.string().optional(),
25846
+ id: exports_external.string().optional(),
25847
+ name: exports_external.string().optional()
25848
+ }, async ({ agent_id, id, name }) => {
25849
+ try {
25850
+ const identifier = agent_id || id || name;
25851
+ if (!identifier) {
25852
+ return { content: [{ type: "text", text: "Provide agent_id, id, or name." }], isError: true };
25853
+ }
25854
+ const agent = getAgent(identifier) || getAgentByName(identifier);
25855
+ if (!agent) {
25856
+ return { content: [{ type: "text", text: `Agent not found: ${identifier}` }], isError: true };
25857
+ }
25858
+ const parts = [
25859
+ `ID: ${agent.id}`,
25860
+ `Name: ${agent.name}`
25861
+ ];
25862
+ if (agent.description)
25863
+ parts.push(`Description: ${agent.description}`);
25864
+ if (Object.keys(agent.metadata).length > 0)
25865
+ parts.push(`Metadata: ${JSON.stringify(agent.metadata)}`);
25866
+ parts.push(`Created: ${agent.created_at}`);
25867
+ parts.push(`Last seen: ${agent.last_seen_at}`);
25868
+ return { content: [{ type: "text", text: parts.join(`
25869
+ `) }] };
25870
+ } catch (e) {
25871
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
25872
+ }
25873
+ });
25874
+ }
25875
+ if (shouldRegisterTool("rename_agent")) {
25876
+ server.tool("rename_agent", "Rename an agent. Resolve by id or current name.", {
25877
+ id: exports_external.string().optional(),
25878
+ name: exports_external.string().optional(),
25879
+ new_name: exports_external.string()
25880
+ }, async ({ id, name, new_name }) => {
25881
+ try {
25882
+ if (!id && !name) {
25883
+ return { content: [{ type: "text", text: "Provide either id or name." }], isError: true };
25884
+ }
25885
+ const agent = id ? getAgent(id) : getAgentByName(name);
25886
+ if (!agent) {
25887
+ return { content: [{ type: "text", text: `Agent not found: ${id || name}` }], isError: true };
25888
+ }
25889
+ const oldName = agent.name;
25890
+ const updated = updateAgent(agent.id, { name: new_name });
25891
+ const db = getDatabase();
25892
+ const tasksResult = db.run("UPDATE tasks SET assigned_to = ? WHERE assigned_to = ?", [new_name, oldName]);
25893
+ const taskNote = tasksResult.changes > 0 ? `
25894
+ Updated assigned_to on ${tasksResult.changes} task(s).` : "";
25895
+ return {
25896
+ content: [{
25897
+ type: "text",
25898
+ text: `Agent renamed: ${oldName} -> ${updated.name}
25899
+ ID: ${updated.id}${taskNote}`
25900
+ }]
25901
+ };
25902
+ } catch (e) {
25903
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
25904
+ }
25905
+ });
25906
+ }
25907
+ if (shouldRegisterTool("update_agent")) {
25908
+ server.tool("update_agent", "Update an agent's description, role, title, or other metadata. Resolve by id or name.", {
25909
+ agent_id: exports_external.string().optional(),
25910
+ id: exports_external.string().optional(),
25911
+ name: exports_external.string().optional(),
25912
+ description: exports_external.string().optional(),
25913
+ role: exports_external.string().optional(),
25914
+ title: exports_external.string().optional(),
25915
+ level: exports_external.string().optional(),
25916
+ capabilities: exports_external.array(exports_external.string()).optional(),
25917
+ permissions: exports_external.array(exports_external.string()).optional(),
25918
+ metadata: exports_external.record(exports_external.unknown()).optional()
25919
+ }, async ({ agent_id, id, name, ...updates }) => {
25920
+ try {
25921
+ const identifier = agent_id || id || name;
25922
+ if (!identifier) {
25923
+ return { content: [{ type: "text", text: "Provide agent_id, id, or name." }], isError: true };
25924
+ }
25925
+ const agent = getAgent(identifier) || getAgentByName(identifier);
25926
+ if (!agent) {
25927
+ return { content: [{ type: "text", text: `Agent not found: ${identifier}` }], isError: true };
25928
+ }
25929
+ const updated = updateAgent(agent.id, updates);
25930
+ return { content: [{ type: "text", text: `Agent updated: ${updated.name} (${updated.id.slice(0, 8)})` }] };
25931
+ } catch (e) {
25932
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
25933
+ }
25934
+ });
25935
+ }
25936
+ if (shouldRegisterTool("delete_agent")) {
25937
+ 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.", {
25938
+ agent_id: exports_external.string().optional(),
25939
+ id: exports_external.string().optional(),
25940
+ name: exports_external.string().optional()
25941
+ }, async ({ agent_id, id, name }) => {
25942
+ try {
25943
+ const identifier = agent_id || id || name;
25944
+ if (!identifier) {
25945
+ return { content: [{ type: "text", text: "Provide agent_id, id, or name." }], isError: true };
25946
+ }
25947
+ const agent = getAgent(identifier) || getAgentByName(identifier);
25948
+ if (!agent) {
25949
+ return { content: [{ type: "text", text: `Agent not found: ${identifier}` }], isError: true };
25950
+ }
25951
+ const archived = archiveAgent(agent.id);
25952
+ return {
25953
+ content: [{
25954
+ type: "text",
25955
+ text: archived ? `Agent archived: ${agent.name} (${agent.id}). Use unarchive_agent to restore.` : `Failed to archive agent: ${agent.name}`
25956
+ }],
25957
+ isError: !archived
25958
+ };
25959
+ } catch (e) {
25960
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
25961
+ }
25962
+ });
25963
+ }
25964
+ if (shouldRegisterTool("unarchive_agent")) {
25965
+ server.tool("unarchive_agent", "Restore an archived agent back to active status. Resolve by id or name.", {
25966
+ agent_id: exports_external.string().optional(),
25967
+ id: exports_external.string().optional(),
25968
+ name: exports_external.string().optional()
25969
+ }, async ({ agent_id, id, name }) => {
25970
+ try {
25971
+ const identifier = agent_id || id || name;
25972
+ if (!identifier) {
25973
+ return { content: [{ type: "text", text: "Provide agent_id, id, or name." }], isError: true };
25974
+ }
25975
+ const agent = getAgent(identifier) || getAgentByName(identifier);
25976
+ if (!agent) {
25977
+ return { content: [{ type: "text", text: `Agent not found: ${identifier}` }], isError: true };
25978
+ }
25979
+ if (agent.status === "active") {
25980
+ return { content: [{ type: "text", text: `Agent ${agent.name} is already active.` }] };
25981
+ }
25982
+ const restored = unarchiveAgent(agent.id);
25983
+ return {
25984
+ content: [{
25985
+ type: "text",
25986
+ text: restored ? `Agent restored: ${agent.name} (${agent.id}) is now active.` : `Failed to restore agent: ${agent.name}`
25987
+ }],
25988
+ isError: !restored
25989
+ };
25990
+ } catch (e) {
25991
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
25992
+ }
25993
+ });
25994
+ }
25995
+ if (shouldRegisterTool("heartbeat")) {
25996
+ 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.", {
25997
+ agent_id: exports_external.string().describe("Your agent ID or name.")
25998
+ }, async ({ agent_id }) => {
25999
+ try {
26000
+ const agent = getAgent(agent_id) || getAgentByName(agent_id);
26001
+ if (!agent) {
26002
+ return { content: [{ type: "text", text: `Agent not found: ${agent_id}` }], isError: true };
26003
+ }
26004
+ updateAgentActivity(agent.id);
26005
+ return {
26006
+ content: [{
26007
+ type: "text",
26008
+ text: `Heartbeat: ${agent.name} (${agent.id}) \u2014 last_seen_at updated to ${new Date().toISOString()}`
26009
+ }]
26010
+ };
26011
+ } catch (e) {
26012
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
26013
+ }
26014
+ });
26015
+ }
26016
+ if (shouldRegisterTool("release_agent")) {
26017
+ 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.", {
26018
+ agent_id: exports_external.string().describe("Your agent ID or name."),
26019
+ 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).")
26020
+ }, async ({ agent_id, session_id }) => {
26021
+ try {
26022
+ const agent = getAgent(agent_id) || getAgentByName(agent_id);
26023
+ if (!agent) {
26024
+ return { content: [{ type: "text", text: `Agent not found: ${agent_id}` }], isError: true };
26025
+ }
26026
+ const released = releaseAgent(agent.id, session_id);
26027
+ if (!released) {
26028
+ return { content: [{ type: "text", text: `Release denied: session_id does not match agent's current session.` }], isError: true };
26029
+ }
26030
+ return {
26031
+ content: [{
26032
+ type: "text",
26033
+ text: `Agent released: ${agent.name} (${agent.id}) \u2014 session cleared, name is now available.`
26034
+ }]
26035
+ };
26036
+ } catch (e) {
26037
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
26038
+ }
26039
+ });
26040
+ }
26041
+ }
26042
+
24002
26043
  // src/mcp/index.ts
24003
26044
  function getMcpVersion() {
24004
26045
  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";
26046
+ let dir = dirname3(fileURLToPath(import.meta.url));
26047
+ for (let i = 0;i < 4; i++) {
26048
+ const pkgPath = join10(dir, "package.json");
26049
+ if (existsSync10(pkgPath)) {
26050
+ return JSON.parse(readFileSync6(pkgPath, "utf-8")).version || "0.0.0";
26051
+ }
26052
+ dir = dirname3(dir);
26053
+ }
24008
26054
  } catch {
24009
26055
  return "0.0.0";
24010
26056
  }
26057
+ return "0.0.0";
24011
26058
  }
24012
26059
  var server = new McpServer({
24013
26060
  name: "todos",
@@ -24026,6 +26073,7 @@ var MINIMAL_TOOLS = new Set([
24026
26073
  "get_next_task",
24027
26074
  "bootstrap",
24028
26075
  "get_tasks_changed_since",
26076
+ "get_health",
24029
26077
  "heartbeat",
24030
26078
  "release_agent"
24031
26079
  ]);
@@ -24204,6 +26252,7 @@ registerTaskMetaTools(server, toolContext);
24204
26252
  registerTaskResources(server, toolContext);
24205
26253
  registerTaskRelTools(server, toolContext);
24206
26254
  registerCodeTools(server, toolContext);
26255
+ registerAgentTools(server, { ...toolContext, agentFocusMap });
24207
26256
  registerMachineTools(server, { shouldRegisterTool, formatError });
24208
26257
  registerDispatchTools(server, { shouldRegisterTool, resolveId, formatError });
24209
26258
  registerCloudSyncTools(server, { shouldRegisterTool, formatError });