@hasna/todos 0.10.18 → 0.10.19

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
@@ -10040,6 +10040,7 @@ __export(exports_task_files, {
10040
10040
  listTaskFiles: () => listTaskFiles,
10041
10041
  listActiveFiles: () => listActiveFiles,
10042
10042
  getTaskFile: () => getTaskFile,
10043
+ getFileHeatMap: () => getFileHeatMap,
10043
10044
  findTasksByFile: () => findTasksByFile,
10044
10045
  detectFileConflicts: () => detectFileConflicts,
10045
10046
  bulkFindTasksByFiles: () => bulkFindTasksByFiles,
@@ -10152,6 +10153,36 @@ function listActiveFiles(db) {
10152
10153
  ORDER BY tf.updated_at DESC
10153
10154
  `).all();
10154
10155
  }
10156
+ function getFileHeatMap(opts, db) {
10157
+ const d = db || getDatabase();
10158
+ const limit = opts?.limit ?? 20;
10159
+ const minEdits = opts?.min_edits ?? 1;
10160
+ const rows = d.query(`
10161
+ SELECT
10162
+ tf.path,
10163
+ COUNT(*) AS edit_count,
10164
+ COUNT(DISTINCT COALESCE(tf.agent_id, t.assigned_to)) AS unique_agents,
10165
+ GROUP_CONCAT(DISTINCT COALESCE(tf.agent_id, t.assigned_to)) AS agent_ids,
10166
+ MAX(tf.updated_at) AS last_edited_at,
10167
+ SUM(CASE WHEN t.status = 'in_progress' THEN 1 ELSE 0 END) AS active_task_count
10168
+ FROM task_files tf
10169
+ JOIN tasks t ON tf.task_id = t.id
10170
+ WHERE tf.status != 'removed'
10171
+ ${opts?.project_id ? `AND t.project_id = '${opts.project_id}'` : ""}
10172
+ GROUP BY tf.path
10173
+ HAVING edit_count >= ${minEdits}
10174
+ ORDER BY edit_count DESC, last_edited_at DESC
10175
+ LIMIT ${limit}
10176
+ `).all();
10177
+ return rows.map((r) => ({
10178
+ path: r.path,
10179
+ edit_count: r.edit_count,
10180
+ unique_agents: r.unique_agents,
10181
+ agent_ids: r.agent_ids ? r.agent_ids.split(",").filter(Boolean) : [],
10182
+ last_edited_at: r.last_edited_at,
10183
+ active_task_count: r.active_task_count
10184
+ }));
10185
+ }
10155
10186
  function bulkAddTaskFiles(taskId, paths, agentId, db) {
10156
10187
  const d = db || getDatabase();
10157
10188
  const results = [];
@@ -13826,6 +13857,22 @@ ${lines.join(`
13826
13857
  }
13827
13858
  });
13828
13859
  }
13860
+ if (shouldRegisterTool("get_file_heat_map")) {
13861
+ server.tool("get_file_heat_map", "Aggregate file edit frequency across all tasks and agents. Returns hottest files with edit count, unique agents, and last edit. Hot files = high coordination risk, good candidates for extra test coverage.", {
13862
+ limit: exports_external.number().optional().describe("Max files to return (default: 20)"),
13863
+ project_id: exports_external.string().optional().describe("Filter to a specific project"),
13864
+ min_edits: exports_external.number().optional().describe("Minimum edit count to include (default: 1)")
13865
+ }, async ({ limit, project_id, min_edits }) => {
13866
+ try {
13867
+ const { getFileHeatMap: getFileHeatMap2 } = (init_task_files(), __toCommonJS(exports_task_files));
13868
+ const resolvedProjectId = project_id ? resolveId(project_id, "projects") : undefined;
13869
+ const results = getFileHeatMap2({ limit, project_id: resolvedProjectId, min_edits });
13870
+ return { content: [{ type: "text", text: JSON.stringify(results, null, 2) }] };
13871
+ } catch (e) {
13872
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
13873
+ }
13874
+ });
13875
+ }
13829
13876
  if (shouldRegisterTool("bulk_find_tasks_by_files")) {
13830
13877
  server.tool("bulk_find_tasks_by_files", "Check multiple file paths at once for task/agent collisions. Returns per-path task list, in-progress count, and conflict flag.", {
13831
13878
  paths: exports_external.array(exports_external.string()).describe("Array of file paths to check")
@@ -57,5 +57,18 @@ export interface ActiveFileInfo {
57
57
  agent_name: string | null;
58
58
  }
59
59
  export declare function listActiveFiles(db?: Database): ActiveFileInfo[];
60
+ export interface FileHeatMapEntry {
61
+ path: string;
62
+ edit_count: number;
63
+ unique_agents: number;
64
+ agent_ids: string[];
65
+ last_edited_at: string;
66
+ active_task_count: number;
67
+ }
68
+ export declare function getFileHeatMap(opts?: {
69
+ limit?: number;
70
+ project_id?: string;
71
+ min_edits?: number;
72
+ }, db?: Database): FileHeatMapEntry[];
60
73
  export declare function bulkAddTaskFiles(taskId: string, paths: string[], agentId?: string, db?: Database): TaskFile[];
61
74
  //# sourceMappingURL=task-files.d.ts.map
@@ -1 +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,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,mBAAmB,EAAE,MAAM,CAAC;IAC5B,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,sBAAsB,EAAE,MAAM,CAAC;IAC/B,uBAAuB,EAAE,MAAM,CAAC;CACjC;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,YAAY,EAAE,CAkBlG;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,YAAY,EAAE,OAAO,CAAC;IACtB,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,cAAc,EAAE,CA6BrF;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,QAAQ,CAAC,QAAQ,CAAC,CAAC;IAChC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,wBAAgB,eAAe,CAAC,EAAE,CAAC,EAAE,QAAQ,GAAG,cAAc,EAAE,CAwB/D;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"}
1
+ {"version":3,"file":"task-files.d.ts","sourceRoot":"","sources":["../../src/db/task-files.ts"],"names":[],"mappings":"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"}
package/dist/mcp/index.js CHANGED
@@ -3091,6 +3091,7 @@ __export(exports_task_files, {
3091
3091
  listTaskFiles: () => listTaskFiles,
3092
3092
  listActiveFiles: () => listActiveFiles,
3093
3093
  getTaskFile: () => getTaskFile,
3094
+ getFileHeatMap: () => getFileHeatMap,
3094
3095
  findTasksByFile: () => findTasksByFile,
3095
3096
  detectFileConflicts: () => detectFileConflicts,
3096
3097
  bulkFindTasksByFiles: () => bulkFindTasksByFiles,
@@ -3203,6 +3204,36 @@ function listActiveFiles(db) {
3203
3204
  ORDER BY tf.updated_at DESC
3204
3205
  `).all();
3205
3206
  }
3207
+ function getFileHeatMap(opts, db) {
3208
+ const d = db || getDatabase();
3209
+ const limit = opts?.limit ?? 20;
3210
+ const minEdits = opts?.min_edits ?? 1;
3211
+ const rows = d.query(`
3212
+ SELECT
3213
+ tf.path,
3214
+ COUNT(*) AS edit_count,
3215
+ COUNT(DISTINCT COALESCE(tf.agent_id, t.assigned_to)) AS unique_agents,
3216
+ GROUP_CONCAT(DISTINCT COALESCE(tf.agent_id, t.assigned_to)) AS agent_ids,
3217
+ MAX(tf.updated_at) AS last_edited_at,
3218
+ SUM(CASE WHEN t.status = 'in_progress' THEN 1 ELSE 0 END) AS active_task_count
3219
+ FROM task_files tf
3220
+ JOIN tasks t ON tf.task_id = t.id
3221
+ WHERE tf.status != 'removed'
3222
+ ${opts?.project_id ? `AND t.project_id = '${opts.project_id}'` : ""}
3223
+ GROUP BY tf.path
3224
+ HAVING edit_count >= ${minEdits}
3225
+ ORDER BY edit_count DESC, last_edited_at DESC
3226
+ LIMIT ${limit}
3227
+ `).all();
3228
+ return rows.map((r) => ({
3229
+ path: r.path,
3230
+ edit_count: r.edit_count,
3231
+ unique_agents: r.unique_agents,
3232
+ agent_ids: r.agent_ids ? r.agent_ids.split(",").filter(Boolean) : [],
3233
+ last_edited_at: r.last_edited_at,
3234
+ active_task_count: r.active_task_count
3235
+ }));
3236
+ }
3206
3237
  function bulkAddTaskFiles(taskId, paths, agentId, db) {
3207
3238
  const d = db || getDatabase();
3208
3239
  const results = [];
@@ -11638,6 +11669,22 @@ ${lines.join(`
11638
11669
  }
11639
11670
  });
11640
11671
  }
11672
+ if (shouldRegisterTool("get_file_heat_map")) {
11673
+ server.tool("get_file_heat_map", "Aggregate file edit frequency across all tasks and agents. Returns hottest files with edit count, unique agents, and last edit. Hot files = high coordination risk, good candidates for extra test coverage.", {
11674
+ limit: exports_external.number().optional().describe("Max files to return (default: 20)"),
11675
+ project_id: exports_external.string().optional().describe("Filter to a specific project"),
11676
+ min_edits: exports_external.number().optional().describe("Minimum edit count to include (default: 1)")
11677
+ }, async ({ limit, project_id, min_edits }) => {
11678
+ try {
11679
+ const { getFileHeatMap: getFileHeatMap2 } = (init_task_files(), __toCommonJS(exports_task_files));
11680
+ const resolvedProjectId = project_id ? resolveId(project_id, "projects") : undefined;
11681
+ const results = getFileHeatMap2({ limit, project_id: resolvedProjectId, min_edits });
11682
+ return { content: [{ type: "text", text: JSON.stringify(results, null, 2) }] };
11683
+ } catch (e) {
11684
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
11685
+ }
11686
+ });
11687
+ }
11641
11688
  if (shouldRegisterTool("bulk_find_tasks_by_files")) {
11642
11689
  server.tool("bulk_find_tasks_by_files", "Check multiple file paths at once for task/agent collisions. Returns per-path task list, in-progress count, and conflict flag.", {
11643
11690
  paths: exports_external.array(exports_external.string()).describe("Array of file paths to check")
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/todos",
3
- "version": "0.10.18",
3
+ "version": "0.10.19",
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",