@hasna/todos 0.10.8 → 0.10.10
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 +90 -4
- package/dist/db/task-files.d.ts +19 -0
- package/dist/db/task-files.d.ts.map +1 -1
- package/dist/mcp/index.js +90 -4
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -9812,6 +9812,8 @@ __export(exports_task_files, {
|
|
|
9812
9812
|
listActiveFiles: () => listActiveFiles,
|
|
9813
9813
|
getTaskFile: () => getTaskFile,
|
|
9814
9814
|
findTasksByFile: () => findTasksByFile,
|
|
9815
|
+
detectFileConflicts: () => detectFileConflicts,
|
|
9816
|
+
bulkFindTasksByFiles: () => bulkFindTasksByFiles,
|
|
9815
9817
|
bulkAddTaskFiles: () => bulkAddTaskFiles,
|
|
9816
9818
|
addTaskFile: () => addTaskFile
|
|
9817
9819
|
});
|
|
@@ -9852,6 +9854,50 @@ function removeTaskFile(taskId, path, db) {
|
|
|
9852
9854
|
const result = d.run("DELETE FROM task_files WHERE task_id = ? AND path = ?", [taskId, path]);
|
|
9853
9855
|
return result.changes > 0;
|
|
9854
9856
|
}
|
|
9857
|
+
function detectFileConflicts(taskId, paths, db) {
|
|
9858
|
+
const d = db || getDatabase();
|
|
9859
|
+
if (paths.length === 0)
|
|
9860
|
+
return [];
|
|
9861
|
+
const placeholders = paths.map(() => "?").join(", ");
|
|
9862
|
+
const rows = d.query(`
|
|
9863
|
+
SELECT tf.path, tf.agent_id AS conflicting_agent_id, t.id AS conflicting_task_id,
|
|
9864
|
+
t.title AS conflicting_task_title, t.status AS conflicting_task_status
|
|
9865
|
+
FROM task_files tf
|
|
9866
|
+
JOIN tasks t ON tf.task_id = t.id
|
|
9867
|
+
WHERE tf.path IN (${placeholders})
|
|
9868
|
+
AND tf.task_id != ?
|
|
9869
|
+
AND tf.status != 'removed'
|
|
9870
|
+
AND t.status = 'in_progress'
|
|
9871
|
+
ORDER BY tf.updated_at DESC
|
|
9872
|
+
`).all(...paths, taskId);
|
|
9873
|
+
return rows;
|
|
9874
|
+
}
|
|
9875
|
+
function bulkFindTasksByFiles(paths, db) {
|
|
9876
|
+
const d = db || getDatabase();
|
|
9877
|
+
if (paths.length === 0)
|
|
9878
|
+
return [];
|
|
9879
|
+
const placeholders = paths.map(() => "?").join(", ");
|
|
9880
|
+
const rows = d.query(`SELECT tf.*, t.status AS task_status FROM task_files tf
|
|
9881
|
+
JOIN tasks t ON tf.task_id = t.id
|
|
9882
|
+
WHERE tf.path IN (${placeholders}) AND tf.status != 'removed'
|
|
9883
|
+
ORDER BY tf.updated_at DESC`).all(...paths);
|
|
9884
|
+
const byPath = new Map;
|
|
9885
|
+
for (const path of paths)
|
|
9886
|
+
byPath.set(path, []);
|
|
9887
|
+
for (const row of rows) {
|
|
9888
|
+
byPath.get(row.path)?.push(row);
|
|
9889
|
+
}
|
|
9890
|
+
return paths.map((path) => {
|
|
9891
|
+
const tasks = byPath.get(path) ?? [];
|
|
9892
|
+
const inProgressCount = tasks.filter((t) => t.task_status === "in_progress").length;
|
|
9893
|
+
return {
|
|
9894
|
+
path,
|
|
9895
|
+
tasks,
|
|
9896
|
+
has_conflict: inProgressCount > 1,
|
|
9897
|
+
in_progress_count: inProgressCount
|
|
9898
|
+
};
|
|
9899
|
+
});
|
|
9900
|
+
}
|
|
9855
9901
|
function listActiveFiles(db) {
|
|
9856
9902
|
const d = db || getDatabase();
|
|
9857
9903
|
return d.query(`
|
|
@@ -13262,7 +13308,7 @@ Claimed: ${formatTask(result.claimed)}`);
|
|
|
13262
13308
|
return { contents: [{ uri: "todos://agents", text: JSON.stringify(agents, null, 2), mimeType: "application/json" }] };
|
|
13263
13309
|
});
|
|
13264
13310
|
if (shouldRegisterTool("add_task_file")) {
|
|
13265
|
-
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.", {
|
|
13311
|
+
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. Auto-detects conflicts with other in-progress tasks.", {
|
|
13266
13312
|
task_id: exports_external.string().describe("Task ID"),
|
|
13267
13313
|
path: exports_external.string().describe("File path (relative or absolute)"),
|
|
13268
13314
|
paths: exports_external.array(exports_external.string()).optional().describe("Multiple file paths to add at once"),
|
|
@@ -13271,14 +13317,41 @@ Claimed: ${formatTask(result.claimed)}`);
|
|
|
13271
13317
|
note: exports_external.string().optional().describe("Note about why this file is linked")
|
|
13272
13318
|
}, async ({ task_id, path, paths: multiplePaths, status, agent_id, note }) => {
|
|
13273
13319
|
try {
|
|
13274
|
-
const { addTaskFile: addTaskFile2, bulkAddTaskFiles: bulkAddTaskFiles2 } = (init_task_files(), __toCommonJS(exports_task_files));
|
|
13320
|
+
const { addTaskFile: addTaskFile2, bulkAddTaskFiles: bulkAddTaskFiles2, detectFileConflicts: detectFileConflicts2 } = (init_task_files(), __toCommonJS(exports_task_files));
|
|
13275
13321
|
const resolvedId = resolveId(task_id);
|
|
13322
|
+
let addedFiles;
|
|
13276
13323
|
if (multiplePaths && multiplePaths.length > 0) {
|
|
13277
13324
|
const allPaths = path ? [path, ...multiplePaths] : multiplePaths;
|
|
13278
|
-
|
|
13279
|
-
|
|
13325
|
+
addedFiles = bulkAddTaskFiles2(resolvedId, allPaths, agent_id);
|
|
13326
|
+
const conflicts2 = detectFileConflicts2(resolvedId, allPaths);
|
|
13327
|
+
if (conflicts2.length > 0) {
|
|
13328
|
+
return {
|
|
13329
|
+
content: [{
|
|
13330
|
+
type: "text",
|
|
13331
|
+
text: JSON.stringify({
|
|
13332
|
+
added: addedFiles.length,
|
|
13333
|
+
conflicts: conflicts2,
|
|
13334
|
+
warning: `${conflicts2.length} file(s) already claimed by other in-progress tasks`
|
|
13335
|
+
}, null, 2)
|
|
13336
|
+
}]
|
|
13337
|
+
};
|
|
13338
|
+
}
|
|
13339
|
+
return { content: [{ type: "text", text: `${addedFiles.length} file(s) linked to task ${resolvedId.slice(0, 8)}` }] };
|
|
13280
13340
|
}
|
|
13281
13341
|
const file = addTaskFile2({ task_id: resolvedId, path, status, agent_id, note });
|
|
13342
|
+
const conflicts = detectFileConflicts2(resolvedId, [path]);
|
|
13343
|
+
if (conflicts.length > 0) {
|
|
13344
|
+
return {
|
|
13345
|
+
content: [{
|
|
13346
|
+
type: "text",
|
|
13347
|
+
text: JSON.stringify({
|
|
13348
|
+
file,
|
|
13349
|
+
conflicts,
|
|
13350
|
+
warning: `${path} is already claimed by another in-progress task`
|
|
13351
|
+
}, null, 2)
|
|
13352
|
+
}]
|
|
13353
|
+
};
|
|
13354
|
+
}
|
|
13282
13355
|
return { content: [{ type: "text", text: `${file.status} ${file.path} \u2192 task ${resolvedId.slice(0, 8)}` }] };
|
|
13283
13356
|
} catch (e) {
|
|
13284
13357
|
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
@@ -13318,6 +13391,19 @@ ${lines.join(`
|
|
|
13318
13391
|
}
|
|
13319
13392
|
});
|
|
13320
13393
|
}
|
|
13394
|
+
if (shouldRegisterTool("bulk_find_tasks_by_files")) {
|
|
13395
|
+
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.", {
|
|
13396
|
+
paths: exports_external.array(exports_external.string()).describe("Array of file paths to check")
|
|
13397
|
+
}, async ({ paths }) => {
|
|
13398
|
+
try {
|
|
13399
|
+
const { bulkFindTasksByFiles: bulkFindTasksByFiles2 } = (init_task_files(), __toCommonJS(exports_task_files));
|
|
13400
|
+
const results = bulkFindTasksByFiles2(paths);
|
|
13401
|
+
return { content: [{ type: "text", text: JSON.stringify(results, null, 2) }] };
|
|
13402
|
+
} catch (e) {
|
|
13403
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
13404
|
+
}
|
|
13405
|
+
});
|
|
13406
|
+
}
|
|
13321
13407
|
if (shouldRegisterTool("list_active_files")) {
|
|
13322
13408
|
server.tool("list_active_files", "Return all files linked to in-progress tasks across all agents \u2014 the bird's-eye view of what's being worked on right now.", {
|
|
13323
13409
|
project_id: exports_external.string().optional().describe("Filter by project")
|
package/dist/db/task-files.d.ts
CHANGED
|
@@ -22,6 +22,25 @@ export declare function listTaskFiles(taskId: string, db?: Database): TaskFile[]
|
|
|
22
22
|
export declare function findTasksByFile(path: string, db?: Database): TaskFile[];
|
|
23
23
|
export declare function updateTaskFileStatus(taskId: string, path: string, status: TaskFile["status"], agentId?: string, db?: Database): TaskFile | null;
|
|
24
24
|
export declare function removeTaskFile(taskId: string, path: string, db?: Database): boolean;
|
|
25
|
+
export interface FileConflict {
|
|
26
|
+
path: string;
|
|
27
|
+
conflicting_task_id: string;
|
|
28
|
+
conflicting_agent_id: string | null;
|
|
29
|
+
conflicting_task_title: string;
|
|
30
|
+
conflicting_task_status: string;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Check if adding a file to a task would conflict with another in-progress task.
|
|
34
|
+
* Returns conflicts (not a hard block — caller decides what to do).
|
|
35
|
+
*/
|
|
36
|
+
export declare function detectFileConflicts(taskId: string, paths: string[], db?: Database): FileConflict[];
|
|
37
|
+
export interface BulkFileResult {
|
|
38
|
+
path: string;
|
|
39
|
+
tasks: TaskFile[];
|
|
40
|
+
has_conflict: boolean;
|
|
41
|
+
in_progress_count: number;
|
|
42
|
+
}
|
|
43
|
+
export declare function bulkFindTasksByFiles(paths: string[], db?: Database): BulkFileResult[];
|
|
25
44
|
export interface ActiveFileInfo {
|
|
26
45
|
path: string;
|
|
27
46
|
file_status: TaskFile["status"];
|
|
@@ -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,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
|
@@ -1357,6 +1357,8 @@ __export(exports_task_files, {
|
|
|
1357
1357
|
listActiveFiles: () => listActiveFiles,
|
|
1358
1358
|
getTaskFile: () => getTaskFile,
|
|
1359
1359
|
findTasksByFile: () => findTasksByFile,
|
|
1360
|
+
detectFileConflicts: () => detectFileConflicts,
|
|
1361
|
+
bulkFindTasksByFiles: () => bulkFindTasksByFiles,
|
|
1360
1362
|
bulkAddTaskFiles: () => bulkAddTaskFiles,
|
|
1361
1363
|
addTaskFile: () => addTaskFile
|
|
1362
1364
|
});
|
|
@@ -1397,6 +1399,50 @@ function removeTaskFile(taskId, path, db) {
|
|
|
1397
1399
|
const result = d.run("DELETE FROM task_files WHERE task_id = ? AND path = ?", [taskId, path]);
|
|
1398
1400
|
return result.changes > 0;
|
|
1399
1401
|
}
|
|
1402
|
+
function detectFileConflicts(taskId, paths, db) {
|
|
1403
|
+
const d = db || getDatabase();
|
|
1404
|
+
if (paths.length === 0)
|
|
1405
|
+
return [];
|
|
1406
|
+
const placeholders = paths.map(() => "?").join(", ");
|
|
1407
|
+
const rows = d.query(`
|
|
1408
|
+
SELECT tf.path, tf.agent_id AS conflicting_agent_id, t.id AS conflicting_task_id,
|
|
1409
|
+
t.title AS conflicting_task_title, t.status AS conflicting_task_status
|
|
1410
|
+
FROM task_files tf
|
|
1411
|
+
JOIN tasks t ON tf.task_id = t.id
|
|
1412
|
+
WHERE tf.path IN (${placeholders})
|
|
1413
|
+
AND tf.task_id != ?
|
|
1414
|
+
AND tf.status != 'removed'
|
|
1415
|
+
AND t.status = 'in_progress'
|
|
1416
|
+
ORDER BY tf.updated_at DESC
|
|
1417
|
+
`).all(...paths, taskId);
|
|
1418
|
+
return rows;
|
|
1419
|
+
}
|
|
1420
|
+
function bulkFindTasksByFiles(paths, db) {
|
|
1421
|
+
const d = db || getDatabase();
|
|
1422
|
+
if (paths.length === 0)
|
|
1423
|
+
return [];
|
|
1424
|
+
const placeholders = paths.map(() => "?").join(", ");
|
|
1425
|
+
const rows = d.query(`SELECT tf.*, t.status AS task_status FROM task_files tf
|
|
1426
|
+
JOIN tasks t ON tf.task_id = t.id
|
|
1427
|
+
WHERE tf.path IN (${placeholders}) AND tf.status != 'removed'
|
|
1428
|
+
ORDER BY tf.updated_at DESC`).all(...paths);
|
|
1429
|
+
const byPath = new Map;
|
|
1430
|
+
for (const path of paths)
|
|
1431
|
+
byPath.set(path, []);
|
|
1432
|
+
for (const row of rows) {
|
|
1433
|
+
byPath.get(row.path)?.push(row);
|
|
1434
|
+
}
|
|
1435
|
+
return paths.map((path) => {
|
|
1436
|
+
const tasks = byPath.get(path) ?? [];
|
|
1437
|
+
const inProgressCount = tasks.filter((t) => t.task_status === "in_progress").length;
|
|
1438
|
+
return {
|
|
1439
|
+
path,
|
|
1440
|
+
tasks,
|
|
1441
|
+
has_conflict: inProgressCount > 1,
|
|
1442
|
+
in_progress_count: inProgressCount
|
|
1443
|
+
};
|
|
1444
|
+
});
|
|
1445
|
+
}
|
|
1400
1446
|
function listActiveFiles(db) {
|
|
1401
1447
|
const d = db || getDatabase();
|
|
1402
1448
|
return d.query(`
|
|
@@ -10990,7 +11036,7 @@ server.resource("agents", "todos://agents", { description: "All registered agent
|
|
|
10990
11036
|
return { contents: [{ uri: "todos://agents", text: JSON.stringify(agents, null, 2), mimeType: "application/json" }] };
|
|
10991
11037
|
});
|
|
10992
11038
|
if (shouldRegisterTool("add_task_file")) {
|
|
10993
|
-
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.", {
|
|
11039
|
+
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. Auto-detects conflicts with other in-progress tasks.", {
|
|
10994
11040
|
task_id: exports_external.string().describe("Task ID"),
|
|
10995
11041
|
path: exports_external.string().describe("File path (relative or absolute)"),
|
|
10996
11042
|
paths: exports_external.array(exports_external.string()).optional().describe("Multiple file paths to add at once"),
|
|
@@ -10999,14 +11045,41 @@ if (shouldRegisterTool("add_task_file")) {
|
|
|
10999
11045
|
note: exports_external.string().optional().describe("Note about why this file is linked")
|
|
11000
11046
|
}, async ({ task_id, path, paths: multiplePaths, status, agent_id, note }) => {
|
|
11001
11047
|
try {
|
|
11002
|
-
const { addTaskFile: addTaskFile2, bulkAddTaskFiles: bulkAddTaskFiles2 } = (init_task_files(), __toCommonJS(exports_task_files));
|
|
11048
|
+
const { addTaskFile: addTaskFile2, bulkAddTaskFiles: bulkAddTaskFiles2, detectFileConflicts: detectFileConflicts2 } = (init_task_files(), __toCommonJS(exports_task_files));
|
|
11003
11049
|
const resolvedId = resolveId(task_id);
|
|
11050
|
+
let addedFiles;
|
|
11004
11051
|
if (multiplePaths && multiplePaths.length > 0) {
|
|
11005
11052
|
const allPaths = path ? [path, ...multiplePaths] : multiplePaths;
|
|
11006
|
-
|
|
11007
|
-
|
|
11053
|
+
addedFiles = bulkAddTaskFiles2(resolvedId, allPaths, agent_id);
|
|
11054
|
+
const conflicts2 = detectFileConflicts2(resolvedId, allPaths);
|
|
11055
|
+
if (conflicts2.length > 0) {
|
|
11056
|
+
return {
|
|
11057
|
+
content: [{
|
|
11058
|
+
type: "text",
|
|
11059
|
+
text: JSON.stringify({
|
|
11060
|
+
added: addedFiles.length,
|
|
11061
|
+
conflicts: conflicts2,
|
|
11062
|
+
warning: `${conflicts2.length} file(s) already claimed by other in-progress tasks`
|
|
11063
|
+
}, null, 2)
|
|
11064
|
+
}]
|
|
11065
|
+
};
|
|
11066
|
+
}
|
|
11067
|
+
return { content: [{ type: "text", text: `${addedFiles.length} file(s) linked to task ${resolvedId.slice(0, 8)}` }] };
|
|
11008
11068
|
}
|
|
11009
11069
|
const file = addTaskFile2({ task_id: resolvedId, path, status, agent_id, note });
|
|
11070
|
+
const conflicts = detectFileConflicts2(resolvedId, [path]);
|
|
11071
|
+
if (conflicts.length > 0) {
|
|
11072
|
+
return {
|
|
11073
|
+
content: [{
|
|
11074
|
+
type: "text",
|
|
11075
|
+
text: JSON.stringify({
|
|
11076
|
+
file,
|
|
11077
|
+
conflicts,
|
|
11078
|
+
warning: `${path} is already claimed by another in-progress task`
|
|
11079
|
+
}, null, 2)
|
|
11080
|
+
}]
|
|
11081
|
+
};
|
|
11082
|
+
}
|
|
11010
11083
|
return { content: [{ type: "text", text: `${file.status} ${file.path} \u2192 task ${resolvedId.slice(0, 8)}` }] };
|
|
11011
11084
|
} catch (e) {
|
|
11012
11085
|
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
@@ -11046,6 +11119,19 @@ ${lines.join(`
|
|
|
11046
11119
|
}
|
|
11047
11120
|
});
|
|
11048
11121
|
}
|
|
11122
|
+
if (shouldRegisterTool("bulk_find_tasks_by_files")) {
|
|
11123
|
+
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.", {
|
|
11124
|
+
paths: exports_external.array(exports_external.string()).describe("Array of file paths to check")
|
|
11125
|
+
}, async ({ paths }) => {
|
|
11126
|
+
try {
|
|
11127
|
+
const { bulkFindTasksByFiles: bulkFindTasksByFiles2 } = (init_task_files(), __toCommonJS(exports_task_files));
|
|
11128
|
+
const results = bulkFindTasksByFiles2(paths);
|
|
11129
|
+
return { content: [{ type: "text", text: JSON.stringify(results, null, 2) }] };
|
|
11130
|
+
} catch (e) {
|
|
11131
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
11132
|
+
}
|
|
11133
|
+
});
|
|
11134
|
+
}
|
|
11049
11135
|
if (shouldRegisterTool("list_active_files")) {
|
|
11050
11136
|
server.tool("list_active_files", "Return all files linked to in-progress tasks across all agents \u2014 the bird's-eye view of what's being worked on right now.", {
|
|
11051
11137
|
project_id: exports_external.string().optional().describe("Filter by project")
|