@hasna/todos 0.11.82 → 0.11.84

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli/index.js CHANGED
@@ -8580,9 +8580,277 @@ async function cloudCountTasks(client, filter = {}) {
8580
8580
  const tasks = await cloudListTasks(client, rest);
8581
8581
  return tasks.length;
8582
8582
  }
8583
- var _cache;
8583
+ async function cloudRegisterAgent(client, input) {
8584
+ const raw = await client.transport.post("/agents", input);
8585
+ if (raw && typeof raw === "object" && "agent" in raw) {
8586
+ return raw.agent;
8587
+ }
8588
+ return raw;
8589
+ }
8590
+ async function cloudLockTask(client, id, agentId) {
8591
+ const raw = await client.transport.post(`/tasks/${encodeURIComponent(id)}/lock`, { agent_id: agentId });
8592
+ if (raw && typeof raw === "object" && "result" in raw) {
8593
+ return raw.result;
8594
+ }
8595
+ return raw ?? { success: true };
8596
+ }
8597
+ async function cloudUnlockTask(client, id, agentId) {
8598
+ const raw = await client.transport.post(`/tasks/${encodeURIComponent(id)}/unlock`, agentId ? { agent_id: agentId } : {});
8599
+ if (raw && typeof raw === "object" && "success" in raw) {
8600
+ return Boolean(raw.success);
8601
+ }
8602
+ return true;
8603
+ }
8604
+ async function cloudGetDependencies(client, id) {
8605
+ const raw = await client.transport.get(`/tasks/${encodeURIComponent(id)}/dependencies`);
8606
+ const env = raw ?? {};
8607
+ return { dependencies: env.dependencies ?? [], blocked_by: env.blocked_by ?? [] };
8608
+ }
8609
+ async function cloudAddDependency(client, id, dependsOn) {
8610
+ const raw = await client.transport.post(`/tasks/${encodeURIComponent(id)}/dependencies`, { depends_on: dependsOn });
8611
+ if (raw && typeof raw === "object" && "dependency" in raw) {
8612
+ return raw.dependency;
8613
+ }
8614
+ return raw;
8615
+ }
8616
+ async function cloudRemoveDependency(client, id, dependsOn) {
8617
+ const raw = await client.transport.del(`/tasks/${encodeURIComponent(id)}/dependencies/${encodeURIComponent(dependsOn)}`);
8618
+ if (raw && typeof raw === "object" && "removed" in raw) {
8619
+ return Boolean(raw.removed);
8620
+ }
8621
+ return true;
8622
+ }
8623
+ async function cloudRecordVerification(client, id, input) {
8624
+ const raw = await client.transport.post(`/tasks/${encodeURIComponent(id)}/verifications`, input);
8625
+ if (raw && typeof raw === "object" && "verification" in raw) {
8626
+ return raw.verification;
8627
+ }
8628
+ return raw;
8629
+ }
8630
+ function priorityRank(priority) {
8631
+ return PRIORITY_RANK[priority ?? ""] ?? 4;
8632
+ }
8633
+ async function cloudActiveTasks(client, filter = {}) {
8634
+ const [pending, inProgress] = await Promise.all([
8635
+ cloudListTasks(client, { ...filter, status: "pending" }),
8636
+ cloudListTasks(client, { ...filter, status: "in_progress" })
8637
+ ]);
8638
+ return [...pending, ...inProgress];
8639
+ }
8640
+ async function cloudActiveWork(client, filter = {}) {
8641
+ const tasks = await cloudListTasks(client, { ...filter, status: "in_progress" });
8642
+ return tasks.sort((a, b) => priorityRank(a.priority) - priorityRank(b.priority) || (b.updated_at ?? "").localeCompare(a.updated_at ?? ""));
8643
+ }
8644
+ async function cloudStaleTasks(client, minutes, filter = {}) {
8645
+ const cutoff = new Date(Date.now() - minutes * 60 * 1000).toISOString();
8646
+ const tasks = await cloudListTasks(client, { ...filter, status: "in_progress" });
8647
+ return tasks.filter((t) => (t.updated_at ?? "") < cutoff || t.locked_at != null && t.locked_at < cutoff).sort((a, b) => (a.updated_at ?? "").localeCompare(b.updated_at ?? ""));
8648
+ }
8649
+ async function cloudOverdueTasks(client, projectId, at = new Date) {
8650
+ const nowStr = at.toISOString();
8651
+ const filter = projectId ? { project_id: projectId } : {};
8652
+ const active = await cloudActiveTasks(client, filter);
8653
+ return active.filter((t) => !t.archived_at && t.due_at != null && t.due_at < nowStr).sort((a, b) => (a.due_at ?? "").localeCompare(b.due_at ?? ""));
8654
+ }
8655
+ async function cloudEscalatedTasks(client, opts = {}, at = new Date) {
8656
+ const nowMs = at.getTime();
8657
+ const filter = {};
8658
+ if (opts.project_id)
8659
+ filter.project_id = opts.project_id;
8660
+ const active = await cloudActiveTasks(client, filter);
8661
+ return active.filter((t) => !t.archived_at && (opts.agent_id ? t.assigned_to === opts.agent_id : true)).map((task) => {
8662
+ const reasons = [];
8663
+ const breachedTimes = [];
8664
+ if (task.due_at) {
8665
+ const dueMs = new Date(task.due_at).getTime();
8666
+ if (Number.isFinite(dueMs) && dueMs < nowMs) {
8667
+ reasons.push("overdue");
8668
+ breachedTimes.push(dueMs);
8669
+ }
8670
+ }
8671
+ if (task.sla_minutes != null) {
8672
+ const startMs = new Date(task.started_at ?? task.created_at).getTime();
8673
+ const breachedMs = startMs + task.sla_minutes * 60000;
8674
+ if (Number.isFinite(breachedMs) && breachedMs < nowMs) {
8675
+ reasons.push("sla_breached");
8676
+ breachedTimes.push(breachedMs);
8677
+ }
8678
+ }
8679
+ if (reasons.length === 0)
8680
+ return null;
8681
+ return { task, reasons, breached_at: new Date(Math.min(...breachedTimes)).toISOString() };
8682
+ }).filter((item) => item !== null).sort((a, b) => (a.task.due_at ?? "").localeCompare(b.task.due_at ?? "") || a.task.created_at.localeCompare(b.task.created_at));
8683
+ }
8684
+ async function cloudChangedSince(client, since, filter = {}) {
8685
+ const tasks = await cloudListTasks(client, filter);
8686
+ return tasks.filter((t) => (t.updated_at ?? "") > since).sort((a, b) => (b.updated_at ?? "").localeCompare(a.updated_at ?? ""));
8687
+ }
8688
+ async function cloudTaskStats(client, filter = {}) {
8689
+ const tasks = await cloudListTasks(client, filter);
8690
+ const by_status = {};
8691
+ const by_priority = {};
8692
+ const by_agent = {};
8693
+ for (const t of tasks) {
8694
+ by_status[t.status] = (by_status[t.status] ?? 0) + 1;
8695
+ by_priority[t.priority] = (by_priority[t.priority] ?? 0) + 1;
8696
+ const agent = t.assigned_to ?? t.agent_id ?? "unassigned";
8697
+ by_agent[agent] = (by_agent[agent] ?? 0) + 1;
8698
+ }
8699
+ const completed = by_status["completed"] ?? 0;
8700
+ return {
8701
+ total: tasks.length,
8702
+ by_status,
8703
+ by_priority,
8704
+ by_agent,
8705
+ completion_rate: tasks.length > 0 ? Math.round(completed / tasks.length * 100) : 0
8706
+ };
8707
+ }
8708
+ async function cloudRecentActivity(client, limit = 50) {
8709
+ const raw = await client.transport.get("/activity", { query: { limit } });
8710
+ const envelope = raw ?? {};
8711
+ if (Array.isArray(envelope.activity))
8712
+ return envelope.activity;
8713
+ if (Array.isArray(envelope.entries))
8714
+ return envelope.entries;
8715
+ return Array.isArray(raw) ? raw : [];
8716
+ }
8717
+ async function cloudListTaskLists(client, projectId) {
8718
+ const query = projectId ? { project_id: projectId } : {};
8719
+ const raw = await client.transport.get("/task-lists", { query });
8720
+ const envelope = raw ?? {};
8721
+ if (Array.isArray(envelope.task_lists))
8722
+ return envelope.task_lists;
8723
+ if (Array.isArray(envelope.taskLists))
8724
+ return envelope.taskLists;
8725
+ return Array.isArray(raw) ? raw : [];
8726
+ }
8727
+ async function cloudNextTask(client, agent, filters) {
8728
+ const query = {};
8729
+ if (agent)
8730
+ query["agent"] = agent;
8731
+ if (filters?.project_id)
8732
+ query["project_id"] = filters.project_id;
8733
+ if (filters?.task_list_id)
8734
+ query["task_list_id"] = filters.task_list_id;
8735
+ if (filters?.plan_id)
8736
+ query["plan_id"] = filters.plan_id;
8737
+ const raw = await client.transport.get("/next", { query });
8738
+ if (raw == null)
8739
+ return null;
8740
+ const task = unwrapTask(raw);
8741
+ return task && task.id ? task : null;
8742
+ }
8743
+ async function cloudAllDependencies(client) {
8744
+ const raw = await client.transport.get("/dependencies");
8745
+ const envelope = raw ?? {};
8746
+ if (Array.isArray(envelope.dependencies))
8747
+ return envelope.dependencies;
8748
+ return Array.isArray(raw) ? raw : [];
8749
+ }
8750
+ async function cloudGetTasksByIds(client, ids) {
8751
+ const unique = Array.from(new Set(ids));
8752
+ const map = new Map;
8753
+ const CONCURRENCY = 8;
8754
+ for (let i = 0;i < unique.length; i += CONCURRENCY) {
8755
+ const batch = unique.slice(i, i + CONCURRENCY);
8756
+ const tasks = await Promise.all(batch.map((id) => cloudGetTask(client, id)));
8757
+ for (const task of tasks)
8758
+ if (task && task.id)
8759
+ map.set(task.id, task);
8760
+ }
8761
+ return map;
8762
+ }
8763
+ async function cloudBlockingDepsMap(client, candidates) {
8764
+ const result = new Map;
8765
+ if (candidates.length === 0)
8766
+ return result;
8767
+ const edges = await cloudAllDependencies(client);
8768
+ const dependsByTask = new Map;
8769
+ for (const edge of edges) {
8770
+ if (!edge.task_id || !edge.depends_on)
8771
+ continue;
8772
+ const arr = dependsByTask.get(edge.task_id) ?? [];
8773
+ arr.push(edge.depends_on);
8774
+ dependsByTask.set(edge.task_id, arr);
8775
+ }
8776
+ const candidateIds = new Set(candidates.map((t) => t.id));
8777
+ const blockerIds = new Set;
8778
+ for (const id of candidateIds)
8779
+ for (const dep of dependsByTask.get(id) ?? [])
8780
+ blockerIds.add(dep);
8781
+ const blockers = await cloudGetTasksByIds(client, Array.from(blockerIds));
8782
+ for (const task of candidates) {
8783
+ const deps = dependsByTask.get(task.id) ?? [];
8784
+ const incomplete = deps.map((depId) => blockers.get(depId)).filter((b) => b != null && b.status !== "completed");
8785
+ if (incomplete.length > 0)
8786
+ result.set(task.id, incomplete);
8787
+ }
8788
+ return result;
8789
+ }
8790
+ async function cloudRecap(client, hours, projectId) {
8791
+ const since = new Date(Date.now() - hours * 60 * 60 * 1000).toISOString();
8792
+ const staleWindow = new Date(Date.now() - 30 * 60 * 1000).toISOString();
8793
+ const filter = projectId ? { project_id: projectId } : {};
8794
+ const [all, agents] = await Promise.all([cloudListTasks(client, filter), cloudListAgents(client)]);
8795
+ const completed = all.filter((t) => t.status === "completed" && t.completed_at != null && t.completed_at > since).sort((a, b) => (b.completed_at ?? "").localeCompare(a.completed_at ?? "")).map((t) => ({
8796
+ ...t,
8797
+ duration_minutes: t.started_at && t.completed_at ? Math.round((new Date(t.completed_at).getTime() - new Date(t.started_at).getTime()) / 60000) : null
8798
+ }));
8799
+ const created = all.filter((t) => t.created_at > since).sort((a, b) => b.created_at.localeCompare(a.created_at));
8800
+ const in_progress = all.filter((t) => t.status === "in_progress").sort((a, b) => (b.updated_at ?? "").localeCompare(a.updated_at ?? ""));
8801
+ const stale = in_progress.filter((t) => (t.updated_at ?? "") < staleWindow).sort((a, b) => (a.updated_at ?? "").localeCompare(b.updated_at ?? ""));
8802
+ const pending = all.filter((t) => t.status === "pending");
8803
+ const blockedMap = await cloudBlockingDepsMap(client, pending);
8804
+ const blocked = pending.filter((t) => blockedMap.has(t.id));
8805
+ const sinceMs = new Date(since).getTime();
8806
+ const agentSummaries = agents.map((agent) => {
8807
+ const owned = all.filter((t) => t.assigned_to === agent.id || t.agent_id === agent.id);
8808
+ return {
8809
+ name: agent.name,
8810
+ completed_count: owned.filter((t) => t.status === "completed" && t.completed_at != null && t.completed_at > since).length,
8811
+ in_progress_count: owned.filter((t) => t.status === "in_progress").length,
8812
+ last_seen_at: agent.last_seen_at
8813
+ };
8814
+ }).filter((a) => a.last_seen_at != null && new Date(a.last_seen_at).getTime() > sinceMs).sort((a, b) => b.completed_count - a.completed_count);
8815
+ return { hours, since, completed, created, in_progress, blocked, stale, agents: agentSummaries };
8816
+ }
8817
+ async function cloudTimeline(client, options = {}) {
8818
+ const activity = await cloudRecentActivity(client, 5000);
8819
+ let entries = activity.map((h) => ({
8820
+ id: h.id,
8821
+ source: "task_history",
8822
+ event_type: h.action,
8823
+ entity_type: "task",
8824
+ entity_id: h.task_id,
8825
+ task_id: h.task_id,
8826
+ project_id: null,
8827
+ plan_id: null,
8828
+ run_id: null,
8829
+ agent_id: h.agent_id ?? null,
8830
+ created_at: h.created_at,
8831
+ title: "",
8832
+ message: h.field ? `${h.field}: ${h.old_value ?? ""}${h.new_value != null ? ` -> ${h.new_value}` : ""}`.trim() : null,
8833
+ metadata: {}
8834
+ }));
8835
+ if (options.entity_type && options.entity_type !== "task") {
8836
+ entries = [];
8837
+ } else if (options.entity_type === "task" && options.entity_id) {
8838
+ entries = entries.filter((e) => e.task_id === options.entity_id);
8839
+ }
8840
+ if (options.since)
8841
+ entries = entries.filter((e) => e.created_at >= options.since);
8842
+ if (options.until)
8843
+ entries = entries.filter((e) => e.created_at <= options.until);
8844
+ entries.sort((a, b) => options.order === "asc" ? a.created_at.localeCompare(b.created_at) : b.created_at.localeCompare(a.created_at));
8845
+ const total = entries.length;
8846
+ const offset = options.offset ?? 0;
8847
+ const limit = options.limit ?? 50;
8848
+ return { entries: entries.slice(offset, offset + limit), total, limit, offset };
8849
+ }
8850
+ var _cache, PRIORITY_RANK;
8584
8851
  var init_cloud_router = __esm(() => {
8585
8852
  init_storage();
8853
+ PRIORITY_RANK = { critical: 0, high: 1, medium: 2, low: 3 };
8586
8854
  });
8587
8855
 
8588
8856
  // src/db/migrations.ts
@@ -16391,11 +16659,11 @@ function listTasks(filter = {}, db) {
16391
16659
  }
16392
16660
  }
16393
16661
  addMetadataConditions(filter.metadata, conditions, params);
16394
- const PRIORITY_RANK = `CASE priority WHEN 'critical' THEN 0 WHEN 'high' THEN 1 WHEN 'medium' THEN 2 WHEN 'low' THEN 3 END`;
16662
+ const PRIORITY_RANK2 = `CASE priority WHEN 'critical' THEN 0 WHEN 'high' THEN 1 WHEN 'medium' THEN 2 WHEN 'low' THEN 3 END`;
16395
16663
  if (filter.cursor) {
16396
16664
  try {
16397
16665
  const decoded = JSON.parse(Buffer.from(filter.cursor, "base64").toString("utf8"));
16398
- conditions.push(`(${PRIORITY_RANK} > ? OR (${PRIORITY_RANK} = ? AND created_at < ?) OR (${PRIORITY_RANK} = ? AND created_at = ? AND id > ?))`);
16666
+ conditions.push(`(${PRIORITY_RANK2} > ? OR (${PRIORITY_RANK2} = ? AND created_at < ?) OR (${PRIORITY_RANK2} = ? AND created_at = ? AND id > ?))`);
16399
16667
  params.push(decoded.p, decoded.p, decoded.c, decoded.p, decoded.c, decoded.i);
16400
16668
  } catch {}
16401
16669
  }
@@ -16412,7 +16680,7 @@ function listTasks(filter = {}, db) {
16412
16680
  params.push(filter.offset);
16413
16681
  }
16414
16682
  }
16415
- const rows = d.query(`SELECT * FROM tasks ${where} ORDER BY ${PRIORITY_RANK}, created_at DESC, id ASC${limitClause}`).all(...params);
16683
+ const rows = d.query(`SELECT * FROM tasks ${where} ORDER BY ${PRIORITY_RANK2}, created_at DESC, id ASC${limitClause}`).all(...params);
16416
16684
  return rows.map(rowToTask);
16417
16685
  }
16418
16686
  function getTaskByFingerprint(fingerprint, db) {
@@ -20886,13 +21154,14 @@ ${chalk2.cyan(sid)} ${statusColor(task2.status)} ${prioColor(task2.priority)} ${
20886
21154
  console.log(formatTaskLine(task2));
20887
21155
  }
20888
21156
  });
20889
- program2.command("lock <id>").description("Acquire exclusive lock on a task").action((id) => {
21157
+ program2.command("lock <id>").description("Acquire exclusive lock on a task").action(async (id) => {
20890
21158
  const globalOpts = program2.opts();
20891
21159
  const agentId = globalOpts.agent || "cli";
21160
+ const cloud = getTodosCloudClient();
20892
21161
  const resolvedId = resolveTaskId(id);
20893
21162
  let result;
20894
21163
  try {
20895
- result = lockTask(resolvedId, agentId);
21164
+ result = cloud ? await cloudLockTask(cloud, resolvedId, agentId) : lockTask(resolvedId, agentId);
20896
21165
  } catch (e) {
20897
21166
  handleError(e);
20898
21167
  }
@@ -20905,11 +21174,15 @@ ${chalk2.cyan(sid)} ${statusColor(task2.status)} ${prioColor(task2.priority)} ${
20905
21174
  process.exit(1);
20906
21175
  }
20907
21176
  });
20908
- program2.command("unlock <id>").description("Release lock on a task").action((id) => {
21177
+ program2.command("unlock <id>").description("Release lock on a task").action(async (id) => {
20909
21178
  const globalOpts = program2.opts();
21179
+ const cloud = getTodosCloudClient();
20910
21180
  const resolvedId = resolveTaskId(id);
20911
21181
  try {
20912
- unlockTask(resolvedId, globalOpts.agent);
21182
+ if (cloud)
21183
+ await cloudUnlockTask(cloud, resolvedId, globalOpts.agent);
21184
+ else
21185
+ unlockTask(resolvedId, globalOpts.agent);
20913
21186
  } catch (e) {
20914
21187
  handleError(e);
20915
21188
  }
@@ -29655,11 +29928,11 @@ function taskSummary(task, blockers) {
29655
29928
  const firstLine = task.description?.split(/\r?\n/).map((line) => line.trim()).find(Boolean);
29656
29929
  return firstLine || undefined;
29657
29930
  }
29658
- function priorityRank(priority) {
29931
+ function priorityRank2(priority) {
29659
29932
  return { critical: 0, high: 1, medium: 2, low: 3 }[priority];
29660
29933
  }
29661
29934
  function sortActionableTasks(a, b) {
29662
- const priority = priorityRank(a.priority) - priorityRank(b.priority);
29935
+ const priority = priorityRank2(a.priority) - priorityRank2(b.priority);
29663
29936
  if (priority !== 0)
29664
29937
  return priority;
29665
29938
  return b.updated_at.localeCompare(a.updated_at);
@@ -31573,6 +31846,49 @@ function registerProjectCommands(program2) {
31573
31846
  });
31574
31847
  program2.command("deps <id>").description("Manage task dependencies").option("--needs <dep-id>", "Add dependency (this task needs dep-id)").option("--remove <dep-id>", "Remove dependency").option("--graph", "Show the dependency graph instead of direct edges").option("--direction <direction>", "Graph direction: up, down, or both", "both").action(async (id, opts) => {
31575
31848
  const globalOpts = program2.opts();
31849
+ const cloud = getTodosCloudClient();
31850
+ if (cloud) {
31851
+ const cloudId = resolveTaskId(id);
31852
+ if (opts.needs) {
31853
+ try {
31854
+ const dep = await cloudAddDependency(cloud, cloudId, resolveTaskId(opts.needs));
31855
+ if (globalOpts.json)
31856
+ output(dep, true);
31857
+ else
31858
+ console.log(chalk4.green("Dependency added."));
31859
+ } catch (e) {
31860
+ handleError(e);
31861
+ }
31862
+ return;
31863
+ }
31864
+ if (opts.remove) {
31865
+ const removed = await cloudRemoveDependency(cloud, cloudId, resolveTaskId(opts.remove));
31866
+ if (globalOpts.json)
31867
+ output({ removed }, true);
31868
+ else
31869
+ console.log(removed ? chalk4.green("Dependency removed.") : chalk4.red("Dependency not found."));
31870
+ return;
31871
+ }
31872
+ const edges = await cloudGetDependencies(cloud, cloudId);
31873
+ if (globalOpts.json) {
31874
+ output(edges, true);
31875
+ return;
31876
+ }
31877
+ if (edges.dependencies.length > 0) {
31878
+ console.log(chalk4.bold("Depends on:"));
31879
+ for (const dep of edges.dependencies)
31880
+ console.log(` ${chalk4.cyan(dep.depends_on)}`);
31881
+ }
31882
+ if (edges.blocked_by.length > 0) {
31883
+ console.log(chalk4.bold("Blocks:"));
31884
+ for (const b of edges.blocked_by)
31885
+ console.log(` ${chalk4.cyan(b.task_id)}`);
31886
+ }
31887
+ if (edges.dependencies.length === 0 && edges.blocked_by.length === 0) {
31888
+ console.log(chalk4.dim("No dependencies."));
31889
+ }
31890
+ return;
31891
+ }
31576
31892
  const { addDependency: addDependency2, removeDependency: removeDependency2, getTaskGraph: getTaskGraph2, getTaskWithRelations: getTaskWithRelations2 } = await Promise.resolve().then(() => (init_tasks(), exports_tasks));
31577
31893
  const resolvedId = resolveTaskId(id);
31578
31894
  if (opts.needs) {
@@ -32789,8 +33105,9 @@ function registerAgentCommands(program2) {
32789
33105
  program2.command("init <name>").description("Register an agents and get a short UUID").option("-d, --description <text>", "Agent description").action(async (name, opts) => {
32790
33106
  const globalOpts = program2.opts();
32791
33107
  try {
32792
- const { registerAgent: registerAgent2, isAgentConflict: isAgentConflict2 } = await Promise.resolve().then(() => (init_agents(), exports_agents));
32793
- const result = registerAgent2({ name, description: opts.description });
33108
+ const cloud = getTodosCloudClient();
33109
+ const result = cloud ? await cloudRegisterAgent(cloud, { name, description: opts.description }) : (await Promise.resolve().then(() => (init_agents(), exports_agents))).registerAgent({ name, description: opts.description });
33110
+ const { isAgentConflict: isAgentConflict2 } = await Promise.resolve().then(() => (init_agents(), exports_agents));
32794
33111
  if (isAgentConflict2(result)) {
32795
33112
  console.error(chalk5.red("CONFLICT:"), result.message);
32796
33113
  process.exit(1);
@@ -32947,14 +33264,15 @@ Use ${chalk5.cyan(`--agent ${result.id}`)} on future commands.`);
32947
33264
  });
32948
33265
  program2.command("agent <name>").description("Show all info about an agent: tasks, status, last seen, stats").option("-j, --json", "Output as JSON").action(async (name, opts) => {
32949
33266
  const globalOpts = program2.opts();
33267
+ const cloud = getTodosCloudClient();
32950
33268
  const { getAgentByName: findByName } = await Promise.resolve().then(() => (init_agents(), exports_agents));
32951
- const agent = findByName(name);
33269
+ const agent = cloud ? (await cloudListAgents(cloud)).find((a) => a.name === name || a.id === name) ?? null : findByName(name);
32952
33270
  if (!agent) {
32953
33271
  console.error(chalk5.red(`Agent not found: ${name}`));
32954
33272
  process.exit(1);
32955
33273
  }
32956
- const byAssigned = listTasks({ assigned_to: agent.name });
32957
- const byId = listTasks({ agent_id: agent.id });
33274
+ const byAssigned = cloud ? await cloudListTasks(cloud, { assigned_to: agent.name }) : listTasks({ assigned_to: agent.name });
33275
+ const byId = cloud ? await cloudListTasks(cloud, { agent_id: agent.id }) : listTasks({ agent_id: agent.id });
32958
33276
  const seen = new Set;
32959
33277
  const allTasks = [...byAssigned, ...byId].filter((t) => {
32960
33278
  if (seen.has(t.id))
@@ -33092,7 +33410,8 @@ ${isOnline ? chalk5.green("\u25CF") : chalk5.dim("\u25CB")} ${chalk5.bold(agent.
33092
33410
  console.log(chalk5.green("Task list deleted."));
33093
33411
  return;
33094
33412
  }
33095
- const lists = listTaskLists(projectId);
33413
+ const cloud = getTodosCloudClient();
33414
+ const lists = cloud ? await cloudListTaskLists(cloud, projectId ?? undefined) : listTaskLists(projectId);
33096
33415
  if (globalOpts.json) {
33097
33416
  output(lists, true);
33098
33417
  return;
@@ -38364,7 +38683,19 @@ function createPostgresTodosStorageAdapter(options) {
38364
38683
  claimNext: (agentId, filters) => claimNextTask2(agentId, filters, store),
38365
38684
  getNext: (_agentId, filters) => getNextTask2(filters, store),
38366
38685
  getActiveWork: (filters) => getActiveWork2(filters, store),
38367
- getChangedSince: (since, filters) => getChangedSince(since, filters, store)
38686
+ getChangedSince: (since, filters) => getChangedSince(since, filters, store),
38687
+ lock: (id, agentId) => lockTask2(id, agentId, store),
38688
+ unlock: (id, agentId) => unlockTask2(id, agentId, store)
38689
+ },
38690
+ dependencies: {
38691
+ add: (taskId, dependsOn, context) => addDependency2(taskId, dependsOn, store, context),
38692
+ remove: (taskId, dependsOn) => removeDependency2(taskId, dependsOn, store),
38693
+ list: (taskId) => listDependencies(taskId, store),
38694
+ listAll: () => store.list("dependencies")
38695
+ },
38696
+ verifications: {
38697
+ add: (input, context) => addVerification(input, store, context),
38698
+ list: (taskId) => listVerifications(taskId, store)
38368
38699
  },
38369
38700
  projects: {
38370
38701
  create: (input, context) => createProject2(input, store, context),
@@ -38819,6 +39150,103 @@ async function patchTask(task, patch, store) {
38819
39150
  await store.upsert("tasks", updated);
38820
39151
  return updated;
38821
39152
  }
39153
+ function cloudLockExpired(lockedAt) {
39154
+ if (!lockedAt)
39155
+ return true;
39156
+ return new Date(lockedAt).getTime() + CLOUD_LOCK_EXPIRY_MINUTES * 60 * 1000 < Date.now();
39157
+ }
39158
+ function cloudLockExpiresAt(lockedAt) {
39159
+ return new Date(new Date(lockedAt).getTime() + CLOUD_LOCK_EXPIRY_MINUTES * 60 * 1000).toISOString();
39160
+ }
39161
+ async function lockTask2(id, agentId, store) {
39162
+ const task = await requireRecord("tasks", id, store);
39163
+ if (task.status === "completed" || task.status === "cancelled") {
39164
+ return { success: false, error: `Task is ${task.status} and cannot be locked` };
39165
+ }
39166
+ if (task.locked_by && task.locked_by !== agentId && !cloudLockExpired(task.locked_at)) {
39167
+ return { success: false, locked_by: task.locked_by, locked_at: task.locked_at ?? undefined, error: `Task is locked by ${task.locked_by}` };
39168
+ }
39169
+ const timestamp = new Date().toISOString();
39170
+ await patchTask(task, { locked_by: agentId, locked_at: timestamp }, store);
39171
+ return { success: true, locked_by: agentId, locked_at: timestamp, expires_at: cloudLockExpiresAt(timestamp) };
39172
+ }
39173
+ async function unlockTask2(id, agentId, store) {
39174
+ const task = await requireRecord("tasks", id, store);
39175
+ if (agentId && task.locked_by && task.locked_by !== agentId) {
39176
+ throw new Error(`Task ${id} is locked by ${task.locked_by}, not ${agentId}`);
39177
+ }
39178
+ await patchTask(task, { locked_by: null, locked_at: null }, store);
39179
+ return true;
39180
+ }
39181
+ function dependencyId(taskId, dependsOn) {
39182
+ return `${taskId}::${dependsOn}`;
39183
+ }
39184
+ async function addDependency2(taskId, dependsOn, store, context) {
39185
+ if (taskId === dependsOn)
39186
+ throw new Error("A task cannot depend on itself");
39187
+ if (!await store.get("tasks", taskId))
39188
+ throw new Error(`Task not found: ${taskId}`);
39189
+ if (!await store.get("tasks", dependsOn))
39190
+ throw new Error(`Task not found: ${dependsOn}`);
39191
+ const edges = await store.list("dependencies");
39192
+ const adjacency = new Map;
39193
+ for (const edge of edges) {
39194
+ if (!adjacency.has(edge.task_id))
39195
+ adjacency.set(edge.task_id, []);
39196
+ adjacency.get(edge.task_id).push(edge.depends_on);
39197
+ }
39198
+ const queue = [dependsOn];
39199
+ const seen = new Set;
39200
+ while (queue.length) {
39201
+ const node = queue.shift();
39202
+ if (node === taskId)
39203
+ throw new Error(`Adding dependency ${taskId} -> ${dependsOn} would create a cycle`);
39204
+ if (seen.has(node))
39205
+ continue;
39206
+ seen.add(node);
39207
+ for (const next of adjacency.get(node) ?? [])
39208
+ queue.push(next);
39209
+ }
39210
+ const timestamp = new Date().toISOString();
39211
+ const record = { id: dependencyId(taskId, dependsOn), task_id: taskId, depends_on: dependsOn, created_at: timestamp, updated_at: timestamp };
39212
+ await store.upsert("dependencies", record, context);
39213
+ return { task_id: taskId, depends_on: dependsOn };
39214
+ }
39215
+ async function removeDependency2(taskId, dependsOn, store) {
39216
+ const existing = await store.get("dependencies", dependencyId(taskId, dependsOn));
39217
+ if (!existing)
39218
+ return false;
39219
+ await store.delete("dependencies", dependencyId(taskId, dependsOn));
39220
+ return true;
39221
+ }
39222
+ async function listDependencies(taskId, store) {
39223
+ const edges = await store.list("dependencies");
39224
+ return {
39225
+ dependencies: edges.filter((edge) => edge.task_id === taskId).map((edge) => ({ task_id: edge.task_id, depends_on: edge.depends_on })),
39226
+ blocked_by: edges.filter((edge) => edge.depends_on === taskId).map((edge) => ({ task_id: edge.task_id, depends_on: edge.depends_on }))
39227
+ };
39228
+ }
39229
+ async function addVerification(input, store, context) {
39230
+ if (!await store.get("tasks", input.task_id))
39231
+ throw new Error(`Task not found: ${input.task_id}`);
39232
+ const timestamp = new Date().toISOString();
39233
+ const verification = {
39234
+ id: randomUUID3(),
39235
+ task_id: input.task_id,
39236
+ command: input.command,
39237
+ status: input.status ?? "unknown",
39238
+ output_summary: input.output_summary ?? null,
39239
+ artifact_path: input.artifact_path ?? null,
39240
+ agent_id: input.agent_id ?? context?.agentId ?? null,
39241
+ run_at: timestamp,
39242
+ created_at: timestamp
39243
+ };
39244
+ await store.upsert("verifications", { ...verification, updated_at: timestamp }, context);
39245
+ return verification;
39246
+ }
39247
+ async function listVerifications(taskId, store) {
39248
+ return (await store.list("verifications")).filter((verification) => verification.task_id === taskId).sort((a, b) => b.run_at.localeCompare(a.run_at));
39249
+ }
38822
39250
  function toFilterArray(value) {
38823
39251
  return Array.isArray(value) ? value : [value];
38824
39252
  }
@@ -39192,7 +39620,7 @@ function compareClock(left, right) {
39192
39620
  function numberValue2(value) {
39193
39621
  return typeof value === "number" && Number.isSafeInteger(value) ? value : null;
39194
39622
  }
39195
- var TASK_ORDER_BY = "ORDER BY CASE payload->>'priority' WHEN 'critical' THEN 0 WHEN 'high' THEN 1 WHEN 'medium' THEN 2 WHEN 'low' THEN 3 ELSE 4 END ASC, payload->>'created_at' ASC, payload->>'id' ASC";
39623
+ var CLOUD_LOCK_EXPIRY_MINUTES = 30, TASK_ORDER_BY = "ORDER BY CASE payload->>'priority' WHEN 'critical' THEN 0 WHEN 'high' THEN 1 WHEN 'medium' THEN 2 WHEN 'low' THEN 3 ELSE 4 END ASC, payload->>'created_at' ASC, payload->>'id' ASC";
39196
39624
  var init_postgres_adapter = () => {};
39197
39625
 
39198
39626
  // src/server/cloud.ts
@@ -39690,6 +40118,7 @@ async function handleV1Request(req, url) {
39690
40118
  const resource = segments[1];
39691
40119
  const id = segments[2];
39692
40120
  const action = segments[3];
40121
+ const subId = segments[4];
39693
40122
  try {
39694
40123
  if (resource === "tasks") {
39695
40124
  if (id === "exists" && !action) {
@@ -39765,6 +40194,89 @@ async function handleV1Request(req, url) {
39765
40194
  }
39766
40195
  return error(405, `method ${method} not allowed on /v1/tasks/:id/comments`);
39767
40196
  }
40197
+ if (action === "lock" || action === "unlock") {
40198
+ if (method !== "POST")
40199
+ return error(405, `method ${method} not allowed on /v1/tasks/:id/${action}`);
40200
+ if (typeof store.tasks.lock !== "function" || typeof store.tasks.unlock !== "function") {
40201
+ return error(501, "task locking is not supported by this storage backend");
40202
+ }
40203
+ const body2 = await readJson(req) ?? {};
40204
+ if (!await store.tasks.get(id))
40205
+ return error(404, "task not found");
40206
+ if (action === "lock") {
40207
+ const agentId2 = body2.agent_id || principal.agent || "todos-serve";
40208
+ return json2({ result: await store.tasks.lock(id, agentId2) });
40209
+ }
40210
+ const released = await store.tasks.unlock(id, body2.agent_id || principal.agent || undefined);
40211
+ return json2({ success: released });
40212
+ }
40213
+ if (action === "dependencies") {
40214
+ if (!store.dependencies)
40215
+ return error(501, "dependencies are not supported by this storage backend");
40216
+ if (method === "GET") {
40217
+ if (!await store.tasks.get(id))
40218
+ return error(404, "task not found");
40219
+ const edges = await store.dependencies.list(id);
40220
+ return json2(edges);
40221
+ }
40222
+ if (method === "POST") {
40223
+ const body2 = await readJson(req) ?? {};
40224
+ if (typeof body2.depends_on !== "string" || !body2.depends_on.trim()) {
40225
+ return error(400, "depends_on is required");
40226
+ }
40227
+ try {
40228
+ const dependency = await store.dependencies.add(id, body2.depends_on, contextFromPrincipal(principal));
40229
+ return json2({ dependency }, 201);
40230
+ } catch (e) {
40231
+ const msg = e.message || "";
40232
+ if (msg.includes("not found"))
40233
+ return error(404, msg);
40234
+ if (msg.includes("cycle") || msg.includes("itself"))
40235
+ return error(409, msg);
40236
+ throw e;
40237
+ }
40238
+ }
40239
+ if (method === "DELETE") {
40240
+ if (!subId)
40241
+ return error(400, "dependency target id is required (/v1/tasks/:id/dependencies/:dep)");
40242
+ const removed = await store.dependencies.remove(id, subId);
40243
+ return json2({ removed });
40244
+ }
40245
+ return error(405, `method ${method} not allowed on /v1/tasks/:id/dependencies`);
40246
+ }
40247
+ if (action === "verifications") {
40248
+ if (!store.verifications)
40249
+ return error(501, "verifications are not supported by this storage backend");
40250
+ if (method === "GET") {
40251
+ if (!await store.tasks.get(id))
40252
+ return error(404, "task not found");
40253
+ const verifications = await store.verifications.list(id);
40254
+ return json2({ verifications, count: verifications.length });
40255
+ }
40256
+ if (method === "POST") {
40257
+ const body2 = await readJson(req) ?? {};
40258
+ if (typeof body2.command !== "string" || !body2.command.trim()) {
40259
+ return error(400, "command is required");
40260
+ }
40261
+ try {
40262
+ const verification = await store.verifications.add({
40263
+ task_id: id,
40264
+ command: body2.command,
40265
+ status: body2.status,
40266
+ output_summary: body2.output_summary,
40267
+ artifact_path: body2.artifact_path,
40268
+ agent_id: body2.agent_id
40269
+ }, contextFromPrincipal(principal, body2));
40270
+ return json2({ verification }, 201);
40271
+ } catch (e) {
40272
+ const msg = e.message || "";
40273
+ if (msg.includes("not found"))
40274
+ return error(404, msg);
40275
+ throw e;
40276
+ }
40277
+ }
40278
+ return error(405, `method ${method} not allowed on /v1/tasks/:id/verifications`);
40279
+ }
39768
40280
  const body = await readJson(req) ?? {};
39769
40281
  const agentId = body.agent_id || principal.agent || "todos-serve";
39770
40282
  if (action === "start" && method === "POST") {
@@ -39869,16 +40381,55 @@ async function handleV1Request(req, url) {
39869
40381
  }
39870
40382
  if (!id && method === "POST") {
39871
40383
  const body = await readJson(req);
39872
- if (!body || typeof body.name !== "string")
40384
+ if (!body || typeof body.name !== "string" || !body.name.trim())
39873
40385
  return error(400, "name is required");
39874
- const agent = await store.agents.register(body, contextFromPrincipal(principal));
39875
- return json2({ agent }, 201);
40386
+ const result = await store.agents.register(body, contextFromPrincipal(principal));
40387
+ if (result && typeof result === "object" && "conflict" in result) {
40388
+ return error(409, result.message ?? "agent name conflict", { conflict: true });
40389
+ }
40390
+ return json2({ agent: result }, 201);
39876
40391
  }
39877
40392
  if (id && method === "GET") {
39878
40393
  const agent = await store.agents.get(id);
39879
40394
  return agent ? json2({ agent }) : error(404, "agent not found");
39880
40395
  }
39881
40396
  }
40397
+ if (resource === "activity" && !id) {
40398
+ if (method !== "GET")
40399
+ return error(405, `method ${method} not allowed on /v1/activity`);
40400
+ const limitParam = url.searchParams.get("limit");
40401
+ const limit = limitParam ? Math.max(1, Math.min(1e4, Number(limitParam) || 50)) : 50;
40402
+ const activity = await store.audit.getRecentActivity(limit);
40403
+ return json2({ activity, count: activity.length });
40404
+ }
40405
+ if (resource === "task-lists" && !id) {
40406
+ if (method !== "GET")
40407
+ return error(405, `method ${method} not allowed on /v1/task-lists`);
40408
+ const projectId = url.searchParams.get("project_id") ?? undefined;
40409
+ const taskLists = await store.taskLists.list(projectId);
40410
+ return json2({ task_lists: taskLists, count: taskLists.length });
40411
+ }
40412
+ if (resource === "dependencies" && !id) {
40413
+ if (method !== "GET")
40414
+ return error(405, `method ${method} not allowed on /v1/dependencies`);
40415
+ if (typeof store.dependencies?.listAll !== "function") {
40416
+ return error(501, "dependency edge listing is not supported by this storage backend");
40417
+ }
40418
+ const dependencies = await store.dependencies.listAll();
40419
+ return json2({ dependencies, count: dependencies.length });
40420
+ }
40421
+ if (resource === "next" && !id) {
40422
+ if (method !== "GET")
40423
+ return error(405, `method ${method} not allowed on /v1/next`);
40424
+ const agent = url.searchParams.get("agent") ?? undefined;
40425
+ const filters = {
40426
+ ...url.searchParams.get("project_id") ? { project_id: url.searchParams.get("project_id") } : {},
40427
+ ...url.searchParams.get("task_list_id") ? { task_list_id: url.searchParams.get("task_list_id") } : {},
40428
+ ...url.searchParams.get("plan_id") ? { plan_id: url.searchParams.get("plan_id") } : {}
40429
+ };
40430
+ const task = await store.tasks.getNext(agent, filters);
40431
+ return json2({ task: task ?? null });
40432
+ }
39882
40433
  if (resource === "stats" && method === "GET") {
39883
40434
  const [tasks, tasksAll, projects] = await Promise.all([
39884
40435
  store.tasks.count(),
@@ -62399,6 +62950,33 @@ function registerAgentTools(server, { shouldRegisterTool, resolveId, formatError
62399
62950
  force: exports_external3.boolean().optional().describe("Force takeover of an active agent's name. Use with caution \u2014 only when you know the previous session is dead.")
62400
62951
  }, async ({ name, description, role, title, level, permissions, capabilities, session_id, working_dir, force }) => {
62401
62952
  try {
62953
+ const cloud = getTodosCloudClient();
62954
+ if (cloud) {
62955
+ const agent2 = await cloudRegisterAgent(cloud, {
62956
+ name,
62957
+ description,
62958
+ role,
62959
+ title,
62960
+ level,
62961
+ permissions,
62962
+ capabilities,
62963
+ session_id,
62964
+ working_dir,
62965
+ force
62966
+ });
62967
+ return {
62968
+ content: [{
62969
+ type: "text",
62970
+ text: `Agent registered:
62971
+ ID: ${agent2.id}
62972
+ Name: ${agent2.name}${agent2.description ? `
62973
+ Description: ${agent2.description}` : ""}
62974
+ Session: ${agent2.session_id ?? "unbound"}
62975
+ Created: ${agent2.created_at}
62976
+ Last seen: ${agent2.last_seen_at}`
62977
+ }]
62978
+ };
62979
+ }
62402
62980
  const pool = getAgentPoolForProject(working_dir);
62403
62981
  const result = registerAgent({ name, description, role, title, level, permissions, capabilities, session_id, working_dir, force, pool: pool || undefined });
62404
62982
  if (isAgentConflict(result)) {
@@ -68322,7 +68900,8 @@ function registerQueryCommands(program2) {
68322
68900
  if (pid)
68323
68901
  filters.project_id = pid;
68324
68902
  }
68325
- const task2 = getNextTask(opts.agent, Object.keys(filters).length ? filters : undefined, db);
68903
+ const cloud = getTodosCloudClient();
68904
+ const task2 = cloud ? await cloudNextTask(cloud, opts.agent, Object.keys(filters).length ? filters : undefined) : getNextTask(opts.agent, Object.keys(filters).length ? filters : undefined, db);
68326
68905
  if (!task2) {
68327
68906
  if (json3) {
68328
68907
  console.log(JSON.stringify(null));
@@ -68443,9 +69022,10 @@ Next up:`));
68443
69022
  console.log(` ${chalk7.cyan(t.short_id || t.id.slice(0, 8))} ${chalk7.yellow(t.priority)} ${t.title}`);
68444
69023
  }
68445
69024
  });
68446
- program2.command("recap").description("Show what happened in the last N hours \u2014 completed tasks, new tasks, agent activity, blockers").option("--hours <n>", "Look back N hours (default: 8)", "8").option("--project <id>", "Filter to project").action((opts) => {
69025
+ program2.command("recap").description("Show what happened in the last N hours \u2014 completed tasks, new tasks, agent activity, blockers").option("--hours <n>", "Look back N hours (default: 8)", "8").option("--project <id>", "Filter to project").action(async (opts) => {
68447
69026
  const globalOpts = program2.opts();
68448
- const recap = getRecap(parseInt(opts.hours, 10), opts.project);
69027
+ const cloud = getTodosCloudClient();
69028
+ const recap = cloud ? await cloudRecap(cloud, parseInt(opts.hours, 10), opts.project) : getRecap(parseInt(opts.hours, 10), opts.project);
68449
69029
  if (globalOpts.json) {
68450
69030
  output(recap, true);
68451
69031
  return;
@@ -68508,11 +69088,12 @@ Agents:`));
68508
69088
  }
68509
69089
  console.log();
68510
69090
  });
68511
- program2.command("standup").description("Generate standup notes \u2014 completed since yesterday, in progress, blocked. Grouped by agent.").option("--since <date>", "ISO date or 'yesterday' (default: yesterday)").option("--project <id>", "Filter to project").action((opts) => {
69091
+ program2.command("standup").description("Generate standup notes \u2014 completed since yesterday, in progress, blocked. Grouped by agent.").option("--since <date>", "ISO date or 'yesterday' (default: yesterday)").option("--project <id>", "Filter to project").action(async (opts) => {
68512
69092
  const globalOpts = program2.opts();
68513
69093
  const sinceDate = opts.since === "yesterday" || !opts.since ? new Date(Date.now() - 24 * 60 * 60 * 1000) : new Date(opts.since);
68514
69094
  const hours = Math.max(1, Math.round((Date.now() - sinceDate.getTime()) / (60 * 60 * 1000)));
68515
- const recap = getRecap(hours, opts.project);
69095
+ const cloud = getTodosCloudClient();
69096
+ const recap = cloud ? await cloudRecap(cloud, hours, opts.project) : getRecap(hours, opts.project);
68516
69097
  if (globalOpts.json) {
68517
69098
  output(recap, true);
68518
69099
  return;
@@ -68582,7 +69163,8 @@ Blocked:`));
68582
69163
  const filters = {};
68583
69164
  if (opts.project)
68584
69165
  filters.project_id = opts.project;
68585
- const work = getActiveWork(Object.keys(filters).length ? filters : undefined, db);
69166
+ const cloud = getTodosCloudClient();
69167
+ const work = cloud ? await cloudActiveWork(cloud, Object.keys(filters).length ? filters : {}) : getActiveWork(Object.keys(filters).length ? filters : undefined, db);
68586
69168
  if (json3) {
68587
69169
  console.log(JSON.stringify(work, null, 2));
68588
69170
  return;
@@ -68605,7 +69187,8 @@ Blocked:`));
68605
69187
  const filters = {};
68606
69188
  if (opts.project)
68607
69189
  filters.project_id = opts.project;
68608
- const tasks = getStaleTasks(parseInt(opts.minutes, 10), Object.keys(filters).length ? filters : undefined, db);
69190
+ const cloud = getTodosCloudClient();
69191
+ const tasks = cloud ? await cloudStaleTasks(cloud, parseInt(opts.minutes, 10), Object.keys(filters).length ? filters : {}) : getStaleTasks(parseInt(opts.minutes, 10), Object.keys(filters).length ? filters : undefined, db);
68609
69192
  if (json3) {
68610
69193
  console.log(JSON.stringify(tasks, null, 2));
68611
69194
  return;
@@ -68757,12 +69340,13 @@ No task claimed (nothing available).`));
68757
69340
  filter.project_id = projectId;
68758
69341
  if (opts.agent)
68759
69342
  filter.assigned_to = opts.agent;
69343
+ const cloud = getTodosCloudClient();
68760
69344
  const { getTasksChangedSince: getTasksChangedSince2 } = await Promise.resolve().then(() => (init_tasks(), exports_tasks));
68761
- const changed = getTasksChangedSince2(since, Object.keys(filter).length ? filter : undefined, db);
69345
+ const changed = cloud ? await cloudChangedSince(cloud, since, filter) : getTasksChangedSince2(since, Object.keys(filter).length ? filter : undefined, db);
68762
69346
  const completed = changed.filter((t) => t.status === "completed");
68763
69347
  const inProgress = changed.filter((t) => t.status === "in_progress");
68764
69348
  const failed = changed.filter((t) => t.status === "failed");
68765
- const allTasks = listTasks({ ...filter, status: "pending" });
69349
+ const allTasks = cloud ? await cloudListTasks(cloud, { ...filter, status: "pending" }) : listTasks({ ...filter, status: "pending" });
68766
69350
  if (opts.json || globalOpts.json) {
68767
69351
  console.log(JSON.stringify({ completed, in_progress: inProgress, failed, pending: allTasks.length, period_days: days }, null, 2));
68768
69352
  return;
@@ -69029,12 +69613,13 @@ Findings`));
69029
69613
  const filter = {};
69030
69614
  if (projectId)
69031
69615
  filter.project_id = projectId;
69616
+ const cloud = getTodosCloudClient();
69032
69617
  const { getTasksChangedSince: getTasksChangedSince2, getTaskStats: getTaskStats2 } = await Promise.resolve().then(() => (init_tasks(), exports_tasks));
69033
- const changed = getTasksChangedSince2(since, Object.keys(filter).length ? filter : undefined, db);
69618
+ const changed = cloud ? await cloudChangedSince(cloud, since, filter) : getTasksChangedSince2(since, Object.keys(filter).length ? filter : undefined, db);
69034
69619
  const completed = changed.filter((t) => t.status === "completed");
69035
69620
  const failed = changed.filter((t) => t.status === "failed");
69036
- const all = listTasks(filter);
69037
- const stats2 = getTaskStats2(Object.keys(filter).length ? filter : undefined, db);
69621
+ const all = cloud ? await cloudListTasks(cloud, filter) : listTasks(filter);
69622
+ const stats2 = cloud ? await cloudTaskStats(cloud, filter) : getTaskStats2(Object.keys(filter).length ? filter : undefined, db);
69038
69623
  const byDay = {};
69039
69624
  for (const t of changed) {
69040
69625
  const day = t.updated_at.slice(0, 10);
@@ -69088,10 +69673,11 @@ Findings`));
69088
69673
  program2.command("today").description("Show task activity from today").option("-j, --json", "Output as JSON").action(async (opts) => {
69089
69674
  const globalOpts = program2.opts();
69090
69675
  const db = getDatabase();
69676
+ const cloud = getTodosCloudClient();
69091
69677
  const { getTasksChangedSince: getTasksChangedSince2 } = await Promise.resolve().then(() => (init_tasks(), exports_tasks));
69092
69678
  const start = new Date;
69093
69679
  start.setHours(0, 0, 0, 0);
69094
- const tasks = getTasksChangedSince2(start.toISOString(), undefined, db);
69680
+ const tasks = cloud ? await cloudChangedSince(cloud, start.toISOString()) : getTasksChangedSince2(start.toISOString(), undefined, db);
69095
69681
  const completed = tasks.filter((t) => t.status === "completed");
69096
69682
  const started = tasks.filter((t) => t.status === "in_progress");
69097
69683
  const other = tasks.filter((t) => t.status !== "completed" && t.status !== "in_progress");
@@ -69118,13 +69704,14 @@ Findings`));
69118
69704
  program2.command("yesterday").description("Show task activity from yesterday").option("-j, --json", "Output as JSON").action(async (opts) => {
69119
69705
  const globalOpts = program2.opts();
69120
69706
  const db = getDatabase();
69707
+ const cloud = getTodosCloudClient();
69121
69708
  const { getTasksChangedSince: getTasksChangedSince2 } = await Promise.resolve().then(() => (init_tasks(), exports_tasks));
69122
69709
  const start = new Date;
69123
69710
  start.setDate(start.getDate() - 1);
69124
69711
  start.setHours(0, 0, 0, 0);
69125
69712
  const end = new Date(start);
69126
69713
  end.setHours(23, 59, 59, 999);
69127
- const allChanged = getTasksChangedSince2(start.toISOString(), undefined, db);
69714
+ const allChanged = cloud ? await cloudChangedSince(cloud, start.toISOString()) : getTasksChangedSince2(start.toISOString(), undefined, db);
69128
69715
  const tasks = allChanged.filter((t) => t.updated_at <= end.toISOString());
69129
69716
  const completed = tasks.filter((t) => t.status === "completed");
69130
69717
  const started = tasks.filter((t) => t.status === "in_progress");
@@ -69152,14 +69739,15 @@ Findings`));
69152
69739
  const globalOpts = program2.opts();
69153
69740
  const db = getDatabase();
69154
69741
  const projectId = globalOpts.project ? autoProject(globalOpts) || undefined : undefined;
69742
+ const cloud = getTodosCloudClient();
69155
69743
  const filter = { assigned_to: agent };
69156
69744
  if (projectId)
69157
69745
  filter.project_id = projectId;
69158
- const tasks = listTasks(filter, db);
69746
+ const tasks = cloud ? await cloudListTasks(cloud, filter) : listTasks(filter, db);
69159
69747
  const filterByAgent = { agent_id: agent };
69160
69748
  if (projectId)
69161
69749
  filterByAgent.project_id = projectId;
69162
- const agentTasks = listTasks(filterByAgent, db);
69750
+ const agentTasks = cloud ? await cloudListTasks(cloud, filterByAgent) : listTasks(filterByAgent, db);
69163
69751
  const seen = new Set(tasks.map((t) => t.id));
69164
69752
  for (const t of agentTasks) {
69165
69753
  if (!seen.has(t.id)) {
@@ -69201,17 +69789,27 @@ Findings`));
69201
69789
  program2.command("blocked").description("Show tasks blocked by incomplete dependencies").option("-j, --json", "Output as JSON").option("--project <id>", "Filter to project").action(async (opts) => {
69202
69790
  const globalOpts = program2.opts();
69203
69791
  const db = getDatabase();
69204
- const { getBlockingDeps: getBlockingDeps2 } = await Promise.resolve().then(() => (init_tasks(), exports_tasks));
69792
+ const cloud = getTodosCloudClient();
69205
69793
  const projectId = autoProject(globalOpts) || opts.project || undefined;
69206
69794
  const filter = { status: "pending" };
69207
69795
  if (projectId)
69208
69796
  filter.project_id = projectId;
69209
- const allPending = listTasks(filter, db);
69797
+ const allPending = cloud ? await cloudListTasks(cloud, filter) : listTasks(filter, db);
69210
69798
  const blockedTasks = [];
69211
- for (const t of allPending) {
69212
- const blockers = getBlockingDeps2(t.id, db);
69213
- if (blockers.length > 0)
69214
- blockedTasks.push({ task: t, blockers });
69799
+ if (cloud) {
69800
+ const map = await cloudBlockingDepsMap(cloud, allPending);
69801
+ for (const t of allPending) {
69802
+ const blockers = map.get(t.id);
69803
+ if (blockers && blockers.length > 0)
69804
+ blockedTasks.push({ task: t, blockers });
69805
+ }
69806
+ } else {
69807
+ const { getBlockingDeps: getBlockingDeps2 } = await Promise.resolve().then(() => (init_tasks(), exports_tasks));
69808
+ for (const t of allPending) {
69809
+ const blockers = getBlockingDeps2(t.id, db);
69810
+ if (blockers.length > 0)
69811
+ blockedTasks.push({ task: t, blockers });
69812
+ }
69215
69813
  }
69216
69814
  if (opts.json || globalOpts.json) {
69217
69815
  console.log(JSON.stringify(blockedTasks.map((b) => ({ ...b.task, blocked_by: b.blockers.map((bl) => ({ id: bl.id, short_id: bl.short_id, title: bl.title, status: bl.status })) }))));
@@ -69233,8 +69831,9 @@ Findings`));
69233
69831
  program2.command("overdue").description("Show tasks past their due date").option("-j, --json", "Output as JSON").option("--project <id>", "Filter to project").action(async (opts) => {
69234
69832
  const globalOpts = program2.opts();
69235
69833
  const projectId = autoProject(globalOpts) || opts.project || undefined;
69834
+ const cloud = getTodosCloudClient();
69236
69835
  const { getOverdueTasks: getOverdueTasks2 } = await Promise.resolve().then(() => (init_tasks(), exports_tasks));
69237
- const tasks = getOverdueTasks2(projectId);
69836
+ const tasks = cloud ? await cloudOverdueTasks(cloud, projectId) : getOverdueTasks2(projectId);
69238
69837
  if (opts.json || globalOpts.json) {
69239
69838
  console.log(JSON.stringify(tasks));
69240
69839
  return;
@@ -69255,10 +69854,8 @@ Findings`));
69255
69854
  program2.command("sla").description("Show overdue or SLA-breached tasks that need escalation").option("-j, --json", "Output as JSON").option("--project <id>", "Filter to project").option("--agent <id>", "Filter to assigned agent").option("--limit <n>", "Max tasks to show", "50").action(async (opts) => {
69256
69855
  const globalOpts = program2.opts();
69257
69856
  const projectId = autoProject(globalOpts) || opts.project || undefined;
69258
- const escalations = getEscalatedTasks({
69259
- project_id: projectId,
69260
- agent_id: opts.agent
69261
- }).slice(0, parseInt(opts.limit, 10));
69857
+ const cloud = getTodosCloudClient();
69858
+ const escalations = (cloud ? await cloudEscalatedTasks(cloud, { project_id: projectId, agent_id: opts.agent }) : getEscalatedTasks({ project_id: projectId, agent_id: opts.agent })).slice(0, parseInt(opts.limit, 10));
69262
69859
  if (opts.json || globalOpts.json) {
69263
69860
  console.log(JSON.stringify(escalations));
69264
69861
  return;
@@ -69278,12 +69875,13 @@ Findings`));
69278
69875
  program2.command("week").description("Show task activity from the past 7 days").option("-j, --json", "Output as JSON").action(async (opts) => {
69279
69876
  const globalOpts = program2.opts();
69280
69877
  const db = getDatabase();
69878
+ const cloud = getTodosCloudClient();
69281
69879
  const { getTasksChangedSince: getTasksChangedSince2 } = await Promise.resolve().then(() => (init_tasks(), exports_tasks));
69282
69880
  const now4 = new Date;
69283
69881
  const start = new Date(now4);
69284
69882
  start.setDate(start.getDate() - 7);
69285
69883
  start.setHours(0, 0, 0, 0);
69286
- const tasks = getTasksChangedSince2(start.toISOString(), undefined, db);
69884
+ const tasks = cloud ? await cloudChangedSince(cloud, start.toISOString()) : getTasksChangedSince2(start.toISOString(), undefined, db);
69287
69885
  const days = {};
69288
69886
  for (let i = 0;i < 7; i++) {
69289
69887
  const d = new Date(now4);
@@ -69331,9 +69929,10 @@ Findings`));
69331
69929
  program2.command("burndown").description("Show task completion velocity over the past 7 days").option("--days <n>", "Number of days", "7").option("-j, --json", "Output as JSON").action(async (opts) => {
69332
69930
  const globalOpts = program2.opts();
69333
69931
  const db = getDatabase();
69932
+ const cloud = getTodosCloudClient();
69334
69933
  const { getRecentActivity: getRecentActivity2 } = await Promise.resolve().then(() => (init_audit(), exports_audit));
69335
69934
  const numDays = parseInt(opts.days, 10);
69336
- const entries = getRecentActivity2(5000, db);
69935
+ const entries = cloud ? await cloudRecentActivity(cloud, 5000) : getRecentActivity2(5000, db);
69337
69936
  const now4 = new Date;
69338
69937
  const dayStats = [];
69339
69938
  for (let i = numDays - 1;i >= 0; i--) {
@@ -69373,8 +69972,9 @@ Findings`));
69373
69972
  program2.command("log").description("Show recent task activity log (git-log style)").option("--limit <n>", "Number of entries", "30").option("-j, --json", "Output as JSON").action(async (opts) => {
69374
69973
  const globalOpts = program2.opts();
69375
69974
  const db = getDatabase();
69975
+ const cloud = getTodosCloudClient();
69376
69976
  const { getRecentActivity: getRecentActivity2 } = await Promise.resolve().then(() => (init_audit(), exports_audit));
69377
- const entries = getRecentActivity2(parseInt(opts.limit, 10), db);
69977
+ const entries = cloud ? await cloudRecentActivity(cloud, parseInt(opts.limit, 10)) : getRecentActivity2(parseInt(opts.limit, 10), db);
69378
69978
  if (opts.json || globalOpts.json) {
69379
69979
  console.log(JSON.stringify(entries));
69380
69980
  return;
@@ -69417,6 +70017,39 @@ Findings`));
69417
70017
  program2.command("timeline").description("Show a unified local activity timeline for tasks, projects, plans, or runs").option("--task <id>", "Filter to a task").option("--project <id>", "Filter to a project").option("--plan <id>", "Filter to a plan").option("--run <id>", "Filter to a run ledger").option("--since <iso>", "Only include entries at or after this ISO timestamp").option("--until <iso>", "Only include entries at or before this ISO timestamp").option("--limit <n>", "Number of entries", "50").option("--offset <n>", "Entries to skip", "0").option("--order <order>", "Sort order: asc or desc", "desc").option("-j, --json", "Output as JSON").action(async (opts) => {
69418
70018
  const globalOpts = program2.opts();
69419
70019
  const db = getDatabase();
70020
+ const cloud = getTodosCloudClient();
70021
+ if (cloud) {
70022
+ const cloudOptions = {
70023
+ since: opts.since,
70024
+ until: opts.until,
70025
+ limit: parseInt(opts.limit, 10),
70026
+ offset: parseInt(opts.offset, 10),
70027
+ order: opts.order === "asc" ? "asc" : "desc",
70028
+ ...opts.task ? { entity_type: "task", entity_id: opts.task } : {},
70029
+ ...opts.project ? { entity_type: "project", entity_id: opts.project } : {},
70030
+ ...opts.plan ? { entity_type: "plan", entity_id: opts.plan } : {},
70031
+ ...opts.run ? { entity_type: "run", entity_id: opts.run } : {}
70032
+ };
70033
+ const timeline2 = await cloudTimeline(cloud, cloudOptions);
70034
+ if (opts.json || globalOpts.json) {
70035
+ console.log(JSON.stringify(timeline2, null, 2));
70036
+ return;
70037
+ }
70038
+ if (timeline2.entries.length === 0) {
70039
+ console.log(chalk7.dim(" No activity yet."));
70040
+ return;
70041
+ }
70042
+ console.log(chalk7.bold(`Activity timeline (${timeline2.total}${timeline2.total > timeline2.entries.length ? `, showing ${timeline2.entries.length}` : ""}):
70043
+ `));
70044
+ for (const entry2 of timeline2.entries) {
70045
+ const time2 = entry2.created_at.replace("T", " ").slice(0, 16);
70046
+ const ref = entry2.run_id ? `run ${entry2.run_id.slice(0, 8)}` : `task ${entry2.task_id.slice(0, 8)}`;
70047
+ const agent = entry2.agent_id ? chalk7.dim(` (${entry2.agent_id})`) : "";
70048
+ const message = entry2.message ? ` ${entry2.message}` : "";
70049
+ console.log(` ${chalk7.dim(time2)} ${chalk7.cyan(entry2.source)} ${chalk7.dim(ref)} ${entry2.event_type}${message}${agent}`);
70050
+ }
70051
+ return;
70052
+ }
69420
70053
  const { getLocalActivityTimeline: getLocalActivityTimeline2 } = await Promise.resolve().then(() => (init_activity_timeline(), exports_activity_timeline));
69421
70054
  const { resolveTaskRunId: resolveTaskRunId2 } = await Promise.resolve().then(() => (init_task_runs(), exports_task_runs));
69422
70055
  const options = {
@@ -69501,21 +70134,35 @@ Findings`));
69501
70134
  return;
69502
70135
  }
69503
70136
  const db = getDatabase();
69504
- const { getBlockingDeps: getBlockingDeps2 } = await Promise.resolve().then(() => (init_tasks(), exports_tasks));
70137
+ const cloud = getTodosCloudClient();
69505
70138
  const { isLockExpired: isLockExpired2 } = await Promise.resolve().then(() => (init_database(), exports_database));
69506
70139
  const projectId = autoProject(globalOpts) || opts.project || undefined;
69507
70140
  const filter = { status: "pending" };
69508
70141
  if (projectId)
69509
70142
  filter.project_id = projectId;
69510
- const pending = listTasks(filter, db);
69511
- const ready = pending.filter((t) => {
69512
- if (t.locked_by && !isLockExpired2(t.locked_at))
69513
- return false;
69514
- const blockers = getBlockingDeps2(t.id, db);
69515
- return blockers.length === 0;
69516
- });
70143
+ const pending = cloud ? await cloudListTasks(cloud, filter) : listTasks(filter, db);
70144
+ let ready;
70145
+ if (cloud) {
70146
+ const map = await cloudBlockingDepsMap(cloud, pending);
70147
+ ready = pending.filter((t) => {
70148
+ if (t.locked_by && !isLockExpired2(t.locked_at))
70149
+ return false;
70150
+ return !map.has(t.id);
70151
+ });
70152
+ } else {
70153
+ const { getBlockingDeps: getBlockingDeps2 } = await Promise.resolve().then(() => (init_tasks(), exports_tasks));
70154
+ ready = pending.filter((t) => {
70155
+ if (t.locked_by && !isLockExpired2(t.locked_at))
70156
+ return false;
70157
+ return getBlockingDeps2(t.id, db).length === 0;
70158
+ });
70159
+ }
69517
70160
  const limited2 = ready.slice(0, parseInt(opts.limit, 10));
69518
70161
  if (opts.json || globalOpts.json) {
70162
+ if (cloud) {
70163
+ console.log(JSON.stringify(limited2));
70164
+ return;
70165
+ }
69519
70166
  const { getTaskRouteState: getTaskRouteState2 } = await Promise.resolve().then(() => (init_task_routing(), exports_task_routing));
69520
70167
  console.log(JSON.stringify(limited2.map((task2) => ({ ...task2, route_state: getTaskRouteState2(task2, db) }))));
69521
70168
  return;
@@ -69535,19 +70182,29 @@ Findings`));
69535
70182
  program2.command("sprint").description("Sprint dashboard: in-progress, next up, blockers, and overdue").option("-j, --json", "Output as JSON").option("--project <id>", "Filter to project").action(async (opts) => {
69536
70183
  const globalOpts = program2.opts();
69537
70184
  const db = getDatabase();
69538
- const { getBlockingDeps: getBlockingDeps2 } = await Promise.resolve().then(() => (init_tasks(), exports_tasks));
70185
+ const cloud = getTodosCloudClient();
69539
70186
  const projectId = autoProject(globalOpts) || opts.project || undefined;
69540
70187
  const baseFilter = {};
69541
70188
  if (projectId)
69542
70189
  baseFilter.project_id = projectId;
69543
- const inProgress = listTasks({ ...baseFilter, status: "in_progress" }, db);
69544
- const pending = listTasks({ ...baseFilter, status: "pending" }, db);
70190
+ const inProgress = cloud ? await cloudListTasks(cloud, { ...baseFilter, status: "in_progress" }) : listTasks({ ...baseFilter, status: "in_progress" }, db);
70191
+ const pending = cloud ? await cloudListTasks(cloud, { ...baseFilter, status: "pending" }) : listTasks({ ...baseFilter, status: "pending" }, db);
69545
70192
  const nowStr = new Date().toISOString();
69546
70193
  const blocked = [];
69547
- for (const t of pending) {
69548
- const blockers = getBlockingDeps2(t.id, db);
69549
- if (blockers.length > 0)
69550
- blocked.push({ task: t, blockers });
70194
+ if (cloud) {
70195
+ const map = await cloudBlockingDepsMap(cloud, pending);
70196
+ for (const t of pending) {
70197
+ const blockers = map.get(t.id);
70198
+ if (blockers && blockers.length > 0)
70199
+ blocked.push({ task: t, blockers });
70200
+ }
70201
+ } else {
70202
+ const { getBlockingDeps: getBlockingDeps2 } = await Promise.resolve().then(() => (init_tasks(), exports_tasks));
70203
+ for (const t of pending) {
70204
+ const blockers = getBlockingDeps2(t.id, db);
70205
+ if (blockers.length > 0)
70206
+ blocked.push({ task: t, blockers });
70207
+ }
69551
70208
  }
69552
70209
  const overdue = [...inProgress, ...pending].filter((t) => t.due_at && t.due_at < nowStr);
69553
70210
  const blockedIds = new Set(blocked.map((b) => b.task.id));
@@ -69778,17 +70435,20 @@ Findings`));
69778
70435
  program2.command("priorities").description("Show task counts grouped by priority").option("-j, --json", "Output as JSON").option("--project <id>", "Filter to project").action(async (opts) => {
69779
70436
  const globalOpts = program2.opts();
69780
70437
  const db = getDatabase();
70438
+ const cloud = getTodosCloudClient();
69781
70439
  const { countTasks: countTasks2 } = await Promise.resolve().then(() => (init_tasks(), exports_tasks));
70440
+ const countFn = cloud ? (f) => cloudCountTasks(cloud, f) : (f) => Promise.resolve(countTasks2(f, db));
69782
70441
  const projectId = autoProject(globalOpts) || opts.project || undefined;
69783
70442
  const base = projectId ? { project_id: projectId } : {};
69784
70443
  const priorities = ["critical", "high", "medium", "low", "none"];
69785
70444
  const counts = {};
69786
70445
  for (const p of priorities) {
70446
+ const base2 = { ...base, priority: p === "none" ? undefined : p };
69787
70447
  counts[p] = {
69788
- total: countTasks2({ ...base, priority: p === "none" ? undefined : p }, db),
69789
- pending: countTasks2({ ...base, priority: p === "none" ? undefined : p, status: "pending" }, db),
69790
- in_progress: countTasks2({ ...base, priority: p === "none" ? undefined : p, status: "in_progress" }, db),
69791
- completed: countTasks2({ ...base, priority: p === "none" ? undefined : p, status: "completed" }, db)
70448
+ total: await countFn(base2),
70449
+ pending: await countFn({ ...base2, status: "pending" }),
70450
+ in_progress: await countFn({ ...base2, status: "in_progress" }),
70451
+ completed: await countFn({ ...base2, status: "completed" })
69792
70452
  };
69793
70453
  }
69794
70454
  if (opts.json || globalOpts.json) {
@@ -71258,25 +71918,34 @@ Commands:`));
71258
71918
  });
71259
71919
  program2.command("record-verification <task-id> <command>").description("Record a verification command and result for a task").option("--status <status>", "Verification status: passed, failed, or unknown", "unknown").option("--summary <text>", "Short output summary").option("--artifact <path>", "Artifact or log path").option("--agent <name>", "Agent that ran the command").action(async (taskId, command, opts) => {
71260
71920
  const globalOpts = program2.opts();
71261
- const resolvedId = resolveTaskId(taskId);
71262
71921
  if (opts.status !== "passed" && opts.status !== "failed" && opts.status !== "unknown") {
71263
71922
  console.error(chalk8.red("--status must be passed, failed, or unknown"));
71264
71923
  process.exit(1);
71265
71924
  }
71266
- const { addTaskVerification: addTaskVerification2 } = await Promise.resolve().then(() => (init_task_commits(), exports_task_commits));
71267
- const verification = addTaskVerification2({
71268
- task_id: resolvedId,
71269
- command,
71270
- status: opts.status,
71271
- output_summary: opts.summary,
71272
- artifact_path: opts.artifact,
71273
- agent_id: opts.agent
71274
- });
71275
- if (globalOpts.json) {
71276
- output(verification, true);
71277
- return;
71925
+ try {
71926
+ const cloud = getTodosCloudClient();
71927
+ const verification = cloud ? await cloudRecordVerification(cloud, resolveTaskId(taskId), {
71928
+ command,
71929
+ status: opts.status,
71930
+ output_summary: opts.summary,
71931
+ artifact_path: opts.artifact,
71932
+ agent_id: opts.agent
71933
+ }) : (await Promise.resolve().then(() => (init_task_commits(), exports_task_commits))).addTaskVerification({
71934
+ task_id: resolveTaskId(taskId),
71935
+ command,
71936
+ status: opts.status,
71937
+ output_summary: opts.summary,
71938
+ artifact_path: opts.artifact,
71939
+ agent_id: opts.agent
71940
+ });
71941
+ if (globalOpts.json) {
71942
+ output(verification, true);
71943
+ return;
71944
+ }
71945
+ console.log(chalk8.green(`Recorded ${verification.status} verification for task ${taskId}`));
71946
+ } catch (e) {
71947
+ handleError(e);
71278
71948
  }
71279
- console.log(chalk8.green(`Recorded ${verification.status} verification for task ${taskId}`));
71280
71949
  });
71281
71950
  program2.command("trace <task-id>").description("Show local git refs, commits, changed files, and verification commands for a task").action(async (taskId) => {
71282
71951
  const globalOpts = program2.opts();
@@ -71982,6 +72651,7 @@ var HOME2;
71982
72651
  var init_mcp_hooks_commands = __esm(() => {
71983
72652
  init_tasks();
71984
72653
  init_helpers();
72654
+ init_cloud_router();
71985
72655
  HOME2 = process.env["HOME"] || process.env["USERPROFILE"] || "~";
71986
72656
  });
71987
72657