@hasna/todos 0.11.37 → 0.11.38

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
@@ -22624,6 +22624,383 @@ ${lines.join(`
22624
22624
  // src/mcp/tools/task-crud.ts
22625
22625
  init_tasks();
22626
22626
  init_types2();
22627
+
22628
+ // src/mcp/token-utils.ts
22629
+ var CORE_MCP_TOOLS = new Set([
22630
+ "add_comment",
22631
+ "bootstrap",
22632
+ "claim_next_task",
22633
+ "complete_task",
22634
+ "create_task",
22635
+ "fail_task",
22636
+ "get_context",
22637
+ "get_health",
22638
+ "get_next_task",
22639
+ "get_status",
22640
+ "get_task",
22641
+ "get_tasks_changed_since",
22642
+ "heartbeat",
22643
+ "list_agents",
22644
+ "list_tasks",
22645
+ "register_agent",
22646
+ "release_agent",
22647
+ "start_task",
22648
+ "suggest_agent_name"
22649
+ ]);
22650
+ var MCP_TOOL_GROUPS = {
22651
+ core: [...CORE_MCP_TOOLS],
22652
+ tasks: [
22653
+ "archive_completed",
22654
+ "approve_task",
22655
+ "bulk_create_tasks",
22656
+ "bulk_delete_tasks",
22657
+ "bulk_update_tasks",
22658
+ "cancel_task",
22659
+ "claim_task",
22660
+ "clone_task",
22661
+ "delete_task",
22662
+ "extend_task",
22663
+ "get_active_work",
22664
+ "get_archived_tasks",
22665
+ "get_blocked_tasks",
22666
+ "get_blocking_tasks",
22667
+ "get_my_tasks",
22668
+ "get_review_queue",
22669
+ "get_stale_tasks",
22670
+ "list_my_tasks",
22671
+ "move_task",
22672
+ "patrol_tasks",
22673
+ "prioritize_task",
22674
+ "reassign_task",
22675
+ "release_task",
22676
+ "reschedule_task",
22677
+ "search_tasks",
22678
+ "standup",
22679
+ "task_context",
22680
+ "unarchive_task",
22681
+ "update_task"
22682
+ ],
22683
+ projects: [
22684
+ "create_plan",
22685
+ "create_project",
22686
+ "create_task_list",
22687
+ "delete_plan",
22688
+ "delete_project",
22689
+ "delete_task_list",
22690
+ "get_focus",
22691
+ "get_plan",
22692
+ "get_project",
22693
+ "get_task_list",
22694
+ "list_plans",
22695
+ "list_projects",
22696
+ "list_task_lists",
22697
+ "set_focus",
22698
+ "unfocus",
22699
+ "update_plan",
22700
+ "update_project",
22701
+ "update_task_list"
22702
+ ],
22703
+ resources: [
22704
+ "add_task_dependency",
22705
+ "add_task_file",
22706
+ "add_task_relationship",
22707
+ "bulk_find_tasks_by_files",
22708
+ "check_file_lock",
22709
+ "create_comment",
22710
+ "delete_comment",
22711
+ "detect_file_relationships",
22712
+ "find_path",
22713
+ "find_task_by_commit",
22714
+ "find_tasks_by_file",
22715
+ "get_comments",
22716
+ "get_critical_path",
22717
+ "get_file_heat_map",
22718
+ "get_impact_analysis",
22719
+ "get_latest_handoff",
22720
+ "get_related_entities",
22721
+ "get_task_commits",
22722
+ "get_task_dependencies",
22723
+ "get_task_relationships",
22724
+ "get_task_watchers",
22725
+ "link_task_to_commit",
22726
+ "list_active_files",
22727
+ "list_comments",
22728
+ "list_file_locks",
22729
+ "list_task_files",
22730
+ "lock_file",
22731
+ "log_time",
22732
+ "remove_task_dependency",
22733
+ "remove_task_relationship",
22734
+ "sync_kg",
22735
+ "unlock_file",
22736
+ "unwatch_task",
22737
+ "update_comment",
22738
+ "watch_task"
22739
+ ],
22740
+ agents: [
22741
+ "auto_assign_task",
22742
+ "delete_agent",
22743
+ "get_agent",
22744
+ "get_agent_metrics",
22745
+ "get_capable_agents",
22746
+ "get_leaderboard",
22747
+ "get_my_workload",
22748
+ "get_org_chart",
22749
+ "get_project_org_chart",
22750
+ "get_time_report",
22751
+ "list_project_agent_roles",
22752
+ "rebalance_workload",
22753
+ "rename_agent",
22754
+ "set_project_agent_role",
22755
+ "set_reports_to",
22756
+ "unarchive_agent",
22757
+ "update_agent"
22758
+ ],
22759
+ metadata: [
22760
+ "create_label",
22761
+ "create_tag",
22762
+ "delete_label",
22763
+ "delete_tag",
22764
+ "get_label",
22765
+ "get_recent_activity",
22766
+ "get_tag",
22767
+ "get_task_graph",
22768
+ "get_task_history",
22769
+ "get_task_stats",
22770
+ "list_labels",
22771
+ "list_tags",
22772
+ "search_tools",
22773
+ "describe_tools",
22774
+ "update_label",
22775
+ "update_tag"
22776
+ ],
22777
+ dispatch: [
22778
+ "cancel_dispatch",
22779
+ "dispatch_task_list",
22780
+ "dispatch_tasks",
22781
+ "dispatch_to_multiple",
22782
+ "list_dispatches",
22783
+ "run_due_dispatches"
22784
+ ],
22785
+ templates: [
22786
+ "create_task_from_template",
22787
+ "create_template",
22788
+ "delete_template",
22789
+ "export_template",
22790
+ "import_template",
22791
+ "init_templates",
22792
+ "list_templates",
22793
+ "preview_template",
22794
+ "template_history",
22795
+ "update_template"
22796
+ ],
22797
+ webhooks: ["create_webhook", "delete_webhook", "list_webhooks"],
22798
+ machines: [
22799
+ "machines_archive",
22800
+ "machines_delete",
22801
+ "machines_list",
22802
+ "machines_register",
22803
+ "machines_set_primary",
22804
+ "machines_unarchive"
22805
+ ],
22806
+ cloud: [
22807
+ "sync_all",
22808
+ "todos_cloud_conflicts",
22809
+ "todos_cloud_feedback",
22810
+ "todos_cloud_pull",
22811
+ "todos_cloud_push",
22812
+ "todos_cloud_status",
22813
+ "todos_inbox",
22814
+ "todos_retro"
22815
+ ],
22816
+ maintenance: ["extract_todos", "migrate_pg", "notify_upcoming_deadlines", "score_task"]
22817
+ };
22818
+ var MCP_PROFILE_GROUPS = {
22819
+ minimal: ["core"],
22820
+ core: ["core"],
22821
+ standard: ["core", "tasks", "projects", "resources", "agents", "metadata"],
22822
+ agent: ["core", "tasks", "projects", "resources"],
22823
+ maintainer: ["core", "tasks", "projects", "resources", "agents", "metadata", "dispatch", "maintenance"]
22824
+ };
22825
+ function splitTokens(value) {
22826
+ return (value || "").split(",").map((item) => item.trim().toLowerCase()).filter(Boolean);
22827
+ }
22828
+ function addGroupTools(toolNames, groupName) {
22829
+ const tools = MCP_TOOL_GROUPS[groupName];
22830
+ if (!tools)
22831
+ return false;
22832
+ for (const tool of tools)
22833
+ toolNames.add(tool);
22834
+ return true;
22835
+ }
22836
+ function shouldRegisterToolForProfile(name, profileValue = process.env["TODOS_PROFILE"], groupValue = process.env["TODOS_TOOL_GROUPS"]) {
22837
+ const profileTokens = splitTokens(profileValue || "minimal");
22838
+ const groupTokens = splitTokens(groupValue);
22839
+ if (profileTokens.includes("full") || profileTokens.includes("all"))
22840
+ return true;
22841
+ const tools = new Set;
22842
+ let matchedProfile = false;
22843
+ for (const token of profileTokens) {
22844
+ const profileGroups = MCP_PROFILE_GROUPS[token];
22845
+ if (profileGroups) {
22846
+ matchedProfile = true;
22847
+ for (const group of profileGroups)
22848
+ addGroupTools(tools, group);
22849
+ continue;
22850
+ }
22851
+ if (addGroupTools(tools, token)) {
22852
+ matchedProfile = true;
22853
+ continue;
22854
+ }
22855
+ if (token.startsWith("tool:")) {
22856
+ matchedProfile = true;
22857
+ tools.add(token.slice("tool:".length));
22858
+ }
22859
+ }
22860
+ if (!matchedProfile) {
22861
+ addGroupTools(tools, "core");
22862
+ }
22863
+ for (const token of groupTokens) {
22864
+ if (token === "full" || token === "all")
22865
+ return true;
22866
+ if (!addGroupTools(tools, token) && token.startsWith("tool:")) {
22867
+ tools.add(token.slice("tool:".length));
22868
+ }
22869
+ }
22870
+ return tools.has(name);
22871
+ }
22872
+ function truncateText(value, maxChars = 240) {
22873
+ if (!value)
22874
+ return null;
22875
+ if (maxChars <= 0)
22876
+ return "";
22877
+ if (value.length <= maxChars)
22878
+ return value;
22879
+ return `${value.slice(0, Math.max(0, maxChars - 3))}...`;
22880
+ }
22881
+ function compactTask(task, maxDescriptionChars = 180) {
22882
+ const summary = {
22883
+ id: task.id,
22884
+ short_id: task.short_id || task.id.slice(0, 8),
22885
+ title: task.title,
22886
+ status: task.status,
22887
+ priority: task.priority,
22888
+ assigned_to: task.assigned_to,
22889
+ project_id: task.project_id,
22890
+ due_at: task.due_at,
22891
+ updated_at: task.updated_at
22892
+ };
22893
+ if (task.tags?.length)
22894
+ summary["tags"] = task.tags.slice(0, 8);
22895
+ const description = truncateText(task.description, maxDescriptionChars);
22896
+ if (description)
22897
+ summary["description"] = description;
22898
+ return summary;
22899
+ }
22900
+ function compactStatus(status) {
22901
+ const compact = {};
22902
+ for (const key of [
22903
+ "total",
22904
+ "pending",
22905
+ "in_progress",
22906
+ "completed",
22907
+ "failed",
22908
+ "cancelled",
22909
+ "stale_count",
22910
+ "overdue_recurring",
22911
+ "blocked_count"
22912
+ ]) {
22913
+ if (status[key] !== undefined)
22914
+ compact[key] = status[key];
22915
+ }
22916
+ const activeWork = status["active_work"];
22917
+ if (Array.isArray(activeWork)) {
22918
+ compact["active_work"] = activeWork.slice(0, 5).map((task) => {
22919
+ if (!task || typeof task !== "object")
22920
+ return task;
22921
+ const record = task;
22922
+ return {
22923
+ id: record["id"],
22924
+ short_id: record["short_id"],
22925
+ title: record["title"],
22926
+ priority: record["priority"],
22927
+ assigned_to: record["assigned_to"],
22928
+ updated_at: record["updated_at"]
22929
+ };
22930
+ });
22931
+ if (activeWork.length > 5)
22932
+ compact["active_work_omitted"] = activeWork.length - 5;
22933
+ }
22934
+ const blocked = status["blocked"];
22935
+ if (Array.isArray(blocked)) {
22936
+ compact["blocked"] = blocked.slice(0, 5);
22937
+ if (blocked.length > 5)
22938
+ compact["blocked_omitted"] = blocked.length - 5;
22939
+ }
22940
+ return compact;
22941
+ }
22942
+ function compactHandoff(handoff) {
22943
+ if (!handoff || typeof handoff !== "object")
22944
+ return handoff ?? null;
22945
+ const record = handoff;
22946
+ return {
22947
+ id: record["id"],
22948
+ from_agent: record["from_agent"],
22949
+ to_agent: record["to_agent"],
22950
+ summary: truncateText(typeof record["summary"] === "string" ? record["summary"] : null, 240),
22951
+ created_at: record["created_at"]
22952
+ };
22953
+ }
22954
+ function compactJson(value) {
22955
+ return JSON.stringify(value);
22956
+ }
22957
+ function tokenTelemetryEnabled() {
22958
+ return /^(1|true|yes|on)$/i.test(process.env["TODOS_MCP_TOKEN_TELEMETRY"] || "");
22959
+ }
22960
+ function withMcpTokenTelemetry(result, toolName, enabled = tokenTelemetryEnabled()) {
22961
+ if (!enabled || !result || typeof result !== "object")
22962
+ return result;
22963
+ const response = result;
22964
+ if (!Array.isArray(response.content))
22965
+ return result;
22966
+ const content = response.content;
22967
+ const textChars = content.reduce((sum, item) => item["type"] === "text" && typeof item["text"] === "string" ? sum + item["text"].length : sum, 0);
22968
+ const telemetry = `[mcp-token-telemetry tool=${toolName} chars=${textChars} approx_tokens=${Math.max(1, Math.ceil(textChars / 4))}]`;
22969
+ const nextContent = [...content];
22970
+ let lastTextIndex = -1;
22971
+ for (let index = nextContent.length - 1;index >= 0; index -= 1) {
22972
+ const item = nextContent[index];
22973
+ if (item["type"] === "text" && typeof item["text"] === "string") {
22974
+ lastTextIndex = index;
22975
+ break;
22976
+ }
22977
+ }
22978
+ if (lastTextIndex >= 0) {
22979
+ const item = nextContent[lastTextIndex];
22980
+ nextContent[lastTextIndex] = { ...item, text: `${item["text"]}
22981
+
22982
+ ${telemetry}` };
22983
+ } else {
22984
+ nextContent.push({ type: "text", text: telemetry });
22985
+ }
22986
+ return { ...response, content: nextContent };
22987
+ }
22988
+ function installMcpTokenTelemetry(server) {
22989
+ const originalTool = server.tool.bind(server);
22990
+ server.tool = (...args) => {
22991
+ const name = String(args[0]);
22992
+ const last = args[args.length - 1];
22993
+ if (typeof last !== "function")
22994
+ return originalTool(...args);
22995
+ const wrapped = async (...handlerArgs) => {
22996
+ const result = await last(...handlerArgs);
22997
+ return withMcpTokenTelemetry(result, name);
22998
+ };
22999
+ return originalTool(...args.slice(0, -1), wrapped);
23000
+ };
23001
+ }
23002
+
23003
+ // src/mcp/tools/task-crud.ts
22627
23004
  function registerTaskCrudTools(server, ctx) {
22628
23005
  const { shouldRegisterTool, resolveId, formatError, formatTask } = ctx;
22629
23006
  function versionFor(taskId, version) {
@@ -22713,15 +23090,31 @@ function registerTaskCrudTools(server, ctx) {
22713
23090
  });
22714
23091
  }
22715
23092
  if (shouldRegisterTool("get_task")) {
22716
- server.tool("get_task", "Get full details for a task.", {
22717
- task_id: exports_external.string().describe("Task ID (full or short)")
22718
- }, async ({ task_id }) => {
23093
+ server.tool("get_task", "Get compact task details by default. Pass detail=full for the full multi-line view.", {
23094
+ task_id: exports_external.string().describe("Task ID (full or short)"),
23095
+ detail: exports_external.enum(["compact", "full"]).optional().describe("Response detail (default: compact)"),
23096
+ max_description_chars: exports_external.number().optional().describe("Max description chars in compact mode (default: 240)"),
23097
+ include_metadata: exports_external.boolean().optional().describe("Include metadata in compact mode")
23098
+ }, async ({ task_id, detail, max_description_chars, include_metadata }) => {
22719
23099
  try {
22720
23100
  const resolvedId = resolveId(task_id);
22721
23101
  const task = getTask(resolvedId);
22722
23102
  if (!task)
22723
23103
  throw new TaskNotFoundError(task_id);
22724
23104
  const focus = ctx.getAgentFocus(task.assigned_to || "");
23105
+ if (detail !== "full") {
23106
+ const compact = compactTask(task, max_description_chars || 240);
23107
+ compact["version"] = task.version;
23108
+ compact["created_at"] = task.created_at;
23109
+ compact["focus"] = focus ? { agent_id: focus.agent_id, project_id: focus.project_id || null } : null;
23110
+ if (include_metadata && task.metadata && Object.keys(task.metadata).length > 0) {
23111
+ compact["metadata"] = task.metadata;
23112
+ }
23113
+ if (task.description && !compact["description"]) {
23114
+ compact["description"] = truncateText(task.description, max_description_chars || 240);
23115
+ }
23116
+ return { content: [{ type: "text", text: compactJson(compact) }] };
23117
+ }
22725
23118
  const lines = [
22726
23119
  `ID: ${task.id}`,
22727
23120
  `Short ID: ${task.short_id || "(none)"}`,
@@ -23042,8 +23435,17 @@ function registerTaskProjectTools(server, ctx) {
23042
23435
  direction: exports_external.enum(["upstream", "downstream", "both"]).optional().describe("Upstream = tasks this task depends on; downstream = tasks depending on this")
23043
23436
  }, async ({ task_id, direction }) => {
23044
23437
  try {
23045
- const { getTaskDependencies: getTaskDependencies3 } = (init_tasks(), __toCommonJS(exports_tasks));
23046
- const deps = getTaskDependencies3(resolveId(task_id), direction);
23438
+ const { getTaskDependencies: getTaskDependencies3, getTaskDependents: getTaskDependents2, getTask: getTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
23439
+ const resolvedId = resolveId(task_id);
23440
+ const upstream = direction !== "downstream" ? getTaskDependencies3(resolvedId).map((dep) => {
23441
+ const dependency = getTask2(dep.depends_on);
23442
+ return { direction: "upstream", task_id: dep.depends_on, status: dependency?.status || "unknown" };
23443
+ }) : [];
23444
+ const downstream = direction !== "upstream" ? getTaskDependents2(resolvedId).map((dep) => {
23445
+ const dependent = getTask2(dep.task_id);
23446
+ return { direction: "downstream", task_id: dep.task_id, status: dependent?.status || "unknown" };
23447
+ }) : [];
23448
+ const deps = [...upstream, ...downstream];
23047
23449
  if (deps.length === 0)
23048
23450
  return { content: [{ type: "text", text: "No dependencies." }] };
23049
23451
  const lines = deps.map((d) => `[${d.direction}] ${d.task_id.slice(0, 8)} (${d.status})`);
@@ -23832,8 +24234,10 @@ function registerTaskWorkflowTools(server, ctx) {
23832
24234
  agent_id: exports_external.string().optional().describe("Agent ID or name"),
23833
24235
  project_id: exports_external.string().optional().describe("Filter by project"),
23834
24236
  task_list_id: exports_external.string().optional().describe("Filter by task list"),
23835
- explain_blocked: exports_external.boolean().optional().describe("Include blocked task details")
23836
- }, async ({ agent_id, project_id, task_list_id, explain_blocked }) => {
24237
+ explain_blocked: exports_external.boolean().optional().describe("Include blocked task details"),
24238
+ detail: exports_external.enum(["compact", "full"]).optional().describe("Response detail (default: compact)"),
24239
+ max_description_chars: exports_external.number().optional().describe("Max task description chars in compact mode (default: 180)")
24240
+ }, async ({ agent_id, project_id, task_list_id, explain_blocked, detail, max_description_chars }) => {
23837
24241
  try {
23838
24242
  const { getStatus: getStatus2, getNextTask: getNextTask2, getOverdueTasks: getOverdueTasks2 } = (init_tasks(), __toCommonJS(exports_tasks));
23839
24243
  const { getLatestHandoff: getLatestHandoff2 } = (init_handoffs(), __toCommonJS(exports_handoffs));
@@ -23846,16 +24250,31 @@ function registerTaskWorkflowTools(server, ctx) {
23846
24250
  const next_task = getNextTask2(agent_id, filters);
23847
24251
  const overdue = getOverdueTasks2(filters.project_id);
23848
24252
  const latest_handoff = getLatestHandoff2(agent_id, filters.project_id);
24253
+ const payload = {
24254
+ status,
24255
+ next_task,
24256
+ overdue_count: overdue.length,
24257
+ latest_handoff,
24258
+ as_of: new Date().toISOString()
24259
+ };
24260
+ if (detail === "full") {
24261
+ return {
24262
+ content: [{
24263
+ type: "text",
24264
+ text: JSON.stringify(payload, null, 2)
24265
+ }]
24266
+ };
24267
+ }
23849
24268
  return {
23850
24269
  content: [{
23851
24270
  type: "text",
23852
- text: JSON.stringify({
23853
- status,
23854
- next_task,
24271
+ text: compactJson({
24272
+ status: compactStatus(status),
24273
+ next_task: next_task ? compactTask(next_task, max_description_chars || 180) : null,
23855
24274
  overdue_count: overdue.length,
23856
- latest_handoff,
23857
- as_of: new Date().toISOString()
23858
- }, null, 2)
24275
+ latest_handoff: compactHandoff(latest_handoff),
24276
+ as_of: payload.as_of
24277
+ })
23859
24278
  }]
23860
24279
  };
23861
24280
  } catch (e) {
@@ -24165,14 +24584,37 @@ ${lines.join(`
24165
24584
  // src/mcp/tools/task-adv-tools.ts
24166
24585
  function registerTaskAdvTools(server, ctx) {
24167
24586
  const { shouldRegisterTool, resolveId, formatError, formatTask, formatTaskDetail } = ctx;
24587
+ function getDependencyContext(taskId) {
24588
+ const { getTaskDependencies: getTaskDependencies3, getTaskDependents: getTaskDependents2, getTask: getTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
24589
+ const upstream = getTaskDependencies3(taskId).map((dep) => {
24590
+ const dependency = getTask2(dep.depends_on);
24591
+ return {
24592
+ direction: "upstream",
24593
+ task_id: dep.depends_on,
24594
+ status: dependency?.status || "unknown"
24595
+ };
24596
+ });
24597
+ const downstream = getTaskDependents2(taskId).map((dep) => {
24598
+ const dependent = getTask2(dep.task_id);
24599
+ return {
24600
+ direction: "downstream",
24601
+ task_id: dep.task_id,
24602
+ status: dependent?.status || "unknown"
24603
+ };
24604
+ });
24605
+ return [...upstream, ...downstream];
24606
+ }
24168
24607
  if (shouldRegisterTool("get_status")) {
24169
24608
  server.tool("get_status", "Get queue status summary, or pass task_id for a task's detailed status.", {
24170
24609
  task_id: exports_external.string().optional().describe("Task ID for task-specific status"),
24171
24610
  project_id: exports_external.string().optional().describe("Filter summary by project"),
24172
24611
  task_list_id: exports_external.string().optional().describe("Filter summary by task list"),
24173
24612
  agent_id: exports_external.string().optional().describe("Agent for next-task affinity"),
24174
- explain_blocked: exports_external.boolean().optional().describe("Include blocked task explanations in summary")
24175
- }, async ({ task_id, project_id, task_list_id, agent_id, explain_blocked }) => {
24613
+ explain_blocked: exports_external.boolean().optional().describe("Include blocked task explanations in summary"),
24614
+ detail: exports_external.enum(["compact", "full"]).optional().describe("Response detail (default: compact)"),
24615
+ comment_limit: exports_external.number().optional().describe("Max recent comments for task status (default: 5)"),
24616
+ file_limit: exports_external.number().optional().describe("Max files for task status (default: 10)")
24617
+ }, async ({ task_id, project_id, task_list_id, agent_id, explain_blocked, detail, comment_limit, file_limit }) => {
24176
24618
  try {
24177
24619
  if (!task_id) {
24178
24620
  const { getStatus: getStatus2 } = (init_tasks(), __toCommonJS(exports_tasks));
@@ -24182,21 +24624,51 @@ function registerTaskAdvTools(server, ctx) {
24182
24624
  if (task_list_id)
24183
24625
  filters.task_list_id = resolveId(task_list_id, "task_lists");
24184
24626
  const status = getStatus2(filters, agent_id, { explain_blocked });
24185
- return { content: [{ type: "text", text: JSON.stringify(status, null, 2) }] };
24627
+ return { content: [{ type: "text", text: detail === "full" ? JSON.stringify(status, null, 2) : compactJson(compactStatus(status)) }] };
24186
24628
  }
24187
24629
  const resolvedId = resolveId(task_id);
24188
24630
  const { getTask: getTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
24189
24631
  const task = getTask2(resolvedId);
24190
24632
  if (!task)
24191
24633
  throw new Error(`Task not found: ${task_id}`);
24192
- const { getTaskDependencies: getTaskDependencies3 } = (init_tasks(), __toCommonJS(exports_tasks));
24193
24634
  const { listComments: listComments2 } = (init_comments(), __toCommonJS(exports_comments));
24194
24635
  const { listTaskFiles: listTaskFiles2 } = (init_task_files(), __toCommonJS(exports_task_files));
24195
24636
  const [deps, comments, files] = await Promise.all([
24196
- Promise.resolve(getTaskDependencies3(resolvedId, "both")),
24637
+ Promise.resolve(getDependencyContext(resolvedId)),
24197
24638
  Promise.resolve(listComments2(resolvedId)),
24198
24639
  Promise.resolve(listTaskFiles2(resolvedId))
24199
24640
  ]);
24641
+ if (detail !== "full") {
24642
+ const commentsShown = comments.slice(-(comment_limit || 5));
24643
+ const filesShown = files.slice(0, file_limit || 10);
24644
+ return {
24645
+ content: [{
24646
+ type: "text",
24647
+ text: compactJson({
24648
+ task: compactTask(task, 160),
24649
+ dependencies: {
24650
+ count: deps.length,
24651
+ items: deps.slice(0, 10).map((d) => ({ direction: d.direction, task_id: d.task_id, status: d.status })),
24652
+ omitted: Math.max(0, deps.length - 10)
24653
+ },
24654
+ comments: {
24655
+ count: comments.length,
24656
+ recent: commentsShown.map((c) => ({
24657
+ agent_id: c.agent_id || null,
24658
+ created_at: c.created_at,
24659
+ content: truncateText(c.content, 120)
24660
+ })),
24661
+ omitted: Math.max(0, comments.length - commentsShown.length)
24662
+ },
24663
+ files: {
24664
+ count: files.length,
24665
+ items: filesShown.map((f) => ({ status: f.status, path: f.path })),
24666
+ omitted: Math.max(0, files.length - filesShown.length)
24667
+ }
24668
+ })
24669
+ }]
24670
+ };
24671
+ }
24200
24672
  const lines = [
24201
24673
  `Status: ${task.status} | Priority: ${task.priority}`,
24202
24674
  task.assigned_to ? `Assigned: ${task.assigned_to}` : "Unassigned",
@@ -24220,29 +24692,81 @@ Files (${files.length}):` : null,
24220
24692
  });
24221
24693
  }
24222
24694
  if (shouldRegisterTool("task_context")) {
24223
- server.tool("task_context", "Get full context for a task: details, dependencies, relationships, comments, files, commits, time logs, and watchers.", {
24224
- task_id: exports_external.string().describe("Task ID")
24225
- }, async ({ task_id }) => {
24695
+ server.tool("task_context", "Get compact context for a task by default: details, dependencies, relationships, comments, files, commits, and watchers. Pass detail=full for all data.", {
24696
+ task_id: exports_external.string().describe("Task ID"),
24697
+ detail: exports_external.enum(["compact", "full"]).optional().describe("Response detail (default: compact)"),
24698
+ comment_limit: exports_external.number().optional().describe("Max recent comments in compact mode (default: 5)"),
24699
+ file_limit: exports_external.number().optional().describe("Max files in compact mode (default: 10)"),
24700
+ commit_limit: exports_external.number().optional().describe("Max commits in compact mode (default: 10)"),
24701
+ max_description_chars: exports_external.number().optional().describe("Max description chars in compact mode (default: 240)")
24702
+ }, async ({ task_id, detail, comment_limit, file_limit, commit_limit, max_description_chars }) => {
24226
24703
  try {
24227
24704
  const resolvedId = resolveId(task_id);
24228
24705
  const { getTask: getTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
24229
24706
  const task = getTask2(resolvedId);
24230
24707
  if (!task)
24231
24708
  throw new Error(`Task not found: ${task_id}`);
24232
- const { getTaskDependencies: getTaskDependencies3 } = (init_tasks(), __toCommonJS(exports_tasks));
24233
24709
  const { getTaskRelationships: getTaskRelationships2 } = (init_task_relationships(), __toCommonJS(exports_task_relationships));
24234
24710
  const { listComments: listComments2 } = (init_comments(), __toCommonJS(exports_comments));
24235
24711
  const { listTaskFiles: listTaskFiles2 } = (init_task_files(), __toCommonJS(exports_task_files));
24236
24712
  const { getTaskCommits: getTaskCommits2 } = (init_task_commits(), __toCommonJS(exports_task_commits));
24237
24713
  const { getTaskWatchers: getTaskWatchers2 } = (init_tasks(), __toCommonJS(exports_tasks));
24238
24714
  const [deps, rels, comments, files, commits, watchers] = await Promise.all([
24239
- Promise.resolve(getTaskDependencies3(resolvedId, "both")),
24715
+ Promise.resolve(getDependencyContext(resolvedId)),
24240
24716
  Promise.resolve(getTaskRelationships2(resolvedId)),
24241
24717
  Promise.resolve(listComments2(resolvedId)),
24242
24718
  Promise.resolve(listTaskFiles2(resolvedId)),
24243
24719
  Promise.resolve(getTaskCommits2(resolvedId)),
24244
24720
  Promise.resolve(getTaskWatchers2(resolvedId))
24245
24721
  ]);
24722
+ if (detail !== "full") {
24723
+ const commentsShown = comments.slice(-(comment_limit || 5));
24724
+ const filesShown = files.slice(0, file_limit || 10);
24725
+ const commitsShown = commits.slice(0, commit_limit || 10);
24726
+ return {
24727
+ content: [{
24728
+ type: "text",
24729
+ text: compactJson({
24730
+ task: compactTask(task, max_description_chars || 240),
24731
+ dependencies: {
24732
+ count: deps.length,
24733
+ items: deps.slice(0, 10).map((d) => ({ direction: d.direction, task_id: d.task_id, status: d.status })),
24734
+ omitted: Math.max(0, deps.length - 10)
24735
+ },
24736
+ relationships: {
24737
+ count: rels.length,
24738
+ items: rels.slice(0, 10).map((r) => ({
24739
+ source_task_id: r.source_task_id,
24740
+ relationship_type: r.relationship_type,
24741
+ target_task_id: r.target_task_id
24742
+ })),
24743
+ omitted: Math.max(0, rels.length - 10)
24744
+ },
24745
+ comments: {
24746
+ count: comments.length,
24747
+ recent: commentsShown.map((c) => ({
24748
+ agent_id: c.agent_id || null,
24749
+ created_at: c.created_at,
24750
+ content: truncateText(c.content, 160)
24751
+ })),
24752
+ omitted: Math.max(0, comments.length - commentsShown.length)
24753
+ },
24754
+ files: {
24755
+ count: files.length,
24756
+ items: filesShown.map((f) => ({ status: f.status, path: f.path })),
24757
+ omitted: Math.max(0, files.length - filesShown.length)
24758
+ },
24759
+ commits: {
24760
+ count: commits.length,
24761
+ items: commitsShown.map((c) => ({ sha: c.sha, message: truncateText(c.message || "(no message)", 120) })),
24762
+ omitted: Math.max(0, commits.length - commitsShown.length)
24763
+ },
24764
+ watchers: { count: watchers.length },
24765
+ metadata_keys: task.metadata ? Object.keys(task.metadata) : []
24766
+ })
24767
+ }]
24768
+ };
24769
+ }
24246
24770
  const lines = [
24247
24771
  `== ${task.title} ====================`,
24248
24772
  `ID: ${task.id}`,
@@ -24408,15 +24932,20 @@ No blocked tasks.`,
24408
24932
  });
24409
24933
  }
24410
24934
  if (shouldRegisterTool("get_comments")) {
24411
- server.tool("get_comments", "Alias for list_comments.", {
24412
- task_id: exports_external.string().describe("Task ID")
24413
- }, async ({ task_id }) => {
24935
+ server.tool("get_comments", "List recent comments for a task. Compact by default; pass detail=full for all comment text.", {
24936
+ task_id: exports_external.string().describe("Task ID"),
24937
+ detail: exports_external.enum(["compact", "full"]).optional().describe("Response detail (default: compact)"),
24938
+ limit: exports_external.number().optional().describe("Max recent comments in compact mode (default: 20)")
24939
+ }, async ({ task_id, detail, limit }) => {
24414
24940
  try {
24415
24941
  const { listComments: listComments2 } = (init_comments(), __toCommonJS(exports_comments));
24416
24942
  const comments = listComments2(resolveId(task_id));
24417
24943
  if (comments.length === 0)
24418
24944
  return { content: [{ type: "text", text: "No comments." }] };
24419
- const lines = comments.map((c) => `[${c.agent_id || "?"}] ${c.created_at?.slice(0, 16)}: ${c.content}`);
24945
+ const shown = detail === "full" ? comments : comments.slice(-(limit || 20));
24946
+ const lines = shown.map((c) => `[${c.agent_id || "?"}] ${c.created_at?.slice(0, 16)}: ${detail === "full" ? c.content : truncateText(c.content, 180)}`);
24947
+ if (shown.length < comments.length)
24948
+ lines.unshift(`Showing ${shown.length} of ${comments.length} comments (${comments.length - shown.length} older omitted).`);
24420
24949
  return { content: [{ type: "text", text: lines.join(`
24421
24950
 
24422
24951
  `) }] };
@@ -24483,7 +25012,7 @@ function registerTaskMetaTools(server, ctx) {
24483
25012
  const toolDocs = {
24484
25013
  create_task: "create_task \u2014 Create a new task. Params: title (required), description, status, priority, project_id, task_list_id, assigned_to, depends_on, short_id (null to disable), tags, estimate (minutes), confidence (0.0-1.0), deadline (ISO), retry_count",
24485
25014
  list_tasks: "list_tasks \u2014 List tasks with filters. Params: status, priority, project_id, task_list_id, assigned_to, tags[], created_after, created_before, limit, offset",
24486
- get_task: "get_task \u2014 Get full details for a task. Params: task_id",
25015
+ get_task: "get_task \u2014 Get compact task details by default. Params: task_id, detail=compact|full, max_description_chars, include_metadata",
24487
25016
  update_task: "update_task \u2014 Update task fields (optimistic locking). Params: task_id (required), title, description, status, priority, assigned_to (null to unassign), project_id, task_list_id, depends_on[], tags[], estimate, actual_minutes, confidence, approved_by, completed_at, deadline, retry_count, version",
24488
25017
  delete_task: "delete_task \u2014 Delete a task. Params: task_id, force (skip child check)",
24489
25018
  start_task: "start_task \u2014 Mark task in_progress. Params: task_id, version",
@@ -24497,8 +25026,8 @@ function registerTaskMetaTools(server, ctx) {
24497
25026
  get_next_task: "get_next_task \u2014 Get the next available task without claiming it. Params: agent_id, project_id, task_list_id, plan_id, tags",
24498
25027
  claim_next_task: "claim_next_task \u2014 Atomically claim and start the next available task. Params: agent_id, project_id, task_list_id, plan_id, tags",
24499
25028
  get_tasks_changed_since: "get_tasks_changed_since \u2014 List tasks changed since an ISO timestamp. Params: since, project_id, task_list_id, limit",
24500
- get_context: "get_context \u2014 Get session start context. Params: agent_id, project_id, task_list_id, explain_blocked",
24501
- bootstrap: "bootstrap \u2014 Bootstrap an agent session with queue context. Params: agent_id, project_id, task_list_id, explain_blocked",
25029
+ get_context: "get_context \u2014 Get compact session start context by default. Params: agent_id, project_id, task_list_id, explain_blocked, detail=compact|full, max_description_chars",
25030
+ bootstrap: "bootstrap \u2014 Bootstrap an agent session with compact queue context by default. Params: agent_id, project_id, task_list_id, explain_blocked, detail=compact|full, max_description_chars",
24502
25031
  standup: "standup \u2014 Get standup report. Params: agent_id, project_id",
24503
25032
  patrol_tasks: "patrol_tasks \u2014 Scan for task issues. Params: stuck_minutes, confidence_threshold, project_id",
24504
25033
  get_review_queue: "get_review_queue \u2014 Get tasks needing review. Params: project_id, limit",
@@ -24535,6 +25064,7 @@ function registerTaskMetaTools(server, ctx) {
24535
25064
  get_task_relationships: "get_task_relationships \u2014 Get all relationships. Params: task_id, relationship_type",
24536
25065
  create_comment: "create_comment \u2014 Add comment. Params: task_id, body, author",
24537
25066
  list_comments: "list_comments \u2014 List comments. Params: task_id",
25067
+ get_comments: "get_comments \u2014 List recent comments by default. Params: task_id, detail=compact|full, limit",
24538
25068
  update_comment: "update_comment \u2014 Edit comment. Params: comment_id, body",
24539
25069
  delete_comment: "delete_comment \u2014 Delete comment. Params: comment_id",
24540
25070
  lock_task: "lock_task \u2014 Acquire exclusive lock. Params: task_id, agent_id, ttl_seconds",
@@ -26097,48 +26627,9 @@ var server = new McpServer({
26097
26627
  name: "todos",
26098
26628
  version: getMcpVersion()
26099
26629
  });
26100
- var TODOS_PROFILE = (process.env["TODOS_PROFILE"] || "full").toLowerCase();
26101
- var MINIMAL_TOOLS = new Set([
26102
- "claim_next_task",
26103
- "complete_task",
26104
- "fail_task",
26105
- "get_status",
26106
- "get_context",
26107
- "get_task",
26108
- "start_task",
26109
- "add_comment",
26110
- "get_next_task",
26111
- "bootstrap",
26112
- "get_tasks_changed_since",
26113
- "get_health",
26114
- "heartbeat",
26115
- "release_agent"
26116
- ]);
26117
- var STANDARD_EXCLUDED = new Set([
26118
- "rename_agent",
26119
- "delete_agent",
26120
- "unarchive_agent",
26121
- "create_webhook",
26122
- "list_webhooks",
26123
- "delete_webhook",
26124
- "create_template",
26125
- "list_templates",
26126
- "create_task_from_template",
26127
- "delete_template",
26128
- "update_template",
26129
- "init_templates",
26130
- "preview_template",
26131
- "export_template",
26132
- "import_template",
26133
- "template_history",
26134
- "approve_task"
26135
- ]);
26630
+ installMcpTokenTelemetry(server);
26136
26631
  function shouldRegisterTool(name) {
26137
- if (TODOS_PROFILE === "minimal")
26138
- return MINIMAL_TOOLS.has(name);
26139
- if (TODOS_PROFILE === "standard")
26140
- return !STANDARD_EXCLUDED.has(name);
26141
- return true;
26632
+ return shouldRegisterToolForProfile(name);
26142
26633
  }
26143
26634
  var agentFocusMap = new Map;
26144
26635
  function getAgentFocus(agentId) {