@hasna/todos 0.8.0 → 0.9.0

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 (57) hide show
  1. package/README.md +2 -23
  2. package/dist/cli/components/App.d.ts +2 -0
  3. package/dist/cli/components/App.d.ts.map +1 -0
  4. package/dist/cli/components/Header.d.ts +8 -0
  5. package/dist/cli/components/Header.d.ts.map +1 -0
  6. package/dist/cli/components/ProjectList.d.ts +8 -0
  7. package/dist/cli/components/ProjectList.d.ts.map +1 -0
  8. package/dist/cli/components/SearchView.d.ts +10 -0
  9. package/dist/cli/components/SearchView.d.ts.map +1 -0
  10. package/dist/cli/components/TaskDetail.d.ts +7 -0
  11. package/dist/cli/components/TaskDetail.d.ts.map +1 -0
  12. package/dist/cli/components/TaskForm.d.ts +15 -0
  13. package/dist/cli/components/TaskForm.d.ts.map +1 -0
  14. package/dist/cli/components/TaskList.d.ts +8 -0
  15. package/dist/cli/components/TaskList.d.ts.map +1 -0
  16. package/dist/cli/index.d.ts +3 -0
  17. package/dist/cli/index.d.ts.map +1 -0
  18. package/dist/cli/index.js +561 -7192
  19. package/dist/db/agents.d.ts +13 -0
  20. package/dist/db/agents.d.ts.map +1 -0
  21. package/dist/db/comments.d.ts +7 -0
  22. package/dist/db/comments.d.ts.map +1 -0
  23. package/dist/db/database.d.ts +12 -0
  24. package/dist/db/database.d.ts.map +1 -0
  25. package/dist/db/plans.d.ts +8 -0
  26. package/dist/db/plans.d.ts.map +1 -0
  27. package/dist/db/projects.d.ts +11 -0
  28. package/dist/db/projects.d.ts.map +1 -0
  29. package/dist/db/sessions.d.ts +8 -0
  30. package/dist/db/sessions.d.ts.map +1 -0
  31. package/dist/db/task-lists.d.ts +10 -0
  32. package/dist/db/task-lists.d.ts.map +1 -0
  33. package/dist/db/tasks.d.ts +17 -0
  34. package/dist/db/tasks.d.ts.map +1 -0
  35. package/dist/index.d.ts +16 -299
  36. package/dist/index.d.ts.map +1 -0
  37. package/dist/index.js +194 -272
  38. package/dist/lib/agent-tasks.d.ts +11 -0
  39. package/dist/lib/agent-tasks.d.ts.map +1 -0
  40. package/dist/lib/claude-tasks.d.ts +20 -0
  41. package/dist/lib/claude-tasks.d.ts.map +1 -0
  42. package/dist/lib/config.d.ts +21 -0
  43. package/dist/lib/config.d.ts.map +1 -0
  44. package/dist/lib/search.d.ts +4 -0
  45. package/dist/lib/search.d.ts.map +1 -0
  46. package/dist/lib/sync-types.d.ts +16 -0
  47. package/dist/lib/sync-types.d.ts.map +1 -0
  48. package/dist/lib/sync-utils.d.ts +12 -0
  49. package/dist/lib/sync-utils.d.ts.map +1 -0
  50. package/dist/lib/sync.d.ts +9 -0
  51. package/dist/lib/sync.d.ts.map +1 -0
  52. package/dist/mcp/index.d.ts +3 -0
  53. package/dist/mcp/index.d.ts.map +1 -0
  54. package/dist/mcp/index.js +343 -67
  55. package/dist/types/index.d.ts +275 -0
  56. package/dist/types/index.d.ts.map +1 -0
  57. package/package.json +3 -6
package/dist/index.js CHANGED
@@ -166,74 +166,34 @@ var MIGRATIONS = [
166
166
  INSERT OR IGNORE INTO _migrations (id) VALUES (4);
167
167
  `,
168
168
  `
169
- CREATE TABLE IF NOT EXISTS api_keys (
169
+ CREATE TABLE IF NOT EXISTS agents (
170
170
  id TEXT PRIMARY KEY,
171
- name TEXT NOT NULL,
172
- key_hash TEXT NOT NULL UNIQUE,
173
- key_prefix TEXT NOT NULL,
171
+ name TEXT NOT NULL UNIQUE,
172
+ description TEXT,
173
+ metadata TEXT DEFAULT '{}',
174
174
  created_at TEXT NOT NULL DEFAULT (datetime('now')),
175
- last_used_at TEXT,
176
- expires_at TEXT
177
- );
178
- CREATE INDEX IF NOT EXISTS idx_api_keys_hash ON api_keys(key_hash);
179
- INSERT OR IGNORE INTO _migrations (id) VALUES (5);
180
- `,
181
- `
182
- CREATE TABLE IF NOT EXISTS audit_log (
183
- id TEXT PRIMARY KEY,
184
- entity_type TEXT NOT NULL CHECK(entity_type IN ('task', 'plan', 'project', 'api_key', 'comment')),
185
- entity_id TEXT NOT NULL,
186
- action TEXT NOT NULL CHECK(action IN ('create', 'update', 'delete', 'start', 'complete', 'lock', 'unlock')),
187
- actor TEXT,
188
- changes TEXT,
189
- created_at TEXT NOT NULL DEFAULT (datetime('now'))
175
+ last_seen_at TEXT NOT NULL DEFAULT (datetime('now'))
190
176
  );
191
- CREATE INDEX IF NOT EXISTS idx_audit_entity ON audit_log(entity_type, entity_id);
192
- CREATE INDEX IF NOT EXISTS idx_audit_created ON audit_log(created_at);
177
+ CREATE INDEX IF NOT EXISTS idx_agents_name ON agents(name);
193
178
 
194
- CREATE TABLE IF NOT EXISTS webhooks (
179
+ CREATE TABLE IF NOT EXISTS task_lists (
195
180
  id TEXT PRIMARY KEY,
196
- url TEXT NOT NULL,
197
- events TEXT NOT NULL DEFAULT '[]',
198
- secret TEXT,
199
- active INTEGER NOT NULL DEFAULT 1,
200
- created_at TEXT NOT NULL DEFAULT (datetime('now'))
201
- );
202
-
203
- CREATE TABLE IF NOT EXISTS rate_limits (
204
- key TEXT PRIMARY KEY,
205
- count INTEGER NOT NULL DEFAULT 0,
206
- window_start TEXT NOT NULL DEFAULT (datetime('now'))
207
- );
208
-
209
- INSERT OR IGNORE INTO _migrations (id) VALUES (6);
210
- `,
211
- `
212
- CREATE TABLE IF NOT EXISTS billing_customers (
213
- id TEXT PRIMARY KEY,
214
- stripe_customer_id TEXT UNIQUE,
215
- email TEXT,
216
- name TEXT,
217
- plan TEXT NOT NULL DEFAULT 'free' CHECK(plan IN ('free', 'pro', 'team', 'enterprise')),
218
- stripe_subscription_id TEXT,
219
- subscription_status TEXT DEFAULT 'active' CHECK(subscription_status IN ('active', 'past_due', 'canceled', 'trialing', 'incomplete')),
220
- current_period_end TEXT,
181
+ project_id TEXT REFERENCES projects(id) ON DELETE SET NULL,
182
+ slug TEXT NOT NULL,
183
+ name TEXT NOT NULL,
184
+ description TEXT,
185
+ metadata TEXT DEFAULT '{}',
221
186
  created_at TEXT NOT NULL DEFAULT (datetime('now')),
222
- updated_at TEXT NOT NULL DEFAULT (datetime('now'))
187
+ updated_at TEXT NOT NULL DEFAULT (datetime('now')),
188
+ UNIQUE(project_id, slug)
223
189
  );
224
- CREATE INDEX IF NOT EXISTS idx_billing_stripe ON billing_customers(stripe_customer_id);
190
+ CREATE INDEX IF NOT EXISTS idx_task_lists_project ON task_lists(project_id);
191
+ CREATE INDEX IF NOT EXISTS idx_task_lists_slug ON task_lists(slug);
225
192
 
226
- CREATE TABLE IF NOT EXISTS usage_records (
227
- id TEXT PRIMARY KEY,
228
- customer_id TEXT REFERENCES billing_customers(id),
229
- metric TEXT NOT NULL,
230
- count INTEGER NOT NULL DEFAULT 0,
231
- period TEXT NOT NULL,
232
- created_at TEXT NOT NULL DEFAULT (datetime('now'))
233
- );
234
- CREATE INDEX IF NOT EXISTS idx_usage_customer ON usage_records(customer_id, period);
193
+ ALTER TABLE tasks ADD COLUMN task_list_id TEXT REFERENCES task_lists(id) ON DELETE SET NULL;
194
+ CREATE INDEX IF NOT EXISTS idx_tasks_task_list ON tasks(task_list_id);
235
195
 
236
- INSERT OR IGNORE INTO _migrations (id) VALUES (7);
196
+ INSERT OR IGNORE INTO _migrations (id) VALUES (5);
237
197
  `
238
198
  ];
239
199
  var _db = null;
@@ -403,6 +363,24 @@ class LockError extends Error {
403
363
  }
404
364
  }
405
365
 
366
+ class AgentNotFoundError extends Error {
367
+ agentId;
368
+ constructor(agentId) {
369
+ super(`Agent not found: ${agentId}`);
370
+ this.agentId = agentId;
371
+ this.name = "AgentNotFoundError";
372
+ }
373
+ }
374
+
375
+ class TaskListNotFoundError extends Error {
376
+ taskListId;
377
+ constructor(taskListId) {
378
+ super(`Task list not found: ${taskListId}`);
379
+ this.taskListId = taskListId;
380
+ this.name = "TaskListNotFoundError";
381
+ }
382
+ }
383
+
406
384
  class DependencyCycleError extends Error {
407
385
  taskId;
408
386
  dependsOn;
@@ -442,12 +420,13 @@ function createTask(input, db) {
442
420
  const id = uuid();
443
421
  const timestamp = now();
444
422
  const tags = input.tags || [];
445
- d.run(`INSERT INTO tasks (id, project_id, parent_id, plan_id, title, description, status, priority, agent_id, assigned_to, session_id, working_dir, tags, metadata, version, created_at, updated_at)
446
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, ?, ?)`, [
423
+ d.run(`INSERT INTO tasks (id, project_id, parent_id, plan_id, task_list_id, title, description, status, priority, agent_id, assigned_to, session_id, working_dir, tags, metadata, version, created_at, updated_at)
424
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, ?, ?)`, [
447
425
  id,
448
426
  input.project_id || null,
449
427
  input.parent_id || null,
450
428
  input.plan_id || null,
429
+ input.task_list_id || null,
451
430
  input.title,
452
431
  input.description || null,
453
432
  input.status || "pending",
@@ -555,6 +534,10 @@ function listTasks(filter = {}, db) {
555
534
  conditions.push("plan_id = ?");
556
535
  params.push(filter.plan_id);
557
536
  }
537
+ if (filter.task_list_id) {
538
+ conditions.push("task_list_id = ?");
539
+ params.push(filter.task_list_id);
540
+ }
558
541
  const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
559
542
  const limitVal = filter.limit || 100;
560
543
  const offsetVal = filter.offset || 0;
@@ -610,6 +593,10 @@ function updateTask(id, input, db) {
610
593
  sets.push("plan_id = ?");
611
594
  params.push(input.plan_id);
612
595
  }
596
+ if (input.task_list_id !== undefined) {
597
+ sets.push("task_list_id = ?");
598
+ params.push(input.task_list_id);
599
+ }
613
600
  params.push(id, input.version);
614
601
  const result = d.run(`UPDATE tasks SET ${sets.join(", ")} WHERE id = ? AND version = ?`, params);
615
602
  if (result.changes === 0) {
@@ -887,6 +874,129 @@ function deleteComment(id, db) {
887
874
  const result = d.run("DELETE FROM task_comments WHERE id = ?", [id]);
888
875
  return result.changes > 0;
889
876
  }
877
+ // src/db/agents.ts
878
+ function shortUuid() {
879
+ return crypto.randomUUID().slice(0, 8);
880
+ }
881
+ function rowToAgent(row) {
882
+ return {
883
+ ...row,
884
+ metadata: JSON.parse(row.metadata || "{}")
885
+ };
886
+ }
887
+ function registerAgent(input, db) {
888
+ const d = db || getDatabase();
889
+ const existing = getAgentByName(input.name, d);
890
+ if (existing) {
891
+ d.run("UPDATE agents SET last_seen_at = ? WHERE id = ?", [now(), existing.id]);
892
+ return getAgent(existing.id, d);
893
+ }
894
+ const id = shortUuid();
895
+ const timestamp = now();
896
+ d.run(`INSERT INTO agents (id, name, description, metadata, created_at, last_seen_at)
897
+ VALUES (?, ?, ?, ?, ?, ?)`, [id, input.name, input.description || null, JSON.stringify(input.metadata || {}), timestamp, timestamp]);
898
+ return getAgent(id, d);
899
+ }
900
+ function getAgent(id, db) {
901
+ const d = db || getDatabase();
902
+ const row = d.query("SELECT * FROM agents WHERE id = ?").get(id);
903
+ return row ? rowToAgent(row) : null;
904
+ }
905
+ function getAgentByName(name, db) {
906
+ const d = db || getDatabase();
907
+ const row = d.query("SELECT * FROM agents WHERE name = ?").get(name);
908
+ return row ? rowToAgent(row) : null;
909
+ }
910
+ function listAgents(db) {
911
+ const d = db || getDatabase();
912
+ return d.query("SELECT * FROM agents ORDER BY name").all().map(rowToAgent);
913
+ }
914
+ function updateAgentActivity(id, db) {
915
+ const d = db || getDatabase();
916
+ d.run("UPDATE agents SET last_seen_at = ? WHERE id = ?", [now(), id]);
917
+ }
918
+ function deleteAgent(id, db) {
919
+ const d = db || getDatabase();
920
+ return d.run("DELETE FROM agents WHERE id = ?", [id]).changes > 0;
921
+ }
922
+ // src/db/task-lists.ts
923
+ function rowToTaskList(row) {
924
+ return {
925
+ ...row,
926
+ metadata: JSON.parse(row.metadata || "{}")
927
+ };
928
+ }
929
+ function createTaskList(input, db) {
930
+ const d = db || getDatabase();
931
+ const id = uuid();
932
+ const timestamp = now();
933
+ const slug = input.slug || slugify(input.name);
934
+ if (!input.project_id) {
935
+ const existing = d.query("SELECT id FROM task_lists WHERE project_id IS NULL AND slug = ?").get(slug);
936
+ if (existing) {
937
+ throw new Error(`Standalone task list with slug "${slug}" already exists`);
938
+ }
939
+ }
940
+ d.run(`INSERT INTO task_lists (id, project_id, slug, name, description, metadata, created_at, updated_at)
941
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, [id, input.project_id || null, slug, input.name, input.description || null, JSON.stringify(input.metadata || {}), timestamp, timestamp]);
942
+ return getTaskList(id, d);
943
+ }
944
+ function getTaskList(id, db) {
945
+ const d = db || getDatabase();
946
+ const row = d.query("SELECT * FROM task_lists WHERE id = ?").get(id);
947
+ return row ? rowToTaskList(row) : null;
948
+ }
949
+ function getTaskListBySlug(slug, projectId, db) {
950
+ const d = db || getDatabase();
951
+ let row;
952
+ if (projectId) {
953
+ row = d.query("SELECT * FROM task_lists WHERE slug = ? AND project_id = ?").get(slug, projectId);
954
+ } else {
955
+ row = d.query("SELECT * FROM task_lists WHERE slug = ? AND project_id IS NULL").get(slug);
956
+ }
957
+ return row ? rowToTaskList(row) : null;
958
+ }
959
+ function listTaskLists(projectId, db) {
960
+ const d = db || getDatabase();
961
+ if (projectId) {
962
+ return d.query("SELECT * FROM task_lists WHERE project_id = ? ORDER BY name").all(projectId).map(rowToTaskList);
963
+ }
964
+ return d.query("SELECT * FROM task_lists ORDER BY name").all().map(rowToTaskList);
965
+ }
966
+ function updateTaskList(id, input, db) {
967
+ const d = db || getDatabase();
968
+ const existing = getTaskList(id, d);
969
+ if (!existing)
970
+ throw new TaskListNotFoundError(id);
971
+ const sets = ["updated_at = ?"];
972
+ const params = [now()];
973
+ if (input.name !== undefined) {
974
+ sets.push("name = ?");
975
+ params.push(input.name);
976
+ }
977
+ if (input.description !== undefined) {
978
+ sets.push("description = ?");
979
+ params.push(input.description);
980
+ }
981
+ if (input.metadata !== undefined) {
982
+ sets.push("metadata = ?");
983
+ params.push(JSON.stringify(input.metadata));
984
+ }
985
+ params.push(id);
986
+ d.run(`UPDATE task_lists SET ${sets.join(", ")} WHERE id = ?`, params);
987
+ return getTaskList(id, d);
988
+ }
989
+ function deleteTaskList(id, db) {
990
+ const d = db || getDatabase();
991
+ return d.run("DELETE FROM task_lists WHERE id = ?", [id]).changes > 0;
992
+ }
993
+ function ensureTaskList(name, slug, projectId, db) {
994
+ const d = db || getDatabase();
995
+ const existing = getTaskListBySlug(slug, projectId, d);
996
+ if (existing)
997
+ return existing;
998
+ return createTaskList({ name, slug, project_id: projectId }, d);
999
+ }
890
1000
  // src/db/sessions.ts
891
1001
  function rowToSession(row) {
892
1002
  return {
@@ -931,56 +1041,6 @@ function deleteSession(id, db) {
931
1041
  const result = d.run("DELETE FROM sessions WHERE id = ?", [id]);
932
1042
  return result.changes > 0;
933
1043
  }
934
- // src/db/api-keys.ts
935
- function generateApiKey() {
936
- const bytes = new Uint8Array(32);
937
- crypto.getRandomValues(bytes);
938
- return "td_" + Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
939
- }
940
- async function hashKey(key) {
941
- const encoder = new TextEncoder;
942
- const data = encoder.encode(key);
943
- const hash = await crypto.subtle.digest("SHA-256", data);
944
- return Array.from(new Uint8Array(hash)).map((b) => b.toString(16).padStart(2, "0")).join("");
945
- }
946
- async function createApiKey(input, db) {
947
- const d = db || getDatabase();
948
- const id = uuid();
949
- const timestamp = now();
950
- const key = generateApiKey();
951
- const keyHash = await hashKey(key);
952
- const keyPrefix = key.slice(0, 10) + "...";
953
- d.run(`INSERT INTO api_keys (id, name, key_hash, key_prefix, created_at, expires_at)
954
- VALUES (?, ?, ?, ?, ?, ?)`, [id, input.name, keyHash, keyPrefix, timestamp, input.expires_at || null]);
955
- const row = d.query("SELECT id, name, key_prefix, created_at, last_used_at, expires_at FROM api_keys WHERE id = ?").get(id);
956
- return { ...row, key };
957
- }
958
- function listApiKeys(db) {
959
- const d = db || getDatabase();
960
- return d.query("SELECT id, name, key_prefix, created_at, last_used_at, expires_at FROM api_keys ORDER BY created_at DESC").all();
961
- }
962
- function deleteApiKey(id, db) {
963
- const d = db || getDatabase();
964
- const result = d.run("DELETE FROM api_keys WHERE id = ?", [id]);
965
- return result.changes > 0;
966
- }
967
- async function validateApiKey(key, db) {
968
- const d = db || getDatabase();
969
- const keyHash = await hashKey(key);
970
- const row = d.query("SELECT id, name, key_prefix, created_at, last_used_at, expires_at FROM api_keys WHERE key_hash = ?").get(keyHash);
971
- if (!row)
972
- return null;
973
- if (row.expires_at && new Date(row.expires_at) < new Date) {
974
- return null;
975
- }
976
- d.run("UPDATE api_keys SET last_used_at = ? WHERE id = ?", [now(), row.id]);
977
- return row;
978
- }
979
- function hasAnyApiKeys(db) {
980
- const d = db || getDatabase();
981
- const row = d.query("SELECT COUNT(*) as count FROM api_keys").get();
982
- return (row?.count ?? 0) > 0;
983
- }
984
1044
  // src/lib/search.ts
985
1045
  function rowToTask2(row) {
986
1046
  return {
@@ -991,7 +1051,7 @@ function rowToTask2(row) {
991
1051
  priority: row.priority
992
1052
  };
993
1053
  }
994
- function searchTasks(query, projectId, db) {
1054
+ function searchTasks(query, projectId, taskListId, db) {
995
1055
  const d = db || getDatabase();
996
1056
  clearExpiredLocks(d);
997
1057
  const pattern = `%${query}%`;
@@ -1001,6 +1061,10 @@ function searchTasks(query, projectId, db) {
1001
1061
  sql += " AND project_id = ?";
1002
1062
  params.push(projectId);
1003
1063
  }
1064
+ if (taskListId) {
1065
+ sql += " AND task_list_id = ?";
1066
+ params.push(taskListId);
1067
+ }
1004
1068
  sql += ` ORDER BY
1005
1069
  CASE priority WHEN 'critical' THEN 0 WHEN 'high' THEN 1 WHEN 'medium' THEN 2 WHEN 'low' THEN 3 END,
1006
1070
  created_at DESC`;
@@ -1619,155 +1683,15 @@ function syncWithAgents(agents, taskListIdByAgent, projectId, direction = "both"
1619
1683
  }
1620
1684
  return { pushed, pulled, errors };
1621
1685
  }
1622
- // src/db/audit.ts
1623
- function rowToEntry(row) {
1624
- return {
1625
- ...row,
1626
- changes: row.changes ? JSON.parse(row.changes) : null
1627
- };
1628
- }
1629
- function logAudit(entityType, entityId, action, actor, changes, db) {
1630
- const d = db || getDatabase();
1631
- d.run(`INSERT INTO audit_log (id, entity_type, entity_id, action, actor, changes, created_at)
1632
- VALUES (?, ?, ?, ?, ?, ?, ?)`, [uuid(), entityType, entityId, action, actor || null, changes ? JSON.stringify(changes) : null, now()]);
1633
- }
1634
- function getAuditLog(entityType, entityId, limit = 50, offset = 0, db) {
1635
- const d = db || getDatabase();
1636
- const conditions = [];
1637
- const params = [];
1638
- if (entityType) {
1639
- conditions.push("entity_type = ?");
1640
- params.push(entityType);
1641
- }
1642
- if (entityId) {
1643
- conditions.push("entity_id = ?");
1644
- params.push(entityId);
1645
- }
1646
- const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
1647
- params.push(limit, offset);
1648
- const rows = d.query(`SELECT * FROM audit_log ${where} ORDER BY created_at DESC LIMIT ? OFFSET ?`).all(...params);
1649
- return rows.map(rowToEntry);
1650
- }
1651
- // src/db/webhooks.ts
1652
- function rowToWebhook(row) {
1653
- return {
1654
- ...row,
1655
- events: JSON.parse(row.events),
1656
- active: row.active === 1
1657
- };
1658
- }
1659
- function createWebhook(input, db) {
1660
- const d = db || getDatabase();
1661
- const id = uuid();
1662
- d.run(`INSERT INTO webhooks (id, url, events, secret, created_at)
1663
- VALUES (?, ?, ?, ?, ?)`, [id, input.url, JSON.stringify(input.events || []), input.secret || null, now()]);
1664
- return getWebhook(id, d);
1665
- }
1666
- function getWebhook(id, db) {
1667
- const d = db || getDatabase();
1668
- const row = d.query("SELECT * FROM webhooks WHERE id = ?").get(id);
1669
- return row ? rowToWebhook(row) : null;
1670
- }
1671
- function listWebhooks(db) {
1672
- const d = db || getDatabase();
1673
- return d.query("SELECT * FROM webhooks ORDER BY created_at DESC").all().map(rowToWebhook);
1674
- }
1675
- function deleteWebhook(id, db) {
1676
- const d = db || getDatabase();
1677
- return d.run("DELETE FROM webhooks WHERE id = ?", [id]).changes > 0;
1678
- }
1679
- async function dispatchWebhooks(event, payload, db) {
1680
- const d = db || getDatabase();
1681
- const rows = d.query("SELECT * FROM webhooks WHERE active = 1").all();
1682
- const webhooks = rows.map(rowToWebhook).filter((w) => w.events.length === 0 || w.events.includes(event));
1683
- for (const webhook of webhooks) {
1684
- try {
1685
- const headers = { "Content-Type": "application/json" };
1686
- if (webhook.secret) {
1687
- const encoder = new TextEncoder;
1688
- const key = await crypto.subtle.importKey("raw", encoder.encode(webhook.secret), { name: "HMAC", hash: "SHA-256" }, false, ["sign"]);
1689
- const body = JSON.stringify({ event, data: payload, timestamp: now() });
1690
- const sig = await crypto.subtle.sign("HMAC", key, encoder.encode(body));
1691
- headers["X-Webhook-Signature"] = Array.from(new Uint8Array(sig)).map((b) => b.toString(16).padStart(2, "0")).join("");
1692
- fetch(webhook.url, {
1693
- method: "POST",
1694
- headers,
1695
- body
1696
- }).catch(() => {});
1697
- } else {
1698
- fetch(webhook.url, {
1699
- method: "POST",
1700
- headers,
1701
- body: JSON.stringify({ event, data: payload, timestamp: now() })
1702
- }).catch(() => {});
1703
- }
1704
- } catch {}
1705
- }
1706
- }
1707
- // src/db/billing.ts
1708
- function getOrCreateCustomer(db) {
1709
- const d = db || getDatabase();
1710
- const existing = d.query("SELECT * FROM billing_customers LIMIT 1").get();
1711
- if (existing)
1712
- return existing;
1713
- const id = uuid();
1714
- const timestamp = now();
1715
- d.run(`INSERT INTO billing_customers (id, plan, created_at, updated_at) VALUES (?, 'free', ?, ?)`, [id, timestamp, timestamp]);
1716
- return d.query("SELECT * FROM billing_customers WHERE id = ?").get(id);
1717
- }
1718
- function updateCustomer(id, input, db) {
1719
- const d = db || getDatabase();
1720
- const sets = ["updated_at = ?"];
1721
- const params = [now()];
1722
- for (const [key, value] of Object.entries(input)) {
1723
- if (value !== undefined) {
1724
- sets.push(`${key} = ?`);
1725
- params.push(value);
1726
- }
1727
- }
1728
- params.push(id);
1729
- d.run(`UPDATE billing_customers SET ${sets.join(", ")} WHERE id = ?`, params);
1730
- return d.query("SELECT * FROM billing_customers WHERE id = ?").get(id);
1731
- }
1732
- function getCustomerByStripeId(stripeCustomerId, db) {
1733
- const d = db || getDatabase();
1734
- return d.query("SELECT * FROM billing_customers WHERE stripe_customer_id = ?").get(stripeCustomerId);
1735
- }
1736
- function getUsage(customerId, period, db) {
1737
- const d = db || getDatabase();
1738
- return d.query("SELECT * FROM usage_records WHERE customer_id = ? AND period = ?").all(customerId, period);
1739
- }
1740
- function trackUsage(customerId, metric, count = 1, db) {
1741
- const d = db || getDatabase();
1742
- const period = new Date().toISOString().slice(0, 7);
1743
- const existing = d.query("SELECT * FROM usage_records WHERE customer_id = ? AND metric = ? AND period = ?").get(customerId, metric, period);
1744
- if (existing) {
1745
- d.run("UPDATE usage_records SET count = count + ? WHERE id = ?", [count, existing.id]);
1746
- } else {
1747
- d.run("INSERT INTO usage_records (id, customer_id, metric, count, period, created_at) VALUES (?, ?, ?, ?, ?, ?)", [uuid(), customerId, metric, count, period, now()]);
1748
- }
1749
- }
1750
- var PLAN_LIMITS = {
1751
- free: { tasks: 100, projects: 3, plans: 5, api_keys: 1, webhooks: 0 },
1752
- pro: { tasks: 1e4, projects: 50, plans: 100, api_keys: 10, webhooks: 10 },
1753
- team: { tasks: 1e5, projects: 500, plans: 1000, api_keys: 50, webhooks: 50 },
1754
- enterprise: { tasks: -1, projects: -1, plans: -1, api_keys: -1, webhooks: -1 }
1755
- };
1756
- var PLAN_PRICES = {
1757
- free: { monthly: 0, yearly: 0 },
1758
- pro: { monthly: 9, yearly: 84 },
1759
- team: { monthly: 29, yearly: 276 },
1760
- enterprise: { monthly: 99, yearly: 948 }
1761
- };
1762
1686
  export {
1763
- validateApiKey,
1687
+ uuid,
1688
+ updateTaskList,
1764
1689
  updateTask,
1765
1690
  updateSessionActivity,
1766
1691
  updateProject,
1767
1692
  updatePlan,
1768
- updateCustomer,
1693
+ updateAgentActivity,
1769
1694
  unlockTask,
1770
- trackUsage,
1771
1695
  syncWithAgents,
1772
1696
  syncWithAgent,
1773
1697
  startTask,
@@ -1776,20 +1700,20 @@ export {
1776
1700
  resolvePartialId,
1777
1701
  resetDatabase,
1778
1702
  removeDependency,
1779
- logAudit,
1703
+ registerAgent,
1704
+ now,
1780
1705
  lockTask,
1781
1706
  loadConfig,
1782
- listWebhooks,
1783
1707
  listTasks,
1708
+ listTaskLists,
1784
1709
  listSessions,
1785
1710
  listProjects,
1786
1711
  listPlans,
1787
1712
  listComments,
1788
- listApiKeys,
1789
- hasAnyApiKeys,
1790
- getWebhook,
1791
- getUsage,
1713
+ listAgents,
1792
1714
  getTaskWithRelations,
1715
+ getTaskListBySlug,
1716
+ getTaskList,
1793
1717
  getTaskDependents,
1794
1718
  getTaskDependencies,
1795
1719
  getTask,
@@ -1797,40 +1721,38 @@ export {
1797
1721
  getProjectByPath,
1798
1722
  getProject,
1799
1723
  getPlan,
1800
- getOrCreateCustomer,
1801
1724
  getDatabase,
1802
- getCustomerByStripeId,
1803
1725
  getComment,
1804
- getAuditLog,
1726
+ getAgentByName,
1727
+ getAgent,
1728
+ ensureTaskList,
1805
1729
  ensureProject,
1806
- dispatchWebhooks,
1807
- deleteWebhook,
1730
+ deleteTaskList,
1808
1731
  deleteTask,
1809
1732
  deleteSession,
1810
1733
  deleteProject,
1811
1734
  deletePlan,
1812
1735
  deleteComment,
1813
- deleteApiKey,
1736
+ deleteAgent,
1814
1737
  defaultSyncAgents,
1815
- createWebhook,
1738
+ createTaskList,
1816
1739
  createTask,
1817
1740
  createSession,
1818
1741
  createProject,
1819
1742
  createPlan,
1820
- createApiKey,
1821
1743
  completeTask,
1822
1744
  closeDatabase,
1823
1745
  addDependency,
1824
1746
  addComment,
1825
1747
  VersionConflictError,
1826
1748
  TaskNotFoundError,
1749
+ TaskListNotFoundError,
1827
1750
  TASK_STATUSES,
1828
1751
  TASK_PRIORITIES,
1829
1752
  ProjectNotFoundError,
1830
1753
  PlanNotFoundError,
1831
1754
  PLAN_STATUSES,
1832
- PLAN_PRICES,
1833
- PLAN_LIMITS,
1834
1755
  LockError,
1835
- DependencyCycleError
1756
+ DependencyCycleError,
1757
+ AgentNotFoundError
1836
1758
  };
@@ -0,0 +1,11 @@
1
+ import type { SyncPrefer, SyncResult } from "./sync-types.js";
2
+ export declare function pushToAgentTaskList(agent: string, taskListId: string, projectId?: string, options?: {
3
+ prefer?: SyncPrefer;
4
+ }): SyncResult;
5
+ export declare function pullFromAgentTaskList(agent: string, taskListId: string, projectId?: string, options?: {
6
+ prefer?: SyncPrefer;
7
+ }): SyncResult;
8
+ export declare function syncAgentTaskList(agent: string, taskListId: string, projectId?: string, options?: {
9
+ prefer?: SyncPrefer;
10
+ }): SyncResult;
11
+ //# sourceMappingURL=agent-tasks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-tasks.d.ts","sourceRoot":"","sources":["../../src/lib/agent-tasks.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAwD9D,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,MAAM,EACb,UAAU,EAAE,MAAM,EAClB,SAAS,CAAC,EAAE,MAAM,EAClB,OAAO,GAAE;IAAE,MAAM,CAAC,EAAE,UAAU,CAAA;CAAO,GACpC,UAAU,CAuFZ;AAED,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,MAAM,EACb,UAAU,EAAE,MAAM,EAClB,SAAS,CAAC,EAAE,MAAM,EAClB,OAAO,GAAE;IAAE,MAAM,CAAC,EAAE,UAAU,CAAA;CAAO,GACpC,UAAU,CAoFZ;AAED,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,MAAM,EACb,UAAU,EAAE,MAAM,EAClB,SAAS,CAAC,EAAE,MAAM,EAClB,OAAO,GAAE;IAAE,MAAM,CAAC,EAAE,UAAU,CAAA;CAAO,GACpC,UAAU,CAQZ"}
@@ -0,0 +1,20 @@
1
+ import type { SyncPrefer, SyncResult } from "./sync-types.js";
2
+ /**
3
+ * Push all SQLite tasks to a Claude Code task list directory.
4
+ */
5
+ export declare function pushToClaudeTaskList(taskListId: string, projectId?: string, options?: {
6
+ prefer?: SyncPrefer;
7
+ }): SyncResult;
8
+ /**
9
+ * Pull tasks from a Claude Code task list into SQLite.
10
+ */
11
+ export declare function pullFromClaudeTaskList(taskListId: string, projectId?: string, options?: {
12
+ prefer?: SyncPrefer;
13
+ }): SyncResult;
14
+ /**
15
+ * Bidirectional sync: pull first, then push.
16
+ */
17
+ export declare function syncClaudeTaskList(taskListId: string, projectId?: string, options?: {
18
+ prefer?: SyncPrefer;
19
+ }): SyncResult;
20
+ //# sourceMappingURL=claude-tasks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude-tasks.d.ts","sourceRoot":"","sources":["../../src/lib/claude-tasks.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAwF9D;;GAEG;AACH,wBAAgB,oBAAoB,CAClC,UAAU,EAAE,MAAM,EAClB,SAAS,CAAC,EAAE,MAAM,EAClB,OAAO,GAAE;IAAE,MAAM,CAAC,EAAE,UAAU,CAAA;CAAO,GACpC,UAAU,CA4GZ;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CACpC,UAAU,EAAE,MAAM,EAClB,SAAS,CAAC,EAAE,MAAM,EAClB,OAAO,GAAE;IAAE,MAAM,CAAC,EAAE,UAAU,CAAA;CAAO,GACpC,UAAU,CAwFZ;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,UAAU,EAAE,MAAM,EAClB,SAAS,CAAC,EAAE,MAAM,EAClB,OAAO,GAAE;IAAE,MAAM,CAAC,EAAE,UAAU,CAAA;CAAO,GACpC,UAAU,CAQZ"}
@@ -0,0 +1,21 @@
1
+ export interface AgentConfig {
2
+ task_list_id?: string;
3
+ tasks_dir?: string;
4
+ }
5
+ export interface TaskPrefixConfig {
6
+ prefix: string;
7
+ start_from?: number;
8
+ }
9
+ export interface TodosConfig {
10
+ sync_agents?: string[] | string;
11
+ task_list_id?: string;
12
+ agent_tasks_dir?: string;
13
+ agents?: Record<string, AgentConfig>;
14
+ task_prefix?: TaskPrefixConfig;
15
+ }
16
+ export declare function loadConfig(): TodosConfig;
17
+ export declare function getSyncAgentsFromConfig(): string[] | null;
18
+ export declare function getAgentTaskListId(agent: string): string | null;
19
+ export declare function getAgentTasksDir(agent: string): string | null;
20
+ export declare function getTaskPrefixConfig(): TaskPrefixConfig | null;
21
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,WAAW;IAC1B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,WAAW;IAC1B,WAAW,CAAC,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC;IAChC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IACrC,WAAW,CAAC,EAAE,gBAAgB,CAAC;CAChC;AASD,wBAAgB,UAAU,IAAI,WAAW,CAYxC;AAED,wBAAgB,uBAAuB,IAAI,MAAM,EAAE,GAAG,IAAI,CAKzD;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAM/D;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAM7D;AAED,wBAAgB,mBAAmB,IAAI,gBAAgB,GAAG,IAAI,CAG7D"}
@@ -0,0 +1,4 @@
1
+ import type { Database } from "bun:sqlite";
2
+ import type { Task } from "../types/index.js";
3
+ export declare function searchTasks(query: string, projectId?: string, taskListId?: string, db?: Database): Task[];
4
+ //# sourceMappingURL=search.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../src/lib/search.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAoB,MAAM,YAAY,CAAC;AAC7D,OAAO,KAAK,EAAE,IAAI,EAAW,MAAM,mBAAmB,CAAC;AAavD,wBAAgB,WAAW,CACzB,KAAK,EAAE,MAAM,EACb,SAAS,CAAC,EAAE,MAAM,EAClB,UAAU,CAAC,EAAE,MAAM,EACnB,EAAE,CAAC,EAAE,QAAQ,GACZ,IAAI,EAAE,CAwBR"}
@@ -0,0 +1,16 @@
1
+ export interface SyncResult {
2
+ pushed: number;
3
+ pulled: number;
4
+ errors: string[];
5
+ }
6
+ export type SyncPrefer = "local" | "remote";
7
+ export interface SyncConflict {
8
+ agent: string;
9
+ direction: "push" | "pull";
10
+ prefer: SyncPrefer;
11
+ local_updated_at?: string;
12
+ remote_updated_at?: string;
13
+ detected_at: string;
14
+ notes?: string;
15
+ }
16
+ //# sourceMappingURL=sync-types.d.ts.map