@hasna/todos 0.9.79 → 0.9.81

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
@@ -2776,6 +2776,22 @@ var init_database = __esm(() => {
2776
2776
  CREATE INDEX IF NOT EXISTS idx_resource_locks_type_id ON resource_locks(resource_type, resource_id);
2777
2777
  CREATE INDEX IF NOT EXISTS idx_resource_locks_agent ON resource_locks(agent_id);
2778
2778
  INSERT OR IGNORE INTO _migrations (id) VALUES (24);
2779
+ `,
2780
+ `
2781
+ CREATE TABLE IF NOT EXISTS task_files (
2782
+ id TEXT PRIMARY KEY,
2783
+ task_id TEXT NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
2784
+ path TEXT NOT NULL,
2785
+ status TEXT NOT NULL DEFAULT 'active',
2786
+ agent_id TEXT,
2787
+ note TEXT,
2788
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
2789
+ updated_at TEXT NOT NULL DEFAULT (datetime('now'))
2790
+ );
2791
+ CREATE INDEX IF NOT EXISTS idx_task_files_task ON task_files(task_id);
2792
+ CREATE INDEX IF NOT EXISTS idx_task_files_path ON task_files(path);
2793
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_task_files_task_path ON task_files(task_id, path);
2794
+ INSERT OR IGNORE INTO _migrations (id) VALUES (25);
2779
2795
  `
2780
2796
  ];
2781
2797
  });
@@ -9585,6 +9601,69 @@ var init_zod = __esm(() => {
9585
9601
  init_external();
9586
9602
  });
9587
9603
 
9604
+ // src/db/task-files.ts
9605
+ var exports_task_files = {};
9606
+ __export(exports_task_files, {
9607
+ updateTaskFileStatus: () => updateTaskFileStatus,
9608
+ removeTaskFile: () => removeTaskFile,
9609
+ listTaskFiles: () => listTaskFiles,
9610
+ getTaskFile: () => getTaskFile,
9611
+ findTasksByFile: () => findTasksByFile,
9612
+ bulkAddTaskFiles: () => bulkAddTaskFiles,
9613
+ addTaskFile: () => addTaskFile
9614
+ });
9615
+ function addTaskFile(input, db) {
9616
+ const d = db || getDatabase();
9617
+ const id = uuid();
9618
+ const timestamp = now();
9619
+ const existing = d.query("SELECT id FROM task_files WHERE task_id = ? AND path = ?").get(input.task_id, input.path);
9620
+ if (existing) {
9621
+ d.run("UPDATE task_files SET status = ?, agent_id = ?, note = ?, updated_at = ? WHERE id = ?", [input.status || "active", input.agent_id || null, input.note || null, timestamp, existing.id]);
9622
+ return getTaskFile(existing.id, d);
9623
+ }
9624
+ d.run(`INSERT INTO task_files (id, task_id, path, status, agent_id, note, created_at, updated_at)
9625
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, [id, input.task_id, input.path, input.status || "active", input.agent_id || null, input.note || null, timestamp, timestamp]);
9626
+ return getTaskFile(id, d);
9627
+ }
9628
+ function getTaskFile(id, db) {
9629
+ const d = db || getDatabase();
9630
+ return d.query("SELECT * FROM task_files WHERE id = ?").get(id);
9631
+ }
9632
+ function listTaskFiles(taskId, db) {
9633
+ const d = db || getDatabase();
9634
+ return d.query("SELECT * FROM task_files WHERE task_id = ? ORDER BY path").all(taskId);
9635
+ }
9636
+ function findTasksByFile(path, db) {
9637
+ const d = db || getDatabase();
9638
+ return d.query("SELECT * FROM task_files WHERE path = ? AND status != 'removed' ORDER BY updated_at DESC").all(path);
9639
+ }
9640
+ function updateTaskFileStatus(taskId, path, status, agentId, db) {
9641
+ const d = db || getDatabase();
9642
+ const timestamp = now();
9643
+ d.run("UPDATE task_files SET status = ?, agent_id = COALESCE(?, agent_id), updated_at = ? WHERE task_id = ? AND path = ?", [status, agentId || null, timestamp, taskId, path]);
9644
+ const row = d.query("SELECT * FROM task_files WHERE task_id = ? AND path = ?").get(taskId, path);
9645
+ return row;
9646
+ }
9647
+ function removeTaskFile(taskId, path, db) {
9648
+ const d = db || getDatabase();
9649
+ const result = d.run("DELETE FROM task_files WHERE task_id = ? AND path = ?", [taskId, path]);
9650
+ return result.changes > 0;
9651
+ }
9652
+ function bulkAddTaskFiles(taskId, paths, agentId, db) {
9653
+ const d = db || getDatabase();
9654
+ const results = [];
9655
+ const tx = d.transaction(() => {
9656
+ for (const path of paths) {
9657
+ results.push(addTaskFile({ task_id: taskId, path, agent_id: agentId }, d));
9658
+ }
9659
+ });
9660
+ tx();
9661
+ return results;
9662
+ }
9663
+ var init_task_files = __esm(() => {
9664
+ init_database();
9665
+ });
9666
+
9588
9667
  // src/db/handoffs.ts
9589
9668
  var exports_handoffs = {};
9590
9669
  __export(exports_handoffs, {
@@ -9658,6 +9737,9 @@ var init_handoffs = __esm(() => {
9658
9737
 
9659
9738
  // src/mcp/index.ts
9660
9739
  var exports_mcp = {};
9740
+ __export(exports_mcp, {
9741
+ applyFocus: () => applyFocus
9742
+ });
9661
9743
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
9662
9744
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
9663
9745
  import { readFileSync as readFileSync3 } from "fs";
@@ -9679,6 +9761,28 @@ function shouldRegisterTool(name) {
9679
9761
  return !STANDARD_EXCLUDED.has(name);
9680
9762
  return true;
9681
9763
  }
9764
+ function getAgentFocus(agentId) {
9765
+ const sessionFocus = agentFocusMap.get(agentId);
9766
+ if (sessionFocus)
9767
+ return sessionFocus;
9768
+ try {
9769
+ const agent = getAgentByName(agentId) || getAgent(agentId);
9770
+ if (agent && agent.active_project_id) {
9771
+ return { agent_id: agentId, project_id: agent.active_project_id };
9772
+ }
9773
+ } catch {}
9774
+ return;
9775
+ }
9776
+ function applyFocus(params, agentId) {
9777
+ if (!agentId)
9778
+ return;
9779
+ if (params.project_id)
9780
+ return;
9781
+ const focus = getAgentFocus(agentId);
9782
+ if (focus?.project_id) {
9783
+ params.project_id = focus.project_id;
9784
+ }
9785
+ }
9682
9786
  function formatError(error) {
9683
9787
  if (error instanceof VersionConflictError) {
9684
9788
  return JSON.stringify({ code: VersionConflictError.code, message: error.message, suggestion: VersionConflictError.suggestion });
@@ -9777,7 +9881,7 @@ async function main() {
9777
9881
  const transport = new StdioServerTransport;
9778
9882
  await server.connect(transport);
9779
9883
  }
9780
- var server, TODOS_PROFILE, MINIMAL_TOOLS, STANDARD_EXCLUDED;
9884
+ var server, TODOS_PROFILE, MINIMAL_TOOLS, STANDARD_EXCLUDED, agentFocusMap;
9781
9885
  var init_mcp = __esm(() => {
9782
9886
  init_zod();
9783
9887
  init_tasks();
@@ -9824,6 +9928,7 @@ var init_mcp = __esm(() => {
9824
9928
  "delete_template",
9825
9929
  "approve_task"
9826
9930
  ]);
9931
+ agentFocusMap = new Map;
9827
9932
  if (shouldRegisterTool("create_task")) {
9828
9933
  server.tool("create_task", "Create a new task", {
9829
9934
  title: exports_external.string(),
@@ -10602,6 +10707,52 @@ ${text}` }] };
10602
10707
  }
10603
10708
  });
10604
10709
  }
10710
+ if (shouldRegisterTool("set_focus")) {
10711
+ server.tool("set_focus", "Focus this agent on a project. All list/search/status tools will default to this project.", {
10712
+ agent_id: exports_external.string().describe("Agent ID or name"),
10713
+ project_id: exports_external.string().optional().describe("Project to focus on. Omit to clear."),
10714
+ task_list_id: exports_external.string().optional().describe("Task list to focus on")
10715
+ }, async ({ agent_id, project_id, task_list_id }) => {
10716
+ try {
10717
+ const resolvedProject = project_id ? resolveId(project_id, "projects") : undefined;
10718
+ const focus = { agent_id, project_id: resolvedProject, task_list_id };
10719
+ agentFocusMap.set(agent_id, focus);
10720
+ try {
10721
+ const agent = getAgentByName(agent_id) || getAgent(agent_id);
10722
+ if (agent) {
10723
+ const db = getDatabase();
10724
+ db.run("UPDATE agents SET active_project_id = ? WHERE id = ?", [resolvedProject || null, agent.id]);
10725
+ }
10726
+ } catch {}
10727
+ const projectName = resolvedProject ? ` (${resolvedProject.slice(0, 8)})` : "";
10728
+ return { content: [{ type: "text", text: `Focused on project${projectName}. Read tools will default to this scope. Pass explicit project_id to override.` }] };
10729
+ } catch (e) {
10730
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
10731
+ }
10732
+ });
10733
+ }
10734
+ if (shouldRegisterTool("get_focus")) {
10735
+ server.tool("get_focus", "Get the current focus for an agent.", { agent_id: exports_external.string().describe("Agent ID or name") }, async ({ agent_id }) => {
10736
+ const focus = getAgentFocus(agent_id);
10737
+ if (!focus?.project_id) {
10738
+ return { content: [{ type: "text", text: "No focus set. Showing all projects." }] };
10739
+ }
10740
+ return { content: [{ type: "text", text: `Focused on project: ${focus.project_id}${focus.task_list_id ? `, task list: ${focus.task_list_id}` : ""}` }] };
10741
+ });
10742
+ }
10743
+ if (shouldRegisterTool("unfocus")) {
10744
+ server.tool("unfocus", "Clear focus \u2014 show all projects and tasks.", { agent_id: exports_external.string().describe("Agent ID or name") }, async ({ agent_id }) => {
10745
+ agentFocusMap.delete(agent_id);
10746
+ try {
10747
+ const agent = getAgentByName(agent_id) || getAgent(agent_id);
10748
+ if (agent) {
10749
+ const db = getDatabase();
10750
+ db.run("UPDATE agents SET active_project_id = NULL WHERE id = ?", [agent.id]);
10751
+ }
10752
+ } catch {}
10753
+ return { content: [{ type: "text", text: "Focus cleared. Showing all projects." }] };
10754
+ });
10755
+ }
10605
10756
  if (shouldRegisterTool("register_agent")) {
10606
10757
  server.tool("register_agent", "Register an agent. Pass session_id (unique per coding session) to prevent name conflicts. Returns conflict error if name is taken by an active agent in a different session.", {
10607
10758
  name: exports_external.string(),
@@ -12115,6 +12266,63 @@ Claimed: ${formatTask(result.claimed)}`);
12115
12266
  const agents = listAgents();
12116
12267
  return { contents: [{ uri: "todos://agents", text: JSON.stringify(agents, null, 2), mimeType: "application/json" }] };
12117
12268
  });
12269
+ if (shouldRegisterTool("add_task_file")) {
12270
+ server.tool("add_task_file", "Link a file path to a task. Tracks which files an agent is working on. Upserts if same task+path exists.", {
12271
+ task_id: exports_external.string().describe("Task ID"),
12272
+ path: exports_external.string().describe("File path (relative or absolute)"),
12273
+ paths: exports_external.array(exports_external.string()).optional().describe("Multiple file paths to add at once"),
12274
+ status: exports_external.enum(["planned", "active", "modified", "reviewed", "removed"]).optional().describe("File status (default: active)"),
12275
+ agent_id: exports_external.string().optional().describe("Agent working on this file"),
12276
+ note: exports_external.string().optional().describe("Note about why this file is linked")
12277
+ }, async ({ task_id, path, paths: multiplePaths, status, agent_id, note }) => {
12278
+ try {
12279
+ const { addTaskFile: addTaskFile2, bulkAddTaskFiles: bulkAddTaskFiles2 } = (init_task_files(), __toCommonJS(exports_task_files));
12280
+ const resolvedId = resolveId(task_id);
12281
+ if (multiplePaths && multiplePaths.length > 0) {
12282
+ const allPaths = path ? [path, ...multiplePaths] : multiplePaths;
12283
+ const files = bulkAddTaskFiles2(resolvedId, allPaths, agent_id);
12284
+ return { content: [{ type: "text", text: `${files.length} file(s) linked to task ${resolvedId.slice(0, 8)}` }] };
12285
+ }
12286
+ const file = addTaskFile2({ task_id: resolvedId, path, status, agent_id, note });
12287
+ return { content: [{ type: "text", text: `${file.status} ${file.path} \u2192 task ${resolvedId.slice(0, 8)}` }] };
12288
+ } catch (e) {
12289
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
12290
+ }
12291
+ });
12292
+ }
12293
+ if (shouldRegisterTool("list_task_files")) {
12294
+ server.tool("list_task_files", "List all files linked to a task.", { task_id: exports_external.string().describe("Task ID") }, async ({ task_id }) => {
12295
+ try {
12296
+ const { listTaskFiles: listTaskFiles2 } = (init_task_files(), __toCommonJS(exports_task_files));
12297
+ const resolvedId = resolveId(task_id);
12298
+ const files = listTaskFiles2(resolvedId);
12299
+ if (files.length === 0)
12300
+ return { content: [{ type: "text", text: "No files linked." }] };
12301
+ const lines = files.map((f) => `[${f.status}] ${f.path}${f.agent_id ? ` (${f.agent_id})` : ""}${f.note ? ` \u2014 ${f.note}` : ""}`);
12302
+ return { content: [{ type: "text", text: `${files.length} file(s):
12303
+ ${lines.join(`
12304
+ `)}` }] };
12305
+ } catch (e) {
12306
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
12307
+ }
12308
+ });
12309
+ }
12310
+ if (shouldRegisterTool("find_tasks_by_file")) {
12311
+ server.tool("find_tasks_by_file", "Find which tasks are linked to a specific file path. Shows who's working on what files.", { path: exports_external.string().describe("File path to search for") }, async ({ path }) => {
12312
+ try {
12313
+ const { findTasksByFile: findTasksByFile2 } = (init_task_files(), __toCommonJS(exports_task_files));
12314
+ const files = findTasksByFile2(path);
12315
+ if (files.length === 0)
12316
+ return { content: [{ type: "text", text: `No tasks linked to ${path}` }] };
12317
+ const lines = files.map((f) => `${f.task_id.slice(0, 8)} [${f.status}]${f.agent_id ? ` (${f.agent_id})` : ""}`);
12318
+ return { content: [{ type: "text", text: `${files.length} task(s) linked to ${path}:
12319
+ ${lines.join(`
12320
+ `)}` }] };
12321
+ } catch (e) {
12322
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
12323
+ }
12324
+ });
12325
+ }
12118
12326
  if (shouldRegisterTool("create_handoff")) {
12119
12327
  server.tool("create_handoff", "Create a session handoff note for agent coordination.", {
12120
12328
  agent_id: exports_external.string().optional().describe("Agent creating the handoff"),
@@ -1 +1 @@
1
- {"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../src/db/database.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAItC,eAAO,MAAM,mBAAmB,KAAK,CAAC;AAuctC,wBAAgB,WAAW,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,QAAQ,CAkBrD;AAgPD,wBAAgB,aAAa,IAAI,IAAI,CAKpC;AAED,wBAAgB,aAAa,IAAI,IAAI,CAEpC;AAED,wBAAgB,GAAG,IAAI,MAAM,CAE5B;AAED,wBAAgB,IAAI,IAAI,MAAM,CAE7B;AAED,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAK9D;AAED,wBAAgB,gBAAgB,CAAC,KAAK,SAAa,GAAG,MAAM,CAG3D;AAED,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,QAAQ,GAAG,IAAI,CAGpD;AAED,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CA0B9F"}
1
+ {"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../src/db/database.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAItC,eAAO,MAAM,mBAAmB,KAAK,CAAC;AAwdtC,wBAAgB,WAAW,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,QAAQ,CAkBrD;AAgPD,wBAAgB,aAAa,IAAI,IAAI,CAKpC;AAED,wBAAgB,aAAa,IAAI,IAAI,CAEpC;AAED,wBAAgB,GAAG,IAAI,MAAM,CAE5B;AAED,wBAAgB,IAAI,IAAI,MAAM,CAE7B;AAED,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAK9D;AAED,wBAAgB,gBAAgB,CAAC,KAAK,SAAa,GAAG,MAAM,CAG3D;AAED,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,QAAQ,GAAG,IAAI,CAGpD;AAED,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CA0B9F"}
@@ -0,0 +1,26 @@
1
+ import type { Database } from "bun:sqlite";
2
+ export interface TaskFile {
3
+ id: string;
4
+ task_id: string;
5
+ path: string;
6
+ status: "planned" | "active" | "modified" | "reviewed" | "removed";
7
+ agent_id: string | null;
8
+ note: string | null;
9
+ created_at: string;
10
+ updated_at: string;
11
+ }
12
+ export interface AddTaskFileInput {
13
+ task_id: string;
14
+ path: string;
15
+ status?: TaskFile["status"];
16
+ agent_id?: string;
17
+ note?: string;
18
+ }
19
+ export declare function addTaskFile(input: AddTaskFileInput, db?: Database): TaskFile;
20
+ export declare function getTaskFile(id: string, db?: Database): TaskFile | null;
21
+ export declare function listTaskFiles(taskId: string, db?: Database): TaskFile[];
22
+ export declare function findTasksByFile(path: string, db?: Database): TaskFile[];
23
+ export declare function updateTaskFileStatus(taskId: string, path: string, status: TaskFile["status"], agentId?: string, db?: Database): TaskFile | null;
24
+ export declare function removeTaskFile(taskId: string, path: string, db?: Database): boolean;
25
+ export declare function bulkAddTaskFiles(taskId: string, paths: string[], agentId?: string, db?: Database): TaskFile[];
26
+ //# sourceMappingURL=task-files.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"task-files.d.ts","sourceRoot":"","sources":["../../src/db/task-files.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAG3C,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,SAAS,GAAG,QAAQ,GAAG,UAAU,GAAG,UAAU,GAAG,SAAS,CAAC;IACnE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,QAAQ,CAAC,QAAQ,CAAC,CAAC;IAC5B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,gBAAgB,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,QAAQ,CAwB5E;AAED,wBAAgB,WAAW,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,QAAQ,GAAG,IAAI,CAGtE;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,QAAQ,EAAE,CAKvE;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,QAAQ,EAAE,CAKvE;AAED,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,QAAQ,CAAC,QAAQ,CAAC,EAC1B,OAAO,CAAC,EAAE,MAAM,EAChB,EAAE,CAAC,EAAE,QAAQ,GACZ,QAAQ,GAAG,IAAI,CAWjB;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,OAAO,CAOnF;AAED,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,EAAE,EACf,OAAO,CAAC,EAAE,MAAM,EAChB,EAAE,CAAC,EAAE,QAAQ,GACZ,QAAQ,EAAE,CAUZ"}
package/dist/index.d.ts CHANGED
@@ -16,6 +16,8 @@ export { createTemplate, getTemplate, listTemplates, deleteTemplate, taskFromTem
16
16
  export { getChecklist, addChecklistItem, checkChecklistItem, updateChecklistItemText, removeChecklistItem, clearChecklist, getChecklistStats, } from "./db/checklists.js";
17
17
  export { createHandoff, listHandoffs, getLatestHandoff } from "./db/handoffs.js";
18
18
  export type { Handoff, CreateHandoffInput } from "./db/handoffs.js";
19
+ export { addTaskFile, getTaskFile, listTaskFiles, findTasksByFile, updateTaskFileStatus, removeTaskFile, bulkAddTaskFiles } from "./db/task-files.js";
20
+ export type { TaskFile, AddTaskFileInput } from "./db/task-files.js";
19
21
  export { acquireLock, releaseLock, checkLock, cleanExpiredLocks } from "./db/locks.js";
20
22
  export type { ResourceLock } from "./db/locks.js";
21
23
  export { createOrg, getOrg, getOrgByName, listOrgs, updateOrg, deleteOrg } from "./db/orgs.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -637,6 +637,22 @@ var MIGRATIONS = [
637
637
  CREATE INDEX IF NOT EXISTS idx_resource_locks_type_id ON resource_locks(resource_type, resource_id);
638
638
  CREATE INDEX IF NOT EXISTS idx_resource_locks_agent ON resource_locks(agent_id);
639
639
  INSERT OR IGNORE INTO _migrations (id) VALUES (24);
640
+ `,
641
+ `
642
+ CREATE TABLE IF NOT EXISTS task_files (
643
+ id TEXT PRIMARY KEY,
644
+ task_id TEXT NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
645
+ path TEXT NOT NULL,
646
+ status TEXT NOT NULL DEFAULT 'active',
647
+ agent_id TEXT,
648
+ note TEXT,
649
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
650
+ updated_at TEXT NOT NULL DEFAULT (datetime('now'))
651
+ );
652
+ CREATE INDEX IF NOT EXISTS idx_task_files_task ON task_files(task_id);
653
+ CREATE INDEX IF NOT EXISTS idx_task_files_path ON task_files(path);
654
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_task_files_task_path ON task_files(task_id, path);
655
+ INSERT OR IGNORE INTO _migrations (id) VALUES (25);
640
656
  `
641
657
  ];
642
658
  var _db = null;
@@ -3075,6 +3091,55 @@ function getLatestHandoff(agentId, projectId, db) {
3075
3091
  const row = d.query(query).get(...params);
3076
3092
  return row ? rowToHandoff(row) : null;
3077
3093
  }
3094
+ // src/db/task-files.ts
3095
+ function addTaskFile(input, db) {
3096
+ const d = db || getDatabase();
3097
+ const id = uuid();
3098
+ const timestamp = now();
3099
+ const existing = d.query("SELECT id FROM task_files WHERE task_id = ? AND path = ?").get(input.task_id, input.path);
3100
+ if (existing) {
3101
+ d.run("UPDATE task_files SET status = ?, agent_id = ?, note = ?, updated_at = ? WHERE id = ?", [input.status || "active", input.agent_id || null, input.note || null, timestamp, existing.id]);
3102
+ return getTaskFile(existing.id, d);
3103
+ }
3104
+ d.run(`INSERT INTO task_files (id, task_id, path, status, agent_id, note, created_at, updated_at)
3105
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, [id, input.task_id, input.path, input.status || "active", input.agent_id || null, input.note || null, timestamp, timestamp]);
3106
+ return getTaskFile(id, d);
3107
+ }
3108
+ function getTaskFile(id, db) {
3109
+ const d = db || getDatabase();
3110
+ return d.query("SELECT * FROM task_files WHERE id = ?").get(id);
3111
+ }
3112
+ function listTaskFiles(taskId, db) {
3113
+ const d = db || getDatabase();
3114
+ return d.query("SELECT * FROM task_files WHERE task_id = ? ORDER BY path").all(taskId);
3115
+ }
3116
+ function findTasksByFile(path, db) {
3117
+ const d = db || getDatabase();
3118
+ return d.query("SELECT * FROM task_files WHERE path = ? AND status != 'removed' ORDER BY updated_at DESC").all(path);
3119
+ }
3120
+ function updateTaskFileStatus(taskId, path, status, agentId, db) {
3121
+ const d = db || getDatabase();
3122
+ const timestamp = now();
3123
+ d.run("UPDATE task_files SET status = ?, agent_id = COALESCE(?, agent_id), updated_at = ? WHERE task_id = ? AND path = ?", [status, agentId || null, timestamp, taskId, path]);
3124
+ const row = d.query("SELECT * FROM task_files WHERE task_id = ? AND path = ?").get(taskId, path);
3125
+ return row;
3126
+ }
3127
+ function removeTaskFile(taskId, path, db) {
3128
+ const d = db || getDatabase();
3129
+ const result = d.run("DELETE FROM task_files WHERE task_id = ? AND path = ?", [taskId, path]);
3130
+ return result.changes > 0;
3131
+ }
3132
+ function bulkAddTaskFiles(taskId, paths, agentId, db) {
3133
+ const d = db || getDatabase();
3134
+ const results = [];
3135
+ const tx = d.transaction(() => {
3136
+ for (const path of paths) {
3137
+ results.push(addTaskFile({ task_id: taskId, path, agent_id: agentId }, d));
3138
+ }
3139
+ });
3140
+ tx();
3141
+ return results;
3142
+ }
3078
3143
  // src/db/locks.ts
3079
3144
  function acquireLock(resourceType, resourceId, agentId, lockType = "advisory", expiryMs = 5 * 60 * 1000, db) {
3080
3145
  const d = db || getDatabase();
@@ -3785,6 +3850,7 @@ function syncWithAgents(agents, taskListIdByAgent, projectId, direction = "both"
3785
3850
  export {
3786
3851
  uuid,
3787
3852
  updateTaskList,
3853
+ updateTaskFileStatus,
3788
3854
  updateTask,
3789
3855
  updateSessionActivity,
3790
3856
  updateProject,
@@ -3804,6 +3870,7 @@ export {
3804
3870
  searchTasks,
3805
3871
  resolvePartialId,
3806
3872
  resetDatabase,
3873
+ removeTaskFile,
3807
3874
  removeProjectSource,
3808
3875
  removeDependency,
3809
3876
  removeChecklistItem,
@@ -3823,6 +3890,7 @@ export {
3823
3890
  listTemplates,
3824
3891
  listTasks,
3825
3892
  listTaskLists,
3893
+ listTaskFiles,
3826
3894
  listSessions,
3827
3895
  listProjects,
3828
3896
  listProjectSources,
@@ -3842,6 +3910,7 @@ export {
3842
3910
  getTaskList,
3843
3911
  getTaskHistory,
3844
3912
  getTaskGraph,
3913
+ getTaskFile,
3845
3914
  getTaskDependents,
3846
3915
  getTaskDependencies,
3847
3916
  getTask,
@@ -3869,6 +3938,7 @@ export {
3869
3938
  getAgentByName,
3870
3939
  getAgent,
3871
3940
  getActiveWork,
3941
+ findTasksByFile,
3872
3942
  failTask,
3873
3943
  ensureTaskList,
3874
3944
  ensureProject,
@@ -3907,6 +3977,8 @@ export {
3907
3977
  checkChecklistItem,
3908
3978
  bulkUpdateTasks,
3909
3979
  bulkCreateTasks,
3980
+ bulkAddTaskFiles,
3981
+ addTaskFile,
3910
3982
  addProjectSource,
3911
3983
  addDependency,
3912
3984
  addComment,
@@ -1,3 +1,3 @@
1
1
  #!/usr/bin/env bun
2
- export {};
2
+ export declare function applyFocus(params: Record<string, any>, agentId?: string): void;
3
3
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/mcp/index.ts"],"names":[],"mappings":""}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/mcp/index.ts"],"names":[],"mappings":";AA6IA,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAO9E"}
package/dist/mcp/index.js CHANGED
@@ -705,6 +705,22 @@ var init_database = __esm(() => {
705
705
  CREATE INDEX IF NOT EXISTS idx_resource_locks_type_id ON resource_locks(resource_type, resource_id);
706
706
  CREATE INDEX IF NOT EXISTS idx_resource_locks_agent ON resource_locks(agent_id);
707
707
  INSERT OR IGNORE INTO _migrations (id) VALUES (24);
708
+ `,
709
+ `
710
+ CREATE TABLE IF NOT EXISTS task_files (
711
+ id TEXT PRIMARY KEY,
712
+ task_id TEXT NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
713
+ path TEXT NOT NULL,
714
+ status TEXT NOT NULL DEFAULT 'active',
715
+ agent_id TEXT,
716
+ note TEXT,
717
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
718
+ updated_at TEXT NOT NULL DEFAULT (datetime('now'))
719
+ );
720
+ CREATE INDEX IF NOT EXISTS idx_task_files_task ON task_files(task_id);
721
+ CREATE INDEX IF NOT EXISTS idx_task_files_path ON task_files(path);
722
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_task_files_task_path ON task_files(task_id, path);
723
+ INSERT OR IGNORE INTO _migrations (id) VALUES (25);
708
724
  `
709
725
  ];
710
726
  });
@@ -1036,6 +1052,69 @@ var init_agents = __esm(() => {
1036
1052
  AGENT_ACTIVE_WINDOW_MS = 30 * 60 * 1000;
1037
1053
  });
1038
1054
 
1055
+ // src/db/task-files.ts
1056
+ var exports_task_files = {};
1057
+ __export(exports_task_files, {
1058
+ updateTaskFileStatus: () => updateTaskFileStatus,
1059
+ removeTaskFile: () => removeTaskFile,
1060
+ listTaskFiles: () => listTaskFiles,
1061
+ getTaskFile: () => getTaskFile,
1062
+ findTasksByFile: () => findTasksByFile,
1063
+ bulkAddTaskFiles: () => bulkAddTaskFiles,
1064
+ addTaskFile: () => addTaskFile
1065
+ });
1066
+ function addTaskFile(input, db) {
1067
+ const d = db || getDatabase();
1068
+ const id = uuid();
1069
+ const timestamp = now();
1070
+ const existing = d.query("SELECT id FROM task_files WHERE task_id = ? AND path = ?").get(input.task_id, input.path);
1071
+ if (existing) {
1072
+ d.run("UPDATE task_files SET status = ?, agent_id = ?, note = ?, updated_at = ? WHERE id = ?", [input.status || "active", input.agent_id || null, input.note || null, timestamp, existing.id]);
1073
+ return getTaskFile(existing.id, d);
1074
+ }
1075
+ d.run(`INSERT INTO task_files (id, task_id, path, status, agent_id, note, created_at, updated_at)
1076
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, [id, input.task_id, input.path, input.status || "active", input.agent_id || null, input.note || null, timestamp, timestamp]);
1077
+ return getTaskFile(id, d);
1078
+ }
1079
+ function getTaskFile(id, db) {
1080
+ const d = db || getDatabase();
1081
+ return d.query("SELECT * FROM task_files WHERE id = ?").get(id);
1082
+ }
1083
+ function listTaskFiles(taskId, db) {
1084
+ const d = db || getDatabase();
1085
+ return d.query("SELECT * FROM task_files WHERE task_id = ? ORDER BY path").all(taskId);
1086
+ }
1087
+ function findTasksByFile(path, db) {
1088
+ const d = db || getDatabase();
1089
+ return d.query("SELECT * FROM task_files WHERE path = ? AND status != 'removed' ORDER BY updated_at DESC").all(path);
1090
+ }
1091
+ function updateTaskFileStatus(taskId, path, status, agentId, db) {
1092
+ const d = db || getDatabase();
1093
+ const timestamp = now();
1094
+ d.run("UPDATE task_files SET status = ?, agent_id = COALESCE(?, agent_id), updated_at = ? WHERE task_id = ? AND path = ?", [status, agentId || null, timestamp, taskId, path]);
1095
+ const row = d.query("SELECT * FROM task_files WHERE task_id = ? AND path = ?").get(taskId, path);
1096
+ return row;
1097
+ }
1098
+ function removeTaskFile(taskId, path, db) {
1099
+ const d = db || getDatabase();
1100
+ const result = d.run("DELETE FROM task_files WHERE task_id = ? AND path = ?", [taskId, path]);
1101
+ return result.changes > 0;
1102
+ }
1103
+ function bulkAddTaskFiles(taskId, paths, agentId, db) {
1104
+ const d = db || getDatabase();
1105
+ const results = [];
1106
+ const tx = d.transaction(() => {
1107
+ for (const path of paths) {
1108
+ results.push(addTaskFile({ task_id: taskId, path, agent_id: agentId }, d));
1109
+ }
1110
+ });
1111
+ tx();
1112
+ return results;
1113
+ }
1114
+ var init_task_files = __esm(() => {
1115
+ init_database();
1116
+ });
1117
+
1039
1118
  // src/db/handoffs.ts
1040
1119
  var exports_handoffs = {};
1041
1120
  __export(exports_handoffs, {
@@ -7447,6 +7526,29 @@ function shouldRegisterTool(name) {
7447
7526
  return !STANDARD_EXCLUDED.has(name);
7448
7527
  return true;
7449
7528
  }
7529
+ var agentFocusMap = new Map;
7530
+ function getAgentFocus(agentId) {
7531
+ const sessionFocus = agentFocusMap.get(agentId);
7532
+ if (sessionFocus)
7533
+ return sessionFocus;
7534
+ try {
7535
+ const agent = getAgentByName(agentId) || getAgent(agentId);
7536
+ if (agent && agent.active_project_id) {
7537
+ return { agent_id: agentId, project_id: agent.active_project_id };
7538
+ }
7539
+ } catch {}
7540
+ return;
7541
+ }
7542
+ function applyFocus(params, agentId) {
7543
+ if (!agentId)
7544
+ return;
7545
+ if (params.project_id)
7546
+ return;
7547
+ const focus = getAgentFocus(agentId);
7548
+ if (focus?.project_id) {
7549
+ params.project_id = focus.project_id;
7550
+ }
7551
+ }
7450
7552
  function formatError(error) {
7451
7553
  if (error instanceof VersionConflictError) {
7452
7554
  return JSON.stringify({ code: VersionConflictError.code, message: error.message, suggestion: VersionConflictError.suggestion });
@@ -8319,6 +8421,52 @@ if (shouldRegisterTool("sync")) {
8319
8421
  }
8320
8422
  });
8321
8423
  }
8424
+ if (shouldRegisterTool("set_focus")) {
8425
+ server.tool("set_focus", "Focus this agent on a project. All list/search/status tools will default to this project.", {
8426
+ agent_id: exports_external.string().describe("Agent ID or name"),
8427
+ project_id: exports_external.string().optional().describe("Project to focus on. Omit to clear."),
8428
+ task_list_id: exports_external.string().optional().describe("Task list to focus on")
8429
+ }, async ({ agent_id, project_id, task_list_id }) => {
8430
+ try {
8431
+ const resolvedProject = project_id ? resolveId(project_id, "projects") : undefined;
8432
+ const focus = { agent_id, project_id: resolvedProject, task_list_id };
8433
+ agentFocusMap.set(agent_id, focus);
8434
+ try {
8435
+ const agent = getAgentByName(agent_id) || getAgent(agent_id);
8436
+ if (agent) {
8437
+ const db = getDatabase();
8438
+ db.run("UPDATE agents SET active_project_id = ? WHERE id = ?", [resolvedProject || null, agent.id]);
8439
+ }
8440
+ } catch {}
8441
+ const projectName = resolvedProject ? ` (${resolvedProject.slice(0, 8)})` : "";
8442
+ return { content: [{ type: "text", text: `Focused on project${projectName}. Read tools will default to this scope. Pass explicit project_id to override.` }] };
8443
+ } catch (e) {
8444
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
8445
+ }
8446
+ });
8447
+ }
8448
+ if (shouldRegisterTool("get_focus")) {
8449
+ server.tool("get_focus", "Get the current focus for an agent.", { agent_id: exports_external.string().describe("Agent ID or name") }, async ({ agent_id }) => {
8450
+ const focus = getAgentFocus(agent_id);
8451
+ if (!focus?.project_id) {
8452
+ return { content: [{ type: "text", text: "No focus set. Showing all projects." }] };
8453
+ }
8454
+ return { content: [{ type: "text", text: `Focused on project: ${focus.project_id}${focus.task_list_id ? `, task list: ${focus.task_list_id}` : ""}` }] };
8455
+ });
8456
+ }
8457
+ if (shouldRegisterTool("unfocus")) {
8458
+ server.tool("unfocus", "Clear focus \u2014 show all projects and tasks.", { agent_id: exports_external.string().describe("Agent ID or name") }, async ({ agent_id }) => {
8459
+ agentFocusMap.delete(agent_id);
8460
+ try {
8461
+ const agent = getAgentByName(agent_id) || getAgent(agent_id);
8462
+ if (agent) {
8463
+ const db = getDatabase();
8464
+ db.run("UPDATE agents SET active_project_id = NULL WHERE id = ?", [agent.id]);
8465
+ }
8466
+ } catch {}
8467
+ return { content: [{ type: "text", text: "Focus cleared. Showing all projects." }] };
8468
+ });
8469
+ }
8322
8470
  if (shouldRegisterTool("register_agent")) {
8323
8471
  server.tool("register_agent", "Register an agent. Pass session_id (unique per coding session) to prevent name conflicts. Returns conflict error if name is taken by an active agent in a different session.", {
8324
8472
  name: exports_external.string(),
@@ -9832,6 +9980,63 @@ server.resource("agents", "todos://agents", { description: "All registered agent
9832
9980
  const agents = listAgents();
9833
9981
  return { contents: [{ uri: "todos://agents", text: JSON.stringify(agents, null, 2), mimeType: "application/json" }] };
9834
9982
  });
9983
+ if (shouldRegisterTool("add_task_file")) {
9984
+ server.tool("add_task_file", "Link a file path to a task. Tracks which files an agent is working on. Upserts if same task+path exists.", {
9985
+ task_id: exports_external.string().describe("Task ID"),
9986
+ path: exports_external.string().describe("File path (relative or absolute)"),
9987
+ paths: exports_external.array(exports_external.string()).optional().describe("Multiple file paths to add at once"),
9988
+ status: exports_external.enum(["planned", "active", "modified", "reviewed", "removed"]).optional().describe("File status (default: active)"),
9989
+ agent_id: exports_external.string().optional().describe("Agent working on this file"),
9990
+ note: exports_external.string().optional().describe("Note about why this file is linked")
9991
+ }, async ({ task_id, path, paths: multiplePaths, status, agent_id, note }) => {
9992
+ try {
9993
+ const { addTaskFile: addTaskFile2, bulkAddTaskFiles: bulkAddTaskFiles2 } = (init_task_files(), __toCommonJS(exports_task_files));
9994
+ const resolvedId = resolveId(task_id);
9995
+ if (multiplePaths && multiplePaths.length > 0) {
9996
+ const allPaths = path ? [path, ...multiplePaths] : multiplePaths;
9997
+ const files = bulkAddTaskFiles2(resolvedId, allPaths, agent_id);
9998
+ return { content: [{ type: "text", text: `${files.length} file(s) linked to task ${resolvedId.slice(0, 8)}` }] };
9999
+ }
10000
+ const file = addTaskFile2({ task_id: resolvedId, path, status, agent_id, note });
10001
+ return { content: [{ type: "text", text: `${file.status} ${file.path} \u2192 task ${resolvedId.slice(0, 8)}` }] };
10002
+ } catch (e) {
10003
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
10004
+ }
10005
+ });
10006
+ }
10007
+ if (shouldRegisterTool("list_task_files")) {
10008
+ server.tool("list_task_files", "List all files linked to a task.", { task_id: exports_external.string().describe("Task ID") }, async ({ task_id }) => {
10009
+ try {
10010
+ const { listTaskFiles: listTaskFiles2 } = (init_task_files(), __toCommonJS(exports_task_files));
10011
+ const resolvedId = resolveId(task_id);
10012
+ const files = listTaskFiles2(resolvedId);
10013
+ if (files.length === 0)
10014
+ return { content: [{ type: "text", text: "No files linked." }] };
10015
+ const lines = files.map((f) => `[${f.status}] ${f.path}${f.agent_id ? ` (${f.agent_id})` : ""}${f.note ? ` \u2014 ${f.note}` : ""}`);
10016
+ return { content: [{ type: "text", text: `${files.length} file(s):
10017
+ ${lines.join(`
10018
+ `)}` }] };
10019
+ } catch (e) {
10020
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
10021
+ }
10022
+ });
10023
+ }
10024
+ if (shouldRegisterTool("find_tasks_by_file")) {
10025
+ server.tool("find_tasks_by_file", "Find which tasks are linked to a specific file path. Shows who's working on what files.", { path: exports_external.string().describe("File path to search for") }, async ({ path }) => {
10026
+ try {
10027
+ const { findTasksByFile: findTasksByFile2 } = (init_task_files(), __toCommonJS(exports_task_files));
10028
+ const files = findTasksByFile2(path);
10029
+ if (files.length === 0)
10030
+ return { content: [{ type: "text", text: `No tasks linked to ${path}` }] };
10031
+ const lines = files.map((f) => `${f.task_id.slice(0, 8)} [${f.status}]${f.agent_id ? ` (${f.agent_id})` : ""}`);
10032
+ return { content: [{ type: "text", text: `${files.length} task(s) linked to ${path}:
10033
+ ${lines.join(`
10034
+ `)}` }] };
10035
+ } catch (e) {
10036
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
10037
+ }
10038
+ });
10039
+ }
9835
10040
  if (shouldRegisterTool("create_handoff")) {
9836
10041
  server.tool("create_handoff", "Create a session handoff note for agent coordination.", {
9837
10042
  agent_id: exports_external.string().optional().describe("Agent creating the handoff"),
@@ -9900,3 +10105,6 @@ main().catch((err) => {
9900
10105
  console.error("MCP server error:", err);
9901
10106
  process.exit(1);
9902
10107
  });
10108
+ export {
10109
+ applyFocus
10110
+ };
@@ -769,6 +769,22 @@ var init_database = __esm(() => {
769
769
  CREATE INDEX IF NOT EXISTS idx_resource_locks_type_id ON resource_locks(resource_type, resource_id);
770
770
  CREATE INDEX IF NOT EXISTS idx_resource_locks_agent ON resource_locks(agent_id);
771
771
  INSERT OR IGNORE INTO _migrations (id) VALUES (24);
772
+ `,
773
+ `
774
+ CREATE TABLE IF NOT EXISTS task_files (
775
+ id TEXT PRIMARY KEY,
776
+ task_id TEXT NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
777
+ path TEXT NOT NULL,
778
+ status TEXT NOT NULL DEFAULT 'active',
779
+ agent_id TEXT,
780
+ note TEXT,
781
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
782
+ updated_at TEXT NOT NULL DEFAULT (datetime('now'))
783
+ );
784
+ CREATE INDEX IF NOT EXISTS idx_task_files_task ON task_files(task_id);
785
+ CREATE INDEX IF NOT EXISTS idx_task_files_path ON task_files(path);
786
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_task_files_task_path ON task_files(task_id, path);
787
+ INSERT OR IGNORE INTO _migrations (id) VALUES (25);
772
788
  `
773
789
  ];
774
790
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/todos",
3
- "version": "0.9.79",
3
+ "version": "0.9.81",
4
4
  "description": "Universal task management for AI coding agents - CLI + MCP server + interactive TUI",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",