@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/mcp/index.js CHANGED
@@ -6855,6 +6855,38 @@ var server = new McpServer({
6855
6855
  name: "todos",
6856
6856
  version: "0.9.35"
6857
6857
  });
6858
+ var TODOS_PROFILE = (process.env["TODOS_PROFILE"] || "full").toLowerCase();
6859
+ var MINIMAL_TOOLS = new Set([
6860
+ "claim_next_task",
6861
+ "complete_task",
6862
+ "fail_task",
6863
+ "get_status",
6864
+ "get_task",
6865
+ "start_task",
6866
+ "add_comment",
6867
+ "get_next_task"
6868
+ ]);
6869
+ var STANDARD_EXCLUDED = new Set([
6870
+ "get_org_chart",
6871
+ "set_reports_to",
6872
+ "rename_agent",
6873
+ "delete_agent",
6874
+ "create_webhook",
6875
+ "list_webhooks",
6876
+ "delete_webhook",
6877
+ "create_template",
6878
+ "list_templates",
6879
+ "create_task_from_template",
6880
+ "delete_template",
6881
+ "approve_task"
6882
+ ]);
6883
+ function shouldRegisterTool(name) {
6884
+ if (TODOS_PROFILE === "minimal")
6885
+ return MINIMAL_TOOLS.has(name);
6886
+ if (TODOS_PROFILE === "standard")
6887
+ return !STANDARD_EXCLUDED.has(name);
6888
+ return true;
6889
+ }
6858
6890
  function formatError(error) {
6859
6891
  if (error instanceof VersionConflictError) {
6860
6892
  return JSON.stringify({ code: VersionConflictError.code, message: error.message, suggestion: VersionConflictError.suggestion });
@@ -6947,1596 +6979,1719 @@ function formatTaskDetail(task) {
6947
6979
  return parts.join(`
6948
6980
  `);
6949
6981
  }
6950
- server.tool("create_task", "Create a new task", {
6951
- title: exports_external.string(),
6952
- description: exports_external.string().optional(),
6953
- project_id: exports_external.string().optional(),
6954
- parent_id: exports_external.string().optional(),
6955
- priority: exports_external.enum(["low", "medium", "high", "critical"]).optional(),
6956
- status: exports_external.enum(["pending", "in_progress", "completed", "failed", "cancelled"]).optional(),
6957
- agent_id: exports_external.string().optional(),
6958
- assigned_to: exports_external.string().optional(),
6959
- session_id: exports_external.string().optional(),
6960
- working_dir: exports_external.string().optional(),
6961
- plan_id: exports_external.string().optional(),
6962
- task_list_id: exports_external.string().optional(),
6963
- tags: exports_external.array(exports_external.string()).optional(),
6964
- metadata: exports_external.record(exports_external.unknown()).optional(),
6965
- estimated_minutes: exports_external.number().optional(),
6966
- requires_approval: exports_external.boolean().optional(),
6967
- recurrence_rule: exports_external.string().optional()
6968
- }, async (params) => {
6969
- try {
6970
- const resolved = { ...params };
6971
- if (resolved.project_id)
6972
- resolved.project_id = resolveId(resolved.project_id, "projects");
6973
- if (resolved.parent_id)
6974
- resolved.parent_id = resolveId(resolved.parent_id);
6975
- if (resolved.plan_id)
6976
- resolved.plan_id = resolveId(resolved.plan_id, "plans");
6977
- if (resolved.task_list_id)
6978
- resolved.task_list_id = resolveId(resolved.task_list_id, "task_lists");
6979
- const task = createTask(resolved);
6980
- return { content: [{ type: "text", text: `created: ${formatTask(task)}` }] };
6981
- } catch (e) {
6982
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
6983
- }
6984
- });
6985
- server.tool("list_tasks", "List tasks with optional filters and pagination.", {
6986
- project_id: exports_external.string().optional(),
6987
- status: exports_external.union([
6988
- exports_external.enum(["pending", "in_progress", "completed", "failed", "cancelled"]),
6989
- exports_external.array(exports_external.enum(["pending", "in_progress", "completed", "failed", "cancelled"]))
6990
- ]).optional(),
6991
- priority: exports_external.union([
6992
- exports_external.enum(["low", "medium", "high", "critical"]),
6993
- exports_external.array(exports_external.enum(["low", "medium", "high", "critical"]))
6994
- ]).optional(),
6995
- assigned_to: exports_external.string().optional(),
6996
- tags: exports_external.array(exports_external.string()).optional(),
6997
- plan_id: exports_external.string().optional(),
6998
- task_list_id: exports_external.string().optional(),
6999
- has_recurrence: exports_external.boolean().optional(),
7000
- limit: exports_external.number().optional(),
7001
- offset: exports_external.number().optional()
7002
- }, async (params) => {
7003
- try {
7004
- const resolved = { ...params };
7005
- if (resolved.project_id)
7006
- resolved.project_id = resolveId(resolved.project_id, "projects");
7007
- if (resolved.plan_id)
7008
- resolved.plan_id = resolveId(resolved.plan_id, "plans");
7009
- if (resolved.task_list_id)
7010
- resolved.task_list_id = resolveId(resolved.task_list_id, "task_lists");
7011
- const tasks = listTasks(resolved);
7012
- const { limit: _limit, offset: _offset, ...countFilter } = resolved;
7013
- const total = countTasks(countFilter);
7014
- if (tasks.length === 0) {
7015
- return { content: [{ type: "text", text: total > 0 ? `No tasks in this page (total: ${total}).` : "No tasks found." }] };
7016
- }
7017
- const text = tasks.map((t) => {
7018
- const lock = t.locked_by ? ` [locked by ${t.locked_by}]` : "";
7019
- const assigned = t.assigned_to ? ` -> ${t.assigned_to}` : "";
7020
- return `[${t.status}] ${t.id.slice(0, 8)} | ${t.priority} | ${t.title}${assigned}${lock}`;
7021
- }).join(`
6982
+ if (shouldRegisterTool("create_task")) {
6983
+ server.tool("create_task", "Create a new task", {
6984
+ title: exports_external.string(),
6985
+ description: exports_external.string().optional(),
6986
+ project_id: exports_external.string().optional(),
6987
+ parent_id: exports_external.string().optional(),
6988
+ priority: exports_external.enum(["low", "medium", "high", "critical"]).optional(),
6989
+ status: exports_external.enum(["pending", "in_progress", "completed", "failed", "cancelled"]).optional(),
6990
+ agent_id: exports_external.string().optional(),
6991
+ assigned_to: exports_external.string().optional(),
6992
+ session_id: exports_external.string().optional(),
6993
+ working_dir: exports_external.string().optional(),
6994
+ plan_id: exports_external.string().optional(),
6995
+ task_list_id: exports_external.string().optional(),
6996
+ tags: exports_external.array(exports_external.string()).optional(),
6997
+ metadata: exports_external.record(exports_external.unknown()).optional(),
6998
+ estimated_minutes: exports_external.number().optional(),
6999
+ requires_approval: exports_external.boolean().optional(),
7000
+ recurrence_rule: exports_external.string().optional()
7001
+ }, async (params) => {
7002
+ try {
7003
+ const resolved = { ...params };
7004
+ if (resolved.project_id)
7005
+ resolved.project_id = resolveId(resolved.project_id, "projects");
7006
+ if (resolved.parent_id)
7007
+ resolved.parent_id = resolveId(resolved.parent_id);
7008
+ if (resolved.plan_id)
7009
+ resolved.plan_id = resolveId(resolved.plan_id, "plans");
7010
+ if (resolved.task_list_id)
7011
+ resolved.task_list_id = resolveId(resolved.task_list_id, "task_lists");
7012
+ const task = createTask(resolved);
7013
+ return { content: [{ type: "text", text: `created: ${formatTask(task)}` }] };
7014
+ } catch (e) {
7015
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
7016
+ }
7017
+ });
7018
+ }
7019
+ if (shouldRegisterTool("list_tasks")) {
7020
+ server.tool("list_tasks", "List tasks with optional filters and pagination.", {
7021
+ project_id: exports_external.string().optional(),
7022
+ status: exports_external.union([
7023
+ exports_external.enum(["pending", "in_progress", "completed", "failed", "cancelled"]),
7024
+ exports_external.array(exports_external.enum(["pending", "in_progress", "completed", "failed", "cancelled"]))
7025
+ ]).optional(),
7026
+ priority: exports_external.union([
7027
+ exports_external.enum(["low", "medium", "high", "critical"]),
7028
+ exports_external.array(exports_external.enum(["low", "medium", "high", "critical"]))
7029
+ ]).optional(),
7030
+ assigned_to: exports_external.string().optional(),
7031
+ tags: exports_external.array(exports_external.string()).optional(),
7032
+ plan_id: exports_external.string().optional(),
7033
+ task_list_id: exports_external.string().optional(),
7034
+ has_recurrence: exports_external.boolean().optional(),
7035
+ limit: exports_external.number().optional(),
7036
+ offset: exports_external.number().optional()
7037
+ }, async (params) => {
7038
+ try {
7039
+ const resolved = { ...params };
7040
+ if (resolved.project_id)
7041
+ resolved.project_id = resolveId(resolved.project_id, "projects");
7042
+ if (resolved.plan_id)
7043
+ resolved.plan_id = resolveId(resolved.plan_id, "plans");
7044
+ if (resolved.task_list_id)
7045
+ resolved.task_list_id = resolveId(resolved.task_list_id, "task_lists");
7046
+ const tasks = listTasks(resolved);
7047
+ const { limit: _limit, offset: _offset, ...countFilter } = resolved;
7048
+ const total = countTasks(countFilter);
7049
+ if (tasks.length === 0) {
7050
+ return { content: [{ type: "text", text: total > 0 ? `No tasks in this page (total: ${total}).` : "No tasks found." }] };
7051
+ }
7052
+ const text = tasks.map((t) => {
7053
+ const lock = t.locked_by ? ` [locked by ${t.locked_by}]` : "";
7054
+ const assigned = t.assigned_to ? ` -> ${t.assigned_to}` : "";
7055
+ return `[${t.status}] ${t.id.slice(0, 8)} | ${t.priority} | ${t.title}${assigned}${lock}`;
7056
+ }).join(`
7022
7057
  `);
7023
- const pagination = resolved.limit ? `
7058
+ const pagination = resolved.limit ? `
7024
7059
  (showing ${tasks.length} of ${total}, offset: ${resolved.offset || 0})` : "";
7025
- return { content: [{ type: "text", text: `${tasks.length} task(s):
7060
+ return { content: [{ type: "text", text: `${tasks.length} task(s):
7026
7061
  ${text}${pagination}` }] };
7027
- } catch (e) {
7028
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
7029
- }
7030
- });
7031
- server.tool("get_task", "Get full task details with subtasks, deps, and comments.", {
7032
- id: exports_external.string()
7033
- }, async ({ id }) => {
7034
- try {
7035
- const resolvedId = resolveId(id);
7036
- const task = getTaskWithRelations(resolvedId);
7037
- if (!task)
7038
- return { content: [{ type: "text", text: `Task not found: ${id}` }], isError: true };
7039
- const parts = [formatTaskDetail(task)];
7040
- if (task.subtasks.length > 0) {
7041
- parts.push(`
7062
+ } catch (e) {
7063
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
7064
+ }
7065
+ });
7066
+ }
7067
+ if (shouldRegisterTool("get_task")) {
7068
+ server.tool("get_task", "Get full task details with subtasks, deps, and comments.", {
7069
+ id: exports_external.string()
7070
+ }, async ({ id }) => {
7071
+ try {
7072
+ const resolvedId = resolveId(id);
7073
+ const task = getTaskWithRelations(resolvedId);
7074
+ if (!task)
7075
+ return { content: [{ type: "text", text: `Task not found: ${id}` }], isError: true };
7076
+ const parts = [formatTaskDetail(task)];
7077
+ if (task.subtasks.length > 0) {
7078
+ parts.push(`
7042
7079
  Subtasks (${task.subtasks.length}):`);
7043
- for (const st of task.subtasks) {
7044
- parts.push(` [${st.status}] ${st.id.slice(0, 8)} | ${st.title}`);
7080
+ for (const st of task.subtasks) {
7081
+ parts.push(` [${st.status}] ${st.id.slice(0, 8)} | ${st.title}`);
7082
+ }
7045
7083
  }
7046
- }
7047
- if (task.dependencies.length > 0) {
7048
- parts.push(`
7084
+ if (task.dependencies.length > 0) {
7085
+ parts.push(`
7049
7086
  Depends on (${task.dependencies.length}):`);
7050
- for (const dep of task.dependencies) {
7051
- parts.push(` [${dep.status}] ${dep.id.slice(0, 8)} | ${dep.title}`);
7087
+ for (const dep of task.dependencies) {
7088
+ parts.push(` [${dep.status}] ${dep.id.slice(0, 8)} | ${dep.title}`);
7089
+ }
7052
7090
  }
7053
- }
7054
- if (task.blocked_by.length > 0) {
7055
- parts.push(`
7091
+ if (task.blocked_by.length > 0) {
7092
+ parts.push(`
7056
7093
  Blocks (${task.blocked_by.length}):`);
7057
- for (const b of task.blocked_by) {
7058
- parts.push(` [${b.status}] ${b.id.slice(0, 8)} | ${b.title}`);
7094
+ for (const b of task.blocked_by) {
7095
+ parts.push(` [${b.status}] ${b.id.slice(0, 8)} | ${b.title}`);
7096
+ }
7059
7097
  }
7060
- }
7061
- if (task.comments.length > 0) {
7062
- parts.push(`
7098
+ if (task.comments.length > 0) {
7099
+ parts.push(`
7063
7100
  Comments (${task.comments.length}):`);
7064
- for (const c of task.comments) {
7065
- const agent = c.agent_id ? `[${c.agent_id}] ` : "";
7066
- parts.push(` ${agent}${c.created_at}: ${c.content}`);
7101
+ for (const c of task.comments) {
7102
+ const agent = c.agent_id ? `[${c.agent_id}] ` : "";
7103
+ parts.push(` ${agent}${c.created_at}: ${c.content}`);
7104
+ }
7067
7105
  }
7068
- }
7069
- if (task.parent) {
7070
- parts.push(`
7106
+ if (task.parent) {
7107
+ parts.push(`
7071
7108
  Parent: ${task.parent.id.slice(0, 8)} | ${task.parent.title}`);
7072
- }
7073
- return { content: [{ type: "text", text: parts.join(`
7109
+ }
7110
+ return { content: [{ type: "text", text: parts.join(`
7074
7111
  `) }] };
7075
- } catch (e) {
7076
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
7077
- }
7078
- });
7079
- server.tool("update_task", "Update task fields. Version required for optimistic locking.", {
7080
- id: exports_external.string(),
7081
- version: exports_external.number(),
7082
- title: exports_external.string().optional(),
7083
- description: exports_external.string().optional(),
7084
- status: exports_external.enum(["pending", "in_progress", "completed", "failed", "cancelled"]).optional(),
7085
- priority: exports_external.enum(["low", "medium", "high", "critical"]).optional(),
7086
- assigned_to: exports_external.string().optional(),
7087
- tags: exports_external.array(exports_external.string()).optional(),
7088
- metadata: exports_external.record(exports_external.unknown()).optional(),
7089
- plan_id: exports_external.string().optional(),
7090
- task_list_id: exports_external.string().optional()
7091
- }, async ({ id, ...rest }) => {
7092
- try {
7093
- const resolvedId = resolveId(id);
7094
- const task = updateTask(resolvedId, rest);
7095
- return { content: [{ type: "text", text: `updated: ${formatTask(task)}` }] };
7096
- } catch (e) {
7097
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
7098
- }
7099
- });
7100
- server.tool("delete_task", "Delete a task permanently. Subtasks cascade-deleted.", {
7101
- id: exports_external.string()
7102
- }, async ({ id }) => {
7103
- try {
7104
- const resolvedId = resolveId(id);
7105
- const deleted = deleteTask(resolvedId);
7106
- return {
7107
- content: [{
7108
- type: "text",
7109
- text: deleted ? `Task ${id} deleted.` : `Task ${id} not found.`
7110
- }]
7111
- };
7112
- } catch (e) {
7113
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
7114
- }
7115
- });
7116
- server.tool("start_task", "Claim, lock, and set task to in_progress.", {
7117
- id: exports_external.string(),
7118
- agent_id: exports_external.string()
7119
- }, async ({ id, agent_id }) => {
7120
- try {
7121
- const resolvedId = resolveId(id);
7122
- const task = startTask(resolvedId, agent_id);
7123
- return { content: [{ type: "text", text: `started: ${formatTask(task)}` }] };
7124
- } catch (e) {
7125
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
7126
- }
7127
- });
7128
- server.tool("complete_task", "Complete a task. For recurring tasks, auto-spawns next instance.", {
7129
- id: exports_external.string(),
7130
- agent_id: exports_external.string().optional(),
7131
- skip_recurrence: exports_external.boolean().optional(),
7132
- files_changed: exports_external.array(exports_external.string()).optional().describe("List of files changed as part of completing this task"),
7133
- test_results: exports_external.string().optional().describe("Summary of test results"),
7134
- commit_hash: exports_external.string().optional().describe("Git commit hash associated with this completion"),
7135
- notes: exports_external.string().optional().describe("Notes about the completion"),
7136
- attachment_ids: exports_external.array(exports_external.string()).optional().describe("IDs of attachments uploaded via @hasna/attachments to link as evidence")
7137
- }, async ({ id, agent_id, skip_recurrence, files_changed, test_results, commit_hash, notes, attachment_ids }) => {
7138
- try {
7139
- const resolvedId = resolveId(id);
7140
- const evidence = files_changed || test_results || commit_hash || notes || attachment_ids ? { files_changed, test_results, commit_hash, notes, attachment_ids } : undefined;
7141
- const task = completeTask(resolvedId, agent_id, undefined, { skip_recurrence, ...evidence });
7142
- let text = `completed: ${formatTask(task)}`;
7143
- if (task.metadata._next_recurrence) {
7144
- const next = task.metadata._next_recurrence;
7145
- text += `
7112
+ } catch (e) {
7113
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
7114
+ }
7115
+ });
7116
+ }
7117
+ if (shouldRegisterTool("update_task")) {
7118
+ server.tool("update_task", "Update task fields. Version required for optimistic locking.", {
7119
+ id: exports_external.string(),
7120
+ version: exports_external.number(),
7121
+ title: exports_external.string().optional(),
7122
+ description: exports_external.string().optional(),
7123
+ status: exports_external.enum(["pending", "in_progress", "completed", "failed", "cancelled"]).optional(),
7124
+ priority: exports_external.enum(["low", "medium", "high", "critical"]).optional(),
7125
+ assigned_to: exports_external.string().optional(),
7126
+ tags: exports_external.array(exports_external.string()).optional(),
7127
+ metadata: exports_external.record(exports_external.unknown()).optional(),
7128
+ plan_id: exports_external.string().optional(),
7129
+ task_list_id: exports_external.string().optional()
7130
+ }, async ({ id, ...rest }) => {
7131
+ try {
7132
+ const resolvedId = resolveId(id);
7133
+ const task = updateTask(resolvedId, rest);
7134
+ return { content: [{ type: "text", text: `updated: ${formatTask(task)}` }] };
7135
+ } catch (e) {
7136
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
7137
+ }
7138
+ });
7139
+ }
7140
+ if (shouldRegisterTool("delete_task")) {
7141
+ server.tool("delete_task", "Delete a task permanently. Subtasks cascade-deleted.", {
7142
+ id: exports_external.string()
7143
+ }, async ({ id }) => {
7144
+ try {
7145
+ const resolvedId = resolveId(id);
7146
+ const deleted = deleteTask(resolvedId);
7147
+ return {
7148
+ content: [{
7149
+ type: "text",
7150
+ text: deleted ? `Task ${id} deleted.` : `Task ${id} not found.`
7151
+ }]
7152
+ };
7153
+ } catch (e) {
7154
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
7155
+ }
7156
+ });
7157
+ }
7158
+ if (shouldRegisterTool("start_task")) {
7159
+ server.tool("start_task", "Claim, lock, and set task to in_progress.", {
7160
+ id: exports_external.string(),
7161
+ agent_id: exports_external.string()
7162
+ }, async ({ id, agent_id }) => {
7163
+ try {
7164
+ const resolvedId = resolveId(id);
7165
+ const task = startTask(resolvedId, agent_id);
7166
+ return { content: [{ type: "text", text: `started: ${formatTask(task)}` }] };
7167
+ } catch (e) {
7168
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
7169
+ }
7170
+ });
7171
+ }
7172
+ if (shouldRegisterTool("complete_task")) {
7173
+ server.tool("complete_task", "Complete a task. For recurring tasks, auto-spawns next instance.", {
7174
+ id: exports_external.string(),
7175
+ agent_id: exports_external.string().optional(),
7176
+ skip_recurrence: exports_external.boolean().optional(),
7177
+ files_changed: exports_external.array(exports_external.string()).optional().describe("List of files changed as part of completing this task"),
7178
+ test_results: exports_external.string().optional().describe("Summary of test results"),
7179
+ commit_hash: exports_external.string().optional().describe("Git commit hash associated with this completion"),
7180
+ notes: exports_external.string().optional().describe("Notes about the completion"),
7181
+ attachment_ids: exports_external.array(exports_external.string()).optional().describe("IDs of attachments uploaded via @hasna/attachments to link as evidence")
7182
+ }, async ({ id, agent_id, skip_recurrence, files_changed, test_results, commit_hash, notes, attachment_ids }) => {
7183
+ try {
7184
+ const resolvedId = resolveId(id);
7185
+ const evidence = files_changed || test_results || commit_hash || notes || attachment_ids ? { files_changed, test_results, commit_hash, notes, attachment_ids } : undefined;
7186
+ const task = completeTask(resolvedId, agent_id, undefined, { skip_recurrence, ...evidence });
7187
+ let text = `completed: ${formatTask(task)}`;
7188
+ if (task.metadata._next_recurrence) {
7189
+ const next = task.metadata._next_recurrence;
7190
+ text += `
7146
7191
  next: ${next.short_id || next.id.slice(0, 8)} due ${next.due_at}`;
7192
+ }
7193
+ return { content: [{ type: "text", text }] };
7194
+ } catch (e) {
7195
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
7147
7196
  }
7148
- return { content: [{ type: "text", text }] };
7149
- } catch (e) {
7150
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
7151
- }
7152
- });
7153
- server.tool("lock_task", "Acquire exclusive lock. Expires after 30 min. Idempotent per agent.", {
7154
- id: exports_external.string(),
7155
- agent_id: exports_external.string()
7156
- }, async ({ id, agent_id }) => {
7157
- try {
7158
- const resolvedId = resolveId(id);
7159
- const result = lockTask(resolvedId, agent_id);
7160
- if (result.success) {
7161
- return { content: [{ type: "text", text: `Lock acquired by ${agent_id} at ${result.locked_at}` }] };
7197
+ });
7198
+ }
7199
+ if (shouldRegisterTool("lock_task")) {
7200
+ server.tool("lock_task", "Acquire exclusive lock. Expires after 30 min. Idempotent per agent.", {
7201
+ id: exports_external.string(),
7202
+ agent_id: exports_external.string()
7203
+ }, async ({ id, agent_id }) => {
7204
+ try {
7205
+ const resolvedId = resolveId(id);
7206
+ const result = lockTask(resolvedId, agent_id);
7207
+ if (result.success) {
7208
+ return { content: [{ type: "text", text: `Lock acquired by ${agent_id} at ${result.locked_at}` }] };
7209
+ }
7210
+ return { content: [{ type: "text", text: `Lock failed: ${result.error}` }], isError: true };
7211
+ } catch (e) {
7212
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
7162
7213
  }
7163
- return { content: [{ type: "text", text: `Lock failed: ${result.error}` }], isError: true };
7164
- } catch (e) {
7165
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
7166
- }
7167
- });
7168
- server.tool("unlock_task", "Release exclusive lock on a task.", {
7169
- id: exports_external.string(),
7170
- agent_id: exports_external.string().optional()
7171
- }, async ({ id, agent_id }) => {
7172
- try {
7173
- const resolvedId = resolveId(id);
7174
- unlockTask(resolvedId, agent_id);
7175
- return { content: [{ type: "text", text: `Lock released on task ${id}` }] };
7176
- } catch (e) {
7177
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
7178
- }
7179
- });
7180
- server.tool("add_dependency", "Add a dependency. Prevents cycles via BFS detection.", {
7181
- task_id: exports_external.string(),
7182
- depends_on: exports_external.string()
7183
- }, async ({ task_id, depends_on }) => {
7184
- try {
7185
- const resolvedTaskId = resolveId(task_id);
7186
- const resolvedDepsOn = resolveId(depends_on);
7187
- addDependency(resolvedTaskId, resolvedDepsOn);
7188
- return { content: [{ type: "text", text: `Dependency added: ${task_id} depends on ${depends_on}` }] };
7189
- } catch (e) {
7190
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
7191
- }
7192
- });
7193
- server.tool("remove_dependency", "Remove a dependency link between two tasks.", {
7194
- task_id: exports_external.string(),
7195
- depends_on: exports_external.string()
7196
- }, async ({ task_id, depends_on }) => {
7197
- try {
7198
- const resolvedTaskId = resolveId(task_id);
7199
- const resolvedDepsOn = resolveId(depends_on);
7200
- const removed = removeDependency(resolvedTaskId, resolvedDepsOn);
7201
- return {
7202
- content: [{
7203
- type: "text",
7204
- text: removed ? `Dependency removed.` : `Dependency not found.`
7205
- }]
7206
- };
7207
- } catch (e) {
7208
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
7209
- }
7210
- });
7211
- server.tool("add_comment", "Add a comment or note to a task. Comments are append-only.", {
7212
- task_id: exports_external.string(),
7213
- content: exports_external.string(),
7214
- agent_id: exports_external.string().optional(),
7215
- session_id: exports_external.string().optional()
7216
- }, async ({ task_id, ...rest }) => {
7217
- try {
7218
- const resolvedId = resolveId(task_id);
7219
- const comment = addComment({ task_id: resolvedId, ...rest });
7220
- return { content: [{ type: "text", text: `Comment added (${comment.id.slice(0, 8)}) at ${comment.created_at}` }] };
7221
- } catch (e) {
7222
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
7223
- }
7224
- });
7225
- server.tool("list_projects", "List all registered projects", {}, async () => {
7226
- try {
7227
- const projects = listProjects();
7228
- if (projects.length === 0) {
7229
- return { content: [{ type: "text", text: "No projects registered." }] };
7230
- }
7231
- const text = projects.map((p) => {
7232
- const taskList = p.task_list_id ? ` [${p.task_list_id}]` : "";
7233
- return `${p.id.slice(0, 8)} | ${p.name} | ${p.path}${taskList}${p.description ? ` - ${p.description}` : ""}`;
7234
- }).join(`
7214
+ });
7215
+ }
7216
+ if (shouldRegisterTool("unlock_task")) {
7217
+ server.tool("unlock_task", "Release exclusive lock on a task.", {
7218
+ id: exports_external.string(),
7219
+ agent_id: exports_external.string().optional()
7220
+ }, async ({ id, agent_id }) => {
7221
+ try {
7222
+ const resolvedId = resolveId(id);
7223
+ unlockTask(resolvedId, agent_id);
7224
+ return { content: [{ type: "text", text: `Lock released on task ${id}` }] };
7225
+ } catch (e) {
7226
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
7227
+ }
7228
+ });
7229
+ }
7230
+ if (shouldRegisterTool("add_dependency")) {
7231
+ server.tool("add_dependency", "Add a dependency. Prevents cycles via BFS detection.", {
7232
+ task_id: exports_external.string(),
7233
+ depends_on: exports_external.string()
7234
+ }, async ({ task_id, depends_on }) => {
7235
+ try {
7236
+ const resolvedTaskId = resolveId(task_id);
7237
+ const resolvedDepsOn = resolveId(depends_on);
7238
+ addDependency(resolvedTaskId, resolvedDepsOn);
7239
+ return { content: [{ type: "text", text: `Dependency added: ${task_id} depends on ${depends_on}` }] };
7240
+ } catch (e) {
7241
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
7242
+ }
7243
+ });
7244
+ }
7245
+ if (shouldRegisterTool("remove_dependency")) {
7246
+ server.tool("remove_dependency", "Remove a dependency link between two tasks.", {
7247
+ task_id: exports_external.string(),
7248
+ depends_on: exports_external.string()
7249
+ }, async ({ task_id, depends_on }) => {
7250
+ try {
7251
+ const resolvedTaskId = resolveId(task_id);
7252
+ const resolvedDepsOn = resolveId(depends_on);
7253
+ const removed = removeDependency(resolvedTaskId, resolvedDepsOn);
7254
+ return {
7255
+ content: [{
7256
+ type: "text",
7257
+ text: removed ? `Dependency removed.` : `Dependency not found.`
7258
+ }]
7259
+ };
7260
+ } catch (e) {
7261
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
7262
+ }
7263
+ });
7264
+ }
7265
+ if (shouldRegisterTool("add_comment")) {
7266
+ server.tool("add_comment", "Add a comment or note to a task. Comments are append-only.", {
7267
+ task_id: exports_external.string(),
7268
+ content: exports_external.string(),
7269
+ agent_id: exports_external.string().optional(),
7270
+ session_id: exports_external.string().optional()
7271
+ }, async ({ task_id, ...rest }) => {
7272
+ try {
7273
+ const resolvedId = resolveId(task_id);
7274
+ const comment = addComment({ task_id: resolvedId, ...rest });
7275
+ return { content: [{ type: "text", text: `Comment added (${comment.id.slice(0, 8)}) at ${comment.created_at}` }] };
7276
+ } catch (e) {
7277
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
7278
+ }
7279
+ });
7280
+ }
7281
+ if (shouldRegisterTool("list_projects")) {
7282
+ server.tool("list_projects", "List all registered projects", {}, async () => {
7283
+ try {
7284
+ const projects = listProjects();
7285
+ if (projects.length === 0) {
7286
+ return { content: [{ type: "text", text: "No projects registered." }] };
7287
+ }
7288
+ const text = projects.map((p) => {
7289
+ const taskList = p.task_list_id ? ` [${p.task_list_id}]` : "";
7290
+ return `${p.id.slice(0, 8)} | ${p.name} | ${p.path}${taskList}${p.description ? ` - ${p.description}` : ""}`;
7291
+ }).join(`
7235
7292
  `);
7236
- return { content: [{ type: "text", text: `${projects.length} project(s):
7293
+ return { content: [{ type: "text", text: `${projects.length} project(s):
7237
7294
  ${text}` }] };
7238
- } catch (e) {
7239
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
7240
- }
7241
- });
7242
- server.tool("create_project", "Register a new project with auto-generated task prefix.", {
7243
- name: exports_external.string(),
7244
- path: exports_external.string(),
7245
- description: exports_external.string().optional(),
7246
- task_list_id: exports_external.string().optional()
7247
- }, async (params) => {
7248
- try {
7249
- const project = createProject(params);
7250
- const taskList = project.task_list_id ? ` [${project.task_list_id}]` : "";
7251
- return {
7252
- content: [{
7253
- type: "text",
7254
- text: `Project created: ${project.id.slice(0, 8)} | ${project.name} | ${project.path}${taskList}`
7255
- }]
7256
- };
7257
- } catch (e) {
7258
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
7259
- }
7260
- });
7261
- server.tool("create_plan", "Create a plan to group related tasks.", {
7262
- name: exports_external.string(),
7263
- project_id: exports_external.string().optional(),
7264
- description: exports_external.string().optional(),
7265
- status: exports_external.enum(["active", "completed", "archived"]).optional(),
7266
- task_list_id: exports_external.string().optional(),
7267
- agent_id: exports_external.string().optional()
7268
- }, async (params) => {
7269
- try {
7270
- const resolved = { ...params };
7271
- if (resolved.project_id)
7272
- resolved.project_id = resolveId(resolved.project_id, "projects");
7273
- if (resolved.task_list_id)
7274
- resolved.task_list_id = resolveId(resolved.task_list_id, "task_lists");
7275
- const plan = createPlan(resolved);
7276
- return {
7277
- content: [{
7278
- type: "text",
7279
- text: `Plan created: ${plan.id.slice(0, 8)} | ${plan.name} | ${plan.status}`
7280
- }]
7281
- };
7282
- } catch (e) {
7283
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
7284
- }
7285
- });
7286
- server.tool("list_plans", "List all plans, optionally filtered by project.", {
7287
- project_id: exports_external.string().optional()
7288
- }, async ({ project_id }) => {
7289
- try {
7290
- const resolvedProjectId = project_id ? resolveId(project_id, "projects") : undefined;
7291
- const plans = listPlans(resolvedProjectId);
7292
- if (plans.length === 0) {
7293
- return { content: [{ type: "text", text: "No plans found." }] };
7294
- }
7295
- const text = plans.map((p) => {
7296
- const project = p.project_id ? ` (project: ${p.project_id.slice(0, 8)})` : "";
7297
- return `[${p.status}] ${p.id.slice(0, 8)} | ${p.name}${project}`;
7298
- }).join(`
7295
+ } catch (e) {
7296
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
7297
+ }
7298
+ });
7299
+ }
7300
+ if (shouldRegisterTool("create_project")) {
7301
+ server.tool("create_project", "Register a new project with auto-generated task prefix.", {
7302
+ name: exports_external.string(),
7303
+ path: exports_external.string(),
7304
+ description: exports_external.string().optional(),
7305
+ task_list_id: exports_external.string().optional()
7306
+ }, async (params) => {
7307
+ try {
7308
+ const project = createProject(params);
7309
+ const taskList = project.task_list_id ? ` [${project.task_list_id}]` : "";
7310
+ return {
7311
+ content: [{
7312
+ type: "text",
7313
+ text: `Project created: ${project.id.slice(0, 8)} | ${project.name} | ${project.path}${taskList}`
7314
+ }]
7315
+ };
7316
+ } catch (e) {
7317
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
7318
+ }
7319
+ });
7320
+ }
7321
+ if (shouldRegisterTool("create_plan")) {
7322
+ server.tool("create_plan", "Create a plan to group related tasks.", {
7323
+ name: exports_external.string(),
7324
+ project_id: exports_external.string().optional(),
7325
+ description: exports_external.string().optional(),
7326
+ status: exports_external.enum(["active", "completed", "archived"]).optional(),
7327
+ task_list_id: exports_external.string().optional(),
7328
+ agent_id: exports_external.string().optional()
7329
+ }, async (params) => {
7330
+ try {
7331
+ const resolved = { ...params };
7332
+ if (resolved.project_id)
7333
+ resolved.project_id = resolveId(resolved.project_id, "projects");
7334
+ if (resolved.task_list_id)
7335
+ resolved.task_list_id = resolveId(resolved.task_list_id, "task_lists");
7336
+ const plan = createPlan(resolved);
7337
+ return {
7338
+ content: [{
7339
+ type: "text",
7340
+ text: `Plan created: ${plan.id.slice(0, 8)} | ${plan.name} | ${plan.status}`
7341
+ }]
7342
+ };
7343
+ } catch (e) {
7344
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
7345
+ }
7346
+ });
7347
+ }
7348
+ if (shouldRegisterTool("list_plans")) {
7349
+ server.tool("list_plans", "List all plans, optionally filtered by project.", {
7350
+ project_id: exports_external.string().optional()
7351
+ }, async ({ project_id }) => {
7352
+ try {
7353
+ const resolvedProjectId = project_id ? resolveId(project_id, "projects") : undefined;
7354
+ const plans = listPlans(resolvedProjectId);
7355
+ if (plans.length === 0) {
7356
+ return { content: [{ type: "text", text: "No plans found." }] };
7357
+ }
7358
+ const text = plans.map((p) => {
7359
+ const project = p.project_id ? ` (project: ${p.project_id.slice(0, 8)})` : "";
7360
+ return `[${p.status}] ${p.id.slice(0, 8)} | ${p.name}${project}`;
7361
+ }).join(`
7299
7362
  `);
7300
- return { content: [{ type: "text", text: `${plans.length} plan(s):
7363
+ return { content: [{ type: "text", text: `${plans.length} plan(s):
7301
7364
  ${text}` }] };
7302
- } catch (e) {
7303
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
7304
- }
7305
- });
7306
- server.tool("get_plan", "Get plan details including status and timestamps.", {
7307
- id: exports_external.string()
7308
- }, async ({ id }) => {
7309
- try {
7310
- const resolvedId = resolveId(id, "plans");
7311
- const plan = getPlan(resolvedId);
7312
- if (!plan)
7313
- return { content: [{ type: "text", text: `Plan not found: ${id}` }], isError: true };
7314
- const parts = [
7315
- `ID: ${plan.id}`,
7316
- `Name: ${plan.name}`,
7317
- `Status: ${plan.status}`
7318
- ];
7319
- if (plan.description)
7320
- parts.push(`Description: ${plan.description}`);
7321
- if (plan.project_id)
7322
- parts.push(`Project: ${plan.project_id}`);
7323
- parts.push(`Created: ${plan.created_at}`);
7324
- parts.push(`Updated: ${plan.updated_at}`);
7325
- return { content: [{ type: "text", text: parts.join(`
7365
+ } catch (e) {
7366
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
7367
+ }
7368
+ });
7369
+ }
7370
+ if (shouldRegisterTool("get_plan")) {
7371
+ server.tool("get_plan", "Get plan details including status and timestamps.", {
7372
+ id: exports_external.string()
7373
+ }, async ({ id }) => {
7374
+ try {
7375
+ const resolvedId = resolveId(id, "plans");
7376
+ const plan = getPlan(resolvedId);
7377
+ if (!plan)
7378
+ return { content: [{ type: "text", text: `Plan not found: ${id}` }], isError: true };
7379
+ const parts = [
7380
+ `ID: ${plan.id}`,
7381
+ `Name: ${plan.name}`,
7382
+ `Status: ${plan.status}`
7383
+ ];
7384
+ if (plan.description)
7385
+ parts.push(`Description: ${plan.description}`);
7386
+ if (plan.project_id)
7387
+ parts.push(`Project: ${plan.project_id}`);
7388
+ parts.push(`Created: ${plan.created_at}`);
7389
+ parts.push(`Updated: ${plan.updated_at}`);
7390
+ return { content: [{ type: "text", text: parts.join(`
7326
7391
  `) }] };
7327
- } catch (e) {
7328
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
7329
- }
7330
- });
7331
- server.tool("update_plan", "Update plan fields (name, description, status).", {
7332
- id: exports_external.string(),
7333
- name: exports_external.string().optional(),
7334
- description: exports_external.string().optional(),
7335
- status: exports_external.enum(["active", "completed", "archived"]).optional(),
7336
- task_list_id: exports_external.string().optional(),
7337
- agent_id: exports_external.string().optional()
7338
- }, async ({ id, ...rest }) => {
7339
- try {
7340
- const resolvedId = resolveId(id, "plans");
7341
- const resolved = { ...rest };
7342
- if (resolved.task_list_id)
7343
- resolved.task_list_id = resolveId(resolved.task_list_id, "task_lists");
7344
- const plan = updatePlan(resolvedId, resolved);
7345
- return {
7346
- content: [{
7347
- type: "text",
7348
- text: `Plan updated: ${plan.id.slice(0, 8)} | ${plan.name} | ${plan.status}`
7349
- }]
7350
- };
7351
- } catch (e) {
7352
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
7353
- }
7354
- });
7355
- server.tool("delete_plan", "Delete a plan. Tasks in the plan are orphaned (not deleted).", {
7356
- id: exports_external.string()
7357
- }, async ({ id }) => {
7358
- try {
7359
- const resolvedId = resolveId(id, "plans");
7360
- const deleted = deletePlan(resolvedId);
7361
- return {
7362
- content: [{
7363
- type: "text",
7364
- text: deleted ? `Plan ${id} deleted.` : `Plan ${id} not found.`
7365
- }]
7366
- };
7367
- } catch (e) {
7368
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
7369
- }
7370
- });
7371
- server.tool("search_tasks", "Full-text search across tasks with filters.", {
7372
- query: exports_external.string(),
7373
- project_id: exports_external.string().optional(),
7374
- task_list_id: exports_external.string().optional(),
7375
- status: exports_external.union([
7376
- exports_external.enum(["pending", "in_progress", "completed", "failed", "cancelled"]),
7377
- exports_external.array(exports_external.enum(["pending", "in_progress", "completed", "failed", "cancelled"]))
7378
- ]).optional(),
7379
- priority: exports_external.union([
7380
- exports_external.enum(["low", "medium", "high", "critical"]),
7381
- exports_external.array(exports_external.enum(["low", "medium", "high", "critical"]))
7382
- ]).optional(),
7383
- assigned_to: exports_external.string().optional(),
7384
- agent_id: exports_external.string().optional(),
7385
- created_after: exports_external.string().optional(),
7386
- updated_after: exports_external.string().optional(),
7387
- has_dependencies: exports_external.boolean().optional(),
7388
- is_blocked: exports_external.boolean().optional()
7389
- }, async ({ query, project_id, task_list_id, ...filters }) => {
7390
- try {
7391
- const resolvedProjectId = project_id ? resolveId(project_id, "projects") : undefined;
7392
- const resolvedTaskListId = task_list_id ? resolveId(task_list_id, "task_lists") : undefined;
7393
- const tasks = searchTasks({
7394
- query,
7395
- project_id: resolvedProjectId,
7396
- task_list_id: resolvedTaskListId,
7397
- ...filters
7398
- });
7399
- if (tasks.length === 0) {
7400
- return { content: [{ type: "text", text: `No tasks matching "${query}".` }] };
7392
+ } catch (e) {
7393
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
7401
7394
  }
7402
- const text = tasks.map((t) => `[${t.status}] ${t.id.slice(0, 8)} | ${t.priority} | ${t.title}`).join(`
7403
- `);
7404
- return { content: [{ type: "text", text: `${tasks.length} result(s) for "${query}":
7405
- ${text}` }] };
7406
- } catch (e) {
7407
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
7408
- }
7409
- });
7410
- server.tool("sync", "Sync tasks between local DB and agent task list.", {
7411
- task_list_id: exports_external.string().optional(),
7412
- agent: exports_external.string().optional(),
7413
- all_agents: exports_external.boolean().optional(),
7414
- project_id: exports_external.string().optional(),
7415
- direction: exports_external.enum(["push", "pull", "both"]).optional(),
7416
- prefer: exports_external.enum(["local", "remote"]).optional()
7417
- }, async ({ task_list_id, agent, all_agents, project_id, direction, prefer }) => {
7418
- try {
7419
- const resolvedProjectId = project_id ? resolveId(project_id, "projects") : undefined;
7420
- const project = resolvedProjectId ? getProject(resolvedProjectId) : undefined;
7421
- const dir = direction ?? "both";
7422
- const options = { prefer: prefer ?? "remote" };
7423
- let result;
7424
- if (all_agents) {
7425
- const agents = defaultSyncAgents();
7426
- result = syncWithAgents(agents, (a) => resolveTaskListId(a, task_list_id || project?.task_list_id || undefined), resolvedProjectId, dir, options);
7427
- } else {
7428
- const resolvedAgent = agent || "claude";
7429
- const taskListId = resolveTaskListId(resolvedAgent, task_list_id || project?.task_list_id || undefined);
7430
- if (!taskListId) {
7431
- return {
7432
- content: [{
7433
- type: "text",
7434
- text: `Could not determine task list ID for ${resolvedAgent}. Provide task_list_id or set task_list_id on the project.`
7435
- }],
7436
- isError: true
7437
- };
7438
- }
7439
- result = syncWithAgent(resolvedAgent, taskListId, resolvedProjectId, dir, options);
7395
+ });
7396
+ }
7397
+ if (shouldRegisterTool("update_plan")) {
7398
+ server.tool("update_plan", "Update plan fields (name, description, status).", {
7399
+ id: exports_external.string(),
7400
+ name: exports_external.string().optional(),
7401
+ description: exports_external.string().optional(),
7402
+ status: exports_external.enum(["active", "completed", "archived"]).optional(),
7403
+ task_list_id: exports_external.string().optional(),
7404
+ agent_id: exports_external.string().optional()
7405
+ }, async ({ id, ...rest }) => {
7406
+ try {
7407
+ const resolvedId = resolveId(id, "plans");
7408
+ const resolved = { ...rest };
7409
+ if (resolved.task_list_id)
7410
+ resolved.task_list_id = resolveId(resolved.task_list_id, "task_lists");
7411
+ const plan = updatePlan(resolvedId, resolved);
7412
+ return {
7413
+ content: [{
7414
+ type: "text",
7415
+ text: `Plan updated: ${plan.id.slice(0, 8)} | ${plan.name} | ${plan.status}`
7416
+ }]
7417
+ };
7418
+ } catch (e) {
7419
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
7440
7420
  }
7441
- const parts = [];
7442
- if (result.pulled > 0)
7443
- parts.push(`Pulled ${result.pulled} task(s).`);
7444
- if (result.pushed > 0)
7445
- parts.push(`Pushed ${result.pushed} task(s).`);
7446
- if (result.pulled === 0 && result.pushed === 0 && result.errors.length === 0) {
7447
- parts.push("Nothing to sync.");
7421
+ });
7422
+ }
7423
+ if (shouldRegisterTool("delete_plan")) {
7424
+ server.tool("delete_plan", "Delete a plan. Tasks in the plan are orphaned (not deleted).", {
7425
+ id: exports_external.string()
7426
+ }, async ({ id }) => {
7427
+ try {
7428
+ const resolvedId = resolveId(id, "plans");
7429
+ const deleted = deletePlan(resolvedId);
7430
+ return {
7431
+ content: [{
7432
+ type: "text",
7433
+ text: deleted ? `Plan ${id} deleted.` : `Plan ${id} not found.`
7434
+ }]
7435
+ };
7436
+ } catch (e) {
7437
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
7448
7438
  }
7449
- for (const err of result.errors) {
7450
- parts.push(`Error: ${err}`);
7439
+ });
7440
+ }
7441
+ if (shouldRegisterTool("search_tasks")) {
7442
+ server.tool("search_tasks", "Full-text search across tasks with filters.", {
7443
+ query: exports_external.string(),
7444
+ project_id: exports_external.string().optional(),
7445
+ task_list_id: exports_external.string().optional(),
7446
+ status: exports_external.union([
7447
+ exports_external.enum(["pending", "in_progress", "completed", "failed", "cancelled"]),
7448
+ exports_external.array(exports_external.enum(["pending", "in_progress", "completed", "failed", "cancelled"]))
7449
+ ]).optional(),
7450
+ priority: exports_external.union([
7451
+ exports_external.enum(["low", "medium", "high", "critical"]),
7452
+ exports_external.array(exports_external.enum(["low", "medium", "high", "critical"]))
7453
+ ]).optional(),
7454
+ assigned_to: exports_external.string().optional(),
7455
+ agent_id: exports_external.string().optional(),
7456
+ created_after: exports_external.string().optional(),
7457
+ updated_after: exports_external.string().optional(),
7458
+ has_dependencies: exports_external.boolean().optional(),
7459
+ is_blocked: exports_external.boolean().optional()
7460
+ }, async ({ query, project_id, task_list_id, ...filters }) => {
7461
+ try {
7462
+ const resolvedProjectId = project_id ? resolveId(project_id, "projects") : undefined;
7463
+ const resolvedTaskListId = task_list_id ? resolveId(task_list_id, "task_lists") : undefined;
7464
+ const tasks = searchTasks({
7465
+ query,
7466
+ project_id: resolvedProjectId,
7467
+ task_list_id: resolvedTaskListId,
7468
+ ...filters
7469
+ });
7470
+ if (tasks.length === 0) {
7471
+ return { content: [{ type: "text", text: `No tasks matching "${query}".` }] };
7472
+ }
7473
+ const text = tasks.map((t) => `[${t.status}] ${t.id.slice(0, 8)} | ${t.priority} | ${t.title}`).join(`
7474
+ `);
7475
+ return { content: [{ type: "text", text: `${tasks.length} result(s) for "${query}":
7476
+ ${text}` }] };
7477
+ } catch (e) {
7478
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
7451
7479
  }
7452
- return { content: [{ type: "text", text: parts.join(`
7480
+ });
7481
+ }
7482
+ if (shouldRegisterTool("sync")) {
7483
+ server.tool("sync", "Sync tasks between local DB and agent task list.", {
7484
+ task_list_id: exports_external.string().optional(),
7485
+ agent: exports_external.string().optional(),
7486
+ all_agents: exports_external.boolean().optional(),
7487
+ project_id: exports_external.string().optional(),
7488
+ direction: exports_external.enum(["push", "pull", "both"]).optional(),
7489
+ prefer: exports_external.enum(["local", "remote"]).optional()
7490
+ }, async ({ task_list_id, agent, all_agents, project_id, direction, prefer }) => {
7491
+ try {
7492
+ const resolvedProjectId = project_id ? resolveId(project_id, "projects") : undefined;
7493
+ const project = resolvedProjectId ? getProject(resolvedProjectId) : undefined;
7494
+ const dir = direction ?? "both";
7495
+ const options = { prefer: prefer ?? "remote" };
7496
+ let result;
7497
+ if (all_agents) {
7498
+ const agents = defaultSyncAgents();
7499
+ result = syncWithAgents(agents, (a) => resolveTaskListId(a, task_list_id || project?.task_list_id || undefined), resolvedProjectId, dir, options);
7500
+ } else {
7501
+ const resolvedAgent = agent || "claude";
7502
+ const taskListId = resolveTaskListId(resolvedAgent, task_list_id || project?.task_list_id || undefined);
7503
+ if (!taskListId) {
7504
+ return {
7505
+ content: [{
7506
+ type: "text",
7507
+ text: `Could not determine task list ID for ${resolvedAgent}. Provide task_list_id or set task_list_id on the project.`
7508
+ }],
7509
+ isError: true
7510
+ };
7511
+ }
7512
+ result = syncWithAgent(resolvedAgent, taskListId, resolvedProjectId, dir, options);
7513
+ }
7514
+ const parts = [];
7515
+ if (result.pulled > 0)
7516
+ parts.push(`Pulled ${result.pulled} task(s).`);
7517
+ if (result.pushed > 0)
7518
+ parts.push(`Pushed ${result.pushed} task(s).`);
7519
+ if (result.pulled === 0 && result.pushed === 0 && result.errors.length === 0) {
7520
+ parts.push("Nothing to sync.");
7521
+ }
7522
+ for (const err of result.errors) {
7523
+ parts.push(`Error: ${err}`);
7524
+ }
7525
+ return { content: [{ type: "text", text: parts.join(`
7453
7526
  `) }] };
7454
- } catch (e) {
7455
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
7456
- }
7457
- });
7458
- server.tool("register_agent", "Register an agent (idempotent by name). Updates last_seen_at.", {
7459
- name: exports_external.string(),
7460
- description: exports_external.string().optional()
7461
- }, async ({ name, description }) => {
7462
- try {
7463
- const agent = registerAgent({ name, description });
7464
- return {
7465
- content: [{
7466
- type: "text",
7467
- text: `Agent registered:
7527
+ } catch (e) {
7528
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
7529
+ }
7530
+ });
7531
+ }
7532
+ if (shouldRegisterTool("register_agent")) {
7533
+ server.tool("register_agent", "Register an agent (idempotent by name). Updates last_seen_at.", {
7534
+ name: exports_external.string(),
7535
+ description: exports_external.string().optional()
7536
+ }, async ({ name, description }) => {
7537
+ try {
7538
+ const agent = registerAgent({ name, description });
7539
+ return {
7540
+ content: [{
7541
+ type: "text",
7542
+ text: `Agent registered:
7468
7543
  ID: ${agent.id}
7469
7544
  Name: ${agent.name}${agent.description ? `
7470
7545
  Description: ${agent.description}` : ""}
7471
7546
  Created: ${agent.created_at}
7472
7547
  Last seen: ${agent.last_seen_at}`
7473
- }]
7474
- };
7475
- } catch (e) {
7476
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
7477
- }
7478
- });
7479
- server.tool("list_agents", "List all registered agents", {}, async () => {
7480
- try {
7481
- const agents = listAgents();
7482
- if (agents.length === 0) {
7483
- return { content: [{ type: "text", text: "No agents registered." }] };
7548
+ }]
7549
+ };
7550
+ } catch (e) {
7551
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
7484
7552
  }
7485
- const text = agents.map((a) => {
7486
- return `${a.id} | ${a.name}${a.description ? ` - ${a.description}` : ""} (last seen: ${a.last_seen_at})`;
7487
- }).join(`
7553
+ });
7554
+ }
7555
+ if (shouldRegisterTool("list_agents")) {
7556
+ server.tool("list_agents", "List all registered agents", {}, async () => {
7557
+ try {
7558
+ const agents = listAgents();
7559
+ if (agents.length === 0) {
7560
+ return { content: [{ type: "text", text: "No agents registered." }] };
7561
+ }
7562
+ const text = agents.map((a) => {
7563
+ return `${a.id} | ${a.name}${a.description ? ` - ${a.description}` : ""} (last seen: ${a.last_seen_at})`;
7564
+ }).join(`
7488
7565
  `);
7489
- return { content: [{ type: "text", text: `${agents.length} agent(s):
7566
+ return { content: [{ type: "text", text: `${agents.length} agent(s):
7490
7567
  ${text}` }] };
7491
- } catch (e) {
7492
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
7493
- }
7494
- });
7495
- server.tool("get_agent", "Get agent details by ID or name. Provide one of id or name.", {
7496
- id: exports_external.string().optional(),
7497
- name: exports_external.string().optional()
7498
- }, async ({ id, name }) => {
7499
- try {
7500
- if (!id && !name) {
7501
- return { content: [{ type: "text", text: "Provide either id or name." }], isError: true };
7502
- }
7503
- const agent = id ? getAgent(id) : getAgentByName(name);
7504
- if (!agent) {
7505
- return { content: [{ type: "text", text: `Agent not found: ${id || name}` }], isError: true };
7506
- }
7507
- const parts = [
7508
- `ID: ${agent.id}`,
7509
- `Name: ${agent.name}`
7510
- ];
7511
- if (agent.description)
7512
- parts.push(`Description: ${agent.description}`);
7513
- if (Object.keys(agent.metadata).length > 0)
7514
- parts.push(`Metadata: ${JSON.stringify(agent.metadata)}`);
7515
- parts.push(`Created: ${agent.created_at}`);
7516
- parts.push(`Last seen: ${agent.last_seen_at}`);
7517
- return { content: [{ type: "text", text: parts.join(`
7518
- `) }] };
7519
- } catch (e) {
7520
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
7521
- }
7522
- });
7523
- server.tool("rename_agent", "Rename an agent. Resolve by id or current name.", {
7524
- id: exports_external.string().optional(),
7525
- name: exports_external.string().optional(),
7526
- new_name: exports_external.string()
7527
- }, async ({ id, name, new_name }) => {
7528
- try {
7529
- if (!id && !name) {
7530
- return { content: [{ type: "text", text: "Provide either id or name." }], isError: true };
7568
+ } catch (e) {
7569
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
7531
7570
  }
7532
- const agent = id ? getAgent(id) : getAgentByName(name);
7533
- if (!agent) {
7534
- return { content: [{ type: "text", text: `Agent not found: ${id || name}` }], isError: true };
7571
+ });
7572
+ }
7573
+ if (shouldRegisterTool("get_agent")) {
7574
+ server.tool("get_agent", "Get agent details by ID or name. Provide one of id or name.", {
7575
+ id: exports_external.string().optional(),
7576
+ name: exports_external.string().optional()
7577
+ }, async ({ id, name }) => {
7578
+ try {
7579
+ if (!id && !name) {
7580
+ return { content: [{ type: "text", text: "Provide either id or name." }], isError: true };
7581
+ }
7582
+ const agent = id ? getAgent(id) : getAgentByName(name);
7583
+ if (!agent) {
7584
+ return { content: [{ type: "text", text: `Agent not found: ${id || name}` }], isError: true };
7585
+ }
7586
+ const parts = [
7587
+ `ID: ${agent.id}`,
7588
+ `Name: ${agent.name}`
7589
+ ];
7590
+ if (agent.description)
7591
+ parts.push(`Description: ${agent.description}`);
7592
+ if (Object.keys(agent.metadata).length > 0)
7593
+ parts.push(`Metadata: ${JSON.stringify(agent.metadata)}`);
7594
+ parts.push(`Created: ${agent.created_at}`);
7595
+ parts.push(`Last seen: ${agent.last_seen_at}`);
7596
+ return { content: [{ type: "text", text: parts.join(`
7597
+ `) }] };
7598
+ } catch (e) {
7599
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
7535
7600
  }
7536
- const updated = updateAgent(agent.id, { name: new_name });
7537
- return {
7538
- content: [{
7539
- type: "text",
7540
- text: `Agent renamed: ${agent.name} -> ${updated.name}
7601
+ });
7602
+ }
7603
+ if (shouldRegisterTool("rename_agent")) {
7604
+ server.tool("rename_agent", "Rename an agent. Resolve by id or current name.", {
7605
+ id: exports_external.string().optional(),
7606
+ name: exports_external.string().optional(),
7607
+ new_name: exports_external.string()
7608
+ }, async ({ id, name, new_name }) => {
7609
+ try {
7610
+ if (!id && !name) {
7611
+ return { content: [{ type: "text", text: "Provide either id or name." }], isError: true };
7612
+ }
7613
+ const agent = id ? getAgent(id) : getAgentByName(name);
7614
+ if (!agent) {
7615
+ return { content: [{ type: "text", text: `Agent not found: ${id || name}` }], isError: true };
7616
+ }
7617
+ const updated = updateAgent(agent.id, { name: new_name });
7618
+ return {
7619
+ content: [{
7620
+ type: "text",
7621
+ text: `Agent renamed: ${agent.name} -> ${updated.name}
7541
7622
  ID: ${updated.id}`
7542
- }]
7543
- };
7544
- } catch (e) {
7545
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
7546
- }
7547
- });
7548
- server.tool("delete_agent", "Delete an agent permanently. Resolve by id or name.", {
7549
- id: exports_external.string().optional(),
7550
- name: exports_external.string().optional()
7551
- }, async ({ id, name }) => {
7552
- try {
7553
- if (!id && !name) {
7554
- return { content: [{ type: "text", text: "Provide either id or name." }], isError: true };
7623
+ }]
7624
+ };
7625
+ } catch (e) {
7626
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
7555
7627
  }
7556
- const agent = id ? getAgent(id) : getAgentByName(name);
7557
- if (!agent) {
7558
- return { content: [{ type: "text", text: `Agent not found: ${id || name}` }], isError: true };
7628
+ });
7629
+ }
7630
+ if (shouldRegisterTool("delete_agent")) {
7631
+ server.tool("delete_agent", "Delete an agent permanently. Resolve by id or name.", {
7632
+ id: exports_external.string().optional(),
7633
+ name: exports_external.string().optional()
7634
+ }, async ({ id, name }) => {
7635
+ try {
7636
+ if (!id && !name) {
7637
+ return { content: [{ type: "text", text: "Provide either id or name." }], isError: true };
7638
+ }
7639
+ const agent = id ? getAgent(id) : getAgentByName(name);
7640
+ if (!agent) {
7641
+ return { content: [{ type: "text", text: `Agent not found: ${id || name}` }], isError: true };
7642
+ }
7643
+ const deleted = deleteAgent(agent.id);
7644
+ return {
7645
+ content: [{
7646
+ type: "text",
7647
+ text: deleted ? `Agent deleted: ${agent.name} (${agent.id})` : `Failed to delete agent: ${agent.name}`
7648
+ }],
7649
+ isError: !deleted
7650
+ };
7651
+ } catch (e) {
7652
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
7559
7653
  }
7560
- const deleted = deleteAgent(agent.id);
7561
- return {
7562
- content: [{
7563
- type: "text",
7564
- text: deleted ? `Agent deleted: ${agent.name} (${agent.id})` : `Failed to delete agent: ${agent.name}`
7565
- }],
7566
- isError: !deleted
7567
- };
7568
- } catch (e) {
7569
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
7570
- }
7571
- });
7572
- server.tool("create_task_list", "Create a task list container for organizing tasks.", {
7573
- name: exports_external.string(),
7574
- slug: exports_external.string().optional(),
7575
- project_id: exports_external.string().optional(),
7576
- description: exports_external.string().optional()
7577
- }, async (params) => {
7578
- try {
7579
- const resolved = { ...params };
7580
- if (resolved.project_id)
7581
- resolved.project_id = resolveId(resolved.project_id, "projects");
7582
- const list = createTaskList(resolved);
7583
- return {
7584
- content: [{
7585
- type: "text",
7586
- text: `Task list created:
7654
+ });
7655
+ }
7656
+ if (shouldRegisterTool("create_task_list")) {
7657
+ server.tool("create_task_list", "Create a task list container for organizing tasks.", {
7658
+ name: exports_external.string(),
7659
+ slug: exports_external.string().optional(),
7660
+ project_id: exports_external.string().optional(),
7661
+ description: exports_external.string().optional()
7662
+ }, async (params) => {
7663
+ try {
7664
+ const resolved = { ...params };
7665
+ if (resolved.project_id)
7666
+ resolved.project_id = resolveId(resolved.project_id, "projects");
7667
+ const list = createTaskList(resolved);
7668
+ return {
7669
+ content: [{
7670
+ type: "text",
7671
+ text: `Task list created:
7587
7672
  ID: ${list.id}
7588
7673
  Name: ${list.name}
7589
7674
  Slug: ${list.slug}${list.project_id ? `
7590
7675
  Project: ${list.project_id}` : ""}${list.description ? `
7591
7676
  Description: ${list.description}` : ""}`
7592
- }]
7593
- };
7594
- } catch (e) {
7595
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
7596
- }
7597
- });
7598
- server.tool("list_task_lists", "List all task lists, optionally filtered by project.", {
7599
- project_id: exports_external.string().optional()
7600
- }, async ({ project_id }) => {
7601
- try {
7602
- const resolvedProjectId = project_id ? resolveId(project_id, "projects") : undefined;
7603
- const lists = listTaskLists(resolvedProjectId);
7604
- if (lists.length === 0) {
7605
- return { content: [{ type: "text", text: "No task lists found." }] };
7606
- }
7607
- const text = lists.map((l) => {
7608
- const project = l.project_id ? ` (project: ${l.project_id.slice(0, 8)})` : "";
7609
- return `${l.id.slice(0, 8)} | ${l.name} [${l.slug}]${project}${l.description ? ` - ${l.description}` : ""}`;
7610
- }).join(`
7677
+ }]
7678
+ };
7679
+ } catch (e) {
7680
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
7681
+ }
7682
+ });
7683
+ }
7684
+ if (shouldRegisterTool("list_task_lists")) {
7685
+ server.tool("list_task_lists", "List all task lists, optionally filtered by project.", {
7686
+ project_id: exports_external.string().optional()
7687
+ }, async ({ project_id }) => {
7688
+ try {
7689
+ const resolvedProjectId = project_id ? resolveId(project_id, "projects") : undefined;
7690
+ const lists = listTaskLists(resolvedProjectId);
7691
+ if (lists.length === 0) {
7692
+ return { content: [{ type: "text", text: "No task lists found." }] };
7693
+ }
7694
+ const text = lists.map((l) => {
7695
+ const project = l.project_id ? ` (project: ${l.project_id.slice(0, 8)})` : "";
7696
+ return `${l.id.slice(0, 8)} | ${l.name} [${l.slug}]${project}${l.description ? ` - ${l.description}` : ""}`;
7697
+ }).join(`
7611
7698
  `);
7612
- return { content: [{ type: "text", text: `${lists.length} task list(s):
7699
+ return { content: [{ type: "text", text: `${lists.length} task list(s):
7613
7700
  ${text}` }] };
7614
- } catch (e) {
7615
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
7616
- }
7617
- });
7618
- server.tool("get_task_list", "Get task list details including slug and metadata.", {
7619
- id: exports_external.string()
7620
- }, async ({ id }) => {
7621
- try {
7622
- const resolvedId = resolveId(id, "task_lists");
7623
- const list = getTaskList(resolvedId);
7624
- if (!list) {
7625
- return { content: [{ type: "text", text: `Task list not found: ${id}` }], isError: true };
7626
- }
7627
- const parts = [
7628
- `ID: ${list.id}`,
7629
- `Name: ${list.name}`,
7630
- `Slug: ${list.slug}`
7631
- ];
7632
- if (list.project_id)
7633
- parts.push(`Project: ${list.project_id}`);
7634
- if (list.description)
7635
- parts.push(`Description: ${list.description}`);
7636
- if (Object.keys(list.metadata).length > 0)
7637
- parts.push(`Metadata: ${JSON.stringify(list.metadata)}`);
7638
- parts.push(`Created: ${list.created_at}`);
7639
- parts.push(`Updated: ${list.updated_at}`);
7640
- return { content: [{ type: "text", text: parts.join(`
7701
+ } catch (e) {
7702
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
7703
+ }
7704
+ });
7705
+ }
7706
+ if (shouldRegisterTool("get_task_list")) {
7707
+ server.tool("get_task_list", "Get task list details including slug and metadata.", {
7708
+ id: exports_external.string()
7709
+ }, async ({ id }) => {
7710
+ try {
7711
+ const resolvedId = resolveId(id, "task_lists");
7712
+ const list = getTaskList(resolvedId);
7713
+ if (!list) {
7714
+ return { content: [{ type: "text", text: `Task list not found: ${id}` }], isError: true };
7715
+ }
7716
+ const parts = [
7717
+ `ID: ${list.id}`,
7718
+ `Name: ${list.name}`,
7719
+ `Slug: ${list.slug}`
7720
+ ];
7721
+ if (list.project_id)
7722
+ parts.push(`Project: ${list.project_id}`);
7723
+ if (list.description)
7724
+ parts.push(`Description: ${list.description}`);
7725
+ if (Object.keys(list.metadata).length > 0)
7726
+ parts.push(`Metadata: ${JSON.stringify(list.metadata)}`);
7727
+ parts.push(`Created: ${list.created_at}`);
7728
+ parts.push(`Updated: ${list.updated_at}`);
7729
+ return { content: [{ type: "text", text: parts.join(`
7641
7730
  `) }] };
7642
- } catch (e) {
7643
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
7644
- }
7645
- });
7646
- server.tool("update_task_list", "Update a task list's name or description.", {
7647
- id: exports_external.string(),
7648
- name: exports_external.string().optional(),
7649
- description: exports_external.string().optional()
7650
- }, async ({ id, ...rest }) => {
7651
- try {
7652
- const resolvedId = resolveId(id, "task_lists");
7653
- const list = updateTaskList(resolvedId, rest);
7654
- return {
7655
- content: [{
7656
- type: "text",
7657
- text: `Task list updated:
7731
+ } catch (e) {
7732
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
7733
+ }
7734
+ });
7735
+ }
7736
+ if (shouldRegisterTool("update_task_list")) {
7737
+ server.tool("update_task_list", "Update a task list's name or description.", {
7738
+ id: exports_external.string(),
7739
+ name: exports_external.string().optional(),
7740
+ description: exports_external.string().optional()
7741
+ }, async ({ id, ...rest }) => {
7742
+ try {
7743
+ const resolvedId = resolveId(id, "task_lists");
7744
+ const list = updateTaskList(resolvedId, rest);
7745
+ return {
7746
+ content: [{
7747
+ type: "text",
7748
+ text: `Task list updated:
7658
7749
  ID: ${list.id}
7659
7750
  Name: ${list.name}
7660
7751
  Slug: ${list.slug}`
7661
- }]
7662
- };
7663
- } catch (e) {
7664
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
7665
- }
7666
- });
7667
- server.tool("delete_task_list", "Delete a task list. Tasks are orphaned, not deleted.", {
7668
- id: exports_external.string()
7669
- }, async ({ id }) => {
7670
- try {
7671
- const resolvedId = resolveId(id, "task_lists");
7672
- const deleted = deleteTaskList(resolvedId);
7673
- return {
7674
- content: [{
7675
- type: "text",
7676
- text: deleted ? `Task list ${id} deleted.` : `Task list ${id} not found.`
7677
- }]
7678
- };
7679
- } catch (e) {
7680
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
7681
- }
7682
- });
7683
- server.tool("get_task_history", "Get audit log \u2014 field changes with timestamps and actors.", {
7684
- task_id: exports_external.string()
7685
- }, async ({ task_id }) => {
7686
- try {
7687
- const resolvedId = resolveId(task_id);
7688
- const { getTaskHistory: getTaskHistory2 } = await Promise.resolve().then(() => (init_audit(), exports_audit));
7689
- const history = getTaskHistory2(resolvedId);
7690
- if (history.length === 0)
7691
- return { content: [{ type: "text", text: "No history for this task." }] };
7692
- 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(`
7752
+ }]
7753
+ };
7754
+ } catch (e) {
7755
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
7756
+ }
7757
+ });
7758
+ }
7759
+ if (shouldRegisterTool("delete_task_list")) {
7760
+ server.tool("delete_task_list", "Delete a task list. Tasks are orphaned, not deleted.", {
7761
+ id: exports_external.string()
7762
+ }, async ({ id }) => {
7763
+ try {
7764
+ const resolvedId = resolveId(id, "task_lists");
7765
+ const deleted = deleteTaskList(resolvedId);
7766
+ return {
7767
+ content: [{
7768
+ type: "text",
7769
+ text: deleted ? `Task list ${id} deleted.` : `Task list ${id} not found.`
7770
+ }]
7771
+ };
7772
+ } catch (e) {
7773
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
7774
+ }
7775
+ });
7776
+ }
7777
+ if (shouldRegisterTool("get_task_history")) {
7778
+ server.tool("get_task_history", "Get audit log \u2014 field changes with timestamps and actors.", {
7779
+ task_id: exports_external.string()
7780
+ }, async ({ task_id }) => {
7781
+ try {
7782
+ const resolvedId = resolveId(task_id);
7783
+ const { getTaskHistory: getTaskHistory2 } = await Promise.resolve().then(() => (init_audit(), exports_audit));
7784
+ const history = getTaskHistory2(resolvedId);
7785
+ if (history.length === 0)
7786
+ return { content: [{ type: "text", text: "No history for this task." }] };
7787
+ 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(`
7693
7788
  `);
7694
- return { content: [{ type: "text", text: `${history.length} change(s):
7789
+ return { content: [{ type: "text", text: `${history.length} change(s):
7695
7790
  ${text}` }] };
7696
- } catch (e) {
7697
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
7698
- }
7699
- });
7700
- server.tool("get_recent_activity", "Get recent task changes \u2014 global activity feed.", {
7701
- limit: exports_external.number().optional()
7702
- }, async ({ limit }) => {
7703
- try {
7704
- const { getRecentActivity: getRecentActivity2 } = await Promise.resolve().then(() => (init_audit(), exports_audit));
7705
- const activity = getRecentActivity2(limit || 50);
7706
- if (activity.length === 0)
7707
- return { content: [{ type: "text", text: "No recent activity." }] };
7708
- 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(`
7791
+ } catch (e) {
7792
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
7793
+ }
7794
+ });
7795
+ }
7796
+ if (shouldRegisterTool("get_recent_activity")) {
7797
+ server.tool("get_recent_activity", "Get recent task changes \u2014 global activity feed.", {
7798
+ limit: exports_external.number().optional()
7799
+ }, async ({ limit }) => {
7800
+ try {
7801
+ const { getRecentActivity: getRecentActivity2 } = await Promise.resolve().then(() => (init_audit(), exports_audit));
7802
+ const activity = getRecentActivity2(limit || 50);
7803
+ if (activity.length === 0)
7804
+ return { content: [{ type: "text", text: "No recent activity." }] };
7805
+ 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(`
7709
7806
  `);
7710
- return { content: [{ type: "text", text: `${activity.length} recent change(s):
7807
+ return { content: [{ type: "text", text: `${activity.length} recent change(s):
7711
7808
  ${text}` }] };
7712
- } catch (e) {
7713
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
7714
- }
7715
- });
7716
- server.tool("create_webhook", "Register a webhook for task change events.", {
7717
- url: exports_external.string(),
7718
- events: exports_external.array(exports_external.string()).optional(),
7719
- secret: exports_external.string().optional()
7720
- }, async (params) => {
7721
- try {
7722
- const { createWebhook: createWebhook2 } = await Promise.resolve().then(() => (init_webhooks(), exports_webhooks));
7723
- const wh = createWebhook2(params);
7724
- return { content: [{ type: "text", text: `Webhook created: ${wh.id.slice(0, 8)} | ${wh.url} | events: ${wh.events.length === 0 ? "all" : wh.events.join(",")}` }] };
7725
- } catch (e) {
7726
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
7727
- }
7728
- });
7729
- server.tool("list_webhooks", "List all registered webhooks", {}, async () => {
7730
- try {
7731
- const { listWebhooks: listWebhooks2 } = await Promise.resolve().then(() => (init_webhooks(), exports_webhooks));
7732
- const webhooks = listWebhooks2();
7733
- if (webhooks.length === 0)
7734
- return { content: [{ type: "text", text: "No webhooks registered." }] };
7735
- 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(`
7809
+ } catch (e) {
7810
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
7811
+ }
7812
+ });
7813
+ }
7814
+ if (shouldRegisterTool("create_webhook")) {
7815
+ server.tool("create_webhook", "Register a webhook for task change events.", {
7816
+ url: exports_external.string(),
7817
+ events: exports_external.array(exports_external.string()).optional(),
7818
+ secret: exports_external.string().optional()
7819
+ }, async (params) => {
7820
+ try {
7821
+ const { createWebhook: createWebhook2 } = await Promise.resolve().then(() => (init_webhooks(), exports_webhooks));
7822
+ const wh = createWebhook2(params);
7823
+ return { content: [{ type: "text", text: `Webhook created: ${wh.id.slice(0, 8)} | ${wh.url} | events: ${wh.events.length === 0 ? "all" : wh.events.join(",")}` }] };
7824
+ } catch (e) {
7825
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
7826
+ }
7827
+ });
7828
+ }
7829
+ if (shouldRegisterTool("list_webhooks")) {
7830
+ server.tool("list_webhooks", "List all registered webhooks", {}, async () => {
7831
+ try {
7832
+ const { listWebhooks: listWebhooks2 } = await Promise.resolve().then(() => (init_webhooks(), exports_webhooks));
7833
+ const webhooks = listWebhooks2();
7834
+ if (webhooks.length === 0)
7835
+ return { content: [{ type: "text", text: "No webhooks registered." }] };
7836
+ 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(`
7736
7837
  `);
7737
- return { content: [{ type: "text", text: `${webhooks.length} webhook(s):
7838
+ return { content: [{ type: "text", text: `${webhooks.length} webhook(s):
7738
7839
  ${text}` }] };
7739
- } catch (e) {
7740
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
7741
- }
7742
- });
7743
- server.tool("delete_webhook", "Delete a webhook by ID.", {
7744
- id: exports_external.string()
7745
- }, async ({ id }) => {
7746
- try {
7747
- const { deleteWebhook: deleteWebhook2 } = await Promise.resolve().then(() => (init_webhooks(), exports_webhooks));
7748
- const deleted = deleteWebhook2(id);
7749
- return { content: [{ type: "text", text: deleted ? "Webhook deleted." : "Webhook not found." }] };
7750
- } catch (e) {
7751
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
7752
- }
7753
- });
7754
- server.tool("create_template", "Create a reusable task template.", {
7755
- name: exports_external.string(),
7756
- title_pattern: exports_external.string(),
7757
- description: exports_external.string().optional(),
7758
- priority: exports_external.enum(["low", "medium", "high", "critical"]).optional(),
7759
- tags: exports_external.array(exports_external.string()).optional(),
7760
- project_id: exports_external.string().optional(),
7761
- plan_id: exports_external.string().optional()
7762
- }, async (params) => {
7763
- try {
7764
- const { createTemplate: createTemplate2 } = await Promise.resolve().then(() => (init_templates(), exports_templates));
7765
- const t = createTemplate2(params);
7766
- return { content: [{ type: "text", text: `Template created: ${t.id.slice(0, 8)} | ${t.name} | "${t.title_pattern}"` }] };
7767
- } catch (e) {
7768
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
7769
- }
7770
- });
7771
- server.tool("list_templates", "List all task templates", {}, async () => {
7772
- try {
7773
- const { listTemplates: listTemplates2 } = await Promise.resolve().then(() => (init_templates(), exports_templates));
7774
- const templates = listTemplates2();
7775
- if (templates.length === 0)
7776
- return { content: [{ type: "text", text: "No templates." }] };
7777
- const text = templates.map((t) => `${t.id.slice(0, 8)} | ${t.name} | "${t.title_pattern}" | ${t.priority}`).join(`
7840
+ } catch (e) {
7841
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
7842
+ }
7843
+ });
7844
+ }
7845
+ if (shouldRegisterTool("delete_webhook")) {
7846
+ server.tool("delete_webhook", "Delete a webhook by ID.", {
7847
+ id: exports_external.string()
7848
+ }, async ({ id }) => {
7849
+ try {
7850
+ const { deleteWebhook: deleteWebhook2 } = await Promise.resolve().then(() => (init_webhooks(), exports_webhooks));
7851
+ const deleted = deleteWebhook2(id);
7852
+ return { content: [{ type: "text", text: deleted ? "Webhook deleted." : "Webhook not found." }] };
7853
+ } catch (e) {
7854
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
7855
+ }
7856
+ });
7857
+ }
7858
+ if (shouldRegisterTool("create_template")) {
7859
+ server.tool("create_template", "Create a reusable task template.", {
7860
+ name: exports_external.string(),
7861
+ title_pattern: exports_external.string(),
7862
+ description: exports_external.string().optional(),
7863
+ priority: exports_external.enum(["low", "medium", "high", "critical"]).optional(),
7864
+ tags: exports_external.array(exports_external.string()).optional(),
7865
+ project_id: exports_external.string().optional(),
7866
+ plan_id: exports_external.string().optional()
7867
+ }, async (params) => {
7868
+ try {
7869
+ const { createTemplate: createTemplate2 } = await Promise.resolve().then(() => (init_templates(), exports_templates));
7870
+ const t = createTemplate2(params);
7871
+ return { content: [{ type: "text", text: `Template created: ${t.id.slice(0, 8)} | ${t.name} | "${t.title_pattern}"` }] };
7872
+ } catch (e) {
7873
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
7874
+ }
7875
+ });
7876
+ }
7877
+ if (shouldRegisterTool("list_templates")) {
7878
+ server.tool("list_templates", "List all task templates", {}, async () => {
7879
+ try {
7880
+ const { listTemplates: listTemplates2 } = await Promise.resolve().then(() => (init_templates(), exports_templates));
7881
+ const templates = listTemplates2();
7882
+ if (templates.length === 0)
7883
+ return { content: [{ type: "text", text: "No templates." }] };
7884
+ const text = templates.map((t) => `${t.id.slice(0, 8)} | ${t.name} | "${t.title_pattern}" | ${t.priority}`).join(`
7778
7885
  `);
7779
- return { content: [{ type: "text", text: `${templates.length} template(s):
7886
+ return { content: [{ type: "text", text: `${templates.length} template(s):
7780
7887
  ${text}` }] };
7781
- } catch (e) {
7782
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
7783
- }
7784
- });
7785
- server.tool("create_task_from_template", "Create a task from a template with optional overrides.", {
7786
- template_id: exports_external.string(),
7787
- title: exports_external.string().optional(),
7788
- description: exports_external.string().optional(),
7789
- priority: exports_external.enum(["low", "medium", "high", "critical"]).optional(),
7790
- assigned_to: exports_external.string().optional(),
7791
- project_id: exports_external.string().optional()
7792
- }, async (params) => {
7793
- try {
7794
- const { taskFromTemplate: taskFromTemplate2 } = await Promise.resolve().then(() => (init_templates(), exports_templates));
7795
- const input = taskFromTemplate2(params.template_id, {
7796
- title: params.title,
7797
- description: params.description,
7798
- priority: params.priority,
7799
- assigned_to: params.assigned_to,
7800
- project_id: params.project_id
7801
- });
7802
- const task = createTask(input);
7803
- return { content: [{ type: "text", text: `Task created from template:
7888
+ } catch (e) {
7889
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
7890
+ }
7891
+ });
7892
+ }
7893
+ if (shouldRegisterTool("create_task_from_template")) {
7894
+ server.tool("create_task_from_template", "Create a task from a template with optional overrides.", {
7895
+ template_id: exports_external.string(),
7896
+ title: exports_external.string().optional(),
7897
+ description: exports_external.string().optional(),
7898
+ priority: exports_external.enum(["low", "medium", "high", "critical"]).optional(),
7899
+ assigned_to: exports_external.string().optional(),
7900
+ project_id: exports_external.string().optional()
7901
+ }, async (params) => {
7902
+ try {
7903
+ const { taskFromTemplate: taskFromTemplate2 } = await Promise.resolve().then(() => (init_templates(), exports_templates));
7904
+ const input = taskFromTemplate2(params.template_id, {
7905
+ title: params.title,
7906
+ description: params.description,
7907
+ priority: params.priority,
7908
+ assigned_to: params.assigned_to,
7909
+ project_id: params.project_id
7910
+ });
7911
+ const task = createTask(input);
7912
+ return { content: [{ type: "text", text: `Task created from template:
7804
7913
  ${task.id.slice(0, 8)} | ${task.priority} | ${task.title}` }] };
7805
- } catch (e) {
7806
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
7807
- }
7808
- });
7809
- server.tool("delete_template", "Delete a task template by ID.", { id: exports_external.string() }, async ({ id }) => {
7810
- try {
7811
- const { deleteTemplate: deleteTemplate2 } = await Promise.resolve().then(() => (init_templates(), exports_templates));
7812
- const deleted = deleteTemplate2(id);
7813
- return { content: [{ type: "text", text: deleted ? "Template deleted." : "Template not found." }] };
7814
- } catch (e) {
7815
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
7816
- }
7817
- });
7818
- server.tool("approve_task", "Approve a task with requires_approval=true.", {
7819
- id: exports_external.string(),
7820
- agent_id: exports_external.string().optional()
7821
- }, async ({ id, agent_id }) => {
7822
- try {
7823
- const resolvedId = resolveId(id);
7824
- const task = getTaskWithRelations(resolvedId);
7825
- if (!task)
7826
- return { content: [{ type: "text", text: `Task not found: ${id}` }], isError: true };
7827
- if (!task.requires_approval)
7828
- return { content: [{ type: "text", text: `Task ${id} does not require approval.` }] };
7829
- if (task.approved_by)
7830
- return { content: [{ type: "text", text: `Task already approved by ${task.approved_by}.` }] };
7831
- const updated = updateTask(resolvedId, { approved_by: agent_id || "system", version: task.version });
7832
- return { content: [{ type: "text", text: `Task approved by ${agent_id || "system"}: ${updated.id.slice(0, 8)} | ${updated.title}` }] };
7833
- } catch (e) {
7834
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
7835
- }
7836
- });
7837
- server.tool("fail_task", "Mark a task as failed with structured reason and optional auto-retry.", {
7838
- id: exports_external.string(),
7839
- agent_id: exports_external.string().optional(),
7840
- reason: exports_external.string().optional(),
7841
- error_code: exports_external.string().optional(),
7842
- retry: exports_external.boolean().optional(),
7843
- retry_after: exports_external.string().optional()
7844
- }, async ({ id, agent_id, reason, error_code, retry, retry_after }) => {
7845
- try {
7846
- const resolvedId = resolveId(id);
7847
- const result = failTask(resolvedId, agent_id, reason, { retry, retry_after, error_code });
7848
- let text = `failed: ${formatTask(result.task)}`;
7849
- if (reason)
7850
- text += `
7914
+ } catch (e) {
7915
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
7916
+ }
7917
+ });
7918
+ }
7919
+ if (shouldRegisterTool("delete_template")) {
7920
+ server.tool("delete_template", "Delete a task template by ID.", { id: exports_external.string() }, async ({ id }) => {
7921
+ try {
7922
+ const { deleteTemplate: deleteTemplate2 } = await Promise.resolve().then(() => (init_templates(), exports_templates));
7923
+ const deleted = deleteTemplate2(id);
7924
+ return { content: [{ type: "text", text: deleted ? "Template deleted." : "Template not found." }] };
7925
+ } catch (e) {
7926
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
7927
+ }
7928
+ });
7929
+ }
7930
+ if (shouldRegisterTool("approve_task")) {
7931
+ server.tool("approve_task", "Approve a task with requires_approval=true.", {
7932
+ id: exports_external.string(),
7933
+ agent_id: exports_external.string().optional()
7934
+ }, async ({ id, agent_id }) => {
7935
+ try {
7936
+ const resolvedId = resolveId(id);
7937
+ const task = getTaskWithRelations(resolvedId);
7938
+ if (!task)
7939
+ return { content: [{ type: "text", text: `Task not found: ${id}` }], isError: true };
7940
+ if (!task.requires_approval)
7941
+ return { content: [{ type: "text", text: `Task ${id} does not require approval.` }] };
7942
+ if (task.approved_by)
7943
+ return { content: [{ type: "text", text: `Task already approved by ${task.approved_by}.` }] };
7944
+ const updated = updateTask(resolvedId, { approved_by: agent_id || "system", version: task.version });
7945
+ return { content: [{ type: "text", text: `Task approved by ${agent_id || "system"}: ${updated.id.slice(0, 8)} | ${updated.title}` }] };
7946
+ } catch (e) {
7947
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
7948
+ }
7949
+ });
7950
+ }
7951
+ if (shouldRegisterTool("fail_task")) {
7952
+ server.tool("fail_task", "Mark a task as failed with structured reason and optional auto-retry.", {
7953
+ id: exports_external.string(),
7954
+ agent_id: exports_external.string().optional(),
7955
+ reason: exports_external.string().optional(),
7956
+ error_code: exports_external.string().optional(),
7957
+ retry: exports_external.boolean().optional(),
7958
+ retry_after: exports_external.string().optional()
7959
+ }, async ({ id, agent_id, reason, error_code, retry, retry_after }) => {
7960
+ try {
7961
+ const resolvedId = resolveId(id);
7962
+ const result = failTask(resolvedId, agent_id, reason, { retry, retry_after, error_code });
7963
+ let text = `failed: ${formatTask(result.task)}`;
7964
+ if (reason)
7965
+ text += `
7851
7966
  Reason: ${reason}`;
7852
- if (result.retryTask) {
7853
- text += `
7967
+ if (result.retryTask) {
7968
+ text += `
7854
7969
  Retry task created: ${formatTask(result.retryTask)}`;
7970
+ }
7971
+ return { content: [{ type: "text", text }] };
7972
+ } catch (e) {
7973
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
7855
7974
  }
7856
- return { content: [{ type: "text", text }] };
7857
- } catch (e) {
7858
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
7859
- }
7860
- });
7861
- server.tool("get_my_tasks", "Get tasks assigned to/created by an agent with stats.", {
7862
- agent_name: exports_external.string()
7863
- }, async ({ agent_name }) => {
7864
- try {
7865
- const agent = registerAgent({ name: agent_name });
7866
- const tasks = listTasks({});
7867
- 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);
7868
- const pending = myTasks.filter((t) => t.status === "pending");
7869
- const inProgress = myTasks.filter((t) => t.status === "in_progress");
7870
- const completed = myTasks.filter((t) => t.status === "completed");
7871
- const rate = myTasks.length > 0 ? Math.round(completed.length / myTasks.length * 100) : 0;
7872
- const lines = [
7873
- `Agent: ${agent.name} (${agent.id})`,
7874
- `Tasks: ${myTasks.length} total, ${pending.length} pending, ${inProgress.length} active, ${completed.length} done (${rate}%)`
7875
- ];
7876
- if (pending.length > 0) {
7877
- lines.push(`
7975
+ });
7976
+ }
7977
+ if (shouldRegisterTool("get_my_tasks")) {
7978
+ server.tool("get_my_tasks", "Get tasks assigned to/created by an agent with stats.", {
7979
+ agent_name: exports_external.string()
7980
+ }, async ({ agent_name }) => {
7981
+ try {
7982
+ const agent = registerAgent({ name: agent_name });
7983
+ const tasks = listTasks({});
7984
+ 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);
7985
+ const pending = myTasks.filter((t) => t.status === "pending");
7986
+ const inProgress = myTasks.filter((t) => t.status === "in_progress");
7987
+ const completed = myTasks.filter((t) => t.status === "completed");
7988
+ const rate = myTasks.length > 0 ? Math.round(completed.length / myTasks.length * 100) : 0;
7989
+ const lines = [
7990
+ `Agent: ${agent.name} (${agent.id})`,
7991
+ `Tasks: ${myTasks.length} total, ${pending.length} pending, ${inProgress.length} active, ${completed.length} done (${rate}%)`
7992
+ ];
7993
+ if (pending.length > 0) {
7994
+ lines.push(`
7878
7995
  Pending:`);
7879
- for (const t of pending.slice(0, 10))
7880
- lines.push(` [${t.priority}] ${t.id.slice(0, 8)} | ${t.title}`);
7881
- }
7882
- if (inProgress.length > 0) {
7883
- lines.push(`
7996
+ for (const t of pending.slice(0, 10))
7997
+ lines.push(` [${t.priority}] ${t.id.slice(0, 8)} | ${t.title}`);
7998
+ }
7999
+ if (inProgress.length > 0) {
8000
+ lines.push(`
7884
8001
  In Progress:`);
7885
- for (const t of inProgress)
7886
- lines.push(` ${t.id.slice(0, 8)} | ${t.title}`);
7887
- }
7888
- return { content: [{ type: "text", text: lines.join(`
8002
+ for (const t of inProgress)
8003
+ lines.push(` ${t.id.slice(0, 8)} | ${t.title}`);
8004
+ }
8005
+ return { content: [{ type: "text", text: lines.join(`
7889
8006
  `) }] };
7890
- } catch (e) {
7891
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
7892
- }
7893
- });
7894
- server.tool("get_org_chart", "Get agent org chart showing reporting hierarchy.", {}, async () => {
7895
- try {
7896
- let render = function(nodes, indent = 0) {
7897
- return nodes.map((n) => {
7898
- const prefix = " ".repeat(indent);
7899
- const title = n.agent.title ? ` \u2014 ${n.agent.title}` : "";
7900
- const level = n.agent.level ? ` (${n.agent.level})` : "";
7901
- const line = `${prefix}${n.agent.name}${title}${level}`;
7902
- const children = n.reports.length > 0 ? `
8007
+ } catch (e) {
8008
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
8009
+ }
8010
+ });
8011
+ }
8012
+ if (shouldRegisterTool("get_org_chart")) {
8013
+ server.tool("get_org_chart", "Get agent org chart showing reporting hierarchy.", {}, async () => {
8014
+ try {
8015
+ let render = function(nodes, indent = 0) {
8016
+ return nodes.map((n) => {
8017
+ const prefix = " ".repeat(indent);
8018
+ const title = n.agent.title ? ` \u2014 ${n.agent.title}` : "";
8019
+ const level = n.agent.level ? ` (${n.agent.level})` : "";
8020
+ const line = `${prefix}${n.agent.name}${title}${level}`;
8021
+ const children = n.reports.length > 0 ? `
7903
8022
  ` + render(n.reports, indent + 1) : "";
7904
- return line + children;
7905
- }).join(`
8023
+ return line + children;
8024
+ }).join(`
7906
8025
  `);
7907
- };
7908
- const { getOrgChart: getOrgChart2 } = await Promise.resolve().then(() => (init_agents(), exports_agents));
7909
- const tree = getOrgChart2();
7910
- const text = tree.length > 0 ? render(tree) : "No agents registered.";
7911
- return { content: [{ type: "text", text }] };
7912
- } catch (e) {
7913
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
7914
- }
7915
- });
7916
- server.tool("set_reports_to", "Set agent reporting relationship in org chart.", {
7917
- agent_name: exports_external.string(),
7918
- manager_name: exports_external.string().optional()
7919
- }, async ({ agent_name, manager_name }) => {
7920
- try {
7921
- const agent = getAgentByName(agent_name);
7922
- if (!agent)
7923
- return { content: [{ type: "text", text: `Agent not found: ${agent_name}` }], isError: true };
7924
- let managerId = null;
7925
- if (manager_name) {
7926
- const manager = getAgentByName(manager_name);
7927
- if (!manager)
7928
- return { content: [{ type: "text", text: `Manager not found: ${manager_name}` }], isError: true };
7929
- managerId = manager.id;
7930
- }
7931
- const { updateAgent: updateAgent2 } = await Promise.resolve().then(() => (init_agents(), exports_agents));
7932
- updateAgent2(agent.id, { reports_to: managerId });
7933
- const result = managerId ? `${agent_name} now reports to ${manager_name}` : `${agent_name} reports to no one (top-level)`;
7934
- return { content: [{ type: "text", text: result }] };
7935
- } catch (e) {
7936
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
7937
- }
7938
- });
7939
- server.tool("bulk_update_tasks", "Update multiple tasks at once with the same changes.", {
7940
- task_ids: exports_external.array(exports_external.string()),
7941
- status: exports_external.enum(["pending", "in_progress", "completed", "failed", "cancelled"]).optional(),
7942
- priority: exports_external.enum(["low", "medium", "high", "critical"]).optional(),
7943
- assigned_to: exports_external.string().optional(),
7944
- tags: exports_external.array(exports_external.string()).optional()
7945
- }, async ({ task_ids, ...updates }) => {
7946
- try {
7947
- const resolvedIds = task_ids.map((id) => resolveId(id));
7948
- const result = bulkUpdateTasks(resolvedIds, updates);
7949
- const parts = [`Updated ${result.updated} task(s).`];
7950
- if (result.failed.length > 0) {
7951
- parts.push(`Failed ${result.failed.length}:`);
7952
- for (const f of result.failed)
7953
- parts.push(` ${f.id.slice(0, 8)}: ${f.error}`);
7954
- }
7955
- return { content: [{ type: "text", text: parts.join(`
8026
+ };
8027
+ const { getOrgChart: getOrgChart2 } = await Promise.resolve().then(() => (init_agents(), exports_agents));
8028
+ const tree = getOrgChart2();
8029
+ const text = tree.length > 0 ? render(tree) : "No agents registered.";
8030
+ return { content: [{ type: "text", text }] };
8031
+ } catch (e) {
8032
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
8033
+ }
8034
+ });
8035
+ }
8036
+ if (shouldRegisterTool("set_reports_to")) {
8037
+ server.tool("set_reports_to", "Set agent reporting relationship in org chart.", {
8038
+ agent_name: exports_external.string(),
8039
+ manager_name: exports_external.string().optional()
8040
+ }, async ({ agent_name, manager_name }) => {
8041
+ try {
8042
+ const agent = getAgentByName(agent_name);
8043
+ if (!agent)
8044
+ return { content: [{ type: "text", text: `Agent not found: ${agent_name}` }], isError: true };
8045
+ let managerId = null;
8046
+ if (manager_name) {
8047
+ const manager = getAgentByName(manager_name);
8048
+ if (!manager)
8049
+ return { content: [{ type: "text", text: `Manager not found: ${manager_name}` }], isError: true };
8050
+ managerId = manager.id;
8051
+ }
8052
+ const { updateAgent: updateAgent2 } = await Promise.resolve().then(() => (init_agents(), exports_agents));
8053
+ updateAgent2(agent.id, { reports_to: managerId });
8054
+ const result = managerId ? `${agent_name} now reports to ${manager_name}` : `${agent_name} reports to no one (top-level)`;
8055
+ return { content: [{ type: "text", text: result }] };
8056
+ } catch (e) {
8057
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
8058
+ }
8059
+ });
8060
+ }
8061
+ if (shouldRegisterTool("bulk_update_tasks")) {
8062
+ server.tool("bulk_update_tasks", "Update multiple tasks at once with the same changes.", {
8063
+ task_ids: exports_external.array(exports_external.string()),
8064
+ status: exports_external.enum(["pending", "in_progress", "completed", "failed", "cancelled"]).optional(),
8065
+ priority: exports_external.enum(["low", "medium", "high", "critical"]).optional(),
8066
+ assigned_to: exports_external.string().optional(),
8067
+ tags: exports_external.array(exports_external.string()).optional()
8068
+ }, async ({ task_ids, ...updates }) => {
8069
+ try {
8070
+ const resolvedIds = task_ids.map((id) => resolveId(id));
8071
+ const result = bulkUpdateTasks(resolvedIds, updates);
8072
+ const parts = [`Updated ${result.updated} task(s).`];
8073
+ if (result.failed.length > 0) {
8074
+ parts.push(`Failed ${result.failed.length}:`);
8075
+ for (const f of result.failed)
8076
+ parts.push(` ${f.id.slice(0, 8)}: ${f.error}`);
8077
+ }
8078
+ return { content: [{ type: "text", text: parts.join(`
7956
8079
  `) }] };
7957
- } catch (e) {
7958
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
7959
- }
7960
- });
7961
- server.tool("clone_task", "Duplicate a task with optional field overrides.", {
7962
- task_id: exports_external.string(),
7963
- title: exports_external.string().optional(),
7964
- description: exports_external.string().optional(),
7965
- priority: exports_external.enum(["low", "medium", "high", "critical"]).optional(),
7966
- status: exports_external.enum(["pending", "in_progress", "completed", "failed", "cancelled"]).optional(),
7967
- project_id: exports_external.string().optional(),
7968
- plan_id: exports_external.string().optional(),
7969
- task_list_id: exports_external.string().optional(),
7970
- assigned_to: exports_external.string().optional(),
7971
- tags: exports_external.array(exports_external.string()).optional(),
7972
- estimated_minutes: exports_external.number().optional()
7973
- }, async ({ task_id, ...overrides }) => {
7974
- try {
7975
- const resolvedId = resolveId(task_id);
7976
- const resolved = { ...overrides };
7977
- if (resolved.project_id)
7978
- resolved.project_id = resolveId(resolved.project_id, "projects");
7979
- if (resolved.plan_id)
7980
- resolved.plan_id = resolveId(resolved.plan_id, "plans");
7981
- if (resolved.task_list_id)
7982
- resolved.task_list_id = resolveId(resolved.task_list_id, "task_lists");
7983
- const task = cloneTask(resolvedId, resolved);
7984
- return { content: [{ type: "text", text: `cloned: ${formatTask(task)}` }] };
7985
- } catch (e) {
7986
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
7987
- }
7988
- });
7989
- server.tool("get_task_stats", "Get task analytics: counts by status, priority, agent.", {
7990
- project_id: exports_external.string().optional(),
7991
- task_list_id: exports_external.string().optional(),
7992
- agent_id: exports_external.string().optional()
7993
- }, async ({ project_id, task_list_id, agent_id }) => {
7994
- try {
7995
- const filters = {};
7996
- if (project_id)
7997
- filters.project_id = resolveId(project_id, "projects");
7998
- if (task_list_id)
7999
- filters.task_list_id = resolveId(task_list_id, "task_lists");
8000
- if (agent_id)
8001
- filters.agent_id = agent_id;
8002
- const stats = getTaskStats(Object.keys(filters).length > 0 ? filters : undefined);
8003
- return { content: [{ type: "text", text: JSON.stringify(stats, null, 2) }] };
8004
- } catch (e) {
8005
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
8006
- }
8007
- });
8008
- server.tool("get_task_graph", "Get full dependency tree for a task.", {
8009
- id: exports_external.string(),
8010
- direction: exports_external.enum(["up", "down", "both"]).optional()
8011
- }, async ({ id, direction }) => {
8012
- try {
8013
- let formatNode = function(node, indent) {
8014
- const prefix = " ".repeat(indent);
8015
- const idLabel = node.task.short_id || node.task.id.slice(0, 8);
8016
- const blocked = node.task.is_blocked ? " (blocked: yes)" : "";
8017
- let out = `${prefix}[${node.task.status}] ${idLabel} | ${node.task.title}${blocked}
8080
+ } catch (e) {
8081
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
8082
+ }
8083
+ });
8084
+ }
8085
+ if (shouldRegisterTool("clone_task")) {
8086
+ server.tool("clone_task", "Duplicate a task with optional field overrides.", {
8087
+ task_id: exports_external.string(),
8088
+ title: exports_external.string().optional(),
8089
+ description: exports_external.string().optional(),
8090
+ priority: exports_external.enum(["low", "medium", "high", "critical"]).optional(),
8091
+ status: exports_external.enum(["pending", "in_progress", "completed", "failed", "cancelled"]).optional(),
8092
+ project_id: exports_external.string().optional(),
8093
+ plan_id: exports_external.string().optional(),
8094
+ task_list_id: exports_external.string().optional(),
8095
+ assigned_to: exports_external.string().optional(),
8096
+ tags: exports_external.array(exports_external.string()).optional(),
8097
+ estimated_minutes: exports_external.number().optional()
8098
+ }, async ({ task_id, ...overrides }) => {
8099
+ try {
8100
+ const resolvedId = resolveId(task_id);
8101
+ const resolved = { ...overrides };
8102
+ if (resolved.project_id)
8103
+ resolved.project_id = resolveId(resolved.project_id, "projects");
8104
+ if (resolved.plan_id)
8105
+ resolved.plan_id = resolveId(resolved.plan_id, "plans");
8106
+ if (resolved.task_list_id)
8107
+ resolved.task_list_id = resolveId(resolved.task_list_id, "task_lists");
8108
+ const task = cloneTask(resolvedId, resolved);
8109
+ return { content: [{ type: "text", text: `cloned: ${formatTask(task)}` }] };
8110
+ } catch (e) {
8111
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
8112
+ }
8113
+ });
8114
+ }
8115
+ if (shouldRegisterTool("get_task_stats")) {
8116
+ server.tool("get_task_stats", "Get task analytics: counts by status, priority, agent.", {
8117
+ project_id: exports_external.string().optional(),
8118
+ task_list_id: exports_external.string().optional(),
8119
+ agent_id: exports_external.string().optional()
8120
+ }, async ({ project_id, task_list_id, agent_id }) => {
8121
+ try {
8122
+ const filters = {};
8123
+ if (project_id)
8124
+ filters.project_id = resolveId(project_id, "projects");
8125
+ if (task_list_id)
8126
+ filters.task_list_id = resolveId(task_list_id, "task_lists");
8127
+ if (agent_id)
8128
+ filters.agent_id = agent_id;
8129
+ const stats = getTaskStats(Object.keys(filters).length > 0 ? filters : undefined);
8130
+ return { content: [{ type: "text", text: JSON.stringify(stats, null, 2) }] };
8131
+ } catch (e) {
8132
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
8133
+ }
8134
+ });
8135
+ }
8136
+ if (shouldRegisterTool("get_task_graph")) {
8137
+ server.tool("get_task_graph", "Get full dependency tree for a task.", {
8138
+ id: exports_external.string(),
8139
+ direction: exports_external.enum(["up", "down", "both"]).optional()
8140
+ }, async ({ id, direction }) => {
8141
+ try {
8142
+ let formatNode = function(node, indent) {
8143
+ const prefix = " ".repeat(indent);
8144
+ const idLabel = node.task.short_id || node.task.id.slice(0, 8);
8145
+ const blocked = node.task.is_blocked ? " (blocked: yes)" : "";
8146
+ let out = `${prefix}[${node.task.status}] ${idLabel} | ${node.task.title}${blocked}
8018
8147
  `;
8019
- if (node.depends_on.length > 0) {
8020
- out += `${prefix} Depends on:
8148
+ if (node.depends_on.length > 0) {
8149
+ out += `${prefix} Depends on:
8021
8150
  `;
8022
- for (const dep of node.depends_on) {
8023
- out += formatNode(dep, indent + 2);
8151
+ for (const dep of node.depends_on) {
8152
+ out += formatNode(dep, indent + 2);
8153
+ }
8024
8154
  }
8025
- }
8026
- if (node.blocks.length > 0) {
8027
- out += `${prefix} Blocks:
8155
+ if (node.blocks.length > 0) {
8156
+ out += `${prefix} Blocks:
8028
8157
  `;
8029
- for (const dep of node.blocks) {
8030
- out += formatNode(dep, indent + 2);
8158
+ for (const dep of node.blocks) {
8159
+ out += formatNode(dep, indent + 2);
8160
+ }
8031
8161
  }
8032
- }
8033
- return out;
8034
- };
8035
- const taskId = resolveId(id, "tasks");
8036
- const graph = getTaskGraph(taskId, direction || "both");
8037
- let text = `Task: ${formatNode(graph, 0)}`;
8038
- if (graph.depends_on.length > 0) {
8039
- text += `
8162
+ return out;
8163
+ };
8164
+ const taskId = resolveId(id, "tasks");
8165
+ const graph = getTaskGraph(taskId, direction || "both");
8166
+ let text = `Task: ${formatNode(graph, 0)}`;
8167
+ if (graph.depends_on.length > 0) {
8168
+ text += `
8040
8169
  Depends on:
8041
8170
  `;
8042
- for (const dep of graph.depends_on) {
8043
- text += formatNode(dep, 1);
8171
+ for (const dep of graph.depends_on) {
8172
+ text += formatNode(dep, 1);
8173
+ }
8044
8174
  }
8045
- }
8046
- if (graph.blocks.length > 0) {
8047
- text += `
8175
+ if (graph.blocks.length > 0) {
8176
+ text += `
8048
8177
  Blocks:
8049
8178
  `;
8050
- for (const dep of graph.blocks) {
8051
- text += formatNode(dep, 1);
8179
+ for (const dep of graph.blocks) {
8180
+ text += formatNode(dep, 1);
8181
+ }
8052
8182
  }
8183
+ return { content: [{ type: "text", text }] };
8184
+ } catch (e) {
8185
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
8053
8186
  }
8054
- return { content: [{ type: "text", text }] };
8055
- } catch (e) {
8056
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
8057
- }
8058
- });
8059
- server.tool("bulk_create_tasks", "Create multiple tasks atomically with dependency support.", {
8060
- tasks: exports_external.array(exports_external.object({
8061
- temp_id: exports_external.string().optional(),
8062
- title: exports_external.string(),
8063
- description: exports_external.string().optional(),
8064
- priority: exports_external.enum(["low", "medium", "high", "critical"]).optional(),
8065
- status: exports_external.enum(["pending", "in_progress", "completed", "failed", "cancelled"]).optional(),
8187
+ });
8188
+ }
8189
+ if (shouldRegisterTool("bulk_create_tasks")) {
8190
+ server.tool("bulk_create_tasks", "Create multiple tasks atomically with dependency support.", {
8191
+ tasks: exports_external.array(exports_external.object({
8192
+ temp_id: exports_external.string().optional(),
8193
+ title: exports_external.string(),
8194
+ description: exports_external.string().optional(),
8195
+ priority: exports_external.enum(["low", "medium", "high", "critical"]).optional(),
8196
+ status: exports_external.enum(["pending", "in_progress", "completed", "failed", "cancelled"]).optional(),
8197
+ project_id: exports_external.string().optional(),
8198
+ plan_id: exports_external.string().optional(),
8199
+ task_list_id: exports_external.string().optional(),
8200
+ agent_id: exports_external.string().optional(),
8201
+ assigned_to: exports_external.string().optional(),
8202
+ tags: exports_external.array(exports_external.string()).optional(),
8203
+ estimated_minutes: exports_external.number().optional(),
8204
+ depends_on_temp_ids: exports_external.array(exports_external.string()).optional()
8205
+ })),
8066
8206
  project_id: exports_external.string().optional(),
8067
8207
  plan_id: exports_external.string().optional(),
8068
- task_list_id: exports_external.string().optional(),
8069
- agent_id: exports_external.string().optional(),
8070
- assigned_to: exports_external.string().optional(),
8071
- tags: exports_external.array(exports_external.string()).optional(),
8072
- estimated_minutes: exports_external.number().optional(),
8073
- depends_on_temp_ids: exports_external.array(exports_external.string()).optional()
8074
- })),
8075
- project_id: exports_external.string().optional(),
8076
- plan_id: exports_external.string().optional(),
8077
- task_list_id: exports_external.string().optional()
8078
- }, async ({ tasks, project_id, plan_id, task_list_id }) => {
8079
- try {
8080
- const resolvedProjectId = project_id ? resolveId(project_id, "projects") : undefined;
8081
- const resolvedPlanId = plan_id ? resolveId(plan_id, "plans") : undefined;
8082
- const resolvedTaskListId = task_list_id ? resolveId(task_list_id, "task_lists") : undefined;
8083
- const enrichedTasks = tasks.map((t) => ({
8084
- ...t,
8085
- project_id: t.project_id || resolvedProjectId,
8086
- plan_id: t.plan_id || resolvedPlanId,
8087
- task_list_id: t.task_list_id || resolvedTaskListId
8088
- }));
8089
- const result = bulkCreateTasks(enrichedTasks);
8090
- const lines = result.created.map((t) => {
8091
- const tid = t.temp_id ? `[${t.temp_id}] ` : "";
8092
- const sid = t.short_id || t.id.slice(0, 8);
8093
- return ` ${tid}${sid} | ${t.title}`;
8094
- });
8095
- return { content: [{ type: "text", text: `Created ${result.created.length} task(s):
8208
+ task_list_id: exports_external.string().optional()
8209
+ }, async ({ tasks, project_id, plan_id, task_list_id }) => {
8210
+ try {
8211
+ const resolvedProjectId = project_id ? resolveId(project_id, "projects") : undefined;
8212
+ const resolvedPlanId = plan_id ? resolveId(plan_id, "plans") : undefined;
8213
+ const resolvedTaskListId = task_list_id ? resolveId(task_list_id, "task_lists") : undefined;
8214
+ const enrichedTasks = tasks.map((t) => ({
8215
+ ...t,
8216
+ project_id: t.project_id || resolvedProjectId,
8217
+ plan_id: t.plan_id || resolvedPlanId,
8218
+ task_list_id: t.task_list_id || resolvedTaskListId
8219
+ }));
8220
+ const result = bulkCreateTasks(enrichedTasks);
8221
+ const lines = result.created.map((t) => {
8222
+ const tid = t.temp_id ? `[${t.temp_id}] ` : "";
8223
+ const sid = t.short_id || t.id.slice(0, 8);
8224
+ return ` ${tid}${sid} | ${t.title}`;
8225
+ });
8226
+ return { content: [{ type: "text", text: `Created ${result.created.length} task(s):
8096
8227
  ${lines.join(`
8097
8228
  `)}` }] };
8098
- } catch (e) {
8099
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
8100
- }
8101
- });
8102
- server.tool("move_task", "Move a task to a different list, project, or plan.", {
8103
- task_id: exports_external.string(),
8104
- task_list_id: exports_external.string().nullable().optional(),
8105
- project_id: exports_external.string().nullable().optional(),
8106
- plan_id: exports_external.string().nullable().optional()
8107
- }, async ({ task_id, ...target }) => {
8108
- try {
8109
- const resolvedId = resolveId(task_id);
8110
- const resolvedTarget = {};
8111
- if (target.task_list_id !== undefined)
8112
- resolvedTarget.task_list_id = target.task_list_id ? resolveId(target.task_list_id, "task_lists") : null;
8113
- if (target.project_id !== undefined)
8114
- resolvedTarget.project_id = target.project_id ? resolveId(target.project_id, "projects") : null;
8115
- if (target.plan_id !== undefined)
8116
- resolvedTarget.plan_id = target.plan_id ? resolveId(target.plan_id, "plans") : null;
8117
- const task = moveTask(resolvedId, resolvedTarget);
8118
- return { content: [{ type: "text", text: `moved: ${formatTask(task)}` }] };
8119
- } catch (e) {
8120
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
8121
- }
8122
- });
8123
- server.tool("get_next_task", "Get the best pending task to work on next.", {
8124
- agent_id: exports_external.string().optional(),
8125
- project_id: exports_external.string().optional(),
8126
- task_list_id: exports_external.string().optional(),
8127
- plan_id: exports_external.string().optional(),
8128
- tags: exports_external.array(exports_external.string()).optional()
8129
- }, async ({ agent_id, project_id, task_list_id, plan_id, tags }) => {
8130
- try {
8131
- const filters = {};
8132
- if (project_id)
8133
- filters.project_id = resolveId(project_id, "projects");
8134
- if (task_list_id)
8135
- filters.task_list_id = resolveId(task_list_id, "task_lists");
8136
- if (plan_id)
8137
- filters.plan_id = resolveId(plan_id, "plans");
8138
- if (tags)
8139
- filters.tags = tags;
8140
- const task = getNextTask(agent_id, Object.keys(filters).length > 0 ? filters : undefined);
8141
- if (!task) {
8142
- return { content: [{ type: "text", text: "No tasks available \u2014 all pending tasks are blocked, locked, or none exist." }] };
8143
- }
8144
- return { content: [{ type: "text", text: `next: ${formatTask(task)}
8229
+ } catch (e) {
8230
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
8231
+ }
8232
+ });
8233
+ }
8234
+ if (shouldRegisterTool("move_task")) {
8235
+ server.tool("move_task", "Move a task to a different list, project, or plan.", {
8236
+ task_id: exports_external.string(),
8237
+ task_list_id: exports_external.string().nullable().optional(),
8238
+ project_id: exports_external.string().nullable().optional(),
8239
+ plan_id: exports_external.string().nullable().optional()
8240
+ }, async ({ task_id, ...target }) => {
8241
+ try {
8242
+ const resolvedId = resolveId(task_id);
8243
+ const resolvedTarget = {};
8244
+ if (target.task_list_id !== undefined)
8245
+ resolvedTarget.task_list_id = target.task_list_id ? resolveId(target.task_list_id, "task_lists") : null;
8246
+ if (target.project_id !== undefined)
8247
+ resolvedTarget.project_id = target.project_id ? resolveId(target.project_id, "projects") : null;
8248
+ if (target.plan_id !== undefined)
8249
+ resolvedTarget.plan_id = target.plan_id ? resolveId(target.plan_id, "plans") : null;
8250
+ const task = moveTask(resolvedId, resolvedTarget);
8251
+ return { content: [{ type: "text", text: `moved: ${formatTask(task)}` }] };
8252
+ } catch (e) {
8253
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
8254
+ }
8255
+ });
8256
+ }
8257
+ if (shouldRegisterTool("get_next_task")) {
8258
+ server.tool("get_next_task", "Get the best pending task to work on next.", {
8259
+ agent_id: exports_external.string().optional(),
8260
+ project_id: exports_external.string().optional(),
8261
+ task_list_id: exports_external.string().optional(),
8262
+ plan_id: exports_external.string().optional(),
8263
+ tags: exports_external.array(exports_external.string()).optional()
8264
+ }, async ({ agent_id, project_id, task_list_id, plan_id, tags }) => {
8265
+ try {
8266
+ const filters = {};
8267
+ if (project_id)
8268
+ filters.project_id = resolveId(project_id, "projects");
8269
+ if (task_list_id)
8270
+ filters.task_list_id = resolveId(task_list_id, "task_lists");
8271
+ if (plan_id)
8272
+ filters.plan_id = resolveId(plan_id, "plans");
8273
+ if (tags)
8274
+ filters.tags = tags;
8275
+ const task = getNextTask(agent_id, Object.keys(filters).length > 0 ? filters : undefined);
8276
+ if (!task) {
8277
+ return { content: [{ type: "text", text: "No tasks available \u2014 all pending tasks are blocked, locked, or none exist." }] };
8278
+ }
8279
+ return { content: [{ type: "text", text: `next: ${formatTask(task)}
8145
8280
  ${formatTaskDetail(task)}` }] };
8146
- } catch (e) {
8147
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
8148
- }
8149
- });
8150
- server.tool("get_active_work", "See all in-progress tasks and who is working on them.", {
8151
- project_id: exports_external.string().optional(),
8152
- task_list_id: exports_external.string().optional()
8153
- }, async ({ project_id, task_list_id }) => {
8154
- try {
8155
- const filters = {};
8156
- if (project_id)
8157
- filters.project_id = resolveId(project_id, "projects");
8158
- if (task_list_id)
8159
- filters.task_list_id = resolveId(task_list_id, "task_lists");
8160
- const work = getActiveWork(Object.keys(filters).length > 0 ? filters : undefined);
8161
- if (work.length === 0) {
8162
- return { content: [{ type: "text", text: "No active work \u2014 no tasks are currently in progress." }] };
8163
- }
8164
- const text = work.map((w) => {
8165
- const id = w.short_id || w.id.slice(0, 8);
8166
- const agent = w.assigned_to || w.locked_by || "unassigned";
8167
- const since = w.updated_at;
8168
- return `${agent.padEnd(12)} | ${w.priority.padEnd(8)} | ${id} | ${w.title} (since ${since})`;
8169
- }).join(`
8281
+ } catch (e) {
8282
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
8283
+ }
8284
+ });
8285
+ }
8286
+ if (shouldRegisterTool("get_active_work")) {
8287
+ server.tool("get_active_work", "See all in-progress tasks and who is working on them.", {
8288
+ project_id: exports_external.string().optional(),
8289
+ task_list_id: exports_external.string().optional()
8290
+ }, async ({ project_id, task_list_id }) => {
8291
+ try {
8292
+ const filters = {};
8293
+ if (project_id)
8294
+ filters.project_id = resolveId(project_id, "projects");
8295
+ if (task_list_id)
8296
+ filters.task_list_id = resolveId(task_list_id, "task_lists");
8297
+ const work = getActiveWork(Object.keys(filters).length > 0 ? filters : undefined);
8298
+ if (work.length === 0) {
8299
+ return { content: [{ type: "text", text: "No active work \u2014 no tasks are currently in progress." }] };
8300
+ }
8301
+ const text = work.map((w) => {
8302
+ const id = w.short_id || w.id.slice(0, 8);
8303
+ const agent = w.assigned_to || w.locked_by || "unassigned";
8304
+ const since = w.updated_at;
8305
+ return `${agent.padEnd(12)} | ${w.priority.padEnd(8)} | ${id} | ${w.title} (since ${since})`;
8306
+ }).join(`
8170
8307
  `);
8171
- return { content: [{ type: "text", text: `${work.length} active task(s):
8308
+ return { content: [{ type: "text", text: `${work.length} active task(s):
8172
8309
  ${text}` }] };
8173
- } catch (e) {
8174
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
8175
- }
8176
- });
8177
- server.tool("get_tasks_changed_since", "Get tasks modified after a timestamp for incremental sync.", {
8178
- since: exports_external.string(),
8179
- project_id: exports_external.string().optional(),
8180
- task_list_id: exports_external.string().optional()
8181
- }, async ({ since, project_id, task_list_id }) => {
8182
- try {
8183
- const filters = {};
8184
- if (project_id)
8185
- filters.project_id = resolveId(project_id, "projects");
8186
- if (task_list_id)
8187
- filters.task_list_id = resolveId(task_list_id, "task_lists");
8188
- const tasks = getTasksChangedSince(since, Object.keys(filters).length > 0 ? filters : undefined);
8189
- if (tasks.length === 0) {
8190
- return { content: [{ type: "text", text: `No tasks changed since ${since}.` }] };
8191
- }
8192
- const text = tasks.map((t) => {
8193
- const assigned = t.assigned_to ? ` -> ${t.assigned_to}` : "";
8194
- return `[${t.status}] ${t.id.slice(0, 8)} | ${t.priority} | ${t.title}${assigned} (updated: ${t.updated_at})`;
8195
- }).join(`
8310
+ } catch (e) {
8311
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
8312
+ }
8313
+ });
8314
+ }
8315
+ if (shouldRegisterTool("get_tasks_changed_since")) {
8316
+ server.tool("get_tasks_changed_since", "Get tasks modified after a timestamp for incremental sync.", {
8317
+ since: exports_external.string(),
8318
+ project_id: exports_external.string().optional(),
8319
+ task_list_id: exports_external.string().optional()
8320
+ }, async ({ since, project_id, task_list_id }) => {
8321
+ try {
8322
+ const filters = {};
8323
+ if (project_id)
8324
+ filters.project_id = resolveId(project_id, "projects");
8325
+ if (task_list_id)
8326
+ filters.task_list_id = resolveId(task_list_id, "task_lists");
8327
+ const tasks = getTasksChangedSince(since, Object.keys(filters).length > 0 ? filters : undefined);
8328
+ if (tasks.length === 0) {
8329
+ return { content: [{ type: "text", text: `No tasks changed since ${since}.` }] };
8330
+ }
8331
+ const text = tasks.map((t) => {
8332
+ const assigned = t.assigned_to ? ` -> ${t.assigned_to}` : "";
8333
+ return `[${t.status}] ${t.id.slice(0, 8)} | ${t.priority} | ${t.title}${assigned} (updated: ${t.updated_at})`;
8334
+ }).join(`
8196
8335
  `);
8197
- return { content: [{ type: "text", text: `${tasks.length} task(s) changed since ${since}:
8336
+ return { content: [{ type: "text", text: `${tasks.length} task(s) changed since ${since}:
8198
8337
  ${text}` }] };
8199
- } catch (e) {
8200
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
8201
- }
8202
- });
8203
- server.tool("claim_next_task", "Atomically claim, lock, and start the best pending task.", {
8204
- agent_id: exports_external.string(),
8205
- project_id: exports_external.string().optional(),
8206
- task_list_id: exports_external.string().optional(),
8207
- plan_id: exports_external.string().optional(),
8208
- tags: exports_external.array(exports_external.string()).optional()
8209
- }, async ({ agent_id, project_id, task_list_id, plan_id, tags }) => {
8210
- try {
8211
- const filters = {};
8212
- if (project_id)
8213
- filters.project_id = resolveId(project_id, "projects");
8214
- if (task_list_id)
8215
- filters.task_list_id = resolveId(task_list_id, "task_lists");
8216
- if (plan_id)
8217
- filters.plan_id = resolveId(plan_id, "plans");
8218
- if (tags)
8219
- filters.tags = tags;
8220
- const task = claimNextTask(agent_id, Object.keys(filters).length > 0 ? filters : undefined);
8221
- if (!task) {
8222
- return { content: [{ type: "text", text: "No tasks available to claim." }] };
8223
- }
8224
- return { content: [{ type: "text", text: `claimed: ${formatTask(task)}
8338
+ } catch (e) {
8339
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
8340
+ }
8341
+ });
8342
+ }
8343
+ if (shouldRegisterTool("claim_next_task")) {
8344
+ server.tool("claim_next_task", "Atomically claim, lock, and start the best pending task.", {
8345
+ agent_id: exports_external.string(),
8346
+ project_id: exports_external.string().optional(),
8347
+ task_list_id: exports_external.string().optional(),
8348
+ plan_id: exports_external.string().optional(),
8349
+ tags: exports_external.array(exports_external.string()).optional()
8350
+ }, async ({ agent_id, project_id, task_list_id, plan_id, tags }) => {
8351
+ try {
8352
+ const filters = {};
8353
+ if (project_id)
8354
+ filters.project_id = resolveId(project_id, "projects");
8355
+ if (task_list_id)
8356
+ filters.task_list_id = resolveId(task_list_id, "task_lists");
8357
+ if (plan_id)
8358
+ filters.plan_id = resolveId(plan_id, "plans");
8359
+ if (tags)
8360
+ filters.tags = tags;
8361
+ const task = claimNextTask(agent_id, Object.keys(filters).length > 0 ? filters : undefined);
8362
+ if (!task) {
8363
+ return { content: [{ type: "text", text: "No tasks available to claim." }] };
8364
+ }
8365
+ return { content: [{ type: "text", text: `claimed: ${formatTask(task)}
8225
8366
  ${formatTaskDetail(task)}` }] };
8226
- } catch (e) {
8227
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
8228
- }
8229
- });
8230
- server.tool("get_stale_tasks", "Find stale in_progress tasks with no recent activity.", {
8231
- stale_minutes: exports_external.number().optional(),
8232
- project_id: exports_external.string().optional(),
8233
- task_list_id: exports_external.string().optional()
8234
- }, async ({ stale_minutes, project_id, task_list_id }) => {
8235
- try {
8236
- const filters = {};
8237
- if (project_id)
8238
- filters.project_id = resolveId(project_id, "projects");
8239
- if (task_list_id)
8240
- filters.task_list_id = resolveId(task_list_id, "task_lists");
8241
- const tasks = getStaleTasks(stale_minutes || 30, Object.keys(filters).length > 0 ? filters : undefined);
8242
- if (tasks.length === 0) {
8243
- return { content: [{ type: "text", text: "No stale tasks found." }] };
8244
- }
8245
- const text = tasks.map((t) => {
8246
- const id = t.short_id || t.id.slice(0, 8);
8247
- const agent = t.locked_by || t.assigned_to || "unknown";
8248
- const staleFor = Math.round((Date.now() - new Date(t.updated_at).getTime()) / 60000);
8249
- return `${id} | ${agent} | ${t.title} (stale ${staleFor}min)`;
8250
- }).join(`
8367
+ } catch (e) {
8368
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
8369
+ }
8370
+ });
8371
+ }
8372
+ if (shouldRegisterTool("get_stale_tasks")) {
8373
+ server.tool("get_stale_tasks", "Find stale in_progress tasks with no recent activity.", {
8374
+ stale_minutes: exports_external.number().optional(),
8375
+ project_id: exports_external.string().optional(),
8376
+ task_list_id: exports_external.string().optional()
8377
+ }, async ({ stale_minutes, project_id, task_list_id }) => {
8378
+ try {
8379
+ const filters = {};
8380
+ if (project_id)
8381
+ filters.project_id = resolveId(project_id, "projects");
8382
+ if (task_list_id)
8383
+ filters.task_list_id = resolveId(task_list_id, "task_lists");
8384
+ const tasks = getStaleTasks(stale_minutes || 30, Object.keys(filters).length > 0 ? filters : undefined);
8385
+ if (tasks.length === 0) {
8386
+ return { content: [{ type: "text", text: "No stale tasks found." }] };
8387
+ }
8388
+ const text = tasks.map((t) => {
8389
+ const id = t.short_id || t.id.slice(0, 8);
8390
+ const agent = t.locked_by || t.assigned_to || "unknown";
8391
+ const staleFor = Math.round((Date.now() - new Date(t.updated_at).getTime()) / 60000);
8392
+ return `${id} | ${agent} | ${t.title} (stale ${staleFor}min)`;
8393
+ }).join(`
8251
8394
  `);
8252
- return { content: [{ type: "text", text: `${tasks.length} stale task(s):
8395
+ return { content: [{ type: "text", text: `${tasks.length} stale task(s):
8253
8396
  ${text}` }] };
8254
- } catch (e) {
8255
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
8256
- }
8257
- });
8258
- server.tool("get_status", "Get a full project health snapshot \u2014 counts, active work, next task, stale/overdue summary.", {
8259
- agent_id: exports_external.string().optional(),
8260
- project_id: exports_external.string().optional(),
8261
- task_list_id: exports_external.string().optional()
8262
- }, async ({ agent_id, project_id, task_list_id }) => {
8263
- try {
8264
- const filters = {};
8265
- if (project_id)
8266
- filters.project_id = resolveId(project_id, "projects");
8267
- if (task_list_id)
8268
- filters.task_list_id = resolveId(task_list_id, "task_lists");
8269
- const status = getStatus(Object.keys(filters).length > 0 ? filters : undefined, agent_id);
8270
- const lines = [
8271
- `Tasks: ${status.pending} pending | ${status.in_progress} active | ${status.completed} done | ${status.total} total`
8272
- ];
8273
- if (status.stale_count > 0)
8274
- lines.push(`\u26A0\uFE0F ${status.stale_count} stale (stuck in_progress)`);
8275
- if (status.overdue_recurring > 0)
8276
- lines.push(`\uD83D\uDD01 ${status.overdue_recurring} overdue recurring`);
8277
- if (status.active_work.length > 0) {
8278
- lines.push(`
8397
+ } catch (e) {
8398
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
8399
+ }
8400
+ });
8401
+ }
8402
+ if (shouldRegisterTool("get_status")) {
8403
+ server.tool("get_status", "Get a full project health snapshot \u2014 counts, active work, next task, stale/overdue summary.", {
8404
+ agent_id: exports_external.string().optional(),
8405
+ project_id: exports_external.string().optional(),
8406
+ task_list_id: exports_external.string().optional()
8407
+ }, async ({ agent_id, project_id, task_list_id }) => {
8408
+ try {
8409
+ const filters = {};
8410
+ if (project_id)
8411
+ filters.project_id = resolveId(project_id, "projects");
8412
+ if (task_list_id)
8413
+ filters.task_list_id = resolveId(task_list_id, "task_lists");
8414
+ const status = getStatus(Object.keys(filters).length > 0 ? filters : undefined, agent_id);
8415
+ const lines = [
8416
+ `Tasks: ${status.pending} pending | ${status.in_progress} active | ${status.completed} done | ${status.total} total`
8417
+ ];
8418
+ if (status.stale_count > 0)
8419
+ lines.push(`\u26A0\uFE0F ${status.stale_count} stale (stuck in_progress)`);
8420
+ if (status.overdue_recurring > 0)
8421
+ lines.push(`\uD83D\uDD01 ${status.overdue_recurring} overdue recurring`);
8422
+ if (status.active_work.length > 0) {
8423
+ lines.push(`
8279
8424
  Active (${status.active_work.length}):`);
8280
- for (const w of status.active_work.slice(0, 5)) {
8281
- const id = w.short_id || w.id.slice(0, 8);
8282
- lines.push(` ${id} | ${w.assigned_to || w.locked_by || "?"} | ${w.title}`);
8425
+ for (const w of status.active_work.slice(0, 5)) {
8426
+ const id = w.short_id || w.id.slice(0, 8);
8427
+ lines.push(` ${id} | ${w.assigned_to || w.locked_by || "?"} | ${w.title}`);
8428
+ }
8283
8429
  }
8284
- }
8285
- if (status.next_task) {
8286
- lines.push(`
8430
+ if (status.next_task) {
8431
+ lines.push(`
8287
8432
  Next up:`);
8288
- lines.push(` ${formatTask(status.next_task)}`);
8289
- } else {
8290
- lines.push(`
8433
+ lines.push(` ${formatTask(status.next_task)}`);
8434
+ } else {
8435
+ lines.push(`
8291
8436
  No pending tasks available.`);
8292
- }
8293
- return { content: [{ type: "text", text: lines.join(`
8437
+ }
8438
+ return { content: [{ type: "text", text: lines.join(`
8294
8439
  `) }] };
8295
- } catch (e) {
8296
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
8297
- }
8298
- });
8299
- server.tool("search_tools", "List all tool names, optionally filtered by substring.", { query: exports_external.string().optional() }, async ({ query }) => {
8300
- const all = [
8301
- "create_task",
8302
- "list_tasks",
8303
- "get_task",
8304
- "update_task",
8305
- "delete_task",
8306
- "start_task",
8307
- "complete_task",
8308
- "fail_task",
8309
- "lock_task",
8310
- "unlock_task",
8311
- "approve_task",
8312
- "add_dependency",
8313
- "remove_dependency",
8314
- "add_comment",
8315
- "create_project",
8316
- "list_projects",
8317
- "create_plan",
8318
- "list_plans",
8319
- "get_plan",
8320
- "update_plan",
8321
- "delete_plan",
8322
- "register_agent",
8323
- "list_agents",
8324
- "get_agent",
8325
- "rename_agent",
8326
- "delete_agent",
8327
- "get_my_tasks",
8328
- "get_org_chart",
8329
- "set_reports_to",
8330
- "create_task_list",
8331
- "list_task_lists",
8332
- "get_task_list",
8333
- "update_task_list",
8334
- "delete_task_list",
8335
- "search_tasks",
8336
- "sync",
8337
- "clone_task",
8338
- "move_task",
8339
- "get_next_task",
8340
- "claim_next_task",
8341
- "get_task_history",
8342
- "get_recent_activity",
8343
- "create_webhook",
8344
- "list_webhooks",
8345
- "delete_webhook",
8346
- "create_template",
8347
- "list_templates",
8348
- "create_task_from_template",
8349
- "delete_template",
8350
- "bulk_update_tasks",
8351
- "bulk_create_tasks",
8352
- "get_task_stats",
8353
- "get_task_graph",
8354
- "get_active_work",
8355
- "get_tasks_changed_since",
8356
- "get_stale_tasks",
8357
- "get_status",
8358
- "search_tools",
8359
- "describe_tools"
8360
- ];
8361
- const q = query?.toLowerCase();
8362
- const matches = q ? all.filter((n) => n.includes(q)) : all;
8363
- return { content: [{ type: "text", text: matches.join(", ") }] };
8364
- });
8365
- server.tool("describe_tools", "Get detailed parameter info for specific tools by name.", { names: exports_external.array(exports_external.string()) }, async ({ names }) => {
8366
- const descriptions = {
8367
- create_task: `Create a new task.
8440
+ } catch (e) {
8441
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
8442
+ }
8443
+ });
8444
+ }
8445
+ if (shouldRegisterTool("search_tools")) {
8446
+ server.tool("search_tools", "List all tool names, optionally filtered by substring.", { query: exports_external.string().optional() }, async ({ query }) => {
8447
+ const all = [
8448
+ "create_task",
8449
+ "list_tasks",
8450
+ "get_task",
8451
+ "update_task",
8452
+ "delete_task",
8453
+ "start_task",
8454
+ "complete_task",
8455
+ "fail_task",
8456
+ "lock_task",
8457
+ "unlock_task",
8458
+ "approve_task",
8459
+ "add_dependency",
8460
+ "remove_dependency",
8461
+ "add_comment",
8462
+ "create_project",
8463
+ "list_projects",
8464
+ "create_plan",
8465
+ "list_plans",
8466
+ "get_plan",
8467
+ "update_plan",
8468
+ "delete_plan",
8469
+ "register_agent",
8470
+ "list_agents",
8471
+ "get_agent",
8472
+ "rename_agent",
8473
+ "delete_agent",
8474
+ "get_my_tasks",
8475
+ "get_org_chart",
8476
+ "set_reports_to",
8477
+ "create_task_list",
8478
+ "list_task_lists",
8479
+ "get_task_list",
8480
+ "update_task_list",
8481
+ "delete_task_list",
8482
+ "search_tasks",
8483
+ "sync",
8484
+ "clone_task",
8485
+ "move_task",
8486
+ "get_next_task",
8487
+ "claim_next_task",
8488
+ "get_task_history",
8489
+ "get_recent_activity",
8490
+ "create_webhook",
8491
+ "list_webhooks",
8492
+ "delete_webhook",
8493
+ "create_template",
8494
+ "list_templates",
8495
+ "create_task_from_template",
8496
+ "delete_template",
8497
+ "bulk_update_tasks",
8498
+ "bulk_create_tasks",
8499
+ "get_task_stats",
8500
+ "get_task_graph",
8501
+ "get_active_work",
8502
+ "get_tasks_changed_since",
8503
+ "get_stale_tasks",
8504
+ "get_status",
8505
+ "search_tools",
8506
+ "describe_tools"
8507
+ ].filter((name) => shouldRegisterTool(name));
8508
+ const q = query?.toLowerCase();
8509
+ const matches = q ? all.filter((n) => n.includes(q)) : all;
8510
+ return { content: [{ type: "text", text: matches.join(", ") }] };
8511
+ });
8512
+ }
8513
+ if (shouldRegisterTool("describe_tools")) {
8514
+ server.tool("describe_tools", "Get detailed parameter info for specific tools by name.", { names: exports_external.array(exports_external.string()) }, async ({ names }) => {
8515
+ const descriptions = {
8516
+ create_task: `Create a new task.
8368
8517
  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)
8369
8518
  Example: {title: 'Daily standup', recurrence_rule: 'every weekday', priority: 'medium'}`,
8370
- list_tasks: `List tasks with optional filters. Supports pagination.
8519
+ list_tasks: `List tasks with optional filters. Supports pagination.
8371
8520
  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)
8372
8521
  Example: {status: ['pending', 'in_progress'], has_recurrence: true, limit: 20}`,
8373
- get_task: `Get full task details with subtasks, deps, and comments.
8522
+ get_task: `Get full task details with subtasks, deps, and comments.
8374
8523
  Params: id(string, req \u2014 task ID, short_id like 'APP-00001', or partial ID)
8375
8524
  Example: {id: 'a1b2c3d4'}`,
8376
- update_task: `Update task fields. Requires version for optimistic locking (get it from get_task first).
8525
+ update_task: `Update task fields. Requires version for optimistic locking (get it from get_task first).
8377
8526
  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)
8378
8527
  Example: {id: 'a1b2c3d4', version: 3, status: 'completed'}`,
8379
- delete_task: `Delete a task permanently. Subtasks cascade-delete. Dependencies removed.
8528
+ delete_task: `Delete a task permanently. Subtasks cascade-delete. Dependencies removed.
8380
8529
  Params: id(string, req)
8381
8530
  Example: {id: 'a1b2c3d4'}`,
8382
- start_task: `Claim, lock, and set task status to in_progress in one call.
8531
+ start_task: `Claim, lock, and set task status to in_progress in one call.
8383
8532
  Params: id(string, req), agent_id(string, req \u2014 your 8-char agent ID)
8384
8533
  Example: {id: 'a1b2c3d4', agent_id: 'e5f6g7h8'}`,
8385
- complete_task: `Mark task completed, release lock, set completed_at timestamp. For recurring tasks, auto-spawns next instance unless skip_recurrence is true.
8534
+ complete_task: `Mark task completed, release lock, set completed_at timestamp. For recurring tasks, auto-spawns next instance unless skip_recurrence is true.
8386
8535
  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)
8387
8536
  Example: {id: 'a1b2c3d4', skip_recurrence: false}`,
8388
- lock_task: `Acquire exclusive lock on a task. Locks auto-expire after 30 min. Re-locking by same agent is idempotent.
8537
+ lock_task: `Acquire exclusive lock on a task. Locks auto-expire after 30 min. Re-locking by same agent is idempotent.
8389
8538
  Params: id(string, req), agent_id(string, req)
8390
8539
  Example: {id: 'a1b2c3d4', agent_id: 'e5f6g7h8'}`,
8391
- unlock_task: `Release exclusive lock on a task.
8540
+ unlock_task: `Release exclusive lock on a task.
8392
8541
  Params: id(string, req), agent_id(string, optional \u2014 omit to force-unlock)
8393
8542
  Example: {id: 'a1b2c3d4', agent_id: 'e5f6g7h8'}`,
8394
- approve_task: `Approve a task with requires_approval=true. Must be approved before completion.
8543
+ approve_task: `Approve a task with requires_approval=true. Must be approved before completion.
8395
8544
  Params: id(string, req), agent_id(string, optional \u2014 defaults to 'system')
8396
8545
  Example: {id: 'a1b2c3d4', agent_id: 'e5f6g7h8'}`,
8397
- fail_task: `Mark a task as failed with structured reason and optional auto-retry. Stores failure info in metadata._failure, releases lock.
8546
+ fail_task: `Mark a task as failed with structured reason and optional auto-retry. Stores failure info in metadata._failure, releases lock.
8398
8547
  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)
8399
8548
  Example: {id: 'a1b2c3d4', reason: 'Build timeout', error_code: 'TIMEOUT', retry: true}`,
8400
- add_dependency: `Add a dependency: task_id depends on depends_on. Prevents cycles via BFS.
8549
+ add_dependency: `Add a dependency: task_id depends on depends_on. Prevents cycles via BFS.
8401
8550
  Params: task_id(string, req), depends_on(string, req)
8402
8551
  Example: {task_id: 'abc12345', depends_on: 'def67890'}`,
8403
- remove_dependency: `Remove a dependency link between two tasks.
8552
+ remove_dependency: `Remove a dependency link between two tasks.
8404
8553
  Params: task_id(string, req), depends_on(string, req)
8405
8554
  Example: {task_id: 'abc12345', depends_on: 'def67890'}`,
8406
- add_comment: `Add a comment/note to a task. Comments are append-only.
8555
+ add_comment: `Add a comment/note to a task. Comments are append-only.
8407
8556
  Params: task_id(string, req), content(string, req), agent_id(string), session_id(string)
8408
8557
  Example: {task_id: 'a1b2c3d4', content: 'Blocked by API rate limit'}`,
8409
- create_project: `Register a new project. Auto-generates task prefix for short IDs (e.g. APP-00001).
8558
+ create_project: `Register a new project. Auto-generates task prefix for short IDs (e.g. APP-00001).
8410
8559
  Params: name(string, req), path(string, req \u2014 unique absolute path), description(string), task_list_id(string)
8411
8560
  Example: {name: 'my-app', path: '/Users/dev/my-app'}`,
8412
- list_projects: "List all registered projects. No params.",
8413
- create_plan: `Create a plan to group related tasks.
8561
+ list_projects: "List all registered projects. No params.",
8562
+ create_plan: `Create a plan to group related tasks.
8414
8563
  Params: name(string, req), project_id(string), description(string), status(active|completed|archived, default:active), task_list_id(string), agent_id(string)
8415
8564
  Example: {name: 'Sprint 1', project_id: 'a1b2c3d4'}`,
8416
- list_plans: `List all plans, optionally filtered by project.
8565
+ list_plans: `List all plans, optionally filtered by project.
8417
8566
  Params: project_id(string)
8418
8567
  Example: {project_id: 'a1b2c3d4'}`,
8419
- get_plan: `Get plan details (name, status, description, timestamps).
8568
+ get_plan: `Get plan details (name, status, description, timestamps).
8420
8569
  Params: id(string, req)
8421
8570
  Example: {id: 'a1b2c3d4'}`,
8422
- update_plan: `Update plan fields.
8571
+ update_plan: `Update plan fields.
8423
8572
  Params: id(string, req), name(string), description(string), status(active|completed|archived), task_list_id(string), agent_id(string)
8424
8573
  Example: {id: 'a1b2c3d4', status: 'completed'}`,
8425
- delete_plan: `Delete a plan. Tasks in the plan are orphaned, not deleted.
8574
+ delete_plan: `Delete a plan. Tasks in the plan are orphaned, not deleted.
8426
8575
  Params: id(string, req)
8427
8576
  Example: {id: 'a1b2c3d4'}`,
8428
- register_agent: `Register an agent (idempotent by name). Returns existing agent if name matches.
8577
+ register_agent: `Register an agent (idempotent by name). Returns existing agent if name matches.
8429
8578
  Params: name(string, req \u2014 e.g. 'maximus'), description(string)
8430
8579
  Example: {name: 'maximus', description: 'Backend developer'}`,
8431
- list_agents: "List all registered agents with IDs, names, and last seen timestamps. No params.",
8432
- get_agent: `Get agent details by ID or name. Provide one of id or name.
8580
+ list_agents: "List all registered agents with IDs, names, and last seen timestamps. No params.",
8581
+ get_agent: `Get agent details by ID or name. Provide one of id or name.
8433
8582
  Params: id(string), name(string)
8434
8583
  Example: {name: 'maximus'}`,
8435
- rename_agent: `Rename an agent. Resolve by id or current name.
8584
+ rename_agent: `Rename an agent. Resolve by id or current name.
8436
8585
  Params: id(string), name(string \u2014 current name), new_name(string, req)
8437
8586
  Example: {name: 'old-name', new_name: 'new-name'}`,
8438
- delete_agent: `Delete an agent permanently. Resolve by id or name.
8587
+ delete_agent: `Delete an agent permanently. Resolve by id or name.
8439
8588
  Params: id(string), name(string)
8440
8589
  Example: {name: 'maximus'}`,
8441
- get_my_tasks: `Get all tasks assigned to/created by an agent, with stats (pending/active/done/rate).
8590
+ get_my_tasks: `Get all tasks assigned to/created by an agent, with stats (pending/active/done/rate).
8442
8591
  Params: agent_name(string, req)
8443
8592
  Example: {agent_name: 'maximus'}`,
8444
- get_org_chart: "Get agent org chart showing reporting hierarchy. No params.",
8445
- set_reports_to: `Set who an agent reports to in the org chart. Omit manager_name for top-level.
8593
+ get_org_chart: "Get agent org chart showing reporting hierarchy. No params.",
8594
+ set_reports_to: `Set who an agent reports to in the org chart. Omit manager_name for top-level.
8446
8595
  Params: agent_name(string, req), manager_name(string, optional)
8447
8596
  Example: {agent_name: 'brutus', manager_name: 'maximus'}`,
8448
- create_task_list: `Create a task list \u2014 a container/folder for organizing tasks.
8597
+ create_task_list: `Create a task list \u2014 a container/folder for organizing tasks.
8449
8598
  Params: name(string, req), slug(string \u2014 auto-generated if omitted), project_id(string), description(string)
8450
8599
  Example: {name: 'Sprint 1', project_id: 'a1b2c3d4'}`,
8451
- list_task_lists: `List all task lists, optionally filtered by project.
8600
+ list_task_lists: `List all task lists, optionally filtered by project.
8452
8601
  Params: project_id(string)
8453
8602
  Example: {project_id: 'a1b2c3d4'}`,
8454
- get_task_list: `Get task list details (name, slug, project, metadata).
8603
+ get_task_list: `Get task list details (name, slug, project, metadata).
8455
8604
  Params: id(string, req)
8456
8605
  Example: {id: 'a1b2c3d4'}`,
8457
- update_task_list: `Update a task list's name or description.
8606
+ update_task_list: `Update a task list's name or description.
8458
8607
  Params: id(string, req), name(string), description(string)
8459
8608
  Example: {id: 'a1b2c3d4', name: 'Sprint 2'}`,
8460
- delete_task_list: `Delete a task list. Tasks are orphaned (not deleted).
8609
+ delete_task_list: `Delete a task list. Tasks are orphaned (not deleted).
8461
8610
  Params: id(string, req)
8462
8611
  Example: {id: 'a1b2c3d4'}`,
8463
- search_tasks: `Full-text search across task titles, descriptions, and tags. Supports filters.
8612
+ search_tasks: `Full-text search across task titles, descriptions, and tags. Supports filters.
8464
8613
  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)
8465
8614
  Example: {query: 'auth bug', status: 'pending'}`,
8466
- 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.
8615
+ 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.
8467
8616
  Params: agent_id(string \u2014 prefers your tasks), project_id(string), task_list_id(string), plan_id(string), tags(string[])
8468
8617
  Example: {agent_id: 'a1b2c3d4', project_id: 'e5f6g7h8'}`,
8469
- 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.
8618
+ 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.
8470
8619
  Params: agent_id(string, req \u2014 used for lock and assignment), project_id(string), task_list_id(string), plan_id(string), tags(string[])
8471
8620
  Example: {agent_id: 'a1b2c3d4', project_id: 'e5f6g7h8'}`,
8472
- sync: `Sync tasks between local DB and agent task list (e.g. Claude Code).
8621
+ sync: `Sync tasks between local DB and agent task list (e.g. Claude Code).
8473
8622
  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)
8474
8623
  Example: {agent: 'claude', direction: 'push'}`,
8475
- clone_task: `Duplicate a task with optional field overrides. Creates new independent copy.
8624
+ clone_task: `Duplicate a task with optional field overrides. Creates new independent copy.
8476
8625
  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)
8477
8626
  Example: {task_id: 'a1b2c3d4', title: 'Cloned task', assigned_to: 'brutus'}`,
8478
- move_task: `Move a task to a different list, project, or plan.
8627
+ move_task: `Move a task to a different list, project, or plan.
8479
8628
  Params: task_id(string, req), task_list_id(string|null), project_id(string|null), plan_id(string|null)
8480
8629
  Example: {task_id: 'a1b2c3d4', task_list_id: 'e5f6g7h8'}`,
8481
- bulk_update_tasks: `Update multiple tasks at once with the same changes.
8630
+ bulk_update_tasks: `Update multiple tasks at once with the same changes.
8482
8631
  Params: task_ids(string[], req), status(pending|in_progress|completed|failed|cancelled), priority(low|medium|high|critical), assigned_to(string), tags(string[])
8483
8632
  Example: {task_ids: ['abc12345', 'def67890'], status: 'completed'}`,
8484
- bulk_create_tasks: `Create multiple tasks atomically. Supports inter-task dependencies via temp_id references.
8633
+ bulk_create_tasks: `Create multiple tasks atomically. Supports inter-task dependencies via temp_id references.
8485
8634
  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)
8486
8635
  Example: {tasks: [{temp_id: 'a', title: 'First'}, {temp_id: 'b', title: 'Second', depends_on_temp_ids: ['a']}]}`,
8487
- get_task_stats: `Get task analytics: counts by status, priority, agent, and completion rate. All via SQL.
8636
+ get_task_stats: `Get task analytics: counts by status, priority, agent, and completion rate. All via SQL.
8488
8637
  Params: project_id(string), task_list_id(string), agent_id(string)
8489
8638
  Example: {project_id: 'a1b2c3d4'}`,
8490
- get_task_graph: `Get full dependency tree for a task \u2014 upstream blockers and downstream dependents.
8639
+ get_task_graph: `Get full dependency tree for a task \u2014 upstream blockers and downstream dependents.
8491
8640
  Params: id(string, req), direction(up|down|both, default:both)
8492
8641
  Example: {id: 'a1b2c3d4', direction: 'up'}`,
8493
- get_task_history: `Get audit log for a task \u2014 all field changes with timestamps and actors.
8642
+ get_task_history: `Get audit log for a task \u2014 all field changes with timestamps and actors.
8494
8643
  Params: task_id(string, req)
8495
8644
  Example: {task_id: 'a1b2c3d4'}`,
8496
- get_recent_activity: `Get recent task changes across all tasks \u2014 global activity feed.
8645
+ get_recent_activity: `Get recent task changes across all tasks \u2014 global activity feed.
8497
8646
  Params: limit(number, default:50)
8498
8647
  Example: {limit: 20}`,
8499
- create_webhook: `Register a webhook for task change events.
8648
+ create_webhook: `Register a webhook for task change events.
8500
8649
  Params: url(string, req), events(string[] \u2014 empty=all), secret(string \u2014 HMAC signing)
8501
8650
  Example: {url: 'https://example.com/hook', events: ['task.created', 'task.completed']}`,
8502
- list_webhooks: "List all registered webhooks. No params.",
8503
- delete_webhook: `Delete a webhook by ID.
8651
+ list_webhooks: "List all registered webhooks. No params.",
8652
+ delete_webhook: `Delete a webhook by ID.
8504
8653
  Params: id(string, req)
8505
8654
  Example: {id: 'a1b2c3d4'}`,
8506
- create_template: `Create a reusable task template.
8655
+ create_template: `Create a reusable task template.
8507
8656
  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)
8508
8657
  Example: {name: 'Bug Report', title_pattern: 'Bug: {description}', priority: 'high', tags: ['bug']}`,
8509
- list_templates: "List all task templates. No params.",
8510
- create_task_from_template: `Create a task from a template with optional overrides.
8658
+ list_templates: "List all task templates. No params.",
8659
+ create_task_from_template: `Create a task from a template with optional overrides.
8511
8660
  Params: template_id(string, req), title(string), description(string), priority(low|medium|high|critical), assigned_to(string), project_id(string)
8512
8661
  Example: {template_id: 'a1b2c3d4', assigned_to: 'maximus'}`,
8513
- delete_template: `Delete a task template.
8662
+ delete_template: `Delete a task template.
8514
8663
  Params: id(string, req)
8515
8664
  Example: {id: 'a1b2c3d4'}`,
8516
- get_active_work: `See all in-progress tasks and who is working on them.
8665
+ get_active_work: `See all in-progress tasks and who is working on them.
8517
8666
  Params: project_id(string, optional), task_list_id(string, optional)
8518
8667
  Example: {project_id: 'a1b2c3d4'}`,
8519
- get_tasks_changed_since: `Get tasks modified after a timestamp \u2014 incremental delta sync.
8668
+ get_tasks_changed_since: `Get tasks modified after a timestamp \u2014 incremental delta sync.
8520
8669
  Params: since(string, req \u2014 ISO date), project_id(string, optional), task_list_id(string, optional)
8521
8670
  Example: {since: '2026-03-14T10:00:00Z'}`,
8522
- get_stale_tasks: `Find stale in_progress tasks with no recent activity.
8671
+ get_stale_tasks: `Find stale in_progress tasks with no recent activity.
8523
8672
  Params: stale_minutes(number, default:30), project_id(string, optional), task_list_id(string, optional)
8524
8673
  Example: {stale_minutes: 60, project_id: 'a1b2c3d4'}`,
8525
- 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.
8674
+ 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.
8526
8675
  Params: agent_id(string, optional \u2014 prefers tasks assigned to this agent for next_task), project_id(string, optional), task_list_id(string, optional)
8527
8676
  Example: {agent_id: 'a1b2c3d4', project_id: 'e5f6g7h8'}`,
8528
- search_tools: `List all tool names or filter by substring.
8677
+ search_tools: `List all tool names or filter by substring.
8529
8678
  Params: query(string, optional)
8530
8679
  Example: {query: 'task'}`,
8531
- describe_tools: `Get detailed descriptions and parameter info for tools by name.
8680
+ describe_tools: `Get detailed descriptions and parameter info for tools by name.
8532
8681
  Params: names(string[], req)
8533
8682
  Example: {names: ['create_task', 'update_task']}`
8534
- };
8535
- const result = names.map((n) => `${n}: ${descriptions[n] || "Unknown tool. Use search_tools to list available tools."}`).join(`
8683
+ };
8684
+ const allToolNames = Object.keys(descriptions);
8685
+ const registeredCount = allToolNames.filter((n) => shouldRegisterTool(n)).length;
8686
+ const profileLine = `Profile: ${TODOS_PROFILE} (${registeredCount} tools active)
8687
+
8688
+ `;
8689
+ const result = names.map((n) => `${n}: ${descriptions[n] || "Unknown tool. Use search_tools to list available tools."}`).join(`
8536
8690
 
8537
8691
  `);
8538
- return { content: [{ type: "text", text: result }] };
8539
- });
8692
+ return { content: [{ type: "text", text: profileLine + result }] };
8693
+ });
8694
+ }
8540
8695
  server.resource("tasks", "todos://tasks", { description: "All active tasks", mimeType: "application/json" }, async () => {
8541
8696
  const tasks = listTasks({ status: ["pending", "in_progress"] });
8542
8697
  return { contents: [{ uri: "todos://tasks", text: JSON.stringify(tasks, null, 2), mimeType: "application/json" }] };