@hasna/todos 0.9.40 → 0.9.42
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 +1614 -1396
- package/dist/db/tasks.d.ts +14 -0
- package/dist/db/tasks.d.ts.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +31 -0
- package/dist/mcp/index.js +1613 -1396
- package/dist/server/index.js +31 -0
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -3176,6 +3176,7 @@ __export(exports_tasks, {
|
|
|
3176
3176
|
getActiveWork: () => getActiveWork,
|
|
3177
3177
|
failTask: () => failTask,
|
|
3178
3178
|
deleteTask: () => deleteTask,
|
|
3179
|
+
decomposeTasks: () => decomposeTasks,
|
|
3179
3180
|
createTask: () => createTask,
|
|
3180
3181
|
countTasks: () => countTasks,
|
|
3181
3182
|
completeTask: () => completeTask,
|
|
@@ -3967,6 +3968,36 @@ function getStatus(filters, agentId, db) {
|
|
|
3967
3968
|
overdue_recurring: overdueRow.count
|
|
3968
3969
|
};
|
|
3969
3970
|
}
|
|
3971
|
+
function decomposeTasks(parentId, subtasks, options, db) {
|
|
3972
|
+
const d = db || getDatabase();
|
|
3973
|
+
const parent = getTask(parentId, d);
|
|
3974
|
+
if (!parent)
|
|
3975
|
+
throw new TaskNotFoundError(parentId);
|
|
3976
|
+
const created = [];
|
|
3977
|
+
const tx = d.transaction(() => {
|
|
3978
|
+
for (const input of subtasks) {
|
|
3979
|
+
const task = createTask({
|
|
3980
|
+
title: input.title,
|
|
3981
|
+
description: input.description,
|
|
3982
|
+
priority: input.priority || parent.priority,
|
|
3983
|
+
parent_id: parentId,
|
|
3984
|
+
project_id: parent.project_id || undefined,
|
|
3985
|
+
plan_id: parent.plan_id || undefined,
|
|
3986
|
+
task_list_id: parent.task_list_id || undefined,
|
|
3987
|
+
assigned_to: input.assigned_to || parent.assigned_to || undefined,
|
|
3988
|
+
estimated_minutes: input.estimated_minutes,
|
|
3989
|
+
tags: input.tags
|
|
3990
|
+
}, d);
|
|
3991
|
+
if (options?.depends_on_prev && created.length > 0) {
|
|
3992
|
+
const prev = created[created.length - 1];
|
|
3993
|
+
addDependency(task.id, prev.id, d);
|
|
3994
|
+
}
|
|
3995
|
+
created.push(task);
|
|
3996
|
+
}
|
|
3997
|
+
});
|
|
3998
|
+
tx();
|
|
3999
|
+
return { parent, subtasks: created };
|
|
4000
|
+
}
|
|
3970
4001
|
function wouldCreateCycle(taskId, dependsOn, db) {
|
|
3971
4002
|
const visited = new Set;
|
|
3972
4003
|
const queue = [dependsOn];
|
|
@@ -9039,6 +9070,13 @@ var init_zod = __esm(() => {
|
|
|
9039
9070
|
var exports_mcp = {};
|
|
9040
9071
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
9041
9072
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
9073
|
+
function shouldRegisterTool(name) {
|
|
9074
|
+
if (TODOS_PROFILE === "minimal")
|
|
9075
|
+
return MINIMAL_TOOLS.has(name);
|
|
9076
|
+
if (TODOS_PROFILE === "standard")
|
|
9077
|
+
return !STANDARD_EXCLUDED.has(name);
|
|
9078
|
+
return true;
|
|
9079
|
+
}
|
|
9042
9080
|
function formatError(error) {
|
|
9043
9081
|
if (error instanceof VersionConflictError) {
|
|
9044
9082
|
return JSON.stringify({ code: VersionConflictError.code, message: error.message, suggestion: VersionConflictError.suggestion });
|
|
@@ -9135,7 +9173,7 @@ async function main() {
|
|
|
9135
9173
|
const transport = new StdioServerTransport;
|
|
9136
9174
|
await server.connect(transport);
|
|
9137
9175
|
}
|
|
9138
|
-
var server;
|
|
9176
|
+
var server, TODOS_PROFILE, MINIMAL_TOOLS, STANDARD_EXCLUDED;
|
|
9139
9177
|
var init_mcp = __esm(() => {
|
|
9140
9178
|
init_zod();
|
|
9141
9179
|
init_tasks();
|
|
@@ -9153,1596 +9191,1776 @@ var init_mcp = __esm(() => {
|
|
|
9153
9191
|
name: "todos",
|
|
9154
9192
|
version: "0.9.35"
|
|
9155
9193
|
});
|
|
9156
|
-
|
|
9157
|
-
|
|
9158
|
-
|
|
9159
|
-
|
|
9160
|
-
|
|
9161
|
-
|
|
9162
|
-
|
|
9163
|
-
|
|
9164
|
-
|
|
9165
|
-
|
|
9166
|
-
|
|
9167
|
-
|
|
9168
|
-
|
|
9169
|
-
|
|
9170
|
-
|
|
9171
|
-
|
|
9172
|
-
|
|
9173
|
-
|
|
9174
|
-
|
|
9175
|
-
|
|
9176
|
-
|
|
9177
|
-
|
|
9178
|
-
|
|
9179
|
-
|
|
9180
|
-
|
|
9181
|
-
|
|
9182
|
-
|
|
9183
|
-
|
|
9184
|
-
|
|
9185
|
-
|
|
9186
|
-
|
|
9187
|
-
|
|
9188
|
-
|
|
9189
|
-
|
|
9190
|
-
|
|
9191
|
-
|
|
9192
|
-
|
|
9193
|
-
|
|
9194
|
-
exports_external.
|
|
9195
|
-
exports_external.array(exports_external.
|
|
9196
|
-
|
|
9197
|
-
|
|
9198
|
-
exports_external.
|
|
9199
|
-
exports_external.
|
|
9200
|
-
|
|
9201
|
-
|
|
9202
|
-
|
|
9203
|
-
|
|
9204
|
-
|
|
9205
|
-
|
|
9206
|
-
|
|
9207
|
-
|
|
9208
|
-
|
|
9209
|
-
|
|
9210
|
-
|
|
9211
|
-
|
|
9212
|
-
|
|
9213
|
-
|
|
9214
|
-
|
|
9215
|
-
|
|
9216
|
-
|
|
9217
|
-
|
|
9218
|
-
|
|
9219
|
-
|
|
9220
|
-
|
|
9221
|
-
|
|
9222
|
-
|
|
9223
|
-
|
|
9224
|
-
|
|
9225
|
-
|
|
9226
|
-
|
|
9227
|
-
|
|
9194
|
+
TODOS_PROFILE = (process.env["TODOS_PROFILE"] || "full").toLowerCase();
|
|
9195
|
+
MINIMAL_TOOLS = new Set([
|
|
9196
|
+
"claim_next_task",
|
|
9197
|
+
"complete_task",
|
|
9198
|
+
"fail_task",
|
|
9199
|
+
"get_status",
|
|
9200
|
+
"get_task",
|
|
9201
|
+
"start_task",
|
|
9202
|
+
"add_comment",
|
|
9203
|
+
"get_next_task"
|
|
9204
|
+
]);
|
|
9205
|
+
STANDARD_EXCLUDED = new Set([
|
|
9206
|
+
"get_org_chart",
|
|
9207
|
+
"set_reports_to",
|
|
9208
|
+
"rename_agent",
|
|
9209
|
+
"delete_agent",
|
|
9210
|
+
"create_webhook",
|
|
9211
|
+
"list_webhooks",
|
|
9212
|
+
"delete_webhook",
|
|
9213
|
+
"create_template",
|
|
9214
|
+
"list_templates",
|
|
9215
|
+
"create_task_from_template",
|
|
9216
|
+
"delete_template",
|
|
9217
|
+
"approve_task"
|
|
9218
|
+
]);
|
|
9219
|
+
if (shouldRegisterTool("create_task")) {
|
|
9220
|
+
server.tool("create_task", "Create a new task", {
|
|
9221
|
+
title: exports_external.string(),
|
|
9222
|
+
description: exports_external.string().optional(),
|
|
9223
|
+
project_id: exports_external.string().optional(),
|
|
9224
|
+
parent_id: exports_external.string().optional(),
|
|
9225
|
+
priority: exports_external.enum(["low", "medium", "high", "critical"]).optional(),
|
|
9226
|
+
status: exports_external.enum(["pending", "in_progress", "completed", "failed", "cancelled"]).optional(),
|
|
9227
|
+
agent_id: exports_external.string().optional(),
|
|
9228
|
+
assigned_to: exports_external.string().optional(),
|
|
9229
|
+
session_id: exports_external.string().optional(),
|
|
9230
|
+
working_dir: exports_external.string().optional(),
|
|
9231
|
+
plan_id: exports_external.string().optional(),
|
|
9232
|
+
task_list_id: exports_external.string().optional(),
|
|
9233
|
+
tags: exports_external.array(exports_external.string()).optional(),
|
|
9234
|
+
metadata: exports_external.record(exports_external.unknown()).optional(),
|
|
9235
|
+
estimated_minutes: exports_external.number().optional(),
|
|
9236
|
+
requires_approval: exports_external.boolean().optional(),
|
|
9237
|
+
recurrence_rule: exports_external.string().optional()
|
|
9238
|
+
}, async (params) => {
|
|
9239
|
+
try {
|
|
9240
|
+
const resolved = { ...params };
|
|
9241
|
+
if (resolved.project_id)
|
|
9242
|
+
resolved.project_id = resolveId(resolved.project_id, "projects");
|
|
9243
|
+
if (resolved.parent_id)
|
|
9244
|
+
resolved.parent_id = resolveId(resolved.parent_id);
|
|
9245
|
+
if (resolved.plan_id)
|
|
9246
|
+
resolved.plan_id = resolveId(resolved.plan_id, "plans");
|
|
9247
|
+
if (resolved.task_list_id)
|
|
9248
|
+
resolved.task_list_id = resolveId(resolved.task_list_id, "task_lists");
|
|
9249
|
+
const task = createTask(resolved);
|
|
9250
|
+
return { content: [{ type: "text", text: `created: ${formatTask(task)}` }] };
|
|
9251
|
+
} catch (e) {
|
|
9252
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
9253
|
+
}
|
|
9254
|
+
});
|
|
9255
|
+
}
|
|
9256
|
+
if (shouldRegisterTool("list_tasks")) {
|
|
9257
|
+
server.tool("list_tasks", "List tasks with optional filters and pagination.", {
|
|
9258
|
+
project_id: exports_external.string().optional(),
|
|
9259
|
+
status: exports_external.union([
|
|
9260
|
+
exports_external.enum(["pending", "in_progress", "completed", "failed", "cancelled"]),
|
|
9261
|
+
exports_external.array(exports_external.enum(["pending", "in_progress", "completed", "failed", "cancelled"]))
|
|
9262
|
+
]).optional(),
|
|
9263
|
+
priority: exports_external.union([
|
|
9264
|
+
exports_external.enum(["low", "medium", "high", "critical"]),
|
|
9265
|
+
exports_external.array(exports_external.enum(["low", "medium", "high", "critical"]))
|
|
9266
|
+
]).optional(),
|
|
9267
|
+
assigned_to: exports_external.string().optional(),
|
|
9268
|
+
tags: exports_external.array(exports_external.string()).optional(),
|
|
9269
|
+
plan_id: exports_external.string().optional(),
|
|
9270
|
+
task_list_id: exports_external.string().optional(),
|
|
9271
|
+
has_recurrence: exports_external.boolean().optional(),
|
|
9272
|
+
limit: exports_external.number().optional(),
|
|
9273
|
+
offset: exports_external.number().optional()
|
|
9274
|
+
}, async (params) => {
|
|
9275
|
+
try {
|
|
9276
|
+
const resolved = { ...params };
|
|
9277
|
+
if (resolved.project_id)
|
|
9278
|
+
resolved.project_id = resolveId(resolved.project_id, "projects");
|
|
9279
|
+
if (resolved.plan_id)
|
|
9280
|
+
resolved.plan_id = resolveId(resolved.plan_id, "plans");
|
|
9281
|
+
if (resolved.task_list_id)
|
|
9282
|
+
resolved.task_list_id = resolveId(resolved.task_list_id, "task_lists");
|
|
9283
|
+
const tasks = listTasks(resolved);
|
|
9284
|
+
const { limit: _limit, offset: _offset, ...countFilter } = resolved;
|
|
9285
|
+
const total = countTasks(countFilter);
|
|
9286
|
+
if (tasks.length === 0) {
|
|
9287
|
+
return { content: [{ type: "text", text: total > 0 ? `No tasks in this page (total: ${total}).` : "No tasks found." }] };
|
|
9288
|
+
}
|
|
9289
|
+
const text = tasks.map((t) => {
|
|
9290
|
+
const lock = t.locked_by ? ` [locked by ${t.locked_by}]` : "";
|
|
9291
|
+
const assigned = t.assigned_to ? ` -> ${t.assigned_to}` : "";
|
|
9292
|
+
return `[${t.status}] ${t.id.slice(0, 8)} | ${t.priority} | ${t.title}${assigned}${lock}`;
|
|
9293
|
+
}).join(`
|
|
9228
9294
|
`);
|
|
9229
|
-
|
|
9295
|
+
const pagination = resolved.limit ? `
|
|
9230
9296
|
(showing ${tasks.length} of ${total}, offset: ${resolved.offset || 0})` : "";
|
|
9231
|
-
|
|
9297
|
+
return { content: [{ type: "text", text: `${tasks.length} task(s):
|
|
9232
9298
|
${text}${pagination}` }] };
|
|
9233
|
-
|
|
9234
|
-
|
|
9235
|
-
|
|
9236
|
-
|
|
9237
|
-
|
|
9238
|
-
|
|
9239
|
-
|
|
9240
|
-
|
|
9241
|
-
|
|
9242
|
-
|
|
9243
|
-
|
|
9244
|
-
|
|
9245
|
-
|
|
9246
|
-
|
|
9247
|
-
parts
|
|
9299
|
+
} catch (e) {
|
|
9300
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
9301
|
+
}
|
|
9302
|
+
});
|
|
9303
|
+
}
|
|
9304
|
+
if (shouldRegisterTool("get_task")) {
|
|
9305
|
+
server.tool("get_task", "Get full task details with subtasks, deps, and comments.", {
|
|
9306
|
+
id: exports_external.string()
|
|
9307
|
+
}, async ({ id }) => {
|
|
9308
|
+
try {
|
|
9309
|
+
const resolvedId = resolveId(id);
|
|
9310
|
+
const task = getTaskWithRelations(resolvedId);
|
|
9311
|
+
if (!task)
|
|
9312
|
+
return { content: [{ type: "text", text: `Task not found: ${id}` }], isError: true };
|
|
9313
|
+
const parts = [formatTaskDetail(task)];
|
|
9314
|
+
if (task.subtasks.length > 0) {
|
|
9315
|
+
parts.push(`
|
|
9248
9316
|
Subtasks (${task.subtasks.length}):`);
|
|
9249
|
-
|
|
9250
|
-
|
|
9317
|
+
for (const st of task.subtasks) {
|
|
9318
|
+
parts.push(` [${st.status}] ${st.id.slice(0, 8)} | ${st.title}`);
|
|
9319
|
+
}
|
|
9251
9320
|
}
|
|
9252
|
-
|
|
9253
|
-
|
|
9254
|
-
parts.push(`
|
|
9321
|
+
if (task.dependencies.length > 0) {
|
|
9322
|
+
parts.push(`
|
|
9255
9323
|
Depends on (${task.dependencies.length}):`);
|
|
9256
|
-
|
|
9257
|
-
|
|
9324
|
+
for (const dep of task.dependencies) {
|
|
9325
|
+
parts.push(` [${dep.status}] ${dep.id.slice(0, 8)} | ${dep.title}`);
|
|
9326
|
+
}
|
|
9258
9327
|
}
|
|
9259
|
-
|
|
9260
|
-
|
|
9261
|
-
parts.push(`
|
|
9328
|
+
if (task.blocked_by.length > 0) {
|
|
9329
|
+
parts.push(`
|
|
9262
9330
|
Blocks (${task.blocked_by.length}):`);
|
|
9263
|
-
|
|
9264
|
-
|
|
9331
|
+
for (const b of task.blocked_by) {
|
|
9332
|
+
parts.push(` [${b.status}] ${b.id.slice(0, 8)} | ${b.title}`);
|
|
9333
|
+
}
|
|
9265
9334
|
}
|
|
9266
|
-
|
|
9267
|
-
|
|
9268
|
-
parts.push(`
|
|
9335
|
+
if (task.comments.length > 0) {
|
|
9336
|
+
parts.push(`
|
|
9269
9337
|
Comments (${task.comments.length}):`);
|
|
9270
|
-
|
|
9271
|
-
|
|
9272
|
-
|
|
9338
|
+
for (const c of task.comments) {
|
|
9339
|
+
const agent = c.agent_id ? `[${c.agent_id}] ` : "";
|
|
9340
|
+
parts.push(` ${agent}${c.created_at}: ${c.content}`);
|
|
9341
|
+
}
|
|
9273
9342
|
}
|
|
9274
|
-
|
|
9275
|
-
|
|
9276
|
-
parts.push(`
|
|
9343
|
+
if (task.parent) {
|
|
9344
|
+
parts.push(`
|
|
9277
9345
|
Parent: ${task.parent.id.slice(0, 8)} | ${task.parent.title}`);
|
|
9278
|
-
|
|
9279
|
-
|
|
9346
|
+
}
|
|
9347
|
+
return { content: [{ type: "text", text: parts.join(`
|
|
9280
9348
|
`) }] };
|
|
9281
|
-
|
|
9282
|
-
|
|
9283
|
-
}
|
|
9284
|
-
});
|
|
9285
|
-
server.tool("update_task", "Update task fields. Version required for optimistic locking.", {
|
|
9286
|
-
id: exports_external.string(),
|
|
9287
|
-
version: exports_external.number(),
|
|
9288
|
-
title: exports_external.string().optional(),
|
|
9289
|
-
description: exports_external.string().optional(),
|
|
9290
|
-
status: exports_external.enum(["pending", "in_progress", "completed", "failed", "cancelled"]).optional(),
|
|
9291
|
-
priority: exports_external.enum(["low", "medium", "high", "critical"]).optional(),
|
|
9292
|
-
assigned_to: exports_external.string().optional(),
|
|
9293
|
-
tags: exports_external.array(exports_external.string()).optional(),
|
|
9294
|
-
metadata: exports_external.record(exports_external.unknown()).optional(),
|
|
9295
|
-
plan_id: exports_external.string().optional(),
|
|
9296
|
-
task_list_id: exports_external.string().optional()
|
|
9297
|
-
}, async ({ id, ...rest }) => {
|
|
9298
|
-
try {
|
|
9299
|
-
const resolvedId = resolveId(id);
|
|
9300
|
-
const task = updateTask(resolvedId, rest);
|
|
9301
|
-
return { content: [{ type: "text", text: `updated: ${formatTask(task)}` }] };
|
|
9302
|
-
} catch (e) {
|
|
9303
|
-
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
9304
|
-
}
|
|
9305
|
-
});
|
|
9306
|
-
server.tool("delete_task", "Delete a task permanently. Subtasks cascade-deleted.", {
|
|
9307
|
-
id: exports_external.string()
|
|
9308
|
-
}, async ({ id }) => {
|
|
9309
|
-
try {
|
|
9310
|
-
const resolvedId = resolveId(id);
|
|
9311
|
-
const deleted = deleteTask(resolvedId);
|
|
9312
|
-
return {
|
|
9313
|
-
content: [{
|
|
9314
|
-
type: "text",
|
|
9315
|
-
text: deleted ? `Task ${id} deleted.` : `Task ${id} not found.`
|
|
9316
|
-
}]
|
|
9317
|
-
};
|
|
9318
|
-
} catch (e) {
|
|
9319
|
-
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
9320
|
-
}
|
|
9321
|
-
});
|
|
9322
|
-
server.tool("start_task", "Claim, lock, and set task to in_progress.", {
|
|
9323
|
-
id: exports_external.string(),
|
|
9324
|
-
agent_id: exports_external.string()
|
|
9325
|
-
}, async ({ id, agent_id }) => {
|
|
9326
|
-
try {
|
|
9327
|
-
const resolvedId = resolveId(id);
|
|
9328
|
-
const task = startTask(resolvedId, agent_id);
|
|
9329
|
-
return { content: [{ type: "text", text: `started: ${formatTask(task)}` }] };
|
|
9330
|
-
} catch (e) {
|
|
9331
|
-
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
9332
|
-
}
|
|
9333
|
-
});
|
|
9334
|
-
server.tool("complete_task", "Complete a task. For recurring tasks, auto-spawns next instance.", {
|
|
9335
|
-
id: exports_external.string(),
|
|
9336
|
-
agent_id: exports_external.string().optional(),
|
|
9337
|
-
skip_recurrence: exports_external.boolean().optional(),
|
|
9338
|
-
files_changed: exports_external.array(exports_external.string()).optional().describe("List of files changed as part of completing this task"),
|
|
9339
|
-
test_results: exports_external.string().optional().describe("Summary of test results"),
|
|
9340
|
-
commit_hash: exports_external.string().optional().describe("Git commit hash associated with this completion"),
|
|
9341
|
-
notes: exports_external.string().optional().describe("Notes about the completion"),
|
|
9342
|
-
attachment_ids: exports_external.array(exports_external.string()).optional().describe("IDs of attachments uploaded via @hasna/attachments to link as evidence")
|
|
9343
|
-
}, async ({ id, agent_id, skip_recurrence, files_changed, test_results, commit_hash, notes, attachment_ids }) => {
|
|
9344
|
-
try {
|
|
9345
|
-
const resolvedId = resolveId(id);
|
|
9346
|
-
const evidence = files_changed || test_results || commit_hash || notes || attachment_ids ? { files_changed, test_results, commit_hash, notes, attachment_ids } : undefined;
|
|
9347
|
-
const task = completeTask(resolvedId, agent_id, undefined, { skip_recurrence, ...evidence });
|
|
9348
|
-
let text = `completed: ${formatTask(task)}`;
|
|
9349
|
-
if (task.metadata._next_recurrence) {
|
|
9350
|
-
const next = task.metadata._next_recurrence;
|
|
9351
|
-
text += `
|
|
9352
|
-
next: ${next.short_id || next.id.slice(0, 8)} due ${next.due_at}`;
|
|
9349
|
+
} catch (e) {
|
|
9350
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
9353
9351
|
}
|
|
9354
|
-
|
|
9355
|
-
|
|
9356
|
-
|
|
9357
|
-
|
|
9358
|
-
|
|
9359
|
-
|
|
9360
|
-
|
|
9361
|
-
|
|
9362
|
-
|
|
9363
|
-
|
|
9364
|
-
|
|
9365
|
-
|
|
9366
|
-
|
|
9367
|
-
|
|
9352
|
+
});
|
|
9353
|
+
}
|
|
9354
|
+
if (shouldRegisterTool("update_task")) {
|
|
9355
|
+
server.tool("update_task", "Update task fields. Version required for optimistic locking.", {
|
|
9356
|
+
id: exports_external.string(),
|
|
9357
|
+
version: exports_external.number(),
|
|
9358
|
+
title: exports_external.string().optional(),
|
|
9359
|
+
description: exports_external.string().optional(),
|
|
9360
|
+
status: exports_external.enum(["pending", "in_progress", "completed", "failed", "cancelled"]).optional(),
|
|
9361
|
+
priority: exports_external.enum(["low", "medium", "high", "critical"]).optional(),
|
|
9362
|
+
assigned_to: exports_external.string().optional(),
|
|
9363
|
+
tags: exports_external.array(exports_external.string()).optional(),
|
|
9364
|
+
metadata: exports_external.record(exports_external.unknown()).optional(),
|
|
9365
|
+
plan_id: exports_external.string().optional(),
|
|
9366
|
+
task_list_id: exports_external.string().optional()
|
|
9367
|
+
}, async ({ id, ...rest }) => {
|
|
9368
|
+
try {
|
|
9369
|
+
const resolvedId = resolveId(id);
|
|
9370
|
+
const task = updateTask(resolvedId, rest);
|
|
9371
|
+
return { content: [{ type: "text", text: `updated: ${formatTask(task)}` }] };
|
|
9372
|
+
} catch (e) {
|
|
9373
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
9368
9374
|
}
|
|
9369
|
-
|
|
9370
|
-
|
|
9371
|
-
|
|
9372
|
-
|
|
9373
|
-
|
|
9374
|
-
|
|
9375
|
-
|
|
9376
|
-
|
|
9377
|
-
|
|
9378
|
-
|
|
9379
|
-
|
|
9380
|
-
|
|
9381
|
-
|
|
9382
|
-
|
|
9383
|
-
|
|
9384
|
-
|
|
9385
|
-
|
|
9386
|
-
server.tool("add_dependency", "Add a dependency. Prevents cycles via BFS detection.", {
|
|
9387
|
-
task_id: exports_external.string(),
|
|
9388
|
-
depends_on: exports_external.string()
|
|
9389
|
-
}, async ({ task_id, depends_on }) => {
|
|
9390
|
-
try {
|
|
9391
|
-
const resolvedTaskId = resolveId(task_id);
|
|
9392
|
-
const resolvedDepsOn = resolveId(depends_on);
|
|
9393
|
-
addDependency(resolvedTaskId, resolvedDepsOn);
|
|
9394
|
-
return { content: [{ type: "text", text: `Dependency added: ${task_id} depends on ${depends_on}` }] };
|
|
9395
|
-
} catch (e) {
|
|
9396
|
-
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
9397
|
-
}
|
|
9398
|
-
});
|
|
9399
|
-
server.tool("remove_dependency", "Remove a dependency link between two tasks.", {
|
|
9400
|
-
task_id: exports_external.string(),
|
|
9401
|
-
depends_on: exports_external.string()
|
|
9402
|
-
}, async ({ task_id, depends_on }) => {
|
|
9403
|
-
try {
|
|
9404
|
-
const resolvedTaskId = resolveId(task_id);
|
|
9405
|
-
const resolvedDepsOn = resolveId(depends_on);
|
|
9406
|
-
const removed = removeDependency(resolvedTaskId, resolvedDepsOn);
|
|
9407
|
-
return {
|
|
9408
|
-
content: [{
|
|
9409
|
-
type: "text",
|
|
9410
|
-
text: removed ? `Dependency removed.` : `Dependency not found.`
|
|
9411
|
-
}]
|
|
9412
|
-
};
|
|
9413
|
-
} catch (e) {
|
|
9414
|
-
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
9415
|
-
}
|
|
9416
|
-
});
|
|
9417
|
-
server.tool("add_comment", "Add a comment or note to a task. Comments are append-only.", {
|
|
9418
|
-
task_id: exports_external.string(),
|
|
9419
|
-
content: exports_external.string(),
|
|
9420
|
-
agent_id: exports_external.string().optional(),
|
|
9421
|
-
session_id: exports_external.string().optional()
|
|
9422
|
-
}, async ({ task_id, ...rest }) => {
|
|
9423
|
-
try {
|
|
9424
|
-
const resolvedId = resolveId(task_id);
|
|
9425
|
-
const comment = addComment({ task_id: resolvedId, ...rest });
|
|
9426
|
-
return { content: [{ type: "text", text: `Comment added (${comment.id.slice(0, 8)}) at ${comment.created_at}` }] };
|
|
9427
|
-
} catch (e) {
|
|
9428
|
-
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
9429
|
-
}
|
|
9430
|
-
});
|
|
9431
|
-
server.tool("list_projects", "List all registered projects", {}, async () => {
|
|
9432
|
-
try {
|
|
9433
|
-
const projects = listProjects();
|
|
9434
|
-
if (projects.length === 0) {
|
|
9435
|
-
return { content: [{ type: "text", text: "No projects registered." }] };
|
|
9436
|
-
}
|
|
9437
|
-
const text = projects.map((p) => {
|
|
9438
|
-
const taskList = p.task_list_id ? ` [${p.task_list_id}]` : "";
|
|
9439
|
-
return `${p.id.slice(0, 8)} | ${p.name} | ${p.path}${taskList}${p.description ? ` - ${p.description}` : ""}`;
|
|
9440
|
-
}).join(`
|
|
9441
|
-
`);
|
|
9442
|
-
return { content: [{ type: "text", text: `${projects.length} project(s):
|
|
9443
|
-
${text}` }] };
|
|
9444
|
-
} catch (e) {
|
|
9445
|
-
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
9446
|
-
}
|
|
9447
|
-
});
|
|
9448
|
-
server.tool("create_project", "Register a new project with auto-generated task prefix.", {
|
|
9449
|
-
name: exports_external.string(),
|
|
9450
|
-
path: exports_external.string(),
|
|
9451
|
-
description: exports_external.string().optional(),
|
|
9452
|
-
task_list_id: exports_external.string().optional()
|
|
9453
|
-
}, async (params) => {
|
|
9454
|
-
try {
|
|
9455
|
-
const project = createProject(params);
|
|
9456
|
-
const taskList = project.task_list_id ? ` [${project.task_list_id}]` : "";
|
|
9457
|
-
return {
|
|
9458
|
-
content: [{
|
|
9459
|
-
type: "text",
|
|
9460
|
-
text: `Project created: ${project.id.slice(0, 8)} | ${project.name} | ${project.path}${taskList}`
|
|
9461
|
-
}]
|
|
9462
|
-
};
|
|
9463
|
-
} catch (e) {
|
|
9464
|
-
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
9465
|
-
}
|
|
9466
|
-
});
|
|
9467
|
-
server.tool("create_plan", "Create a plan to group related tasks.", {
|
|
9468
|
-
name: exports_external.string(),
|
|
9469
|
-
project_id: exports_external.string().optional(),
|
|
9470
|
-
description: exports_external.string().optional(),
|
|
9471
|
-
status: exports_external.enum(["active", "completed", "archived"]).optional(),
|
|
9472
|
-
task_list_id: exports_external.string().optional(),
|
|
9473
|
-
agent_id: exports_external.string().optional()
|
|
9474
|
-
}, async (params) => {
|
|
9475
|
-
try {
|
|
9476
|
-
const resolved = { ...params };
|
|
9477
|
-
if (resolved.project_id)
|
|
9478
|
-
resolved.project_id = resolveId(resolved.project_id, "projects");
|
|
9479
|
-
if (resolved.task_list_id)
|
|
9480
|
-
resolved.task_list_id = resolveId(resolved.task_list_id, "task_lists");
|
|
9481
|
-
const plan = createPlan(resolved);
|
|
9482
|
-
return {
|
|
9483
|
-
content: [{
|
|
9484
|
-
type: "text",
|
|
9485
|
-
text: `Plan created: ${plan.id.slice(0, 8)} | ${plan.name} | ${plan.status}`
|
|
9486
|
-
}]
|
|
9487
|
-
};
|
|
9488
|
-
} catch (e) {
|
|
9489
|
-
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
9490
|
-
}
|
|
9491
|
-
});
|
|
9492
|
-
server.tool("list_plans", "List all plans, optionally filtered by project.", {
|
|
9493
|
-
project_id: exports_external.string().optional()
|
|
9494
|
-
}, async ({ project_id }) => {
|
|
9495
|
-
try {
|
|
9496
|
-
const resolvedProjectId = project_id ? resolveId(project_id, "projects") : undefined;
|
|
9497
|
-
const plans = listPlans(resolvedProjectId);
|
|
9498
|
-
if (plans.length === 0) {
|
|
9499
|
-
return { content: [{ type: "text", text: "No plans found." }] };
|
|
9500
|
-
}
|
|
9501
|
-
const text = plans.map((p) => {
|
|
9502
|
-
const project = p.project_id ? ` (project: ${p.project_id.slice(0, 8)})` : "";
|
|
9503
|
-
return `[${p.status}] ${p.id.slice(0, 8)} | ${p.name}${project}`;
|
|
9504
|
-
}).join(`
|
|
9505
|
-
`);
|
|
9506
|
-
return { content: [{ type: "text", text: `${plans.length} plan(s):
|
|
9507
|
-
${text}` }] };
|
|
9508
|
-
} catch (e) {
|
|
9509
|
-
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
9510
|
-
}
|
|
9511
|
-
});
|
|
9512
|
-
server.tool("get_plan", "Get plan details including status and timestamps.", {
|
|
9513
|
-
id: exports_external.string()
|
|
9514
|
-
}, async ({ id }) => {
|
|
9515
|
-
try {
|
|
9516
|
-
const resolvedId = resolveId(id, "plans");
|
|
9517
|
-
const plan = getPlan(resolvedId);
|
|
9518
|
-
if (!plan)
|
|
9519
|
-
return { content: [{ type: "text", text: `Plan not found: ${id}` }], isError: true };
|
|
9520
|
-
const parts = [
|
|
9521
|
-
`ID: ${plan.id}`,
|
|
9522
|
-
`Name: ${plan.name}`,
|
|
9523
|
-
`Status: ${plan.status}`
|
|
9524
|
-
];
|
|
9525
|
-
if (plan.description)
|
|
9526
|
-
parts.push(`Description: ${plan.description}`);
|
|
9527
|
-
if (plan.project_id)
|
|
9528
|
-
parts.push(`Project: ${plan.project_id}`);
|
|
9529
|
-
parts.push(`Created: ${plan.created_at}`);
|
|
9530
|
-
parts.push(`Updated: ${plan.updated_at}`);
|
|
9531
|
-
return { content: [{ type: "text", text: parts.join(`
|
|
9532
|
-
`) }] };
|
|
9533
|
-
} catch (e) {
|
|
9534
|
-
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
9535
|
-
}
|
|
9536
|
-
});
|
|
9537
|
-
server.tool("update_plan", "Update plan fields (name, description, status).", {
|
|
9538
|
-
id: exports_external.string(),
|
|
9539
|
-
name: exports_external.string().optional(),
|
|
9540
|
-
description: exports_external.string().optional(),
|
|
9541
|
-
status: exports_external.enum(["active", "completed", "archived"]).optional(),
|
|
9542
|
-
task_list_id: exports_external.string().optional(),
|
|
9543
|
-
agent_id: exports_external.string().optional()
|
|
9544
|
-
}, async ({ id, ...rest }) => {
|
|
9545
|
-
try {
|
|
9546
|
-
const resolvedId = resolveId(id, "plans");
|
|
9547
|
-
const resolved = { ...rest };
|
|
9548
|
-
if (resolved.task_list_id)
|
|
9549
|
-
resolved.task_list_id = resolveId(resolved.task_list_id, "task_lists");
|
|
9550
|
-
const plan = updatePlan(resolvedId, resolved);
|
|
9551
|
-
return {
|
|
9552
|
-
content: [{
|
|
9553
|
-
type: "text",
|
|
9554
|
-
text: `Plan updated: ${plan.id.slice(0, 8)} | ${plan.name} | ${plan.status}`
|
|
9555
|
-
}]
|
|
9556
|
-
};
|
|
9557
|
-
} catch (e) {
|
|
9558
|
-
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
9559
|
-
}
|
|
9560
|
-
});
|
|
9561
|
-
server.tool("delete_plan", "Delete a plan. Tasks in the plan are orphaned (not deleted).", {
|
|
9562
|
-
id: exports_external.string()
|
|
9563
|
-
}, async ({ id }) => {
|
|
9564
|
-
try {
|
|
9565
|
-
const resolvedId = resolveId(id, "plans");
|
|
9566
|
-
const deleted = deletePlan(resolvedId);
|
|
9567
|
-
return {
|
|
9568
|
-
content: [{
|
|
9569
|
-
type: "text",
|
|
9570
|
-
text: deleted ? `Plan ${id} deleted.` : `Plan ${id} not found.`
|
|
9571
|
-
}]
|
|
9572
|
-
};
|
|
9573
|
-
} catch (e) {
|
|
9574
|
-
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
9575
|
-
}
|
|
9576
|
-
});
|
|
9577
|
-
server.tool("search_tasks", "Full-text search across tasks with filters.", {
|
|
9578
|
-
query: exports_external.string(),
|
|
9579
|
-
project_id: exports_external.string().optional(),
|
|
9580
|
-
task_list_id: exports_external.string().optional(),
|
|
9581
|
-
status: exports_external.union([
|
|
9582
|
-
exports_external.enum(["pending", "in_progress", "completed", "failed", "cancelled"]),
|
|
9583
|
-
exports_external.array(exports_external.enum(["pending", "in_progress", "completed", "failed", "cancelled"]))
|
|
9584
|
-
]).optional(),
|
|
9585
|
-
priority: exports_external.union([
|
|
9586
|
-
exports_external.enum(["low", "medium", "high", "critical"]),
|
|
9587
|
-
exports_external.array(exports_external.enum(["low", "medium", "high", "critical"]))
|
|
9588
|
-
]).optional(),
|
|
9589
|
-
assigned_to: exports_external.string().optional(),
|
|
9590
|
-
agent_id: exports_external.string().optional(),
|
|
9591
|
-
created_after: exports_external.string().optional(),
|
|
9592
|
-
updated_after: exports_external.string().optional(),
|
|
9593
|
-
has_dependencies: exports_external.boolean().optional(),
|
|
9594
|
-
is_blocked: exports_external.boolean().optional()
|
|
9595
|
-
}, async ({ query, project_id, task_list_id, ...filters }) => {
|
|
9596
|
-
try {
|
|
9597
|
-
const resolvedProjectId = project_id ? resolveId(project_id, "projects") : undefined;
|
|
9598
|
-
const resolvedTaskListId = task_list_id ? resolveId(task_list_id, "task_lists") : undefined;
|
|
9599
|
-
const tasks = searchTasks({
|
|
9600
|
-
query,
|
|
9601
|
-
project_id: resolvedProjectId,
|
|
9602
|
-
task_list_id: resolvedTaskListId,
|
|
9603
|
-
...filters
|
|
9604
|
-
});
|
|
9605
|
-
if (tasks.length === 0) {
|
|
9606
|
-
return { content: [{ type: "text", text: `No tasks matching "${query}".` }] };
|
|
9375
|
+
});
|
|
9376
|
+
}
|
|
9377
|
+
if (shouldRegisterTool("delete_task")) {
|
|
9378
|
+
server.tool("delete_task", "Delete a task permanently. Subtasks cascade-deleted.", {
|
|
9379
|
+
id: exports_external.string()
|
|
9380
|
+
}, async ({ id }) => {
|
|
9381
|
+
try {
|
|
9382
|
+
const resolvedId = resolveId(id);
|
|
9383
|
+
const deleted = deleteTask(resolvedId);
|
|
9384
|
+
return {
|
|
9385
|
+
content: [{
|
|
9386
|
+
type: "text",
|
|
9387
|
+
text: deleted ? `Task ${id} deleted.` : `Task ${id} not found.`
|
|
9388
|
+
}]
|
|
9389
|
+
};
|
|
9390
|
+
} catch (e) {
|
|
9391
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
9607
9392
|
}
|
|
9608
|
-
|
|
9609
|
-
|
|
9610
|
-
|
|
9611
|
-
|
|
9612
|
-
|
|
9613
|
-
|
|
9614
|
-
}
|
|
9615
|
-
|
|
9616
|
-
|
|
9617
|
-
|
|
9618
|
-
|
|
9619
|
-
|
|
9620
|
-
|
|
9621
|
-
|
|
9622
|
-
|
|
9623
|
-
}
|
|
9624
|
-
|
|
9625
|
-
|
|
9626
|
-
|
|
9627
|
-
|
|
9628
|
-
|
|
9629
|
-
|
|
9630
|
-
|
|
9631
|
-
|
|
9632
|
-
|
|
9633
|
-
|
|
9634
|
-
|
|
9635
|
-
|
|
9636
|
-
|
|
9637
|
-
|
|
9638
|
-
|
|
9639
|
-
|
|
9640
|
-
|
|
9641
|
-
|
|
9642
|
-
|
|
9643
|
-
|
|
9393
|
+
});
|
|
9394
|
+
}
|
|
9395
|
+
if (shouldRegisterTool("start_task")) {
|
|
9396
|
+
server.tool("start_task", "Claim, lock, and set task to in_progress.", {
|
|
9397
|
+
id: exports_external.string(),
|
|
9398
|
+
agent_id: exports_external.string()
|
|
9399
|
+
}, async ({ id, agent_id }) => {
|
|
9400
|
+
try {
|
|
9401
|
+
const resolvedId = resolveId(id);
|
|
9402
|
+
const task = startTask(resolvedId, agent_id);
|
|
9403
|
+
return { content: [{ type: "text", text: `started: ${formatTask(task)}` }] };
|
|
9404
|
+
} catch (e) {
|
|
9405
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
9406
|
+
}
|
|
9407
|
+
});
|
|
9408
|
+
}
|
|
9409
|
+
if (shouldRegisterTool("complete_task")) {
|
|
9410
|
+
server.tool("complete_task", "Complete a task. For recurring tasks, auto-spawns next instance.", {
|
|
9411
|
+
id: exports_external.string(),
|
|
9412
|
+
agent_id: exports_external.string().optional(),
|
|
9413
|
+
skip_recurrence: exports_external.boolean().optional(),
|
|
9414
|
+
files_changed: exports_external.array(exports_external.string()).optional().describe("List of files changed as part of completing this task"),
|
|
9415
|
+
test_results: exports_external.string().optional().describe("Summary of test results"),
|
|
9416
|
+
commit_hash: exports_external.string().optional().describe("Git commit hash associated with this completion"),
|
|
9417
|
+
notes: exports_external.string().optional().describe("Notes about the completion"),
|
|
9418
|
+
attachment_ids: exports_external.array(exports_external.string()).optional().describe("IDs of attachments uploaded via @hasna/attachments to link as evidence")
|
|
9419
|
+
}, async ({ id, agent_id, skip_recurrence, files_changed, test_results, commit_hash, notes, attachment_ids }) => {
|
|
9420
|
+
try {
|
|
9421
|
+
const resolvedId = resolveId(id);
|
|
9422
|
+
const evidence = files_changed || test_results || commit_hash || notes || attachment_ids ? { files_changed, test_results, commit_hash, notes, attachment_ids } : undefined;
|
|
9423
|
+
const task = completeTask(resolvedId, agent_id, undefined, { skip_recurrence, ...evidence });
|
|
9424
|
+
let text = `completed: ${formatTask(task)}`;
|
|
9425
|
+
if (task.metadata._next_recurrence) {
|
|
9426
|
+
const next = task.metadata._next_recurrence;
|
|
9427
|
+
text += `
|
|
9428
|
+
next: ${next.short_id || next.id.slice(0, 8)} due ${next.due_at}`;
|
|
9429
|
+
}
|
|
9430
|
+
return { content: [{ type: "text", text }] };
|
|
9431
|
+
} catch (e) {
|
|
9432
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
9433
|
+
}
|
|
9434
|
+
});
|
|
9435
|
+
}
|
|
9436
|
+
if (shouldRegisterTool("lock_task")) {
|
|
9437
|
+
server.tool("lock_task", "Acquire exclusive lock. Expires after 30 min. Idempotent per agent.", {
|
|
9438
|
+
id: exports_external.string(),
|
|
9439
|
+
agent_id: exports_external.string()
|
|
9440
|
+
}, async ({ id, agent_id }) => {
|
|
9441
|
+
try {
|
|
9442
|
+
const resolvedId = resolveId(id);
|
|
9443
|
+
const result = lockTask(resolvedId, agent_id);
|
|
9444
|
+
if (result.success) {
|
|
9445
|
+
return { content: [{ type: "text", text: `Lock acquired by ${agent_id} at ${result.locked_at}` }] };
|
|
9644
9446
|
}
|
|
9645
|
-
|
|
9447
|
+
return { content: [{ type: "text", text: `Lock failed: ${result.error}` }], isError: true };
|
|
9448
|
+
} catch (e) {
|
|
9449
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
9450
|
+
}
|
|
9451
|
+
});
|
|
9452
|
+
}
|
|
9453
|
+
if (shouldRegisterTool("unlock_task")) {
|
|
9454
|
+
server.tool("unlock_task", "Release exclusive lock on a task.", {
|
|
9455
|
+
id: exports_external.string(),
|
|
9456
|
+
agent_id: exports_external.string().optional()
|
|
9457
|
+
}, async ({ id, agent_id }) => {
|
|
9458
|
+
try {
|
|
9459
|
+
const resolvedId = resolveId(id);
|
|
9460
|
+
unlockTask(resolvedId, agent_id);
|
|
9461
|
+
return { content: [{ type: "text", text: `Lock released on task ${id}` }] };
|
|
9462
|
+
} catch (e) {
|
|
9463
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
9646
9464
|
}
|
|
9647
|
-
|
|
9648
|
-
|
|
9649
|
-
|
|
9650
|
-
|
|
9651
|
-
|
|
9652
|
-
|
|
9653
|
-
|
|
9465
|
+
});
|
|
9466
|
+
}
|
|
9467
|
+
if (shouldRegisterTool("add_dependency")) {
|
|
9468
|
+
server.tool("add_dependency", "Add a dependency. Prevents cycles via BFS detection.", {
|
|
9469
|
+
task_id: exports_external.string(),
|
|
9470
|
+
depends_on: exports_external.string()
|
|
9471
|
+
}, async ({ task_id, depends_on }) => {
|
|
9472
|
+
try {
|
|
9473
|
+
const resolvedTaskId = resolveId(task_id);
|
|
9474
|
+
const resolvedDepsOn = resolveId(depends_on);
|
|
9475
|
+
addDependency(resolvedTaskId, resolvedDepsOn);
|
|
9476
|
+
return { content: [{ type: "text", text: `Dependency added: ${task_id} depends on ${depends_on}` }] };
|
|
9477
|
+
} catch (e) {
|
|
9478
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
9654
9479
|
}
|
|
9655
|
-
|
|
9656
|
-
|
|
9480
|
+
});
|
|
9481
|
+
}
|
|
9482
|
+
if (shouldRegisterTool("remove_dependency")) {
|
|
9483
|
+
server.tool("remove_dependency", "Remove a dependency link between two tasks.", {
|
|
9484
|
+
task_id: exports_external.string(),
|
|
9485
|
+
depends_on: exports_external.string()
|
|
9486
|
+
}, async ({ task_id, depends_on }) => {
|
|
9487
|
+
try {
|
|
9488
|
+
const resolvedTaskId = resolveId(task_id);
|
|
9489
|
+
const resolvedDepsOn = resolveId(depends_on);
|
|
9490
|
+
const removed = removeDependency(resolvedTaskId, resolvedDepsOn);
|
|
9491
|
+
return {
|
|
9492
|
+
content: [{
|
|
9493
|
+
type: "text",
|
|
9494
|
+
text: removed ? `Dependency removed.` : `Dependency not found.`
|
|
9495
|
+
}]
|
|
9496
|
+
};
|
|
9497
|
+
} catch (e) {
|
|
9498
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
9657
9499
|
}
|
|
9658
|
-
|
|
9659
|
-
|
|
9660
|
-
|
|
9661
|
-
|
|
9662
|
-
|
|
9663
|
-
|
|
9664
|
-
|
|
9665
|
-
|
|
9666
|
-
|
|
9667
|
-
|
|
9668
|
-
|
|
9669
|
-
|
|
9670
|
-
|
|
9671
|
-
|
|
9672
|
-
|
|
9673
|
-
text: `Agent registered:
|
|
9674
|
-
ID: ${agent.id}
|
|
9675
|
-
Name: ${agent.name}${agent.description ? `
|
|
9676
|
-
Description: ${agent.description}` : ""}
|
|
9677
|
-
Created: ${agent.created_at}
|
|
9678
|
-
Last seen: ${agent.last_seen_at}`
|
|
9679
|
-
}]
|
|
9680
|
-
};
|
|
9681
|
-
} catch (e) {
|
|
9682
|
-
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
9683
|
-
}
|
|
9684
|
-
});
|
|
9685
|
-
server.tool("list_agents", "List all registered agents", {}, async () => {
|
|
9686
|
-
try {
|
|
9687
|
-
const agents = listAgents();
|
|
9688
|
-
if (agents.length === 0) {
|
|
9689
|
-
return { content: [{ type: "text", text: "No agents registered." }] };
|
|
9500
|
+
});
|
|
9501
|
+
}
|
|
9502
|
+
if (shouldRegisterTool("add_comment")) {
|
|
9503
|
+
server.tool("add_comment", "Add a comment or note to a task. Comments are append-only.", {
|
|
9504
|
+
task_id: exports_external.string(),
|
|
9505
|
+
content: exports_external.string(),
|
|
9506
|
+
agent_id: exports_external.string().optional(),
|
|
9507
|
+
session_id: exports_external.string().optional()
|
|
9508
|
+
}, async ({ task_id, ...rest }) => {
|
|
9509
|
+
try {
|
|
9510
|
+
const resolvedId = resolveId(task_id);
|
|
9511
|
+
const comment = addComment({ task_id: resolvedId, ...rest });
|
|
9512
|
+
return { content: [{ type: "text", text: `Comment added (${comment.id.slice(0, 8)}) at ${comment.created_at}` }] };
|
|
9513
|
+
} catch (e) {
|
|
9514
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
9690
9515
|
}
|
|
9691
|
-
|
|
9692
|
-
|
|
9693
|
-
|
|
9516
|
+
});
|
|
9517
|
+
}
|
|
9518
|
+
if (shouldRegisterTool("list_projects")) {
|
|
9519
|
+
server.tool("list_projects", "List all registered projects", {}, async () => {
|
|
9520
|
+
try {
|
|
9521
|
+
const projects = listProjects();
|
|
9522
|
+
if (projects.length === 0) {
|
|
9523
|
+
return { content: [{ type: "text", text: "No projects registered." }] };
|
|
9524
|
+
}
|
|
9525
|
+
const text = projects.map((p) => {
|
|
9526
|
+
const taskList = p.task_list_id ? ` [${p.task_list_id}]` : "";
|
|
9527
|
+
return `${p.id.slice(0, 8)} | ${p.name} | ${p.path}${taskList}${p.description ? ` - ${p.description}` : ""}`;
|
|
9528
|
+
}).join(`
|
|
9694
9529
|
`);
|
|
9695
|
-
|
|
9530
|
+
return { content: [{ type: "text", text: `${projects.length} project(s):
|
|
9696
9531
|
${text}` }] };
|
|
9697
|
-
|
|
9698
|
-
|
|
9699
|
-
}
|
|
9700
|
-
});
|
|
9701
|
-
server.tool("get_agent", "Get agent details by ID or name. Provide one of id or name.", {
|
|
9702
|
-
id: exports_external.string().optional(),
|
|
9703
|
-
name: exports_external.string().optional()
|
|
9704
|
-
}, async ({ id, name }) => {
|
|
9705
|
-
try {
|
|
9706
|
-
if (!id && !name) {
|
|
9707
|
-
return { content: [{ type: "text", text: "Provide either id or name." }], isError: true };
|
|
9708
|
-
}
|
|
9709
|
-
const agent = id ? getAgent(id) : getAgentByName(name);
|
|
9710
|
-
if (!agent) {
|
|
9711
|
-
return { content: [{ type: "text", text: `Agent not found: ${id || name}` }], isError: true };
|
|
9712
|
-
}
|
|
9713
|
-
const parts = [
|
|
9714
|
-
`ID: ${agent.id}`,
|
|
9715
|
-
`Name: ${agent.name}`
|
|
9716
|
-
];
|
|
9717
|
-
if (agent.description)
|
|
9718
|
-
parts.push(`Description: ${agent.description}`);
|
|
9719
|
-
if (Object.keys(agent.metadata).length > 0)
|
|
9720
|
-
parts.push(`Metadata: ${JSON.stringify(agent.metadata)}`);
|
|
9721
|
-
parts.push(`Created: ${agent.created_at}`);
|
|
9722
|
-
parts.push(`Last seen: ${agent.last_seen_at}`);
|
|
9723
|
-
return { content: [{ type: "text", text: parts.join(`
|
|
9724
|
-
`) }] };
|
|
9725
|
-
} catch (e) {
|
|
9726
|
-
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
9727
|
-
}
|
|
9728
|
-
});
|
|
9729
|
-
server.tool("rename_agent", "Rename an agent. Resolve by id or current name.", {
|
|
9730
|
-
id: exports_external.string().optional(),
|
|
9731
|
-
name: exports_external.string().optional(),
|
|
9732
|
-
new_name: exports_external.string()
|
|
9733
|
-
}, async ({ id, name, new_name }) => {
|
|
9734
|
-
try {
|
|
9735
|
-
if (!id && !name) {
|
|
9736
|
-
return { content: [{ type: "text", text: "Provide either id or name." }], isError: true };
|
|
9532
|
+
} catch (e) {
|
|
9533
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
9737
9534
|
}
|
|
9738
|
-
|
|
9739
|
-
|
|
9740
|
-
|
|
9535
|
+
});
|
|
9536
|
+
}
|
|
9537
|
+
if (shouldRegisterTool("create_project")) {
|
|
9538
|
+
server.tool("create_project", "Register a new project with auto-generated task prefix.", {
|
|
9539
|
+
name: exports_external.string(),
|
|
9540
|
+
path: exports_external.string(),
|
|
9541
|
+
description: exports_external.string().optional(),
|
|
9542
|
+
task_list_id: exports_external.string().optional()
|
|
9543
|
+
}, async (params) => {
|
|
9544
|
+
try {
|
|
9545
|
+
const project = createProject(params);
|
|
9546
|
+
const taskList = project.task_list_id ? ` [${project.task_list_id}]` : "";
|
|
9547
|
+
return {
|
|
9548
|
+
content: [{
|
|
9549
|
+
type: "text",
|
|
9550
|
+
text: `Project created: ${project.id.slice(0, 8)} | ${project.name} | ${project.path}${taskList}`
|
|
9551
|
+
}]
|
|
9552
|
+
};
|
|
9553
|
+
} catch (e) {
|
|
9554
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
9741
9555
|
}
|
|
9742
|
-
|
|
9743
|
-
|
|
9744
|
-
|
|
9745
|
-
|
|
9746
|
-
|
|
9747
|
-
|
|
9748
|
-
|
|
9749
|
-
|
|
9750
|
-
|
|
9751
|
-
|
|
9752
|
-
}
|
|
9753
|
-
|
|
9754
|
-
|
|
9755
|
-
|
|
9756
|
-
|
|
9757
|
-
|
|
9758
|
-
|
|
9759
|
-
|
|
9760
|
-
return {
|
|
9556
|
+
});
|
|
9557
|
+
}
|
|
9558
|
+
if (shouldRegisterTool("create_plan")) {
|
|
9559
|
+
server.tool("create_plan", "Create a plan to group related tasks.", {
|
|
9560
|
+
name: exports_external.string(),
|
|
9561
|
+
project_id: exports_external.string().optional(),
|
|
9562
|
+
description: exports_external.string().optional(),
|
|
9563
|
+
status: exports_external.enum(["active", "completed", "archived"]).optional(),
|
|
9564
|
+
task_list_id: exports_external.string().optional(),
|
|
9565
|
+
agent_id: exports_external.string().optional()
|
|
9566
|
+
}, async (params) => {
|
|
9567
|
+
try {
|
|
9568
|
+
const resolved = { ...params };
|
|
9569
|
+
if (resolved.project_id)
|
|
9570
|
+
resolved.project_id = resolveId(resolved.project_id, "projects");
|
|
9571
|
+
if (resolved.task_list_id)
|
|
9572
|
+
resolved.task_list_id = resolveId(resolved.task_list_id, "task_lists");
|
|
9573
|
+
const plan = createPlan(resolved);
|
|
9574
|
+
return {
|
|
9575
|
+
content: [{
|
|
9576
|
+
type: "text",
|
|
9577
|
+
text: `Plan created: ${plan.id.slice(0, 8)} | ${plan.name} | ${plan.status}`
|
|
9578
|
+
}]
|
|
9579
|
+
};
|
|
9580
|
+
} catch (e) {
|
|
9581
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
9582
|
+
}
|
|
9583
|
+
});
|
|
9584
|
+
}
|
|
9585
|
+
if (shouldRegisterTool("list_plans")) {
|
|
9586
|
+
server.tool("list_plans", "List all plans, optionally filtered by project.", {
|
|
9587
|
+
project_id: exports_external.string().optional()
|
|
9588
|
+
}, async ({ project_id }) => {
|
|
9589
|
+
try {
|
|
9590
|
+
const resolvedProjectId = project_id ? resolveId(project_id, "projects") : undefined;
|
|
9591
|
+
const plans = listPlans(resolvedProjectId);
|
|
9592
|
+
if (plans.length === 0) {
|
|
9593
|
+
return { content: [{ type: "text", text: "No plans found." }] };
|
|
9594
|
+
}
|
|
9595
|
+
const text = plans.map((p) => {
|
|
9596
|
+
const project = p.project_id ? ` (project: ${p.project_id.slice(0, 8)})` : "";
|
|
9597
|
+
return `[${p.status}] ${p.id.slice(0, 8)} | ${p.name}${project}`;
|
|
9598
|
+
}).join(`
|
|
9599
|
+
`);
|
|
9600
|
+
return { content: [{ type: "text", text: `${plans.length} plan(s):
|
|
9601
|
+
${text}` }] };
|
|
9602
|
+
} catch (e) {
|
|
9603
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
9761
9604
|
}
|
|
9762
|
-
|
|
9763
|
-
|
|
9764
|
-
|
|
9605
|
+
});
|
|
9606
|
+
}
|
|
9607
|
+
if (shouldRegisterTool("get_plan")) {
|
|
9608
|
+
server.tool("get_plan", "Get plan details including status and timestamps.", {
|
|
9609
|
+
id: exports_external.string()
|
|
9610
|
+
}, async ({ id }) => {
|
|
9611
|
+
try {
|
|
9612
|
+
const resolvedId = resolveId(id, "plans");
|
|
9613
|
+
const plan = getPlan(resolvedId);
|
|
9614
|
+
if (!plan)
|
|
9615
|
+
return { content: [{ type: "text", text: `Plan not found: ${id}` }], isError: true };
|
|
9616
|
+
const parts = [
|
|
9617
|
+
`ID: ${plan.id}`,
|
|
9618
|
+
`Name: ${plan.name}`,
|
|
9619
|
+
`Status: ${plan.status}`
|
|
9620
|
+
];
|
|
9621
|
+
if (plan.description)
|
|
9622
|
+
parts.push(`Description: ${plan.description}`);
|
|
9623
|
+
if (plan.project_id)
|
|
9624
|
+
parts.push(`Project: ${plan.project_id}`);
|
|
9625
|
+
parts.push(`Created: ${plan.created_at}`);
|
|
9626
|
+
parts.push(`Updated: ${plan.updated_at}`);
|
|
9627
|
+
return { content: [{ type: "text", text: parts.join(`
|
|
9628
|
+
`) }] };
|
|
9629
|
+
} catch (e) {
|
|
9630
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
9765
9631
|
}
|
|
9766
|
-
|
|
9767
|
-
|
|
9768
|
-
|
|
9769
|
-
|
|
9770
|
-
|
|
9771
|
-
|
|
9772
|
-
|
|
9773
|
-
|
|
9774
|
-
|
|
9775
|
-
|
|
9776
|
-
}
|
|
9777
|
-
|
|
9778
|
-
|
|
9779
|
-
|
|
9780
|
-
|
|
9781
|
-
|
|
9782
|
-
|
|
9783
|
-
|
|
9784
|
-
|
|
9785
|
-
|
|
9786
|
-
|
|
9787
|
-
|
|
9788
|
-
|
|
9789
|
-
|
|
9790
|
-
content: [{
|
|
9791
|
-
|
|
9792
|
-
|
|
9632
|
+
});
|
|
9633
|
+
}
|
|
9634
|
+
if (shouldRegisterTool("update_plan")) {
|
|
9635
|
+
server.tool("update_plan", "Update plan fields (name, description, status).", {
|
|
9636
|
+
id: exports_external.string(),
|
|
9637
|
+
name: exports_external.string().optional(),
|
|
9638
|
+
description: exports_external.string().optional(),
|
|
9639
|
+
status: exports_external.enum(["active", "completed", "archived"]).optional(),
|
|
9640
|
+
task_list_id: exports_external.string().optional(),
|
|
9641
|
+
agent_id: exports_external.string().optional()
|
|
9642
|
+
}, async ({ id, ...rest }) => {
|
|
9643
|
+
try {
|
|
9644
|
+
const resolvedId = resolveId(id, "plans");
|
|
9645
|
+
const resolved = { ...rest };
|
|
9646
|
+
if (resolved.task_list_id)
|
|
9647
|
+
resolved.task_list_id = resolveId(resolved.task_list_id, "task_lists");
|
|
9648
|
+
const plan = updatePlan(resolvedId, resolved);
|
|
9649
|
+
return {
|
|
9650
|
+
content: [{
|
|
9651
|
+
type: "text",
|
|
9652
|
+
text: `Plan updated: ${plan.id.slice(0, 8)} | ${plan.name} | ${plan.status}`
|
|
9653
|
+
}]
|
|
9654
|
+
};
|
|
9655
|
+
} catch (e) {
|
|
9656
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
9657
|
+
}
|
|
9658
|
+
});
|
|
9659
|
+
}
|
|
9660
|
+
if (shouldRegisterTool("delete_plan")) {
|
|
9661
|
+
server.tool("delete_plan", "Delete a plan. Tasks in the plan are orphaned (not deleted).", {
|
|
9662
|
+
id: exports_external.string()
|
|
9663
|
+
}, async ({ id }) => {
|
|
9664
|
+
try {
|
|
9665
|
+
const resolvedId = resolveId(id, "plans");
|
|
9666
|
+
const deleted = deletePlan(resolvedId);
|
|
9667
|
+
return {
|
|
9668
|
+
content: [{
|
|
9669
|
+
type: "text",
|
|
9670
|
+
text: deleted ? `Plan ${id} deleted.` : `Plan ${id} not found.`
|
|
9671
|
+
}]
|
|
9672
|
+
};
|
|
9673
|
+
} catch (e) {
|
|
9674
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
9675
|
+
}
|
|
9676
|
+
});
|
|
9677
|
+
}
|
|
9678
|
+
if (shouldRegisterTool("search_tasks")) {
|
|
9679
|
+
server.tool("search_tasks", "Full-text search across tasks with filters.", {
|
|
9680
|
+
query: exports_external.string(),
|
|
9681
|
+
project_id: exports_external.string().optional(),
|
|
9682
|
+
task_list_id: exports_external.string().optional(),
|
|
9683
|
+
status: exports_external.union([
|
|
9684
|
+
exports_external.enum(["pending", "in_progress", "completed", "failed", "cancelled"]),
|
|
9685
|
+
exports_external.array(exports_external.enum(["pending", "in_progress", "completed", "failed", "cancelled"]))
|
|
9686
|
+
]).optional(),
|
|
9687
|
+
priority: exports_external.union([
|
|
9688
|
+
exports_external.enum(["low", "medium", "high", "critical"]),
|
|
9689
|
+
exports_external.array(exports_external.enum(["low", "medium", "high", "critical"]))
|
|
9690
|
+
]).optional(),
|
|
9691
|
+
assigned_to: exports_external.string().optional(),
|
|
9692
|
+
agent_id: exports_external.string().optional(),
|
|
9693
|
+
created_after: exports_external.string().optional(),
|
|
9694
|
+
updated_after: exports_external.string().optional(),
|
|
9695
|
+
has_dependencies: exports_external.boolean().optional(),
|
|
9696
|
+
is_blocked: exports_external.boolean().optional()
|
|
9697
|
+
}, async ({ query, project_id, task_list_id, ...filters }) => {
|
|
9698
|
+
try {
|
|
9699
|
+
const resolvedProjectId = project_id ? resolveId(project_id, "projects") : undefined;
|
|
9700
|
+
const resolvedTaskListId = task_list_id ? resolveId(task_list_id, "task_lists") : undefined;
|
|
9701
|
+
const tasks = searchTasks({
|
|
9702
|
+
query,
|
|
9703
|
+
project_id: resolvedProjectId,
|
|
9704
|
+
task_list_id: resolvedTaskListId,
|
|
9705
|
+
...filters
|
|
9706
|
+
});
|
|
9707
|
+
if (tasks.length === 0) {
|
|
9708
|
+
return { content: [{ type: "text", text: `No tasks matching "${query}".` }] };
|
|
9709
|
+
}
|
|
9710
|
+
const text = tasks.map((t) => `[${t.status}] ${t.id.slice(0, 8)} | ${t.priority} | ${t.title}`).join(`
|
|
9711
|
+
`);
|
|
9712
|
+
return { content: [{ type: "text", text: `${tasks.length} result(s) for "${query}":
|
|
9713
|
+
${text}` }] };
|
|
9714
|
+
} catch (e) {
|
|
9715
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
9716
|
+
}
|
|
9717
|
+
});
|
|
9718
|
+
}
|
|
9719
|
+
if (shouldRegisterTool("sync")) {
|
|
9720
|
+
server.tool("sync", "Sync tasks between local DB and agent task list.", {
|
|
9721
|
+
task_list_id: exports_external.string().optional(),
|
|
9722
|
+
agent: exports_external.string().optional(),
|
|
9723
|
+
all_agents: exports_external.boolean().optional(),
|
|
9724
|
+
project_id: exports_external.string().optional(),
|
|
9725
|
+
direction: exports_external.enum(["push", "pull", "both"]).optional(),
|
|
9726
|
+
prefer: exports_external.enum(["local", "remote"]).optional()
|
|
9727
|
+
}, async ({ task_list_id, agent, all_agents, project_id, direction, prefer }) => {
|
|
9728
|
+
try {
|
|
9729
|
+
const resolvedProjectId = project_id ? resolveId(project_id, "projects") : undefined;
|
|
9730
|
+
const project = resolvedProjectId ? getProject(resolvedProjectId) : undefined;
|
|
9731
|
+
const dir = direction ?? "both";
|
|
9732
|
+
const options = { prefer: prefer ?? "remote" };
|
|
9733
|
+
let result;
|
|
9734
|
+
if (all_agents) {
|
|
9735
|
+
const agents = defaultSyncAgents();
|
|
9736
|
+
result = syncWithAgents(agents, (a) => resolveTaskListId(a, task_list_id || project?.task_list_id || undefined), resolvedProjectId, dir, options);
|
|
9737
|
+
} else {
|
|
9738
|
+
const resolvedAgent = agent || "claude";
|
|
9739
|
+
const taskListId = resolveTaskListId(resolvedAgent, task_list_id || project?.task_list_id || undefined);
|
|
9740
|
+
if (!taskListId) {
|
|
9741
|
+
return {
|
|
9742
|
+
content: [{
|
|
9743
|
+
type: "text",
|
|
9744
|
+
text: `Could not determine task list ID for ${resolvedAgent}. Provide task_list_id or set task_list_id on the project.`
|
|
9745
|
+
}],
|
|
9746
|
+
isError: true
|
|
9747
|
+
};
|
|
9748
|
+
}
|
|
9749
|
+
result = syncWithAgent(resolvedAgent, taskListId, resolvedProjectId, dir, options);
|
|
9750
|
+
}
|
|
9751
|
+
const parts = [];
|
|
9752
|
+
if (result.pulled > 0)
|
|
9753
|
+
parts.push(`Pulled ${result.pulled} task(s).`);
|
|
9754
|
+
if (result.pushed > 0)
|
|
9755
|
+
parts.push(`Pushed ${result.pushed} task(s).`);
|
|
9756
|
+
if (result.pulled === 0 && result.pushed === 0 && result.errors.length === 0) {
|
|
9757
|
+
parts.push("Nothing to sync.");
|
|
9758
|
+
}
|
|
9759
|
+
for (const err of result.errors) {
|
|
9760
|
+
parts.push(`Error: ${err}`);
|
|
9761
|
+
}
|
|
9762
|
+
return { content: [{ type: "text", text: parts.join(`
|
|
9763
|
+
`) }] };
|
|
9764
|
+
} catch (e) {
|
|
9765
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
9766
|
+
}
|
|
9767
|
+
});
|
|
9768
|
+
}
|
|
9769
|
+
if (shouldRegisterTool("register_agent")) {
|
|
9770
|
+
server.tool("register_agent", "Register an agent (idempotent by name). Updates last_seen_at.", {
|
|
9771
|
+
name: exports_external.string(),
|
|
9772
|
+
description: exports_external.string().optional()
|
|
9773
|
+
}, async ({ name, description }) => {
|
|
9774
|
+
try {
|
|
9775
|
+
const agent = registerAgent({ name, description });
|
|
9776
|
+
return {
|
|
9777
|
+
content: [{
|
|
9778
|
+
type: "text",
|
|
9779
|
+
text: `Agent registered:
|
|
9780
|
+
ID: ${agent.id}
|
|
9781
|
+
Name: ${agent.name}${agent.description ? `
|
|
9782
|
+
Description: ${agent.description}` : ""}
|
|
9783
|
+
Created: ${agent.created_at}
|
|
9784
|
+
Last seen: ${agent.last_seen_at}`
|
|
9785
|
+
}]
|
|
9786
|
+
};
|
|
9787
|
+
} catch (e) {
|
|
9788
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
9789
|
+
}
|
|
9790
|
+
});
|
|
9791
|
+
}
|
|
9792
|
+
if (shouldRegisterTool("list_agents")) {
|
|
9793
|
+
server.tool("list_agents", "List all registered agents", {}, async () => {
|
|
9794
|
+
try {
|
|
9795
|
+
const agents = listAgents();
|
|
9796
|
+
if (agents.length === 0) {
|
|
9797
|
+
return { content: [{ type: "text", text: "No agents registered." }] };
|
|
9798
|
+
}
|
|
9799
|
+
const text = agents.map((a) => {
|
|
9800
|
+
return `${a.id} | ${a.name}${a.description ? ` - ${a.description}` : ""} (last seen: ${a.last_seen_at})`;
|
|
9801
|
+
}).join(`
|
|
9802
|
+
`);
|
|
9803
|
+
return { content: [{ type: "text", text: `${agents.length} agent(s):
|
|
9804
|
+
${text}` }] };
|
|
9805
|
+
} catch (e) {
|
|
9806
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
9807
|
+
}
|
|
9808
|
+
});
|
|
9809
|
+
}
|
|
9810
|
+
if (shouldRegisterTool("get_agent")) {
|
|
9811
|
+
server.tool("get_agent", "Get agent details by ID or name. Provide one of id or name.", {
|
|
9812
|
+
id: exports_external.string().optional(),
|
|
9813
|
+
name: exports_external.string().optional()
|
|
9814
|
+
}, async ({ id, name }) => {
|
|
9815
|
+
try {
|
|
9816
|
+
if (!id && !name) {
|
|
9817
|
+
return { content: [{ type: "text", text: "Provide either id or name." }], isError: true };
|
|
9818
|
+
}
|
|
9819
|
+
const agent = id ? getAgent(id) : getAgentByName(name);
|
|
9820
|
+
if (!agent) {
|
|
9821
|
+
return { content: [{ type: "text", text: `Agent not found: ${id || name}` }], isError: true };
|
|
9822
|
+
}
|
|
9823
|
+
const parts = [
|
|
9824
|
+
`ID: ${agent.id}`,
|
|
9825
|
+
`Name: ${agent.name}`
|
|
9826
|
+
];
|
|
9827
|
+
if (agent.description)
|
|
9828
|
+
parts.push(`Description: ${agent.description}`);
|
|
9829
|
+
if (Object.keys(agent.metadata).length > 0)
|
|
9830
|
+
parts.push(`Metadata: ${JSON.stringify(agent.metadata)}`);
|
|
9831
|
+
parts.push(`Created: ${agent.created_at}`);
|
|
9832
|
+
parts.push(`Last seen: ${agent.last_seen_at}`);
|
|
9833
|
+
return { content: [{ type: "text", text: parts.join(`
|
|
9834
|
+
`) }] };
|
|
9835
|
+
} catch (e) {
|
|
9836
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
9837
|
+
}
|
|
9838
|
+
});
|
|
9839
|
+
}
|
|
9840
|
+
if (shouldRegisterTool("rename_agent")) {
|
|
9841
|
+
server.tool("rename_agent", "Rename an agent. Resolve by id or current name.", {
|
|
9842
|
+
id: exports_external.string().optional(),
|
|
9843
|
+
name: exports_external.string().optional(),
|
|
9844
|
+
new_name: exports_external.string()
|
|
9845
|
+
}, async ({ id, name, new_name }) => {
|
|
9846
|
+
try {
|
|
9847
|
+
if (!id && !name) {
|
|
9848
|
+
return { content: [{ type: "text", text: "Provide either id or name." }], isError: true };
|
|
9849
|
+
}
|
|
9850
|
+
const agent = id ? getAgent(id) : getAgentByName(name);
|
|
9851
|
+
if (!agent) {
|
|
9852
|
+
return { content: [{ type: "text", text: `Agent not found: ${id || name}` }], isError: true };
|
|
9853
|
+
}
|
|
9854
|
+
const updated = updateAgent(agent.id, { name: new_name });
|
|
9855
|
+
return {
|
|
9856
|
+
content: [{
|
|
9857
|
+
type: "text",
|
|
9858
|
+
text: `Agent renamed: ${agent.name} -> ${updated.name}
|
|
9859
|
+
ID: ${updated.id}`
|
|
9860
|
+
}]
|
|
9861
|
+
};
|
|
9862
|
+
} catch (e) {
|
|
9863
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
9864
|
+
}
|
|
9865
|
+
});
|
|
9866
|
+
}
|
|
9867
|
+
if (shouldRegisterTool("delete_agent")) {
|
|
9868
|
+
server.tool("delete_agent", "Delete an agent permanently. Resolve by id or name.", {
|
|
9869
|
+
id: exports_external.string().optional(),
|
|
9870
|
+
name: exports_external.string().optional()
|
|
9871
|
+
}, async ({ id, name }) => {
|
|
9872
|
+
try {
|
|
9873
|
+
if (!id && !name) {
|
|
9874
|
+
return { content: [{ type: "text", text: "Provide either id or name." }], isError: true };
|
|
9875
|
+
}
|
|
9876
|
+
const agent = id ? getAgent(id) : getAgentByName(name);
|
|
9877
|
+
if (!agent) {
|
|
9878
|
+
return { content: [{ type: "text", text: `Agent not found: ${id || name}` }], isError: true };
|
|
9879
|
+
}
|
|
9880
|
+
const deleted = deleteAgent(agent.id);
|
|
9881
|
+
return {
|
|
9882
|
+
content: [{
|
|
9883
|
+
type: "text",
|
|
9884
|
+
text: deleted ? `Agent deleted: ${agent.name} (${agent.id})` : `Failed to delete agent: ${agent.name}`
|
|
9885
|
+
}],
|
|
9886
|
+
isError: !deleted
|
|
9887
|
+
};
|
|
9888
|
+
} catch (e) {
|
|
9889
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
9890
|
+
}
|
|
9891
|
+
});
|
|
9892
|
+
}
|
|
9893
|
+
if (shouldRegisterTool("create_task_list")) {
|
|
9894
|
+
server.tool("create_task_list", "Create a task list container for organizing tasks.", {
|
|
9895
|
+
name: exports_external.string(),
|
|
9896
|
+
slug: exports_external.string().optional(),
|
|
9897
|
+
project_id: exports_external.string().optional(),
|
|
9898
|
+
description: exports_external.string().optional()
|
|
9899
|
+
}, async (params) => {
|
|
9900
|
+
try {
|
|
9901
|
+
const resolved = { ...params };
|
|
9902
|
+
if (resolved.project_id)
|
|
9903
|
+
resolved.project_id = resolveId(resolved.project_id, "projects");
|
|
9904
|
+
const list = createTaskList(resolved);
|
|
9905
|
+
return {
|
|
9906
|
+
content: [{
|
|
9907
|
+
type: "text",
|
|
9908
|
+
text: `Task list created:
|
|
9793
9909
|
ID: ${list.id}
|
|
9794
9910
|
Name: ${list.name}
|
|
9795
9911
|
Slug: ${list.slug}${list.project_id ? `
|
|
9796
9912
|
Project: ${list.project_id}` : ""}${list.description ? `
|
|
9797
9913
|
Description: ${list.description}` : ""}`
|
|
9798
|
-
|
|
9799
|
-
|
|
9800
|
-
|
|
9801
|
-
|
|
9802
|
-
|
|
9803
|
-
|
|
9804
|
-
|
|
9805
|
-
|
|
9806
|
-
|
|
9807
|
-
|
|
9808
|
-
|
|
9809
|
-
|
|
9810
|
-
|
|
9811
|
-
|
|
9812
|
-
|
|
9813
|
-
|
|
9814
|
-
|
|
9815
|
-
|
|
9816
|
-
|
|
9914
|
+
}]
|
|
9915
|
+
};
|
|
9916
|
+
} catch (e) {
|
|
9917
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
9918
|
+
}
|
|
9919
|
+
});
|
|
9920
|
+
}
|
|
9921
|
+
if (shouldRegisterTool("list_task_lists")) {
|
|
9922
|
+
server.tool("list_task_lists", "List all task lists, optionally filtered by project.", {
|
|
9923
|
+
project_id: exports_external.string().optional()
|
|
9924
|
+
}, async ({ project_id }) => {
|
|
9925
|
+
try {
|
|
9926
|
+
const resolvedProjectId = project_id ? resolveId(project_id, "projects") : undefined;
|
|
9927
|
+
const lists = listTaskLists(resolvedProjectId);
|
|
9928
|
+
if (lists.length === 0) {
|
|
9929
|
+
return { content: [{ type: "text", text: "No task lists found." }] };
|
|
9930
|
+
}
|
|
9931
|
+
const text = lists.map((l) => {
|
|
9932
|
+
const project = l.project_id ? ` (project: ${l.project_id.slice(0, 8)})` : "";
|
|
9933
|
+
return `${l.id.slice(0, 8)} | ${l.name} [${l.slug}]${project}${l.description ? ` - ${l.description}` : ""}`;
|
|
9934
|
+
}).join(`
|
|
9817
9935
|
`);
|
|
9818
|
-
|
|
9936
|
+
return { content: [{ type: "text", text: `${lists.length} task list(s):
|
|
9819
9937
|
${text}` }] };
|
|
9820
|
-
|
|
9821
|
-
|
|
9822
|
-
|
|
9823
|
-
|
|
9824
|
-
|
|
9825
|
-
|
|
9826
|
-
|
|
9827
|
-
|
|
9828
|
-
|
|
9829
|
-
|
|
9830
|
-
|
|
9831
|
-
|
|
9832
|
-
|
|
9833
|
-
|
|
9834
|
-
|
|
9835
|
-
|
|
9836
|
-
|
|
9837
|
-
|
|
9838
|
-
|
|
9839
|
-
|
|
9840
|
-
|
|
9841
|
-
|
|
9842
|
-
|
|
9843
|
-
|
|
9844
|
-
|
|
9845
|
-
|
|
9846
|
-
|
|
9938
|
+
} catch (e) {
|
|
9939
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
9940
|
+
}
|
|
9941
|
+
});
|
|
9942
|
+
}
|
|
9943
|
+
if (shouldRegisterTool("get_task_list")) {
|
|
9944
|
+
server.tool("get_task_list", "Get task list details including slug and metadata.", {
|
|
9945
|
+
id: exports_external.string()
|
|
9946
|
+
}, async ({ id }) => {
|
|
9947
|
+
try {
|
|
9948
|
+
const resolvedId = resolveId(id, "task_lists");
|
|
9949
|
+
const list = getTaskList(resolvedId);
|
|
9950
|
+
if (!list) {
|
|
9951
|
+
return { content: [{ type: "text", text: `Task list not found: ${id}` }], isError: true };
|
|
9952
|
+
}
|
|
9953
|
+
const parts = [
|
|
9954
|
+
`ID: ${list.id}`,
|
|
9955
|
+
`Name: ${list.name}`,
|
|
9956
|
+
`Slug: ${list.slug}`
|
|
9957
|
+
];
|
|
9958
|
+
if (list.project_id)
|
|
9959
|
+
parts.push(`Project: ${list.project_id}`);
|
|
9960
|
+
if (list.description)
|
|
9961
|
+
parts.push(`Description: ${list.description}`);
|
|
9962
|
+
if (Object.keys(list.metadata).length > 0)
|
|
9963
|
+
parts.push(`Metadata: ${JSON.stringify(list.metadata)}`);
|
|
9964
|
+
parts.push(`Created: ${list.created_at}`);
|
|
9965
|
+
parts.push(`Updated: ${list.updated_at}`);
|
|
9966
|
+
return { content: [{ type: "text", text: parts.join(`
|
|
9847
9967
|
`) }] };
|
|
9848
|
-
|
|
9849
|
-
|
|
9850
|
-
|
|
9851
|
-
|
|
9852
|
-
|
|
9853
|
-
|
|
9854
|
-
name
|
|
9855
|
-
|
|
9856
|
-
|
|
9857
|
-
|
|
9858
|
-
|
|
9859
|
-
|
|
9860
|
-
|
|
9861
|
-
|
|
9862
|
-
|
|
9863
|
-
|
|
9968
|
+
} catch (e) {
|
|
9969
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
9970
|
+
}
|
|
9971
|
+
});
|
|
9972
|
+
}
|
|
9973
|
+
if (shouldRegisterTool("update_task_list")) {
|
|
9974
|
+
server.tool("update_task_list", "Update a task list's name or description.", {
|
|
9975
|
+
id: exports_external.string(),
|
|
9976
|
+
name: exports_external.string().optional(),
|
|
9977
|
+
description: exports_external.string().optional()
|
|
9978
|
+
}, async ({ id, ...rest }) => {
|
|
9979
|
+
try {
|
|
9980
|
+
const resolvedId = resolveId(id, "task_lists");
|
|
9981
|
+
const list = updateTaskList(resolvedId, rest);
|
|
9982
|
+
return {
|
|
9983
|
+
content: [{
|
|
9984
|
+
type: "text",
|
|
9985
|
+
text: `Task list updated:
|
|
9864
9986
|
ID: ${list.id}
|
|
9865
9987
|
Name: ${list.name}
|
|
9866
9988
|
Slug: ${list.slug}`
|
|
9867
|
-
|
|
9868
|
-
|
|
9869
|
-
|
|
9870
|
-
|
|
9871
|
-
|
|
9872
|
-
|
|
9873
|
-
|
|
9874
|
-
|
|
9875
|
-
|
|
9876
|
-
|
|
9877
|
-
|
|
9878
|
-
|
|
9879
|
-
|
|
9880
|
-
|
|
9881
|
-
|
|
9882
|
-
|
|
9883
|
-
|
|
9884
|
-
|
|
9885
|
-
|
|
9886
|
-
|
|
9887
|
-
|
|
9888
|
-
|
|
9889
|
-
|
|
9890
|
-
|
|
9891
|
-
}
|
|
9892
|
-
|
|
9893
|
-
|
|
9894
|
-
|
|
9895
|
-
|
|
9896
|
-
|
|
9897
|
-
|
|
9898
|
-
|
|
9989
|
+
}]
|
|
9990
|
+
};
|
|
9991
|
+
} catch (e) {
|
|
9992
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
9993
|
+
}
|
|
9994
|
+
});
|
|
9995
|
+
}
|
|
9996
|
+
if (shouldRegisterTool("delete_task_list")) {
|
|
9997
|
+
server.tool("delete_task_list", "Delete a task list. Tasks are orphaned, not deleted.", {
|
|
9998
|
+
id: exports_external.string()
|
|
9999
|
+
}, async ({ id }) => {
|
|
10000
|
+
try {
|
|
10001
|
+
const resolvedId = resolveId(id, "task_lists");
|
|
10002
|
+
const deleted = deleteTaskList(resolvedId);
|
|
10003
|
+
return {
|
|
10004
|
+
content: [{
|
|
10005
|
+
type: "text",
|
|
10006
|
+
text: deleted ? `Task list ${id} deleted.` : `Task list ${id} not found.`
|
|
10007
|
+
}]
|
|
10008
|
+
};
|
|
10009
|
+
} catch (e) {
|
|
10010
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
10011
|
+
}
|
|
10012
|
+
});
|
|
10013
|
+
}
|
|
10014
|
+
if (shouldRegisterTool("get_task_history")) {
|
|
10015
|
+
server.tool("get_task_history", "Get audit log \u2014 field changes with timestamps and actors.", {
|
|
10016
|
+
task_id: exports_external.string()
|
|
10017
|
+
}, async ({ task_id }) => {
|
|
10018
|
+
try {
|
|
10019
|
+
const resolvedId = resolveId(task_id);
|
|
10020
|
+
const { getTaskHistory: getTaskHistory2 } = await Promise.resolve().then(() => (init_audit(), exports_audit));
|
|
10021
|
+
const history = getTaskHistory2(resolvedId);
|
|
10022
|
+
if (history.length === 0)
|
|
10023
|
+
return { content: [{ type: "text", text: "No history for this task." }] };
|
|
10024
|
+
const text = history.map((h) => `${h.created_at} | ${h.action}${h.field ? ` ${h.field}` : ""}${h.old_value ? ` from "${h.old_value}"` : ""}${h.new_value ? ` to "${h.new_value}"` : ""}${h.agent_id ? ` by ${h.agent_id}` : ""}`).join(`
|
|
9899
10025
|
`);
|
|
9900
|
-
|
|
10026
|
+
return { content: [{ type: "text", text: `${history.length} change(s):
|
|
9901
10027
|
${text}` }] };
|
|
9902
|
-
|
|
9903
|
-
|
|
9904
|
-
|
|
9905
|
-
|
|
9906
|
-
|
|
9907
|
-
|
|
9908
|
-
|
|
9909
|
-
|
|
9910
|
-
|
|
9911
|
-
|
|
9912
|
-
|
|
9913
|
-
|
|
9914
|
-
|
|
10028
|
+
} catch (e) {
|
|
10029
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
10030
|
+
}
|
|
10031
|
+
});
|
|
10032
|
+
}
|
|
10033
|
+
if (shouldRegisterTool("get_recent_activity")) {
|
|
10034
|
+
server.tool("get_recent_activity", "Get recent task changes \u2014 global activity feed.", {
|
|
10035
|
+
limit: exports_external.number().optional()
|
|
10036
|
+
}, async ({ limit }) => {
|
|
10037
|
+
try {
|
|
10038
|
+
const { getRecentActivity: getRecentActivity2 } = await Promise.resolve().then(() => (init_audit(), exports_audit));
|
|
10039
|
+
const activity = getRecentActivity2(limit || 50);
|
|
10040
|
+
if (activity.length === 0)
|
|
10041
|
+
return { content: [{ type: "text", text: "No recent activity." }] };
|
|
10042
|
+
const text = activity.map((h) => `${h.created_at} | ${h.task_id.slice(0, 8)} | ${h.action}${h.field ? ` ${h.field}` : ""}${h.agent_id ? ` by ${h.agent_id}` : ""}`).join(`
|
|
9915
10043
|
`);
|
|
9916
|
-
|
|
10044
|
+
return { content: [{ type: "text", text: `${activity.length} recent change(s):
|
|
9917
10045
|
${text}` }] };
|
|
9918
|
-
|
|
9919
|
-
|
|
9920
|
-
|
|
9921
|
-
|
|
9922
|
-
|
|
9923
|
-
|
|
9924
|
-
|
|
9925
|
-
|
|
9926
|
-
|
|
9927
|
-
|
|
9928
|
-
|
|
9929
|
-
|
|
9930
|
-
|
|
9931
|
-
|
|
9932
|
-
|
|
9933
|
-
|
|
9934
|
-
|
|
9935
|
-
|
|
9936
|
-
|
|
9937
|
-
|
|
9938
|
-
|
|
9939
|
-
|
|
9940
|
-
|
|
9941
|
-
|
|
10046
|
+
} catch (e) {
|
|
10047
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
10048
|
+
}
|
|
10049
|
+
});
|
|
10050
|
+
}
|
|
10051
|
+
if (shouldRegisterTool("create_webhook")) {
|
|
10052
|
+
server.tool("create_webhook", "Register a webhook for task change events.", {
|
|
10053
|
+
url: exports_external.string(),
|
|
10054
|
+
events: exports_external.array(exports_external.string()).optional(),
|
|
10055
|
+
secret: exports_external.string().optional()
|
|
10056
|
+
}, async (params) => {
|
|
10057
|
+
try {
|
|
10058
|
+
const { createWebhook: createWebhook2 } = await Promise.resolve().then(() => (init_webhooks(), exports_webhooks));
|
|
10059
|
+
const wh = createWebhook2(params);
|
|
10060
|
+
return { content: [{ type: "text", text: `Webhook created: ${wh.id.slice(0, 8)} | ${wh.url} | events: ${wh.events.length === 0 ? "all" : wh.events.join(",")}` }] };
|
|
10061
|
+
} catch (e) {
|
|
10062
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
10063
|
+
}
|
|
10064
|
+
});
|
|
10065
|
+
}
|
|
10066
|
+
if (shouldRegisterTool("list_webhooks")) {
|
|
10067
|
+
server.tool("list_webhooks", "List all registered webhooks", {}, async () => {
|
|
10068
|
+
try {
|
|
10069
|
+
const { listWebhooks: listWebhooks2 } = await Promise.resolve().then(() => (init_webhooks(), exports_webhooks));
|
|
10070
|
+
const webhooks = listWebhooks2();
|
|
10071
|
+
if (webhooks.length === 0)
|
|
10072
|
+
return { content: [{ type: "text", text: "No webhooks registered." }] };
|
|
10073
|
+
const text = webhooks.map((w) => `${w.id.slice(0, 8)} | ${w.active ? "active" : "inactive"} | ${w.url} | events: ${w.events.length === 0 ? "all" : w.events.join(",")}`).join(`
|
|
9942
10074
|
`);
|
|
9943
|
-
|
|
10075
|
+
return { content: [{ type: "text", text: `${webhooks.length} webhook(s):
|
|
9944
10076
|
${text}` }] };
|
|
9945
|
-
|
|
9946
|
-
|
|
9947
|
-
|
|
9948
|
-
|
|
9949
|
-
|
|
9950
|
-
|
|
9951
|
-
|
|
9952
|
-
|
|
9953
|
-
|
|
9954
|
-
|
|
9955
|
-
|
|
9956
|
-
|
|
9957
|
-
|
|
9958
|
-
|
|
9959
|
-
|
|
9960
|
-
|
|
9961
|
-
|
|
9962
|
-
|
|
9963
|
-
|
|
9964
|
-
|
|
9965
|
-
|
|
9966
|
-
|
|
9967
|
-
|
|
9968
|
-
|
|
9969
|
-
|
|
9970
|
-
|
|
9971
|
-
|
|
9972
|
-
|
|
9973
|
-
|
|
9974
|
-
|
|
9975
|
-
|
|
9976
|
-
|
|
9977
|
-
|
|
9978
|
-
|
|
9979
|
-
|
|
9980
|
-
|
|
9981
|
-
|
|
9982
|
-
|
|
9983
|
-
|
|
10077
|
+
} catch (e) {
|
|
10078
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
10079
|
+
}
|
|
10080
|
+
});
|
|
10081
|
+
}
|
|
10082
|
+
if (shouldRegisterTool("delete_webhook")) {
|
|
10083
|
+
server.tool("delete_webhook", "Delete a webhook by ID.", {
|
|
10084
|
+
id: exports_external.string()
|
|
10085
|
+
}, async ({ id }) => {
|
|
10086
|
+
try {
|
|
10087
|
+
const { deleteWebhook: deleteWebhook2 } = await Promise.resolve().then(() => (init_webhooks(), exports_webhooks));
|
|
10088
|
+
const deleted = deleteWebhook2(id);
|
|
10089
|
+
return { content: [{ type: "text", text: deleted ? "Webhook deleted." : "Webhook not found." }] };
|
|
10090
|
+
} catch (e) {
|
|
10091
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
10092
|
+
}
|
|
10093
|
+
});
|
|
10094
|
+
}
|
|
10095
|
+
if (shouldRegisterTool("create_template")) {
|
|
10096
|
+
server.tool("create_template", "Create a reusable task template.", {
|
|
10097
|
+
name: exports_external.string(),
|
|
10098
|
+
title_pattern: exports_external.string(),
|
|
10099
|
+
description: exports_external.string().optional(),
|
|
10100
|
+
priority: exports_external.enum(["low", "medium", "high", "critical"]).optional(),
|
|
10101
|
+
tags: exports_external.array(exports_external.string()).optional(),
|
|
10102
|
+
project_id: exports_external.string().optional(),
|
|
10103
|
+
plan_id: exports_external.string().optional()
|
|
10104
|
+
}, async (params) => {
|
|
10105
|
+
try {
|
|
10106
|
+
const { createTemplate: createTemplate2 } = await Promise.resolve().then(() => (init_templates(), exports_templates));
|
|
10107
|
+
const t = createTemplate2(params);
|
|
10108
|
+
return { content: [{ type: "text", text: `Template created: ${t.id.slice(0, 8)} | ${t.name} | "${t.title_pattern}"` }] };
|
|
10109
|
+
} catch (e) {
|
|
10110
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
10111
|
+
}
|
|
10112
|
+
});
|
|
10113
|
+
}
|
|
10114
|
+
if (shouldRegisterTool("list_templates")) {
|
|
10115
|
+
server.tool("list_templates", "List all task templates", {}, async () => {
|
|
10116
|
+
try {
|
|
10117
|
+
const { listTemplates: listTemplates2 } = await Promise.resolve().then(() => (init_templates(), exports_templates));
|
|
10118
|
+
const templates = listTemplates2();
|
|
10119
|
+
if (templates.length === 0)
|
|
10120
|
+
return { content: [{ type: "text", text: "No templates." }] };
|
|
10121
|
+
const text = templates.map((t) => `${t.id.slice(0, 8)} | ${t.name} | "${t.title_pattern}" | ${t.priority}`).join(`
|
|
9984
10122
|
`);
|
|
9985
|
-
|
|
10123
|
+
return { content: [{ type: "text", text: `${templates.length} template(s):
|
|
9986
10124
|
${text}` }] };
|
|
9987
|
-
|
|
9988
|
-
|
|
9989
|
-
|
|
9990
|
-
|
|
9991
|
-
|
|
9992
|
-
|
|
9993
|
-
|
|
9994
|
-
|
|
9995
|
-
|
|
9996
|
-
|
|
9997
|
-
|
|
9998
|
-
|
|
9999
|
-
|
|
10000
|
-
|
|
10001
|
-
|
|
10002
|
-
|
|
10003
|
-
|
|
10004
|
-
|
|
10005
|
-
|
|
10006
|
-
|
|
10007
|
-
|
|
10008
|
-
|
|
10009
|
-
|
|
10010
|
-
|
|
10011
|
-
|
|
10012
|
-
|
|
10013
|
-
|
|
10014
|
-
|
|
10015
|
-
|
|
10016
|
-
|
|
10017
|
-
|
|
10018
|
-
|
|
10019
|
-
|
|
10020
|
-
|
|
10021
|
-
|
|
10022
|
-
|
|
10023
|
-
|
|
10024
|
-
|
|
10025
|
-
|
|
10026
|
-
|
|
10027
|
-
|
|
10028
|
-
|
|
10029
|
-
|
|
10030
|
-
|
|
10031
|
-
|
|
10032
|
-
|
|
10033
|
-
|
|
10034
|
-
|
|
10035
|
-
|
|
10036
|
-
|
|
10037
|
-
|
|
10038
|
-
|
|
10039
|
-
|
|
10040
|
-
|
|
10041
|
-
|
|
10042
|
-
|
|
10043
|
-
|
|
10044
|
-
|
|
10045
|
-
|
|
10046
|
-
|
|
10047
|
-
|
|
10048
|
-
|
|
10049
|
-
|
|
10050
|
-
|
|
10051
|
-
|
|
10052
|
-
|
|
10053
|
-
|
|
10054
|
-
|
|
10055
|
-
|
|
10056
|
-
|
|
10125
|
+
} catch (e) {
|
|
10126
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
10127
|
+
}
|
|
10128
|
+
});
|
|
10129
|
+
}
|
|
10130
|
+
if (shouldRegisterTool("create_task_from_template")) {
|
|
10131
|
+
server.tool("create_task_from_template", "Create a task from a template with optional overrides.", {
|
|
10132
|
+
template_id: exports_external.string(),
|
|
10133
|
+
title: exports_external.string().optional(),
|
|
10134
|
+
description: exports_external.string().optional(),
|
|
10135
|
+
priority: exports_external.enum(["low", "medium", "high", "critical"]).optional(),
|
|
10136
|
+
assigned_to: exports_external.string().optional(),
|
|
10137
|
+
project_id: exports_external.string().optional()
|
|
10138
|
+
}, async (params) => {
|
|
10139
|
+
try {
|
|
10140
|
+
const { taskFromTemplate: taskFromTemplate2 } = await Promise.resolve().then(() => (init_templates(), exports_templates));
|
|
10141
|
+
const input = taskFromTemplate2(params.template_id, {
|
|
10142
|
+
title: params.title,
|
|
10143
|
+
description: params.description,
|
|
10144
|
+
priority: params.priority,
|
|
10145
|
+
assigned_to: params.assigned_to,
|
|
10146
|
+
project_id: params.project_id
|
|
10147
|
+
});
|
|
10148
|
+
const task = createTask(input);
|
|
10149
|
+
return { content: [{ type: "text", text: `Task created from template:
|
|
10150
|
+
${task.id.slice(0, 8)} | ${task.priority} | ${task.title}` }] };
|
|
10151
|
+
} catch (e) {
|
|
10152
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
10153
|
+
}
|
|
10154
|
+
});
|
|
10155
|
+
}
|
|
10156
|
+
if (shouldRegisterTool("delete_template")) {
|
|
10157
|
+
server.tool("delete_template", "Delete a task template by ID.", { id: exports_external.string() }, async ({ id }) => {
|
|
10158
|
+
try {
|
|
10159
|
+
const { deleteTemplate: deleteTemplate2 } = await Promise.resolve().then(() => (init_templates(), exports_templates));
|
|
10160
|
+
const deleted = deleteTemplate2(id);
|
|
10161
|
+
return { content: [{ type: "text", text: deleted ? "Template deleted." : "Template not found." }] };
|
|
10162
|
+
} catch (e) {
|
|
10163
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
10164
|
+
}
|
|
10165
|
+
});
|
|
10166
|
+
}
|
|
10167
|
+
if (shouldRegisterTool("approve_task")) {
|
|
10168
|
+
server.tool("approve_task", "Approve a task with requires_approval=true.", {
|
|
10169
|
+
id: exports_external.string(),
|
|
10170
|
+
agent_id: exports_external.string().optional()
|
|
10171
|
+
}, async ({ id, agent_id }) => {
|
|
10172
|
+
try {
|
|
10173
|
+
const resolvedId = resolveId(id);
|
|
10174
|
+
const task = getTaskWithRelations(resolvedId);
|
|
10175
|
+
if (!task)
|
|
10176
|
+
return { content: [{ type: "text", text: `Task not found: ${id}` }], isError: true };
|
|
10177
|
+
if (!task.requires_approval)
|
|
10178
|
+
return { content: [{ type: "text", text: `Task ${id} does not require approval.` }] };
|
|
10179
|
+
if (task.approved_by)
|
|
10180
|
+
return { content: [{ type: "text", text: `Task already approved by ${task.approved_by}.` }] };
|
|
10181
|
+
const updated = updateTask(resolvedId, { approved_by: agent_id || "system", version: task.version });
|
|
10182
|
+
return { content: [{ type: "text", text: `Task approved by ${agent_id || "system"}: ${updated.id.slice(0, 8)} | ${updated.title}` }] };
|
|
10183
|
+
} catch (e) {
|
|
10184
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
10185
|
+
}
|
|
10186
|
+
});
|
|
10187
|
+
}
|
|
10188
|
+
if (shouldRegisterTool("fail_task")) {
|
|
10189
|
+
server.tool("fail_task", "Mark a task as failed with structured reason and optional auto-retry.", {
|
|
10190
|
+
id: exports_external.string(),
|
|
10191
|
+
agent_id: exports_external.string().optional(),
|
|
10192
|
+
reason: exports_external.string().optional(),
|
|
10193
|
+
error_code: exports_external.string().optional(),
|
|
10194
|
+
retry: exports_external.boolean().optional(),
|
|
10195
|
+
retry_after: exports_external.string().optional()
|
|
10196
|
+
}, async ({ id, agent_id, reason, error_code, retry, retry_after }) => {
|
|
10197
|
+
try {
|
|
10198
|
+
const resolvedId = resolveId(id);
|
|
10199
|
+
const result = failTask(resolvedId, agent_id, reason, { retry, retry_after, error_code });
|
|
10200
|
+
let text = `failed: ${formatTask(result.task)}`;
|
|
10201
|
+
if (reason)
|
|
10202
|
+
text += `
|
|
10057
10203
|
Reason: ${reason}`;
|
|
10058
|
-
|
|
10059
|
-
|
|
10204
|
+
if (result.retryTask) {
|
|
10205
|
+
text += `
|
|
10060
10206
|
Retry task created: ${formatTask(result.retryTask)}`;
|
|
10207
|
+
}
|
|
10208
|
+
return { content: [{ type: "text", text }] };
|
|
10209
|
+
} catch (e) {
|
|
10210
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
10061
10211
|
}
|
|
10062
|
-
|
|
10063
|
-
|
|
10064
|
-
|
|
10065
|
-
|
|
10066
|
-
|
|
10067
|
-
|
|
10068
|
-
|
|
10069
|
-
|
|
10070
|
-
|
|
10071
|
-
|
|
10072
|
-
|
|
10073
|
-
|
|
10074
|
-
|
|
10075
|
-
|
|
10076
|
-
|
|
10077
|
-
|
|
10078
|
-
|
|
10079
|
-
|
|
10080
|
-
|
|
10081
|
-
|
|
10082
|
-
if (pending.length > 0) {
|
|
10083
|
-
lines.push(`
|
|
10212
|
+
});
|
|
10213
|
+
}
|
|
10214
|
+
if (shouldRegisterTool("get_my_tasks")) {
|
|
10215
|
+
server.tool("get_my_tasks", "Get tasks assigned to/created by an agent with stats.", {
|
|
10216
|
+
agent_name: exports_external.string()
|
|
10217
|
+
}, async ({ agent_name }) => {
|
|
10218
|
+
try {
|
|
10219
|
+
const agent = registerAgent({ name: agent_name });
|
|
10220
|
+
const tasks = listTasks({});
|
|
10221
|
+
const myTasks = tasks.filter((t) => t.assigned_to === agent_name || t.assigned_to === agent.id || t.agent_id === agent.id || t.agent_id === agent_name);
|
|
10222
|
+
const pending = myTasks.filter((t) => t.status === "pending");
|
|
10223
|
+
const inProgress = myTasks.filter((t) => t.status === "in_progress");
|
|
10224
|
+
const completed = myTasks.filter((t) => t.status === "completed");
|
|
10225
|
+
const rate = myTasks.length > 0 ? Math.round(completed.length / myTasks.length * 100) : 0;
|
|
10226
|
+
const lines = [
|
|
10227
|
+
`Agent: ${agent.name} (${agent.id})`,
|
|
10228
|
+
`Tasks: ${myTasks.length} total, ${pending.length} pending, ${inProgress.length} active, ${completed.length} done (${rate}%)`
|
|
10229
|
+
];
|
|
10230
|
+
if (pending.length > 0) {
|
|
10231
|
+
lines.push(`
|
|
10084
10232
|
Pending:`);
|
|
10085
|
-
|
|
10086
|
-
|
|
10087
|
-
|
|
10088
|
-
|
|
10089
|
-
|
|
10233
|
+
for (const t of pending.slice(0, 10))
|
|
10234
|
+
lines.push(` [${t.priority}] ${t.id.slice(0, 8)} | ${t.title}`);
|
|
10235
|
+
}
|
|
10236
|
+
if (inProgress.length > 0) {
|
|
10237
|
+
lines.push(`
|
|
10090
10238
|
In Progress:`);
|
|
10091
|
-
|
|
10092
|
-
|
|
10093
|
-
|
|
10094
|
-
|
|
10239
|
+
for (const t of inProgress)
|
|
10240
|
+
lines.push(` ${t.id.slice(0, 8)} | ${t.title}`);
|
|
10241
|
+
}
|
|
10242
|
+
return { content: [{ type: "text", text: lines.join(`
|
|
10095
10243
|
`) }] };
|
|
10096
|
-
|
|
10097
|
-
|
|
10098
|
-
|
|
10099
|
-
|
|
10100
|
-
|
|
10101
|
-
|
|
10102
|
-
|
|
10103
|
-
|
|
10104
|
-
|
|
10105
|
-
|
|
10106
|
-
|
|
10107
|
-
|
|
10108
|
-
|
|
10244
|
+
} catch (e) {
|
|
10245
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
10246
|
+
}
|
|
10247
|
+
});
|
|
10248
|
+
}
|
|
10249
|
+
if (shouldRegisterTool("get_org_chart")) {
|
|
10250
|
+
server.tool("get_org_chart", "Get agent org chart showing reporting hierarchy.", {}, async () => {
|
|
10251
|
+
try {
|
|
10252
|
+
let render = function(nodes, indent = 0) {
|
|
10253
|
+
return nodes.map((n) => {
|
|
10254
|
+
const prefix = " ".repeat(indent);
|
|
10255
|
+
const title = n.agent.title ? ` \u2014 ${n.agent.title}` : "";
|
|
10256
|
+
const level = n.agent.level ? ` (${n.agent.level})` : "";
|
|
10257
|
+
const line = `${prefix}${n.agent.name}${title}${level}`;
|
|
10258
|
+
const children = n.reports.length > 0 ? `
|
|
10109
10259
|
` + render(n.reports, indent + 1) : "";
|
|
10110
|
-
|
|
10111
|
-
|
|
10260
|
+
return line + children;
|
|
10261
|
+
}).join(`
|
|
10112
10262
|
`);
|
|
10113
|
-
|
|
10114
|
-
|
|
10115
|
-
|
|
10116
|
-
|
|
10117
|
-
|
|
10118
|
-
|
|
10119
|
-
|
|
10120
|
-
|
|
10121
|
-
|
|
10122
|
-
|
|
10123
|
-
|
|
10124
|
-
|
|
10125
|
-
|
|
10126
|
-
|
|
10127
|
-
|
|
10128
|
-
|
|
10129
|
-
|
|
10130
|
-
|
|
10131
|
-
|
|
10132
|
-
|
|
10133
|
-
if (
|
|
10134
|
-
|
|
10135
|
-
|
|
10136
|
-
|
|
10137
|
-
|
|
10138
|
-
|
|
10139
|
-
|
|
10140
|
-
|
|
10141
|
-
|
|
10142
|
-
|
|
10143
|
-
|
|
10144
|
-
|
|
10145
|
-
|
|
10146
|
-
|
|
10147
|
-
|
|
10148
|
-
|
|
10149
|
-
|
|
10150
|
-
|
|
10151
|
-
|
|
10152
|
-
|
|
10153
|
-
|
|
10154
|
-
|
|
10155
|
-
|
|
10156
|
-
|
|
10157
|
-
|
|
10158
|
-
|
|
10159
|
-
|
|
10160
|
-
|
|
10161
|
-
|
|
10263
|
+
};
|
|
10264
|
+
const { getOrgChart: getOrgChart2 } = await Promise.resolve().then(() => (init_agents(), exports_agents));
|
|
10265
|
+
const tree = getOrgChart2();
|
|
10266
|
+
const text = tree.length > 0 ? render(tree) : "No agents registered.";
|
|
10267
|
+
return { content: [{ type: "text", text }] };
|
|
10268
|
+
} catch (e) {
|
|
10269
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
10270
|
+
}
|
|
10271
|
+
});
|
|
10272
|
+
}
|
|
10273
|
+
if (shouldRegisterTool("set_reports_to")) {
|
|
10274
|
+
server.tool("set_reports_to", "Set agent reporting relationship in org chart.", {
|
|
10275
|
+
agent_name: exports_external.string(),
|
|
10276
|
+
manager_name: exports_external.string().optional()
|
|
10277
|
+
}, async ({ agent_name, manager_name }) => {
|
|
10278
|
+
try {
|
|
10279
|
+
const agent = getAgentByName(agent_name);
|
|
10280
|
+
if (!agent)
|
|
10281
|
+
return { content: [{ type: "text", text: `Agent not found: ${agent_name}` }], isError: true };
|
|
10282
|
+
let managerId = null;
|
|
10283
|
+
if (manager_name) {
|
|
10284
|
+
const manager = getAgentByName(manager_name);
|
|
10285
|
+
if (!manager)
|
|
10286
|
+
return { content: [{ type: "text", text: `Manager not found: ${manager_name}` }], isError: true };
|
|
10287
|
+
managerId = manager.id;
|
|
10288
|
+
}
|
|
10289
|
+
const { updateAgent: updateAgent2 } = await Promise.resolve().then(() => (init_agents(), exports_agents));
|
|
10290
|
+
updateAgent2(agent.id, { reports_to: managerId });
|
|
10291
|
+
const result = managerId ? `${agent_name} now reports to ${manager_name}` : `${agent_name} reports to no one (top-level)`;
|
|
10292
|
+
return { content: [{ type: "text", text: result }] };
|
|
10293
|
+
} catch (e) {
|
|
10294
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
10295
|
+
}
|
|
10296
|
+
});
|
|
10297
|
+
}
|
|
10298
|
+
if (shouldRegisterTool("bulk_update_tasks")) {
|
|
10299
|
+
server.tool("bulk_update_tasks", "Update multiple tasks at once with the same changes.", {
|
|
10300
|
+
task_ids: exports_external.array(exports_external.string()),
|
|
10301
|
+
status: exports_external.enum(["pending", "in_progress", "completed", "failed", "cancelled"]).optional(),
|
|
10302
|
+
priority: exports_external.enum(["low", "medium", "high", "critical"]).optional(),
|
|
10303
|
+
assigned_to: exports_external.string().optional(),
|
|
10304
|
+
tags: exports_external.array(exports_external.string()).optional()
|
|
10305
|
+
}, async ({ task_ids, ...updates }) => {
|
|
10306
|
+
try {
|
|
10307
|
+
const resolvedIds = task_ids.map((id) => resolveId(id));
|
|
10308
|
+
const result = bulkUpdateTasks(resolvedIds, updates);
|
|
10309
|
+
const parts = [`Updated ${result.updated} task(s).`];
|
|
10310
|
+
if (result.failed.length > 0) {
|
|
10311
|
+
parts.push(`Failed ${result.failed.length}:`);
|
|
10312
|
+
for (const f of result.failed)
|
|
10313
|
+
parts.push(` ${f.id.slice(0, 8)}: ${f.error}`);
|
|
10314
|
+
}
|
|
10315
|
+
return { content: [{ type: "text", text: parts.join(`
|
|
10162
10316
|
`) }] };
|
|
10163
|
-
|
|
10164
|
-
|
|
10165
|
-
|
|
10166
|
-
|
|
10167
|
-
|
|
10168
|
-
|
|
10169
|
-
|
|
10170
|
-
|
|
10171
|
-
|
|
10172
|
-
|
|
10173
|
-
|
|
10174
|
-
|
|
10175
|
-
|
|
10176
|
-
|
|
10177
|
-
|
|
10178
|
-
|
|
10179
|
-
|
|
10180
|
-
|
|
10181
|
-
|
|
10182
|
-
|
|
10183
|
-
|
|
10184
|
-
resolved
|
|
10185
|
-
|
|
10186
|
-
|
|
10187
|
-
|
|
10188
|
-
|
|
10189
|
-
|
|
10190
|
-
|
|
10191
|
-
|
|
10192
|
-
|
|
10193
|
-
|
|
10194
|
-
|
|
10195
|
-
|
|
10196
|
-
|
|
10197
|
-
|
|
10198
|
-
|
|
10199
|
-
|
|
10200
|
-
|
|
10201
|
-
|
|
10202
|
-
|
|
10203
|
-
|
|
10204
|
-
|
|
10205
|
-
filters
|
|
10206
|
-
|
|
10207
|
-
|
|
10208
|
-
|
|
10209
|
-
|
|
10210
|
-
|
|
10211
|
-
|
|
10212
|
-
|
|
10213
|
-
|
|
10214
|
-
|
|
10215
|
-
|
|
10216
|
-
|
|
10217
|
-
|
|
10218
|
-
|
|
10219
|
-
|
|
10220
|
-
|
|
10221
|
-
|
|
10222
|
-
|
|
10223
|
-
|
|
10317
|
+
} catch (e) {
|
|
10318
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
10319
|
+
}
|
|
10320
|
+
});
|
|
10321
|
+
}
|
|
10322
|
+
if (shouldRegisterTool("clone_task")) {
|
|
10323
|
+
server.tool("clone_task", "Duplicate a task with optional field overrides.", {
|
|
10324
|
+
task_id: exports_external.string(),
|
|
10325
|
+
title: exports_external.string().optional(),
|
|
10326
|
+
description: exports_external.string().optional(),
|
|
10327
|
+
priority: exports_external.enum(["low", "medium", "high", "critical"]).optional(),
|
|
10328
|
+
status: exports_external.enum(["pending", "in_progress", "completed", "failed", "cancelled"]).optional(),
|
|
10329
|
+
project_id: exports_external.string().optional(),
|
|
10330
|
+
plan_id: exports_external.string().optional(),
|
|
10331
|
+
task_list_id: exports_external.string().optional(),
|
|
10332
|
+
assigned_to: exports_external.string().optional(),
|
|
10333
|
+
tags: exports_external.array(exports_external.string()).optional(),
|
|
10334
|
+
estimated_minutes: exports_external.number().optional()
|
|
10335
|
+
}, async ({ task_id, ...overrides }) => {
|
|
10336
|
+
try {
|
|
10337
|
+
const resolvedId = resolveId(task_id);
|
|
10338
|
+
const resolved = { ...overrides };
|
|
10339
|
+
if (resolved.project_id)
|
|
10340
|
+
resolved.project_id = resolveId(resolved.project_id, "projects");
|
|
10341
|
+
if (resolved.plan_id)
|
|
10342
|
+
resolved.plan_id = resolveId(resolved.plan_id, "plans");
|
|
10343
|
+
if (resolved.task_list_id)
|
|
10344
|
+
resolved.task_list_id = resolveId(resolved.task_list_id, "task_lists");
|
|
10345
|
+
const task = cloneTask(resolvedId, resolved);
|
|
10346
|
+
return { content: [{ type: "text", text: `cloned: ${formatTask(task)}` }] };
|
|
10347
|
+
} catch (e) {
|
|
10348
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
10349
|
+
}
|
|
10350
|
+
});
|
|
10351
|
+
}
|
|
10352
|
+
if (shouldRegisterTool("get_task_stats")) {
|
|
10353
|
+
server.tool("get_task_stats", "Get task analytics: counts by status, priority, agent.", {
|
|
10354
|
+
project_id: exports_external.string().optional(),
|
|
10355
|
+
task_list_id: exports_external.string().optional(),
|
|
10356
|
+
agent_id: exports_external.string().optional()
|
|
10357
|
+
}, async ({ project_id, task_list_id, agent_id }) => {
|
|
10358
|
+
try {
|
|
10359
|
+
const filters = {};
|
|
10360
|
+
if (project_id)
|
|
10361
|
+
filters.project_id = resolveId(project_id, "projects");
|
|
10362
|
+
if (task_list_id)
|
|
10363
|
+
filters.task_list_id = resolveId(task_list_id, "task_lists");
|
|
10364
|
+
if (agent_id)
|
|
10365
|
+
filters.agent_id = agent_id;
|
|
10366
|
+
const stats = getTaskStats(Object.keys(filters).length > 0 ? filters : undefined);
|
|
10367
|
+
return { content: [{ type: "text", text: JSON.stringify(stats, null, 2) }] };
|
|
10368
|
+
} catch (e) {
|
|
10369
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
10370
|
+
}
|
|
10371
|
+
});
|
|
10372
|
+
}
|
|
10373
|
+
if (shouldRegisterTool("get_task_graph")) {
|
|
10374
|
+
server.tool("get_task_graph", "Get full dependency tree for a task.", {
|
|
10375
|
+
id: exports_external.string(),
|
|
10376
|
+
direction: exports_external.enum(["up", "down", "both"]).optional()
|
|
10377
|
+
}, async ({ id, direction }) => {
|
|
10378
|
+
try {
|
|
10379
|
+
let formatNode = function(node, indent) {
|
|
10380
|
+
const prefix = " ".repeat(indent);
|
|
10381
|
+
const idLabel = node.task.short_id || node.task.id.slice(0, 8);
|
|
10382
|
+
const blocked = node.task.is_blocked ? " (blocked: yes)" : "";
|
|
10383
|
+
let out = `${prefix}[${node.task.status}] ${idLabel} | ${node.task.title}${blocked}
|
|
10224
10384
|
`;
|
|
10225
|
-
|
|
10226
|
-
|
|
10385
|
+
if (node.depends_on.length > 0) {
|
|
10386
|
+
out += `${prefix} Depends on:
|
|
10227
10387
|
`;
|
|
10228
|
-
|
|
10229
|
-
|
|
10388
|
+
for (const dep of node.depends_on) {
|
|
10389
|
+
out += formatNode(dep, indent + 2);
|
|
10390
|
+
}
|
|
10230
10391
|
}
|
|
10231
|
-
|
|
10232
|
-
|
|
10233
|
-
out += `${prefix} Blocks:
|
|
10392
|
+
if (node.blocks.length > 0) {
|
|
10393
|
+
out += `${prefix} Blocks:
|
|
10234
10394
|
`;
|
|
10235
|
-
|
|
10236
|
-
|
|
10395
|
+
for (const dep of node.blocks) {
|
|
10396
|
+
out += formatNode(dep, indent + 2);
|
|
10397
|
+
}
|
|
10237
10398
|
}
|
|
10238
|
-
|
|
10239
|
-
|
|
10240
|
-
|
|
10241
|
-
|
|
10242
|
-
|
|
10243
|
-
|
|
10244
|
-
|
|
10245
|
-
text += `
|
|
10399
|
+
return out;
|
|
10400
|
+
};
|
|
10401
|
+
const taskId = resolveId(id, "tasks");
|
|
10402
|
+
const graph = getTaskGraph(taskId, direction || "both");
|
|
10403
|
+
let text = `Task: ${formatNode(graph, 0)}`;
|
|
10404
|
+
if (graph.depends_on.length > 0) {
|
|
10405
|
+
text += `
|
|
10246
10406
|
Depends on:
|
|
10247
10407
|
`;
|
|
10248
|
-
|
|
10249
|
-
|
|
10408
|
+
for (const dep of graph.depends_on) {
|
|
10409
|
+
text += formatNode(dep, 1);
|
|
10410
|
+
}
|
|
10250
10411
|
}
|
|
10251
|
-
|
|
10252
|
-
|
|
10253
|
-
text += `
|
|
10412
|
+
if (graph.blocks.length > 0) {
|
|
10413
|
+
text += `
|
|
10254
10414
|
Blocks:
|
|
10255
10415
|
`;
|
|
10256
|
-
|
|
10257
|
-
|
|
10416
|
+
for (const dep of graph.blocks) {
|
|
10417
|
+
text += formatNode(dep, 1);
|
|
10418
|
+
}
|
|
10258
10419
|
}
|
|
10420
|
+
return { content: [{ type: "text", text }] };
|
|
10421
|
+
} catch (e) {
|
|
10422
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
10259
10423
|
}
|
|
10260
|
-
|
|
10261
|
-
|
|
10262
|
-
|
|
10263
|
-
|
|
10264
|
-
|
|
10265
|
-
|
|
10266
|
-
|
|
10267
|
-
|
|
10268
|
-
|
|
10269
|
-
|
|
10270
|
-
|
|
10271
|
-
|
|
10424
|
+
});
|
|
10425
|
+
}
|
|
10426
|
+
if (shouldRegisterTool("bulk_create_tasks")) {
|
|
10427
|
+
server.tool("bulk_create_tasks", "Create multiple tasks atomically with dependency support.", {
|
|
10428
|
+
tasks: exports_external.array(exports_external.object({
|
|
10429
|
+
temp_id: exports_external.string().optional(),
|
|
10430
|
+
title: exports_external.string(),
|
|
10431
|
+
description: exports_external.string().optional(),
|
|
10432
|
+
priority: exports_external.enum(["low", "medium", "high", "critical"]).optional(),
|
|
10433
|
+
status: exports_external.enum(["pending", "in_progress", "completed", "failed", "cancelled"]).optional(),
|
|
10434
|
+
project_id: exports_external.string().optional(),
|
|
10435
|
+
plan_id: exports_external.string().optional(),
|
|
10436
|
+
task_list_id: exports_external.string().optional(),
|
|
10437
|
+
agent_id: exports_external.string().optional(),
|
|
10438
|
+
assigned_to: exports_external.string().optional(),
|
|
10439
|
+
tags: exports_external.array(exports_external.string()).optional(),
|
|
10440
|
+
estimated_minutes: exports_external.number().optional(),
|
|
10441
|
+
depends_on_temp_ids: exports_external.array(exports_external.string()).optional()
|
|
10442
|
+
})),
|
|
10272
10443
|
project_id: exports_external.string().optional(),
|
|
10273
10444
|
plan_id: exports_external.string().optional(),
|
|
10274
|
-
task_list_id: exports_external.string().optional()
|
|
10275
|
-
|
|
10276
|
-
|
|
10277
|
-
|
|
10278
|
-
|
|
10279
|
-
|
|
10280
|
-
|
|
10281
|
-
|
|
10282
|
-
|
|
10283
|
-
|
|
10284
|
-
|
|
10285
|
-
|
|
10286
|
-
|
|
10287
|
-
|
|
10288
|
-
|
|
10289
|
-
|
|
10290
|
-
|
|
10291
|
-
|
|
10292
|
-
|
|
10293
|
-
task_list_id: t.task_list_id || resolvedTaskListId
|
|
10294
|
-
}));
|
|
10295
|
-
const result = bulkCreateTasks(enrichedTasks);
|
|
10296
|
-
const lines = result.created.map((t) => {
|
|
10297
|
-
const tid = t.temp_id ? `[${t.temp_id}] ` : "";
|
|
10298
|
-
const sid = t.short_id || t.id.slice(0, 8);
|
|
10299
|
-
return ` ${tid}${sid} | ${t.title}`;
|
|
10300
|
-
});
|
|
10301
|
-
return { content: [{ type: "text", text: `Created ${result.created.length} task(s):
|
|
10445
|
+
task_list_id: exports_external.string().optional()
|
|
10446
|
+
}, async ({ tasks, project_id, plan_id, task_list_id }) => {
|
|
10447
|
+
try {
|
|
10448
|
+
const resolvedProjectId = project_id ? resolveId(project_id, "projects") : undefined;
|
|
10449
|
+
const resolvedPlanId = plan_id ? resolveId(plan_id, "plans") : undefined;
|
|
10450
|
+
const resolvedTaskListId = task_list_id ? resolveId(task_list_id, "task_lists") : undefined;
|
|
10451
|
+
const enrichedTasks = tasks.map((t) => ({
|
|
10452
|
+
...t,
|
|
10453
|
+
project_id: t.project_id || resolvedProjectId,
|
|
10454
|
+
plan_id: t.plan_id || resolvedPlanId,
|
|
10455
|
+
task_list_id: t.task_list_id || resolvedTaskListId
|
|
10456
|
+
}));
|
|
10457
|
+
const result = bulkCreateTasks(enrichedTasks);
|
|
10458
|
+
const lines = result.created.map((t) => {
|
|
10459
|
+
const tid = t.temp_id ? `[${t.temp_id}] ` : "";
|
|
10460
|
+
const sid = t.short_id || t.id.slice(0, 8);
|
|
10461
|
+
return ` ${tid}${sid} | ${t.title}`;
|
|
10462
|
+
});
|
|
10463
|
+
return { content: [{ type: "text", text: `Created ${result.created.length} task(s):
|
|
10302
10464
|
${lines.join(`
|
|
10303
10465
|
`)}` }] };
|
|
10304
|
-
|
|
10305
|
-
|
|
10306
|
-
|
|
10307
|
-
|
|
10308
|
-
|
|
10309
|
-
|
|
10310
|
-
|
|
10311
|
-
|
|
10312
|
-
|
|
10313
|
-
|
|
10314
|
-
|
|
10315
|
-
|
|
10316
|
-
|
|
10317
|
-
|
|
10318
|
-
resolvedTarget
|
|
10319
|
-
|
|
10320
|
-
|
|
10321
|
-
|
|
10322
|
-
|
|
10323
|
-
|
|
10324
|
-
|
|
10325
|
-
|
|
10326
|
-
|
|
10327
|
-
|
|
10328
|
-
|
|
10329
|
-
|
|
10330
|
-
|
|
10331
|
-
|
|
10332
|
-
|
|
10333
|
-
|
|
10334
|
-
|
|
10335
|
-
|
|
10336
|
-
|
|
10337
|
-
|
|
10338
|
-
|
|
10339
|
-
|
|
10340
|
-
|
|
10341
|
-
filters
|
|
10342
|
-
|
|
10343
|
-
|
|
10344
|
-
|
|
10345
|
-
|
|
10346
|
-
|
|
10347
|
-
|
|
10348
|
-
|
|
10349
|
-
|
|
10350
|
-
|
|
10466
|
+
} catch (e) {
|
|
10467
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
10468
|
+
}
|
|
10469
|
+
});
|
|
10470
|
+
}
|
|
10471
|
+
if (shouldRegisterTool("move_task")) {
|
|
10472
|
+
server.tool("move_task", "Move a task to a different list, project, or plan.", {
|
|
10473
|
+
task_id: exports_external.string(),
|
|
10474
|
+
task_list_id: exports_external.string().nullable().optional(),
|
|
10475
|
+
project_id: exports_external.string().nullable().optional(),
|
|
10476
|
+
plan_id: exports_external.string().nullable().optional()
|
|
10477
|
+
}, async ({ task_id, ...target }) => {
|
|
10478
|
+
try {
|
|
10479
|
+
const resolvedId = resolveId(task_id);
|
|
10480
|
+
const resolvedTarget = {};
|
|
10481
|
+
if (target.task_list_id !== undefined)
|
|
10482
|
+
resolvedTarget.task_list_id = target.task_list_id ? resolveId(target.task_list_id, "task_lists") : null;
|
|
10483
|
+
if (target.project_id !== undefined)
|
|
10484
|
+
resolvedTarget.project_id = target.project_id ? resolveId(target.project_id, "projects") : null;
|
|
10485
|
+
if (target.plan_id !== undefined)
|
|
10486
|
+
resolvedTarget.plan_id = target.plan_id ? resolveId(target.plan_id, "plans") : null;
|
|
10487
|
+
const task = moveTask(resolvedId, resolvedTarget);
|
|
10488
|
+
return { content: [{ type: "text", text: `moved: ${formatTask(task)}` }] };
|
|
10489
|
+
} catch (e) {
|
|
10490
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
10491
|
+
}
|
|
10492
|
+
});
|
|
10493
|
+
}
|
|
10494
|
+
if (shouldRegisterTool("get_next_task")) {
|
|
10495
|
+
server.tool("get_next_task", "Get the best pending task to work on next.", {
|
|
10496
|
+
agent_id: exports_external.string().optional(),
|
|
10497
|
+
project_id: exports_external.string().optional(),
|
|
10498
|
+
task_list_id: exports_external.string().optional(),
|
|
10499
|
+
plan_id: exports_external.string().optional(),
|
|
10500
|
+
tags: exports_external.array(exports_external.string()).optional()
|
|
10501
|
+
}, async ({ agent_id, project_id, task_list_id, plan_id, tags }) => {
|
|
10502
|
+
try {
|
|
10503
|
+
const filters = {};
|
|
10504
|
+
if (project_id)
|
|
10505
|
+
filters.project_id = resolveId(project_id, "projects");
|
|
10506
|
+
if (task_list_id)
|
|
10507
|
+
filters.task_list_id = resolveId(task_list_id, "task_lists");
|
|
10508
|
+
if (plan_id)
|
|
10509
|
+
filters.plan_id = resolveId(plan_id, "plans");
|
|
10510
|
+
if (tags)
|
|
10511
|
+
filters.tags = tags;
|
|
10512
|
+
const task = getNextTask(agent_id, Object.keys(filters).length > 0 ? filters : undefined);
|
|
10513
|
+
if (!task) {
|
|
10514
|
+
return { content: [{ type: "text", text: "No tasks available \u2014 all pending tasks are blocked, locked, or none exist." }] };
|
|
10515
|
+
}
|
|
10516
|
+
return { content: [{ type: "text", text: `next: ${formatTask(task)}
|
|
10351
10517
|
${formatTaskDetail(task)}` }] };
|
|
10352
|
-
|
|
10353
|
-
|
|
10354
|
-
|
|
10355
|
-
|
|
10356
|
-
|
|
10357
|
-
|
|
10358
|
-
|
|
10359
|
-
|
|
10360
|
-
|
|
10361
|
-
|
|
10362
|
-
|
|
10363
|
-
filters
|
|
10364
|
-
|
|
10365
|
-
|
|
10366
|
-
|
|
10367
|
-
|
|
10368
|
-
|
|
10369
|
-
|
|
10370
|
-
|
|
10371
|
-
|
|
10372
|
-
const
|
|
10373
|
-
|
|
10374
|
-
|
|
10375
|
-
|
|
10518
|
+
} catch (e) {
|
|
10519
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
10520
|
+
}
|
|
10521
|
+
});
|
|
10522
|
+
}
|
|
10523
|
+
if (shouldRegisterTool("get_active_work")) {
|
|
10524
|
+
server.tool("get_active_work", "See all in-progress tasks and who is working on them.", {
|
|
10525
|
+
project_id: exports_external.string().optional(),
|
|
10526
|
+
task_list_id: exports_external.string().optional()
|
|
10527
|
+
}, async ({ project_id, task_list_id }) => {
|
|
10528
|
+
try {
|
|
10529
|
+
const filters = {};
|
|
10530
|
+
if (project_id)
|
|
10531
|
+
filters.project_id = resolveId(project_id, "projects");
|
|
10532
|
+
if (task_list_id)
|
|
10533
|
+
filters.task_list_id = resolveId(task_list_id, "task_lists");
|
|
10534
|
+
const work = getActiveWork(Object.keys(filters).length > 0 ? filters : undefined);
|
|
10535
|
+
if (work.length === 0) {
|
|
10536
|
+
return { content: [{ type: "text", text: "No active work \u2014 no tasks are currently in progress." }] };
|
|
10537
|
+
}
|
|
10538
|
+
const text = work.map((w) => {
|
|
10539
|
+
const id = w.short_id || w.id.slice(0, 8);
|
|
10540
|
+
const agent = w.assigned_to || w.locked_by || "unassigned";
|
|
10541
|
+
const since = w.updated_at;
|
|
10542
|
+
return `${agent.padEnd(12)} | ${w.priority.padEnd(8)} | ${id} | ${w.title} (since ${since})`;
|
|
10543
|
+
}).join(`
|
|
10376
10544
|
`);
|
|
10377
|
-
|
|
10545
|
+
return { content: [{ type: "text", text: `${work.length} active task(s):
|
|
10378
10546
|
${text}` }] };
|
|
10379
|
-
|
|
10380
|
-
|
|
10381
|
-
|
|
10382
|
-
|
|
10383
|
-
|
|
10384
|
-
|
|
10385
|
-
|
|
10386
|
-
|
|
10387
|
-
|
|
10388
|
-
|
|
10389
|
-
|
|
10390
|
-
|
|
10391
|
-
filters
|
|
10392
|
-
|
|
10393
|
-
|
|
10394
|
-
|
|
10395
|
-
|
|
10396
|
-
|
|
10397
|
-
|
|
10398
|
-
|
|
10399
|
-
|
|
10400
|
-
|
|
10401
|
-
|
|
10547
|
+
} catch (e) {
|
|
10548
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
10549
|
+
}
|
|
10550
|
+
});
|
|
10551
|
+
}
|
|
10552
|
+
if (shouldRegisterTool("get_tasks_changed_since")) {
|
|
10553
|
+
server.tool("get_tasks_changed_since", "Get tasks modified after a timestamp for incremental sync.", {
|
|
10554
|
+
since: exports_external.string(),
|
|
10555
|
+
project_id: exports_external.string().optional(),
|
|
10556
|
+
task_list_id: exports_external.string().optional()
|
|
10557
|
+
}, async ({ since, project_id, task_list_id }) => {
|
|
10558
|
+
try {
|
|
10559
|
+
const filters = {};
|
|
10560
|
+
if (project_id)
|
|
10561
|
+
filters.project_id = resolveId(project_id, "projects");
|
|
10562
|
+
if (task_list_id)
|
|
10563
|
+
filters.task_list_id = resolveId(task_list_id, "task_lists");
|
|
10564
|
+
const tasks = getTasksChangedSince(since, Object.keys(filters).length > 0 ? filters : undefined);
|
|
10565
|
+
if (tasks.length === 0) {
|
|
10566
|
+
return { content: [{ type: "text", text: `No tasks changed since ${since}.` }] };
|
|
10567
|
+
}
|
|
10568
|
+
const text = tasks.map((t) => {
|
|
10569
|
+
const assigned = t.assigned_to ? ` -> ${t.assigned_to}` : "";
|
|
10570
|
+
return `[${t.status}] ${t.id.slice(0, 8)} | ${t.priority} | ${t.title}${assigned} (updated: ${t.updated_at})`;
|
|
10571
|
+
}).join(`
|
|
10402
10572
|
`);
|
|
10403
|
-
|
|
10573
|
+
return { content: [{ type: "text", text: `${tasks.length} task(s) changed since ${since}:
|
|
10404
10574
|
${text}` }] };
|
|
10405
|
-
|
|
10406
|
-
|
|
10407
|
-
|
|
10408
|
-
|
|
10409
|
-
|
|
10410
|
-
|
|
10411
|
-
|
|
10412
|
-
|
|
10413
|
-
|
|
10414
|
-
|
|
10415
|
-
|
|
10416
|
-
|
|
10417
|
-
|
|
10418
|
-
|
|
10419
|
-
filters
|
|
10420
|
-
|
|
10421
|
-
|
|
10422
|
-
|
|
10423
|
-
|
|
10424
|
-
|
|
10425
|
-
|
|
10426
|
-
|
|
10427
|
-
|
|
10428
|
-
|
|
10429
|
-
|
|
10430
|
-
|
|
10575
|
+
} catch (e) {
|
|
10576
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
10577
|
+
}
|
|
10578
|
+
});
|
|
10579
|
+
}
|
|
10580
|
+
if (shouldRegisterTool("claim_next_task")) {
|
|
10581
|
+
server.tool("claim_next_task", "Atomically claim, lock, and start the best pending task.", {
|
|
10582
|
+
agent_id: exports_external.string(),
|
|
10583
|
+
project_id: exports_external.string().optional(),
|
|
10584
|
+
task_list_id: exports_external.string().optional(),
|
|
10585
|
+
plan_id: exports_external.string().optional(),
|
|
10586
|
+
tags: exports_external.array(exports_external.string()).optional()
|
|
10587
|
+
}, async ({ agent_id, project_id, task_list_id, plan_id, tags }) => {
|
|
10588
|
+
try {
|
|
10589
|
+
const filters = {};
|
|
10590
|
+
if (project_id)
|
|
10591
|
+
filters.project_id = resolveId(project_id, "projects");
|
|
10592
|
+
if (task_list_id)
|
|
10593
|
+
filters.task_list_id = resolveId(task_list_id, "task_lists");
|
|
10594
|
+
if (plan_id)
|
|
10595
|
+
filters.plan_id = resolveId(plan_id, "plans");
|
|
10596
|
+
if (tags)
|
|
10597
|
+
filters.tags = tags;
|
|
10598
|
+
const task = claimNextTask(agent_id, Object.keys(filters).length > 0 ? filters : undefined);
|
|
10599
|
+
if (!task) {
|
|
10600
|
+
return { content: [{ type: "text", text: "No tasks available to claim." }] };
|
|
10601
|
+
}
|
|
10602
|
+
return { content: [{ type: "text", text: `claimed: ${formatTask(task)}
|
|
10431
10603
|
${formatTaskDetail(task)}` }] };
|
|
10432
|
-
|
|
10433
|
-
|
|
10434
|
-
|
|
10435
|
-
|
|
10436
|
-
|
|
10437
|
-
|
|
10438
|
-
|
|
10439
|
-
|
|
10440
|
-
|
|
10441
|
-
|
|
10442
|
-
|
|
10443
|
-
|
|
10444
|
-
filters
|
|
10445
|
-
|
|
10446
|
-
|
|
10447
|
-
|
|
10448
|
-
|
|
10449
|
-
|
|
10450
|
-
|
|
10451
|
-
|
|
10452
|
-
|
|
10453
|
-
const
|
|
10454
|
-
|
|
10455
|
-
|
|
10456
|
-
|
|
10604
|
+
} catch (e) {
|
|
10605
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
10606
|
+
}
|
|
10607
|
+
});
|
|
10608
|
+
}
|
|
10609
|
+
if (shouldRegisterTool("get_stale_tasks")) {
|
|
10610
|
+
server.tool("get_stale_tasks", "Find stale in_progress tasks with no recent activity.", {
|
|
10611
|
+
stale_minutes: exports_external.number().optional(),
|
|
10612
|
+
project_id: exports_external.string().optional(),
|
|
10613
|
+
task_list_id: exports_external.string().optional()
|
|
10614
|
+
}, async ({ stale_minutes, project_id, task_list_id }) => {
|
|
10615
|
+
try {
|
|
10616
|
+
const filters = {};
|
|
10617
|
+
if (project_id)
|
|
10618
|
+
filters.project_id = resolveId(project_id, "projects");
|
|
10619
|
+
if (task_list_id)
|
|
10620
|
+
filters.task_list_id = resolveId(task_list_id, "task_lists");
|
|
10621
|
+
const tasks = getStaleTasks(stale_minutes || 30, Object.keys(filters).length > 0 ? filters : undefined);
|
|
10622
|
+
if (tasks.length === 0) {
|
|
10623
|
+
return { content: [{ type: "text", text: "No stale tasks found." }] };
|
|
10624
|
+
}
|
|
10625
|
+
const text = tasks.map((t) => {
|
|
10626
|
+
const id = t.short_id || t.id.slice(0, 8);
|
|
10627
|
+
const agent = t.locked_by || t.assigned_to || "unknown";
|
|
10628
|
+
const staleFor = Math.round((Date.now() - new Date(t.updated_at).getTime()) / 60000);
|
|
10629
|
+
return `${id} | ${agent} | ${t.title} (stale ${staleFor}min)`;
|
|
10630
|
+
}).join(`
|
|
10457
10631
|
`);
|
|
10458
|
-
|
|
10632
|
+
return { content: [{ type: "text", text: `${tasks.length} stale task(s):
|
|
10459
10633
|
${text}` }] };
|
|
10460
|
-
|
|
10461
|
-
|
|
10462
|
-
|
|
10463
|
-
|
|
10464
|
-
|
|
10465
|
-
|
|
10466
|
-
|
|
10467
|
-
|
|
10468
|
-
|
|
10469
|
-
|
|
10470
|
-
|
|
10471
|
-
|
|
10472
|
-
filters
|
|
10473
|
-
|
|
10474
|
-
|
|
10475
|
-
|
|
10476
|
-
|
|
10477
|
-
|
|
10478
|
-
|
|
10479
|
-
|
|
10480
|
-
|
|
10481
|
-
|
|
10482
|
-
|
|
10483
|
-
|
|
10484
|
-
|
|
10634
|
+
} catch (e) {
|
|
10635
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
10636
|
+
}
|
|
10637
|
+
});
|
|
10638
|
+
}
|
|
10639
|
+
if (shouldRegisterTool("get_status")) {
|
|
10640
|
+
server.tool("get_status", "Get a full project health snapshot \u2014 counts, active work, next task, stale/overdue summary.", {
|
|
10641
|
+
agent_id: exports_external.string().optional(),
|
|
10642
|
+
project_id: exports_external.string().optional(),
|
|
10643
|
+
task_list_id: exports_external.string().optional()
|
|
10644
|
+
}, async ({ agent_id, project_id, task_list_id }) => {
|
|
10645
|
+
try {
|
|
10646
|
+
const filters = {};
|
|
10647
|
+
if (project_id)
|
|
10648
|
+
filters.project_id = resolveId(project_id, "projects");
|
|
10649
|
+
if (task_list_id)
|
|
10650
|
+
filters.task_list_id = resolveId(task_list_id, "task_lists");
|
|
10651
|
+
const status = getStatus(Object.keys(filters).length > 0 ? filters : undefined, agent_id);
|
|
10652
|
+
const lines = [
|
|
10653
|
+
`Tasks: ${status.pending} pending | ${status.in_progress} active | ${status.completed} done | ${status.total} total`
|
|
10654
|
+
];
|
|
10655
|
+
if (status.stale_count > 0)
|
|
10656
|
+
lines.push(`\u26A0\uFE0F ${status.stale_count} stale (stuck in_progress)`);
|
|
10657
|
+
if (status.overdue_recurring > 0)
|
|
10658
|
+
lines.push(`\uD83D\uDD01 ${status.overdue_recurring} overdue recurring`);
|
|
10659
|
+
if (status.active_work.length > 0) {
|
|
10660
|
+
lines.push(`
|
|
10485
10661
|
Active (${status.active_work.length}):`);
|
|
10486
|
-
|
|
10487
|
-
|
|
10488
|
-
|
|
10662
|
+
for (const w of status.active_work.slice(0, 5)) {
|
|
10663
|
+
const id = w.short_id || w.id.slice(0, 8);
|
|
10664
|
+
lines.push(` ${id} | ${w.assigned_to || w.locked_by || "?"} | ${w.title}`);
|
|
10665
|
+
}
|
|
10489
10666
|
}
|
|
10490
|
-
|
|
10491
|
-
|
|
10492
|
-
lines.push(`
|
|
10667
|
+
if (status.next_task) {
|
|
10668
|
+
lines.push(`
|
|
10493
10669
|
Next up:`);
|
|
10494
|
-
|
|
10495
|
-
|
|
10496
|
-
|
|
10670
|
+
lines.push(` ${formatTask(status.next_task)}`);
|
|
10671
|
+
} else {
|
|
10672
|
+
lines.push(`
|
|
10497
10673
|
No pending tasks available.`);
|
|
10674
|
+
}
|
|
10675
|
+
return { content: [{ type: "text", text: lines.join(`
|
|
10676
|
+
`) }] };
|
|
10677
|
+
} catch (e) {
|
|
10678
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
10498
10679
|
}
|
|
10499
|
-
|
|
10680
|
+
});
|
|
10681
|
+
}
|
|
10682
|
+
if (shouldRegisterTool("decompose_task")) {
|
|
10683
|
+
server.tool("decompose_task", "Break a task into subtasks in one call. Optionally chain them sequentially with depends_on_prev.", {
|
|
10684
|
+
parent_id: exports_external.string(),
|
|
10685
|
+
subtasks: exports_external.array(exports_external.object({
|
|
10686
|
+
title: exports_external.string(),
|
|
10687
|
+
description: exports_external.string().optional(),
|
|
10688
|
+
priority: exports_external.enum(["low", "medium", "high", "critical"]).optional(),
|
|
10689
|
+
assigned_to: exports_external.string().optional(),
|
|
10690
|
+
estimated_minutes: exports_external.number().optional(),
|
|
10691
|
+
tags: exports_external.array(exports_external.string()).optional()
|
|
10692
|
+
})),
|
|
10693
|
+
depends_on_prev: exports_external.boolean().optional()
|
|
10694
|
+
}, async ({ parent_id, subtasks, depends_on_prev }) => {
|
|
10695
|
+
try {
|
|
10696
|
+
const resolvedId = resolveId(parent_id);
|
|
10697
|
+
const result = decomposeTasks(resolvedId, subtasks, { depends_on_prev }, undefined);
|
|
10698
|
+
const lines = [
|
|
10699
|
+
`Decomposed: ${formatTask(result.parent)}`,
|
|
10700
|
+
`Created ${result.subtasks.length} subtask(s)${depends_on_prev ? " (chained)" : ""}:`,
|
|
10701
|
+
...result.subtasks.map((t, i) => ` ${i + 1}. ${formatTask(t)}`)
|
|
10702
|
+
];
|
|
10703
|
+
return { content: [{ type: "text", text: lines.join(`
|
|
10500
10704
|
`) }] };
|
|
10501
|
-
|
|
10502
|
-
|
|
10503
|
-
|
|
10504
|
-
|
|
10505
|
-
|
|
10506
|
-
|
|
10507
|
-
|
|
10508
|
-
|
|
10509
|
-
|
|
10510
|
-
|
|
10511
|
-
|
|
10512
|
-
|
|
10513
|
-
|
|
10514
|
-
|
|
10515
|
-
|
|
10516
|
-
|
|
10517
|
-
|
|
10518
|
-
|
|
10519
|
-
|
|
10520
|
-
|
|
10521
|
-
|
|
10522
|
-
|
|
10523
|
-
|
|
10524
|
-
|
|
10525
|
-
|
|
10526
|
-
|
|
10527
|
-
|
|
10528
|
-
|
|
10529
|
-
|
|
10530
|
-
|
|
10531
|
-
|
|
10532
|
-
|
|
10533
|
-
|
|
10534
|
-
|
|
10535
|
-
|
|
10536
|
-
|
|
10537
|
-
|
|
10538
|
-
|
|
10539
|
-
|
|
10540
|
-
|
|
10541
|
-
|
|
10542
|
-
|
|
10543
|
-
|
|
10544
|
-
|
|
10545
|
-
|
|
10546
|
-
|
|
10547
|
-
|
|
10548
|
-
|
|
10549
|
-
|
|
10550
|
-
|
|
10551
|
-
|
|
10552
|
-
|
|
10553
|
-
|
|
10554
|
-
|
|
10555
|
-
|
|
10556
|
-
|
|
10557
|
-
|
|
10558
|
-
|
|
10559
|
-
|
|
10560
|
-
|
|
10561
|
-
|
|
10562
|
-
|
|
10563
|
-
|
|
10564
|
-
|
|
10565
|
-
|
|
10566
|
-
|
|
10567
|
-
|
|
10568
|
-
|
|
10569
|
-
|
|
10570
|
-
|
|
10571
|
-
|
|
10572
|
-
|
|
10573
|
-
|
|
10705
|
+
} catch (e) {
|
|
10706
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
10707
|
+
}
|
|
10708
|
+
});
|
|
10709
|
+
}
|
|
10710
|
+
if (shouldRegisterTool("search_tools")) {
|
|
10711
|
+
server.tool("search_tools", "List all tool names, optionally filtered by substring.", { query: exports_external.string().optional() }, async ({ query }) => {
|
|
10712
|
+
const all = [
|
|
10713
|
+
"create_task",
|
|
10714
|
+
"list_tasks",
|
|
10715
|
+
"get_task",
|
|
10716
|
+
"update_task",
|
|
10717
|
+
"delete_task",
|
|
10718
|
+
"start_task",
|
|
10719
|
+
"complete_task",
|
|
10720
|
+
"fail_task",
|
|
10721
|
+
"lock_task",
|
|
10722
|
+
"unlock_task",
|
|
10723
|
+
"approve_task",
|
|
10724
|
+
"add_dependency",
|
|
10725
|
+
"remove_dependency",
|
|
10726
|
+
"add_comment",
|
|
10727
|
+
"create_project",
|
|
10728
|
+
"list_projects",
|
|
10729
|
+
"create_plan",
|
|
10730
|
+
"list_plans",
|
|
10731
|
+
"get_plan",
|
|
10732
|
+
"update_plan",
|
|
10733
|
+
"delete_plan",
|
|
10734
|
+
"register_agent",
|
|
10735
|
+
"list_agents",
|
|
10736
|
+
"get_agent",
|
|
10737
|
+
"rename_agent",
|
|
10738
|
+
"delete_agent",
|
|
10739
|
+
"get_my_tasks",
|
|
10740
|
+
"get_org_chart",
|
|
10741
|
+
"set_reports_to",
|
|
10742
|
+
"create_task_list",
|
|
10743
|
+
"list_task_lists",
|
|
10744
|
+
"get_task_list",
|
|
10745
|
+
"update_task_list",
|
|
10746
|
+
"delete_task_list",
|
|
10747
|
+
"search_tasks",
|
|
10748
|
+
"sync",
|
|
10749
|
+
"clone_task",
|
|
10750
|
+
"move_task",
|
|
10751
|
+
"get_next_task",
|
|
10752
|
+
"claim_next_task",
|
|
10753
|
+
"get_task_history",
|
|
10754
|
+
"get_recent_activity",
|
|
10755
|
+
"create_webhook",
|
|
10756
|
+
"list_webhooks",
|
|
10757
|
+
"delete_webhook",
|
|
10758
|
+
"create_template",
|
|
10759
|
+
"list_templates",
|
|
10760
|
+
"create_task_from_template",
|
|
10761
|
+
"delete_template",
|
|
10762
|
+
"bulk_update_tasks",
|
|
10763
|
+
"bulk_create_tasks",
|
|
10764
|
+
"get_task_stats",
|
|
10765
|
+
"get_task_graph",
|
|
10766
|
+
"get_active_work",
|
|
10767
|
+
"get_tasks_changed_since",
|
|
10768
|
+
"get_stale_tasks",
|
|
10769
|
+
"get_status",
|
|
10770
|
+
"decompose_task",
|
|
10771
|
+
"search_tools",
|
|
10772
|
+
"describe_tools"
|
|
10773
|
+
].filter((name) => shouldRegisterTool(name));
|
|
10774
|
+
const q = query?.toLowerCase();
|
|
10775
|
+
const matches = q ? all.filter((n) => n.includes(q)) : all;
|
|
10776
|
+
return { content: [{ type: "text", text: matches.join(", ") }] };
|
|
10777
|
+
});
|
|
10778
|
+
}
|
|
10779
|
+
if (shouldRegisterTool("describe_tools")) {
|
|
10780
|
+
server.tool("describe_tools", "Get detailed parameter info for specific tools by name.", { names: exports_external.array(exports_external.string()) }, async ({ names }) => {
|
|
10781
|
+
const descriptions = {
|
|
10782
|
+
create_task: `Create a new task.
|
|
10574
10783
|
Params: title(string, req), description(string), priority(low|medium|high|critical, default:medium), status(pending|in_progress|completed|failed|cancelled, default:pending), project_id(string), parent_id(string \u2014 creates subtask), plan_id(string), task_list_id(string), agent_id(string), assigned_to(string), tags(string[]), metadata(object), estimated_minutes(number), requires_approval(boolean), recurrence_rule(string \u2014 e.g. 'every day', 'every weekday', 'every 2 weeks', 'every monday'), session_id(string), working_dir(string)
|
|
10575
10784
|
Example: {title: 'Daily standup', recurrence_rule: 'every weekday', priority: 'medium'}`,
|
|
10576
|
-
|
|
10785
|
+
list_tasks: `List tasks with optional filters. Supports pagination.
|
|
10577
10786
|
Params: status(string|string[]), priority(string|string[]), project_id(string), plan_id(string), task_list_id(string), assigned_to(string), tags(string[]), has_recurrence(boolean \u2014 true=only recurring, false=only non-recurring), limit(number), offset(number)
|
|
10578
10787
|
Example: {status: ['pending', 'in_progress'], has_recurrence: true, limit: 20}`,
|
|
10579
|
-
|
|
10788
|
+
get_task: `Get full task details with subtasks, deps, and comments.
|
|
10580
10789
|
Params: id(string, req \u2014 task ID, short_id like 'APP-00001', or partial ID)
|
|
10581
10790
|
Example: {id: 'a1b2c3d4'}`,
|
|
10582
|
-
|
|
10791
|
+
update_task: `Update task fields. Requires version for optimistic locking (get it from get_task first).
|
|
10583
10792
|
Params: id(string, req), version(number, req), title(string), description(string), status(pending|in_progress|completed|failed|cancelled), priority(low|medium|high|critical), assigned_to(string), tags(string[]), metadata(object), plan_id(string), task_list_id(string)
|
|
10584
10793
|
Example: {id: 'a1b2c3d4', version: 3, status: 'completed'}`,
|
|
10585
|
-
|
|
10794
|
+
delete_task: `Delete a task permanently. Subtasks cascade-delete. Dependencies removed.
|
|
10586
10795
|
Params: id(string, req)
|
|
10587
10796
|
Example: {id: 'a1b2c3d4'}`,
|
|
10588
|
-
|
|
10797
|
+
start_task: `Claim, lock, and set task status to in_progress in one call.
|
|
10589
10798
|
Params: id(string, req), agent_id(string, req \u2014 your 8-char agent ID)
|
|
10590
10799
|
Example: {id: 'a1b2c3d4', agent_id: 'e5f6g7h8'}`,
|
|
10591
|
-
|
|
10800
|
+
complete_task: `Mark task completed, release lock, set completed_at timestamp. For recurring tasks, auto-spawns next instance unless skip_recurrence is true.
|
|
10592
10801
|
Params: id(string, req), agent_id(string, optional \u2014 required if locked by different agent), skip_recurrence(boolean \u2014 set true to prevent auto-creating next recurring instance)
|
|
10593
10802
|
Example: {id: 'a1b2c3d4', skip_recurrence: false}`,
|
|
10594
|
-
|
|
10803
|
+
lock_task: `Acquire exclusive lock on a task. Locks auto-expire after 30 min. Re-locking by same agent is idempotent.
|
|
10595
10804
|
Params: id(string, req), agent_id(string, req)
|
|
10596
10805
|
Example: {id: 'a1b2c3d4', agent_id: 'e5f6g7h8'}`,
|
|
10597
|
-
|
|
10806
|
+
unlock_task: `Release exclusive lock on a task.
|
|
10598
10807
|
Params: id(string, req), agent_id(string, optional \u2014 omit to force-unlock)
|
|
10599
10808
|
Example: {id: 'a1b2c3d4', agent_id: 'e5f6g7h8'}`,
|
|
10600
|
-
|
|
10809
|
+
approve_task: `Approve a task with requires_approval=true. Must be approved before completion.
|
|
10601
10810
|
Params: id(string, req), agent_id(string, optional \u2014 defaults to 'system')
|
|
10602
10811
|
Example: {id: 'a1b2c3d4', agent_id: 'e5f6g7h8'}`,
|
|
10603
|
-
|
|
10812
|
+
fail_task: `Mark a task as failed with structured reason and optional auto-retry. Stores failure info in metadata._failure, releases lock.
|
|
10604
10813
|
Params: id(string, req), agent_id(string, optional), reason(string), error_code(string \u2014 e.g. 'TIMEOUT'), retry(boolean \u2014 create new pending copy), retry_after(ISO date)
|
|
10605
10814
|
Example: {id: 'a1b2c3d4', reason: 'Build timeout', error_code: 'TIMEOUT', retry: true}`,
|
|
10606
|
-
|
|
10815
|
+
add_dependency: `Add a dependency: task_id depends on depends_on. Prevents cycles via BFS.
|
|
10607
10816
|
Params: task_id(string, req), depends_on(string, req)
|
|
10608
10817
|
Example: {task_id: 'abc12345', depends_on: 'def67890'}`,
|
|
10609
|
-
|
|
10818
|
+
remove_dependency: `Remove a dependency link between two tasks.
|
|
10610
10819
|
Params: task_id(string, req), depends_on(string, req)
|
|
10611
10820
|
Example: {task_id: 'abc12345', depends_on: 'def67890'}`,
|
|
10612
|
-
|
|
10821
|
+
add_comment: `Add a comment/note to a task. Comments are append-only.
|
|
10613
10822
|
Params: task_id(string, req), content(string, req), agent_id(string), session_id(string)
|
|
10614
10823
|
Example: {task_id: 'a1b2c3d4', content: 'Blocked by API rate limit'}`,
|
|
10615
|
-
|
|
10824
|
+
create_project: `Register a new project. Auto-generates task prefix for short IDs (e.g. APP-00001).
|
|
10616
10825
|
Params: name(string, req), path(string, req \u2014 unique absolute path), description(string), task_list_id(string)
|
|
10617
10826
|
Example: {name: 'my-app', path: '/Users/dev/my-app'}`,
|
|
10618
|
-
|
|
10619
|
-
|
|
10827
|
+
list_projects: "List all registered projects. No params.",
|
|
10828
|
+
create_plan: `Create a plan to group related tasks.
|
|
10620
10829
|
Params: name(string, req), project_id(string), description(string), status(active|completed|archived, default:active), task_list_id(string), agent_id(string)
|
|
10621
10830
|
Example: {name: 'Sprint 1', project_id: 'a1b2c3d4'}`,
|
|
10622
|
-
|
|
10831
|
+
list_plans: `List all plans, optionally filtered by project.
|
|
10623
10832
|
Params: project_id(string)
|
|
10624
10833
|
Example: {project_id: 'a1b2c3d4'}`,
|
|
10625
|
-
|
|
10834
|
+
get_plan: `Get plan details (name, status, description, timestamps).
|
|
10626
10835
|
Params: id(string, req)
|
|
10627
10836
|
Example: {id: 'a1b2c3d4'}`,
|
|
10628
|
-
|
|
10837
|
+
update_plan: `Update plan fields.
|
|
10629
10838
|
Params: id(string, req), name(string), description(string), status(active|completed|archived), task_list_id(string), agent_id(string)
|
|
10630
10839
|
Example: {id: 'a1b2c3d4', status: 'completed'}`,
|
|
10631
|
-
|
|
10840
|
+
delete_plan: `Delete a plan. Tasks in the plan are orphaned, not deleted.
|
|
10632
10841
|
Params: id(string, req)
|
|
10633
10842
|
Example: {id: 'a1b2c3d4'}`,
|
|
10634
|
-
|
|
10843
|
+
register_agent: `Register an agent (idempotent by name). Returns existing agent if name matches.
|
|
10635
10844
|
Params: name(string, req \u2014 e.g. 'maximus'), description(string)
|
|
10636
10845
|
Example: {name: 'maximus', description: 'Backend developer'}`,
|
|
10637
|
-
|
|
10638
|
-
|
|
10846
|
+
list_agents: "List all registered agents with IDs, names, and last seen timestamps. No params.",
|
|
10847
|
+
get_agent: `Get agent details by ID or name. Provide one of id or name.
|
|
10639
10848
|
Params: id(string), name(string)
|
|
10640
10849
|
Example: {name: 'maximus'}`,
|
|
10641
|
-
|
|
10850
|
+
rename_agent: `Rename an agent. Resolve by id or current name.
|
|
10642
10851
|
Params: id(string), name(string \u2014 current name), new_name(string, req)
|
|
10643
10852
|
Example: {name: 'old-name', new_name: 'new-name'}`,
|
|
10644
|
-
|
|
10853
|
+
delete_agent: `Delete an agent permanently. Resolve by id or name.
|
|
10645
10854
|
Params: id(string), name(string)
|
|
10646
10855
|
Example: {name: 'maximus'}`,
|
|
10647
|
-
|
|
10856
|
+
get_my_tasks: `Get all tasks assigned to/created by an agent, with stats (pending/active/done/rate).
|
|
10648
10857
|
Params: agent_name(string, req)
|
|
10649
10858
|
Example: {agent_name: 'maximus'}`,
|
|
10650
|
-
|
|
10651
|
-
|
|
10859
|
+
get_org_chart: "Get agent org chart showing reporting hierarchy. No params.",
|
|
10860
|
+
set_reports_to: `Set who an agent reports to in the org chart. Omit manager_name for top-level.
|
|
10652
10861
|
Params: agent_name(string, req), manager_name(string, optional)
|
|
10653
10862
|
Example: {agent_name: 'brutus', manager_name: 'maximus'}`,
|
|
10654
|
-
|
|
10863
|
+
create_task_list: `Create a task list \u2014 a container/folder for organizing tasks.
|
|
10655
10864
|
Params: name(string, req), slug(string \u2014 auto-generated if omitted), project_id(string), description(string)
|
|
10656
10865
|
Example: {name: 'Sprint 1', project_id: 'a1b2c3d4'}`,
|
|
10657
|
-
|
|
10866
|
+
list_task_lists: `List all task lists, optionally filtered by project.
|
|
10658
10867
|
Params: project_id(string)
|
|
10659
10868
|
Example: {project_id: 'a1b2c3d4'}`,
|
|
10660
|
-
|
|
10869
|
+
get_task_list: `Get task list details (name, slug, project, metadata).
|
|
10661
10870
|
Params: id(string, req)
|
|
10662
10871
|
Example: {id: 'a1b2c3d4'}`,
|
|
10663
|
-
|
|
10872
|
+
update_task_list: `Update a task list's name or description.
|
|
10664
10873
|
Params: id(string, req), name(string), description(string)
|
|
10665
10874
|
Example: {id: 'a1b2c3d4', name: 'Sprint 2'}`,
|
|
10666
|
-
|
|
10875
|
+
delete_task_list: `Delete a task list. Tasks are orphaned (not deleted).
|
|
10667
10876
|
Params: id(string, req)
|
|
10668
10877
|
Example: {id: 'a1b2c3d4'}`,
|
|
10669
|
-
|
|
10878
|
+
search_tasks: `Full-text search across task titles, descriptions, and tags. Supports filters.
|
|
10670
10879
|
Params: query(string, req), project_id(string), task_list_id(string), status(string|string[]), priority(string|string[]), assigned_to(string), agent_id(string), created_after(ISO date), updated_after(ISO date), has_dependencies(boolean), is_blocked(boolean)
|
|
10671
10880
|
Example: {query: 'auth bug', status: 'pending'}`,
|
|
10672
|
-
|
|
10881
|
+
get_next_task: `Get the optimal next task to work on \u2014 finds highest-priority pending task that is not blocked or locked. Prefers tasks assigned to the given agent.
|
|
10673
10882
|
Params: agent_id(string \u2014 prefers your tasks), project_id(string), task_list_id(string), plan_id(string), tags(string[])
|
|
10674
10883
|
Example: {agent_id: 'a1b2c3d4', project_id: 'e5f6g7h8'}`,
|
|
10675
|
-
|
|
10884
|
+
claim_next_task: `Atomically find the best pending task, lock it, and start it \u2014 one call instead of get_next_task + start_task. Eliminates race conditions between agents.
|
|
10676
10885
|
Params: agent_id(string, req \u2014 used for lock and assignment), project_id(string), task_list_id(string), plan_id(string), tags(string[])
|
|
10677
10886
|
Example: {agent_id: 'a1b2c3d4', project_id: 'e5f6g7h8'}`,
|
|
10678
|
-
|
|
10887
|
+
sync: `Sync tasks between local DB and agent task list (e.g. Claude Code).
|
|
10679
10888
|
Params: agent(string, default:'claude'), task_list_id(string), all_agents(boolean), project_id(string), direction(push|pull|both, default:both), prefer(local|remote, default:remote)
|
|
10680
10889
|
Example: {agent: 'claude', direction: 'push'}`,
|
|
10681
|
-
|
|
10890
|
+
clone_task: `Duplicate a task with optional field overrides. Creates new independent copy.
|
|
10682
10891
|
Params: task_id(string, req), title(string), description(string), priority(low|medium|high|critical), status(pending|in_progress|completed|failed|cancelled), project_id(string), plan_id(string), task_list_id(string), assigned_to(string), tags(string[]), estimated_minutes(number)
|
|
10683
10892
|
Example: {task_id: 'a1b2c3d4', title: 'Cloned task', assigned_to: 'brutus'}`,
|
|
10684
|
-
|
|
10893
|
+
move_task: `Move a task to a different list, project, or plan.
|
|
10685
10894
|
Params: task_id(string, req), task_list_id(string|null), project_id(string|null), plan_id(string|null)
|
|
10686
10895
|
Example: {task_id: 'a1b2c3d4', task_list_id: 'e5f6g7h8'}`,
|
|
10687
|
-
|
|
10896
|
+
bulk_update_tasks: `Update multiple tasks at once with the same changes.
|
|
10688
10897
|
Params: task_ids(string[], req), status(pending|in_progress|completed|failed|cancelled), priority(low|medium|high|critical), assigned_to(string), tags(string[])
|
|
10689
10898
|
Example: {task_ids: ['abc12345', 'def67890'], status: 'completed'}`,
|
|
10690
|
-
|
|
10899
|
+
bulk_create_tasks: `Create multiple tasks atomically. Supports inter-task dependencies via temp_id references.
|
|
10691
10900
|
Params: tasks(array, req \u2014 [{temp_id, title, description, priority, status, project_id, plan_id, task_list_id, agent_id, assigned_to, tags, estimated_minutes, depends_on_temp_ids}]), project_id(string \u2014 default for all), plan_id(string \u2014 default for all), task_list_id(string \u2014 default for all)
|
|
10692
10901
|
Example: {tasks: [{temp_id: 'a', title: 'First'}, {temp_id: 'b', title: 'Second', depends_on_temp_ids: ['a']}]}`,
|
|
10693
|
-
|
|
10902
|
+
get_task_stats: `Get task analytics: counts by status, priority, agent, and completion rate. All via SQL.
|
|
10694
10903
|
Params: project_id(string), task_list_id(string), agent_id(string)
|
|
10695
10904
|
Example: {project_id: 'a1b2c3d4'}`,
|
|
10696
|
-
|
|
10905
|
+
get_task_graph: `Get full dependency tree for a task \u2014 upstream blockers and downstream dependents.
|
|
10697
10906
|
Params: id(string, req), direction(up|down|both, default:both)
|
|
10698
10907
|
Example: {id: 'a1b2c3d4', direction: 'up'}`,
|
|
10699
|
-
|
|
10908
|
+
get_task_history: `Get audit log for a task \u2014 all field changes with timestamps and actors.
|
|
10700
10909
|
Params: task_id(string, req)
|
|
10701
10910
|
Example: {task_id: 'a1b2c3d4'}`,
|
|
10702
|
-
|
|
10911
|
+
get_recent_activity: `Get recent task changes across all tasks \u2014 global activity feed.
|
|
10703
10912
|
Params: limit(number, default:50)
|
|
10704
10913
|
Example: {limit: 20}`,
|
|
10705
|
-
|
|
10914
|
+
create_webhook: `Register a webhook for task change events.
|
|
10706
10915
|
Params: url(string, req), events(string[] \u2014 empty=all), secret(string \u2014 HMAC signing)
|
|
10707
10916
|
Example: {url: 'https://example.com/hook', events: ['task.created', 'task.completed']}`,
|
|
10708
|
-
|
|
10709
|
-
|
|
10917
|
+
list_webhooks: "List all registered webhooks. No params.",
|
|
10918
|
+
delete_webhook: `Delete a webhook by ID.
|
|
10710
10919
|
Params: id(string, req)
|
|
10711
10920
|
Example: {id: 'a1b2c3d4'}`,
|
|
10712
|
-
|
|
10921
|
+
create_template: `Create a reusable task template.
|
|
10713
10922
|
Params: name(string, req), title_pattern(string, req \u2014 e.g. 'Fix: {description}'), description(string), priority(low|medium|high|critical), tags(string[]), project_id(string), plan_id(string)
|
|
10714
10923
|
Example: {name: 'Bug Report', title_pattern: 'Bug: {description}', priority: 'high', tags: ['bug']}`,
|
|
10715
|
-
|
|
10716
|
-
|
|
10924
|
+
list_templates: "List all task templates. No params.",
|
|
10925
|
+
create_task_from_template: `Create a task from a template with optional overrides.
|
|
10717
10926
|
Params: template_id(string, req), title(string), description(string), priority(low|medium|high|critical), assigned_to(string), project_id(string)
|
|
10718
10927
|
Example: {template_id: 'a1b2c3d4', assigned_to: 'maximus'}`,
|
|
10719
|
-
|
|
10928
|
+
delete_template: `Delete a task template.
|
|
10720
10929
|
Params: id(string, req)
|
|
10721
10930
|
Example: {id: 'a1b2c3d4'}`,
|
|
10722
|
-
|
|
10931
|
+
get_active_work: `See all in-progress tasks and who is working on them.
|
|
10723
10932
|
Params: project_id(string, optional), task_list_id(string, optional)
|
|
10724
10933
|
Example: {project_id: 'a1b2c3d4'}`,
|
|
10725
|
-
|
|
10934
|
+
get_tasks_changed_since: `Get tasks modified after a timestamp \u2014 incremental delta sync.
|
|
10726
10935
|
Params: since(string, req \u2014 ISO date), project_id(string, optional), task_list_id(string, optional)
|
|
10727
10936
|
Example: {since: '2026-03-14T10:00:00Z'}`,
|
|
10728
|
-
|
|
10937
|
+
get_stale_tasks: `Find stale in_progress tasks with no recent activity.
|
|
10729
10938
|
Params: stale_minutes(number, default:30), project_id(string, optional), task_list_id(string, optional)
|
|
10730
10939
|
Example: {stale_minutes: 60, project_id: 'a1b2c3d4'}`,
|
|
10731
|
-
|
|
10940
|
+
get_status: `Get a full project health snapshot \u2014 pending/in_progress/completed counts, active work, next recommended task, stale task count, overdue recurring tasks. Saves 4+ round trips at session start.
|
|
10732
10941
|
Params: agent_id(string, optional \u2014 prefers tasks assigned to this agent for next_task), project_id(string, optional), task_list_id(string, optional)
|
|
10733
10942
|
Example: {agent_id: 'a1b2c3d4', project_id: 'e5f6g7h8'}`,
|
|
10734
|
-
|
|
10943
|
+
decompose_task: `Break a task into subtasks in one call. Subtasks inherit project/plan/list from parent.
|
|
10944
|
+
Params: parent_id(string, req), subtasks(array, req \u2014 [{title, description, priority, assigned_to, estimated_minutes, tags}]), depends_on_prev(boolean \u2014 chain subtasks sequentially)
|
|
10945
|
+
Example: {parent_id: 'a1b2c3d4', subtasks: [{title: 'Research'}, {title: 'Implement'}, {title: 'Test'}], depends_on_prev: true}`,
|
|
10946
|
+
search_tools: `List all tool names or filter by substring.
|
|
10735
10947
|
Params: query(string, optional)
|
|
10736
10948
|
Example: {query: 'task'}`,
|
|
10737
|
-
|
|
10949
|
+
describe_tools: `Get detailed descriptions and parameter info for tools by name.
|
|
10738
10950
|
Params: names(string[], req)
|
|
10739
10951
|
Example: {names: ['create_task', 'update_task']}`
|
|
10740
|
-
|
|
10741
|
-
|
|
10952
|
+
};
|
|
10953
|
+
const allToolNames = Object.keys(descriptions);
|
|
10954
|
+
const registeredCount = allToolNames.filter((n) => shouldRegisterTool(n)).length;
|
|
10955
|
+
const profileLine = `Profile: ${TODOS_PROFILE} (${registeredCount} tools active)
|
|
10956
|
+
|
|
10957
|
+
`;
|
|
10958
|
+
const result = names.map((n) => `${n}: ${descriptions[n] || "Unknown tool. Use search_tools to list available tools."}`).join(`
|
|
10742
10959
|
|
|
10743
10960
|
`);
|
|
10744
|
-
|
|
10745
|
-
|
|
10961
|
+
return { content: [{ type: "text", text: profileLine + result }] };
|
|
10962
|
+
});
|
|
10963
|
+
}
|
|
10746
10964
|
server.resource("tasks", "todos://tasks", { description: "All active tasks", mimeType: "application/json" }, async () => {
|
|
10747
10965
|
const tasks = listTasks({ status: ["pending", "in_progress"] });
|
|
10748
10966
|
return { contents: [{ uri: "todos://tasks", text: JSON.stringify(tasks, null, 2), mimeType: "application/json" }] };
|