@hasna/todos 0.9.74 → 0.9.76

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/mcp/index.js CHANGED
@@ -1,6 +1,23 @@
1
1
  #!/usr/bin/env bun
2
2
  // @bun
3
3
  var __defProp = Object.defineProperty;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __moduleCache = /* @__PURE__ */ new WeakMap;
8
+ var __toCommonJS = (from) => {
9
+ var entry = __moduleCache.get(from), desc;
10
+ if (entry)
11
+ return entry;
12
+ entry = __defProp({}, "__esModule", { value: true });
13
+ if (from && typeof from === "object" || typeof from === "function")
14
+ __getOwnPropNames(from).map((key) => !__hasOwnProp.call(entry, key) && __defProp(entry, key, {
15
+ get: () => from[key],
16
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
17
+ }));
18
+ __moduleCache.set(from, entry);
19
+ return entry;
20
+ };
4
21
  var __export = (target, all) => {
5
22
  for (var name in all)
6
23
  __defProp(target, name, {
@@ -181,6 +198,28 @@ function ensureSchema(db) {
181
198
  metadata TEXT DEFAULT '{}',
182
199
  created_at TEXT NOT NULL DEFAULT (datetime('now'))
183
200
  )`);
201
+ ensureTable("task_checklists", `
202
+ CREATE TABLE task_checklists (
203
+ id TEXT PRIMARY KEY,
204
+ task_id TEXT NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
205
+ position INTEGER NOT NULL DEFAULT 0,
206
+ text TEXT NOT NULL,
207
+ checked INTEGER NOT NULL DEFAULT 0,
208
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
209
+ updated_at TEXT NOT NULL DEFAULT (datetime('now'))
210
+ )`);
211
+ ensureTable("project_sources", `
212
+ CREATE TABLE project_sources (
213
+ id TEXT PRIMARY KEY,
214
+ project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
215
+ type TEXT NOT NULL,
216
+ name TEXT NOT NULL,
217
+ uri TEXT NOT NULL,
218
+ description TEXT,
219
+ metadata TEXT DEFAULT '{}',
220
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
221
+ updated_at TEXT NOT NULL DEFAULT (datetime('now'))
222
+ )`);
184
223
  ensureColumn("projects", "task_list_id", "TEXT");
185
224
  ensureColumn("projects", "task_prefix", "TEXT");
186
225
  ensureColumn("projects", "task_counter", "INTEGER NOT NULL DEFAULT 0");
@@ -225,6 +264,9 @@ function ensureSchema(db) {
225
264
  ensureIndex("CREATE INDEX IF NOT EXISTS idx_task_history_agent ON task_history(agent_id)");
226
265
  ensureIndex("CREATE INDEX IF NOT EXISTS idx_tasks_recurrence_parent ON tasks(recurrence_parent_id)");
227
266
  ensureIndex("CREATE INDEX IF NOT EXISTS idx_tasks_recurrence_rule ON tasks(recurrence_rule) WHERE recurrence_rule IS NOT NULL");
267
+ ensureIndex("CREATE INDEX IF NOT EXISTS idx_task_checklists_task ON task_checklists(task_id)");
268
+ ensureIndex("CREATE INDEX IF NOT EXISTS idx_project_sources_project ON project_sources(project_id)");
269
+ ensureIndex("CREATE INDEX IF NOT EXISTS idx_project_sources_type ON project_sources(type)");
228
270
  }
229
271
  function backfillTaskTags(db) {
230
272
  try {
@@ -602,6 +644,49 @@ var init_database = __esm(() => {
602
644
  ALTER TABLE tasks ADD COLUMN reason TEXT;
603
645
  ALTER TABLE tasks ADD COLUMN spawned_from_session TEXT;
604
646
  INSERT OR IGNORE INTO _migrations (id) VALUES (19);
647
+ `,
648
+ `
649
+ CREATE TABLE IF NOT EXISTS handoffs (
650
+ id TEXT PRIMARY KEY,
651
+ agent_id TEXT,
652
+ project_id TEXT REFERENCES projects(id) ON DELETE SET NULL,
653
+ summary TEXT NOT NULL,
654
+ completed TEXT,
655
+ in_progress TEXT,
656
+ blockers TEXT,
657
+ next_steps TEXT,
658
+ created_at TEXT NOT NULL
659
+ );
660
+ INSERT OR IGNORE INTO _migrations (id) VALUES (20);
661
+ `,
662
+ `
663
+ CREATE TABLE IF NOT EXISTS task_checklists (
664
+ id TEXT PRIMARY KEY,
665
+ task_id TEXT NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
666
+ position INTEGER NOT NULL DEFAULT 0,
667
+ text TEXT NOT NULL,
668
+ checked INTEGER NOT NULL DEFAULT 0,
669
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
670
+ updated_at TEXT NOT NULL DEFAULT (datetime('now'))
671
+ );
672
+ CREATE INDEX IF NOT EXISTS idx_task_checklists_task ON task_checklists(task_id);
673
+ INSERT OR IGNORE INTO _migrations (id) VALUES (21);
674
+ `,
675
+ `
676
+ CREATE TABLE IF NOT EXISTS project_sources (
677
+ id TEXT PRIMARY KEY,
678
+ project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
679
+ type TEXT NOT NULL,
680
+ name TEXT NOT NULL,
681
+ uri TEXT NOT NULL,
682
+ description TEXT,
683
+ metadata TEXT DEFAULT '{}',
684
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
685
+ updated_at TEXT NOT NULL DEFAULT (datetime('now'))
686
+ );
687
+ CREATE INDEX IF NOT EXISTS idx_project_sources_project ON project_sources(project_id);
688
+ CREATE INDEX IF NOT EXISTS idx_project_sources_type ON project_sources(type);
689
+ INSERT OR IGNORE INTO _migrations (id) VALUES (22);
605
690
  `
606
691
  ];
607
692
  });
@@ -933,6 +1018,77 @@ var init_agents = __esm(() => {
933
1018
  AGENT_ACTIVE_WINDOW_MS = 30 * 60 * 1000;
934
1019
  });
935
1020
 
1021
+ // src/db/handoffs.ts
1022
+ var exports_handoffs = {};
1023
+ __export(exports_handoffs, {
1024
+ listHandoffs: () => listHandoffs,
1025
+ getLatestHandoff: () => getLatestHandoff,
1026
+ createHandoff: () => createHandoff
1027
+ });
1028
+ function createHandoff(input, db) {
1029
+ const d = db || getDatabase();
1030
+ const id = uuid();
1031
+ const timestamp = now();
1032
+ d.run(`INSERT INTO handoffs (id, agent_id, project_id, summary, completed, in_progress, blockers, next_steps, created_at)
1033
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
1034
+ id,
1035
+ input.agent_id || null,
1036
+ input.project_id || null,
1037
+ input.summary,
1038
+ input.completed ? JSON.stringify(input.completed) : null,
1039
+ input.in_progress ? JSON.stringify(input.in_progress) : null,
1040
+ input.blockers ? JSON.stringify(input.blockers) : null,
1041
+ input.next_steps ? JSON.stringify(input.next_steps) : null,
1042
+ timestamp
1043
+ ]);
1044
+ return {
1045
+ id,
1046
+ agent_id: input.agent_id || null,
1047
+ project_id: input.project_id || null,
1048
+ summary: input.summary,
1049
+ completed: input.completed || null,
1050
+ in_progress: input.in_progress || null,
1051
+ blockers: input.blockers || null,
1052
+ next_steps: input.next_steps || null,
1053
+ created_at: timestamp
1054
+ };
1055
+ }
1056
+ function rowToHandoff(row) {
1057
+ return {
1058
+ ...row,
1059
+ completed: row.completed ? JSON.parse(row.completed) : null,
1060
+ in_progress: row.in_progress ? JSON.parse(row.in_progress) : null,
1061
+ blockers: row.blockers ? JSON.parse(row.blockers) : null,
1062
+ next_steps: row.next_steps ? JSON.parse(row.next_steps) : null
1063
+ };
1064
+ }
1065
+ function listHandoffs(projectId, limit = 10, db) {
1066
+ const d = db || getDatabase();
1067
+ if (projectId) {
1068
+ return d.query("SELECT * FROM handoffs WHERE project_id = ? ORDER BY rowid DESC LIMIT ?").all(projectId, limit).map(rowToHandoff);
1069
+ }
1070
+ return d.query("SELECT * FROM handoffs ORDER BY rowid DESC LIMIT ?").all(limit).map(rowToHandoff);
1071
+ }
1072
+ function getLatestHandoff(agentId, projectId, db) {
1073
+ const d = db || getDatabase();
1074
+ let query = "SELECT * FROM handoffs WHERE 1=1";
1075
+ const params = [];
1076
+ if (agentId) {
1077
+ query += " AND agent_id = ?";
1078
+ params.push(agentId);
1079
+ }
1080
+ if (projectId) {
1081
+ query += " AND project_id = ?";
1082
+ params.push(projectId);
1083
+ }
1084
+ query += " ORDER BY rowid DESC LIMIT 1";
1085
+ const row = d.query(query).get(...params);
1086
+ return row ? rowToHandoff(row) : null;
1087
+ }
1088
+ var init_handoffs = __esm(() => {
1089
+ init_database();
1090
+ });
1091
+
936
1092
  // src/mcp/index.ts
937
1093
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
938
1094
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
@@ -5067,6 +5223,40 @@ function listProjects(db) {
5067
5223
  const d = db || getDatabase();
5068
5224
  return d.query("SELECT * FROM projects ORDER BY name").all();
5069
5225
  }
5226
+ function rowToSource(row) {
5227
+ return {
5228
+ ...row,
5229
+ metadata: row.metadata ? JSON.parse(row.metadata) : {}
5230
+ };
5231
+ }
5232
+ function addProjectSource(input, db) {
5233
+ const d = db || getDatabase();
5234
+ const id = uuid();
5235
+ const timestamp = now();
5236
+ d.run(`INSERT INTO project_sources (id, project_id, type, name, uri, description, metadata, created_at, updated_at)
5237
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
5238
+ id,
5239
+ input.project_id,
5240
+ input.type,
5241
+ input.name,
5242
+ input.uri,
5243
+ input.description || null,
5244
+ JSON.stringify(input.metadata || {}),
5245
+ timestamp,
5246
+ timestamp
5247
+ ]);
5248
+ return rowToSource(d.query("SELECT * FROM project_sources WHERE id = ?").get(id));
5249
+ }
5250
+ function removeProjectSource(id, db) {
5251
+ const d = db || getDatabase();
5252
+ const result = d.run("DELETE FROM project_sources WHERE id = ?", [id]);
5253
+ return result.changes > 0;
5254
+ }
5255
+ function listProjectSources(projectId, db) {
5256
+ const d = db || getDatabase();
5257
+ const rows = d.query("SELECT * FROM project_sources WHERE project_id = ? ORDER BY name").all(projectId);
5258
+ return rows.map(rowToSource);
5259
+ }
5070
5260
  function nextTaskShortId(projectId, db) {
5071
5261
  const d = db || getDatabase();
5072
5262
  const project = getProject(projectId, d);
@@ -5331,6 +5521,52 @@ function nextOccurrence(rule, from) {
5331
5521
  // src/db/tasks.ts
5332
5522
  init_webhooks();
5333
5523
  init_templates();
5524
+
5525
+ // src/db/checklists.ts
5526
+ init_database();
5527
+ function rowToItem(row) {
5528
+ return { ...row, checked: !!row.checked };
5529
+ }
5530
+ function getChecklist(taskId, db) {
5531
+ const d = db || getDatabase();
5532
+ const rows = d.query("SELECT * FROM task_checklists WHERE task_id = ? ORDER BY position, created_at").all(taskId);
5533
+ return rows.map(rowToItem);
5534
+ }
5535
+ function addChecklistItem(input, db) {
5536
+ const d = db || getDatabase();
5537
+ const id = uuid();
5538
+ const timestamp = now();
5539
+ let position = input.position;
5540
+ if (position === undefined) {
5541
+ const maxRow = d.query("SELECT MAX(position) as max_pos FROM task_checklists WHERE task_id = ?").get(input.task_id);
5542
+ position = (maxRow?.max_pos ?? -1) + 1;
5543
+ }
5544
+ d.run("INSERT INTO task_checklists (id, task_id, position, text, checked, created_at, updated_at) VALUES (?, ?, ?, ?, 0, ?, ?)", [id, input.task_id, position, input.text, timestamp, timestamp]);
5545
+ return rowToItem(d.query("SELECT * FROM task_checklists WHERE id = ?").get(id));
5546
+ }
5547
+ function checkChecklistItem(id, checked, db) {
5548
+ const d = db || getDatabase();
5549
+ const timestamp = now();
5550
+ const result = d.run("UPDATE task_checklists SET checked = ?, updated_at = ? WHERE id = ?", [checked ? 1 : 0, timestamp, id]);
5551
+ if (result.changes === 0)
5552
+ return null;
5553
+ return rowToItem(d.query("SELECT * FROM task_checklists WHERE id = ?").get(id));
5554
+ }
5555
+ function updateChecklistItemText(id, text, db) {
5556
+ const d = db || getDatabase();
5557
+ const timestamp = now();
5558
+ const result = d.run("UPDATE task_checklists SET text = ?, updated_at = ? WHERE id = ?", [text, timestamp, id]);
5559
+ if (result.changes === 0)
5560
+ return null;
5561
+ return rowToItem(d.query("SELECT * FROM task_checklists WHERE id = ?").get(id));
5562
+ }
5563
+ function removeChecklistItem(id, db) {
5564
+ const d = db || getDatabase();
5565
+ const result = d.run("DELETE FROM task_checklists WHERE id = ?", [id]);
5566
+ return result.changes > 0;
5567
+ }
5568
+
5569
+ // src/db/tasks.ts
5334
5570
  function rowToTask(row) {
5335
5571
  return {
5336
5572
  ...row,
@@ -5423,13 +5659,15 @@ function getTaskWithRelations(id, db) {
5423
5659
  const blocked_by = blockedByRows.map(rowToTask);
5424
5660
  const comments = d.query("SELECT * FROM task_comments WHERE task_id = ? ORDER BY created_at").all(id);
5425
5661
  const parent = task.parent_id ? getTask(task.parent_id, d) : null;
5662
+ const checklist = getChecklist(id, d);
5426
5663
  return {
5427
5664
  ...task,
5428
5665
  subtasks,
5429
5666
  dependencies,
5430
5667
  blocked_by,
5431
5668
  comments,
5432
- parent
5669
+ parent,
5670
+ checklist
5433
5671
  };
5434
5672
  }
5435
5673
  function listTasks(filter = {}, db) {
@@ -7450,6 +7688,14 @@ Comments (${task.comments.length}):`);
7450
7688
  parts.push(`
7451
7689
  Parent: ${task.parent.id.slice(0, 8)} | ${task.parent.title}`);
7452
7690
  }
7691
+ if (task.checklist.length > 0) {
7692
+ const done = task.checklist.filter((i) => i.checked).length;
7693
+ parts.push(`
7694
+ Checklist (${done}/${task.checklist.length}):`);
7695
+ for (const item of task.checklist) {
7696
+ parts.push(` ${item.position + 1}. [${item.checked ? "x" : " "}] ${item.text} (${item.id.slice(0, 8)})`);
7697
+ }
7698
+ }
7453
7699
  return { content: [{ type: "text", text: parts.join(`
7454
7700
  `) }] };
7455
7701
  } catch (e) {
@@ -7679,6 +7925,171 @@ if (shouldRegisterTool("create_project")) {
7679
7925
  }
7680
7926
  });
7681
7927
  }
7928
+ if (shouldRegisterTool("add_project_source")) {
7929
+ server.tool("add_project_source", "Add a data source to a project (S3 bucket, Google Drive folder, local path, GitHub repo, Notion page, etc.). Sources are revealed to agents when they load the project.", {
7930
+ project_id: exports_external.string().describe("Project ID"),
7931
+ type: exports_external.string().describe("Source type: 's3', 'gdrive', 'local', 'github', 'notion', 'http', or any custom label"),
7932
+ name: exports_external.string().describe("Human-readable label for this source"),
7933
+ uri: exports_external.string().describe("The source URI (bucket path, folder URL, local path, repo URL, etc.)"),
7934
+ description: exports_external.string().optional().describe("What this source contains or how agents should use it"),
7935
+ metadata: exports_external.record(exports_external.unknown()).optional().describe("Extra config (e.g. region, access role, subfolder)")
7936
+ }, async (params) => {
7937
+ try {
7938
+ const resolvedProjectId = resolveId(params.project_id, "projects");
7939
+ const source = addProjectSource({ ...params, project_id: resolvedProjectId });
7940
+ return {
7941
+ content: [{
7942
+ type: "text",
7943
+ text: `Source added: ${source.id.slice(0, 8)} | [${source.type}] ${source.name} \u2192 ${source.uri}`
7944
+ }]
7945
+ };
7946
+ } catch (e) {
7947
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
7948
+ }
7949
+ });
7950
+ }
7951
+ if (shouldRegisterTool("remove_project_source")) {
7952
+ server.tool("remove_project_source", "Remove a data source from a project by source ID.", {
7953
+ source_id: exports_external.string().describe("Source ID to remove")
7954
+ }, async ({ source_id }) => {
7955
+ try {
7956
+ const db = getDatabase();
7957
+ const row = db.query("SELECT * FROM project_sources WHERE id LIKE ?").get(`${source_id}%`);
7958
+ if (!row)
7959
+ return { content: [{ type: "text", text: `Source not found: ${source_id}` }], isError: true };
7960
+ removeProjectSource(row.id);
7961
+ return { content: [{ type: "text", text: `Source removed: ${row.name}` }] };
7962
+ } catch (e) {
7963
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
7964
+ }
7965
+ });
7966
+ }
7967
+ if (shouldRegisterTool("list_project_sources")) {
7968
+ server.tool("list_project_sources", "List all data sources attached to a project.", {
7969
+ project_id: exports_external.string().describe("Project ID")
7970
+ }, async ({ project_id }) => {
7971
+ try {
7972
+ const resolvedId = resolveId(project_id, "projects");
7973
+ const sources = listProjectSources(resolvedId);
7974
+ if (sources.length === 0) {
7975
+ return { content: [{ type: "text", text: "No sources configured for this project." }] };
7976
+ }
7977
+ const lines = sources.map((s) => `${s.id.slice(0, 8)} | [${s.type}] ${s.name} \u2192 ${s.uri}${s.description ? `
7978
+ ${s.description}` : ""}`);
7979
+ return { content: [{ type: "text", text: `${sources.length} source(s):
7980
+ ${lines.join(`
7981
+ `)}` }] };
7982
+ } catch (e) {
7983
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
7984
+ }
7985
+ });
7986
+ }
7987
+ if (shouldRegisterTool("add_checklist_item")) {
7988
+ server.tool("add_checklist_item", "Add a checklist item to a task. Items are numbered and individually checkable.", {
7989
+ task_id: exports_external.string().describe("Task ID"),
7990
+ text: exports_external.string().describe("Checklist item text"),
7991
+ position: exports_external.number().optional().describe("Position (0-based). Appended to end if omitted.")
7992
+ }, async ({ task_id, text, position }) => {
7993
+ try {
7994
+ const resolvedId = resolveId(task_id, "tasks");
7995
+ const item = addChecklistItem({ task_id: resolvedId, text, position });
7996
+ return {
7997
+ content: [{
7998
+ type: "text",
7999
+ text: `Item added: ${item.position + 1}. [ ] ${item.text} (${item.id.slice(0, 8)})`
8000
+ }]
8001
+ };
8002
+ } catch (e) {
8003
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
8004
+ }
8005
+ });
8006
+ }
8007
+ if (shouldRegisterTool("check_checklist_item")) {
8008
+ server.tool("check_checklist_item", "Mark a checklist item as checked or unchecked.", {
8009
+ item_id: exports_external.string().describe("Checklist item ID or prefix"),
8010
+ checked: exports_external.boolean().describe("true to check, false to uncheck")
8011
+ }, async ({ item_id, checked }) => {
8012
+ try {
8013
+ const db = getDatabase();
8014
+ const row = db.query("SELECT id FROM task_checklists WHERE id LIKE ?").get(`${item_id}%`);
8015
+ if (!row)
8016
+ return { content: [{ type: "text", text: `Checklist item not found: ${item_id}` }], isError: true };
8017
+ const item = checkChecklistItem(row.id, checked);
8018
+ if (!item)
8019
+ return { content: [{ type: "text", text: "Update failed" }], isError: true };
8020
+ return {
8021
+ content: [{
8022
+ type: "text",
8023
+ text: `${item.position + 1}. [${item.checked ? "x" : " "}] ${item.text}`
8024
+ }]
8025
+ };
8026
+ } catch (e) {
8027
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
8028
+ }
8029
+ });
8030
+ }
8031
+ if (shouldRegisterTool("update_checklist_item")) {
8032
+ server.tool("update_checklist_item", "Update the text of a checklist item.", {
8033
+ item_id: exports_external.string().describe("Checklist item ID or prefix"),
8034
+ text: exports_external.string().describe("New text")
8035
+ }, async ({ item_id, text }) => {
8036
+ try {
8037
+ const db = getDatabase();
8038
+ const row = db.query("SELECT id FROM task_checklists WHERE id LIKE ?").get(`${item_id}%`);
8039
+ if (!row)
8040
+ return { content: [{ type: "text", text: `Checklist item not found: ${item_id}` }], isError: true };
8041
+ const item = updateChecklistItemText(row.id, text);
8042
+ if (!item)
8043
+ return { content: [{ type: "text", text: "Update failed" }], isError: true };
8044
+ return {
8045
+ content: [{
8046
+ type: "text",
8047
+ text: `Updated: ${item.position + 1}. [${item.checked ? "x" : " "}] ${item.text}`
8048
+ }]
8049
+ };
8050
+ } catch (e) {
8051
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
8052
+ }
8053
+ });
8054
+ }
8055
+ if (shouldRegisterTool("remove_checklist_item")) {
8056
+ server.tool("remove_checklist_item", "Remove a checklist item from a task.", {
8057
+ item_id: exports_external.string().describe("Checklist item ID or prefix")
8058
+ }, async ({ item_id }) => {
8059
+ try {
8060
+ const db = getDatabase();
8061
+ const row = db.query("SELECT id, text FROM task_checklists WHERE id LIKE ?").get(`${item_id}%`);
8062
+ if (!row)
8063
+ return { content: [{ type: "text", text: `Checklist item not found: ${item_id}` }], isError: true };
8064
+ removeChecklistItem(row.id);
8065
+ return { content: [{ type: "text", text: `Removed: ${row.text}` }] };
8066
+ } catch (e) {
8067
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
8068
+ }
8069
+ });
8070
+ }
8071
+ if (shouldRegisterTool("get_checklist")) {
8072
+ server.tool("get_checklist", "Get all checklist items for a task with progress summary.", {
8073
+ task_id: exports_external.string().describe("Task ID")
8074
+ }, async ({ task_id }) => {
8075
+ try {
8076
+ const resolvedId = resolveId(task_id, "tasks");
8077
+ const items = getChecklist(resolvedId);
8078
+ if (items.length === 0) {
8079
+ return { content: [{ type: "text", text: "No checklist items." }] };
8080
+ }
8081
+ const done = items.filter((i) => i.checked).length;
8082
+ const lines = [`Checklist (${done}/${items.length} done):`];
8083
+ for (const item of items) {
8084
+ lines.push(` ${item.position + 1}. [${item.checked ? "x" : " "}] ${item.text} (${item.id.slice(0, 8)})`);
8085
+ }
8086
+ return { content: [{ type: "text", text: lines.join(`
8087
+ `) }] };
8088
+ } catch (e) {
8089
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
8090
+ }
8091
+ });
8092
+ }
7682
8093
  if (shouldRegisterTool("create_plan")) {
7683
8094
  server.tool("create_plan", "Create a plan to group related tasks.", {
7684
8095
  name: exports_external.string(),
@@ -8995,6 +9406,19 @@ if (shouldRegisterTool("bootstrap")) {
8995
9406
  lines.push(`Other agents active: ${others.slice(0, 3).map((w) => `${w.short_id || w.id.slice(0, 8)} (${w.assigned_to || "?"})`).join(", ")}`);
8996
9407
  }
8997
9408
  }
9409
+ if (project_id) {
9410
+ const resolvedId = resolveId(project_id, "projects");
9411
+ const sources = listProjectSources(resolvedId);
9412
+ if (sources.length > 0) {
9413
+ lines.push("");
9414
+ lines.push(`## Data Sources`);
9415
+ for (const s of sources) {
9416
+ lines.push(`[${s.type}] ${s.name}: ${s.uri}${s.description ? ` \u2014 ${s.description}` : ""}`);
9417
+ }
9418
+ }
9419
+ }
9420
+ lines.push(`
9421
+ as_of: ${new Date().toISOString()}`);
8998
9422
  return { content: [{ type: "text", text: lines.join(`
8999
9423
  `) }] };
9000
9424
  } catch (e) {
@@ -9045,6 +9469,14 @@ if (shouldRegisterTool("search_tools")) {
9045
9469
  "log_progress",
9046
9470
  "create_project",
9047
9471
  "list_projects",
9472
+ "add_project_source",
9473
+ "remove_project_source",
9474
+ "list_project_sources",
9475
+ "add_checklist_item",
9476
+ "check_checklist_item",
9477
+ "update_checklist_item",
9478
+ "remove_checklist_item",
9479
+ "get_checklist",
9048
9480
  "create_plan",
9049
9481
  "list_plans",
9050
9482
  "get_plan",
@@ -9153,6 +9585,30 @@ if (shouldRegisterTool("describe_tools")) {
9153
9585
  Params: name(string, req), path(string, req \u2014 unique absolute path), description(string), task_list_id(string)
9154
9586
  Example: {name: 'my-app', path: '/Users/dev/my-app'}`,
9155
9587
  list_projects: "List all registered projects. No params.",
9588
+ add_checklist_item: `Add a checklist item (numbered sub-step) to a task.
9589
+ Params: task_id(string, req), text(string, req), position(number \u2014 0-based, appended to end if omitted)
9590
+ Example: {task_id: 'a1b2c3d4', text: 'Cancel Slack subscription'}`,
9591
+ check_checklist_item: `Mark a checklist item checked or unchecked.
9592
+ Params: item_id(string, req \u2014 item ID or prefix), checked(boolean, req)
9593
+ Example: {item_id: 'abc12345', checked: true}`,
9594
+ update_checklist_item: `Update the text of a checklist item.
9595
+ Params: item_id(string, req), text(string, req)
9596
+ Example: {item_id: 'abc12345', text: 'Cancel GitHub subscription'}`,
9597
+ remove_checklist_item: `Remove a checklist item permanently.
9598
+ Params: item_id(string, req)
9599
+ Example: {item_id: 'abc12345'}`,
9600
+ get_checklist: `Get all checklist items for a task with progress (done/total).
9601
+ Params: task_id(string, req)
9602
+ Example: {task_id: 'a1b2c3d4'}`,
9603
+ add_project_source: `Add a data source to a project (S3, GDrive, local path, GitHub, Notion, HTTP, etc.).
9604
+ Params: project_id(string, req), type(string, req \u2014 e.g. 's3','gdrive','local','github','notion','http'), name(string, req), uri(string, req), description(string), metadata(object)
9605
+ Example: {project_id: 'a1b2c3d4', type: 's3', name: 'Assets bucket', uri: 's3://my-bucket/assets/', description: 'Project media files'}`,
9606
+ remove_project_source: `Remove a data source from a project.
9607
+ Params: source_id(string, req \u2014 source ID or prefix)
9608
+ Example: {source_id: 'abc12345'}`,
9609
+ list_project_sources: `List all data sources for a project.
9610
+ Params: project_id(string, req)
9611
+ Example: {project_id: 'a1b2c3d4'}`,
9156
9612
  create_plan: `Create a plan to group related tasks.
9157
9613
  Params: name(string, req), project_id(string), description(string), status(active|completed|archived, default:active), task_list_id(string), agent_id(string)
9158
9614
  Example: {name: 'Sprint 1', project_id: 'a1b2c3d4'}`,
@@ -9313,6 +9769,62 @@ server.resource("agents", "todos://agents", { description: "All registered agent
9313
9769
  const agents = listAgents();
9314
9770
  return { contents: [{ uri: "todos://agents", text: JSON.stringify(agents, null, 2), mimeType: "application/json" }] };
9315
9771
  });
9772
+ if (shouldRegisterTool("create_handoff")) {
9773
+ server.tool("create_handoff", "Create a session handoff note for agent coordination.", {
9774
+ agent_id: exports_external.string().optional().describe("Agent creating the handoff"),
9775
+ project_id: exports_external.string().optional().describe("Project ID"),
9776
+ summary: exports_external.string().describe("What was accomplished this session"),
9777
+ completed: exports_external.array(exports_external.string()).optional().describe("Items completed"),
9778
+ in_progress: exports_external.array(exports_external.string()).optional().describe("Items still in progress"),
9779
+ blockers: exports_external.array(exports_external.string()).optional().describe("Blocking issues"),
9780
+ next_steps: exports_external.array(exports_external.string()).optional().describe("Recommended next actions")
9781
+ }, async ({ agent_id, project_id, summary, completed, in_progress, blockers, next_steps }) => {
9782
+ try {
9783
+ const { createHandoff: createHandoff2 } = (init_handoffs(), __toCommonJS(exports_handoffs));
9784
+ const handoff = createHandoff2({
9785
+ agent_id,
9786
+ project_id: project_id ? resolveId(project_id, "projects") : undefined,
9787
+ summary,
9788
+ completed,
9789
+ in_progress,
9790
+ blockers,
9791
+ next_steps
9792
+ });
9793
+ return { content: [{ type: "text", text: `Handoff created: ${handoff.id.slice(0, 8)} by ${handoff.agent_id || "unknown"}` }] };
9794
+ } catch (e) {
9795
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
9796
+ }
9797
+ });
9798
+ }
9799
+ if (shouldRegisterTool("get_latest_handoff")) {
9800
+ server.tool("get_latest_handoff", "Get the most recent handoff for an agent or project.", {
9801
+ agent_id: exports_external.string().optional().describe("Filter by agent"),
9802
+ project_id: exports_external.string().optional().describe("Filter by project")
9803
+ }, async ({ agent_id, project_id }) => {
9804
+ try {
9805
+ const { getLatestHandoff: getLatestHandoff2 } = (init_handoffs(), __toCommonJS(exports_handoffs));
9806
+ const handoff = getLatestHandoff2(agent_id, project_id ? resolveId(project_id, "projects") : undefined);
9807
+ if (!handoff)
9808
+ return { content: [{ type: "text", text: "No handoffs found." }] };
9809
+ const lines = [
9810
+ `${handoff.created_at.slice(0, 16)} ${handoff.agent_id || "unknown"}`,
9811
+ handoff.summary
9812
+ ];
9813
+ if (handoff.completed?.length)
9814
+ lines.push(`Done: ${handoff.completed.join(", ")}`);
9815
+ if (handoff.in_progress?.length)
9816
+ lines.push(`In progress: ${handoff.in_progress.join(", ")}`);
9817
+ if (handoff.blockers?.length)
9818
+ lines.push(`Blocked: ${handoff.blockers.join(", ")}`);
9819
+ if (handoff.next_steps?.length)
9820
+ lines.push(`Next: ${handoff.next_steps.join(", ")}`);
9821
+ return { content: [{ type: "text", text: lines.join(`
9822
+ `) }] };
9823
+ } catch (e) {
9824
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
9825
+ }
9826
+ });
9827
+ }
9316
9828
  server.resource("task-lists", "todos://task-lists", { description: "All task lists", mimeType: "application/json" }, async () => {
9317
9829
  const lists = listTaskLists();
9318
9830
  return { contents: [{ uri: "todos://task-lists", text: JSON.stringify(lists, null, 2), mimeType: "application/json" }] };