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