@hasna/todos 0.11.36 → 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
@@ -22383,9 +22383,6 @@ async function logError(message, opts) {
22383
22383
 
22384
22384
  // src/mcp/index.ts
22385
22385
  init_types2();
22386
- import { existsSync as existsSync10, readFileSync as readFileSync6 } from "fs";
22387
- import { join as join10, dirname as dirname3 } from "path";
22388
- import { fileURLToPath } from "url";
22389
22386
 
22390
22387
  // src/mcp/tools/dispatch.ts
22391
22388
  init_dispatches();
@@ -22627,6 +22624,383 @@ ${lines.join(`
22627
22624
  // src/mcp/tools/task-crud.ts
22628
22625
  init_tasks();
22629
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
22630
23004
  function registerTaskCrudTools(server, ctx) {
22631
23005
  const { shouldRegisterTool, resolveId, formatError, formatTask } = ctx;
22632
23006
  function versionFor(taskId, version) {
@@ -22716,15 +23090,31 @@ function registerTaskCrudTools(server, ctx) {
22716
23090
  });
22717
23091
  }
22718
23092
  if (shouldRegisterTool("get_task")) {
22719
- server.tool("get_task", "Get full details for a task.", {
22720
- task_id: exports_external.string().describe("Task ID (full or short)")
22721
- }, 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 }) => {
22722
23099
  try {
22723
23100
  const resolvedId = resolveId(task_id);
22724
23101
  const task = getTask(resolvedId);
22725
23102
  if (!task)
22726
23103
  throw new TaskNotFoundError(task_id);
22727
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
+ }
22728
23118
  const lines = [
22729
23119
  `ID: ${task.id}`,
22730
23120
  `Short ID: ${task.short_id || "(none)"}`,
@@ -23045,8 +23435,17 @@ function registerTaskProjectTools(server, ctx) {
23045
23435
  direction: exports_external.enum(["upstream", "downstream", "both"]).optional().describe("Upstream = tasks this task depends on; downstream = tasks depending on this")
23046
23436
  }, async ({ task_id, direction }) => {
23047
23437
  try {
23048
- const { getTaskDependencies: getTaskDependencies3 } = (init_tasks(), __toCommonJS(exports_tasks));
23049
- 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];
23050
23449
  if (deps.length === 0)
23051
23450
  return { content: [{ type: "text", text: "No dependencies." }] };
23052
23451
  const lines = deps.map((d) => `[${d.direction}] ${d.task_id.slice(0, 8)} (${d.status})`);
@@ -23835,8 +24234,10 @@ function registerTaskWorkflowTools(server, ctx) {
23835
24234
  agent_id: exports_external.string().optional().describe("Agent ID or name"),
23836
24235
  project_id: exports_external.string().optional().describe("Filter by project"),
23837
24236
  task_list_id: exports_external.string().optional().describe("Filter by task list"),
23838
- explain_blocked: exports_external.boolean().optional().describe("Include blocked task details")
23839
- }, 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 }) => {
23840
24241
  try {
23841
24242
  const { getStatus: getStatus2, getNextTask: getNextTask2, getOverdueTasks: getOverdueTasks2 } = (init_tasks(), __toCommonJS(exports_tasks));
23842
24243
  const { getLatestHandoff: getLatestHandoff2 } = (init_handoffs(), __toCommonJS(exports_handoffs));
@@ -23849,16 +24250,31 @@ function registerTaskWorkflowTools(server, ctx) {
23849
24250
  const next_task = getNextTask2(agent_id, filters);
23850
24251
  const overdue = getOverdueTasks2(filters.project_id);
23851
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
+ }
23852
24268
  return {
23853
24269
  content: [{
23854
24270
  type: "text",
23855
- text: JSON.stringify({
23856
- status,
23857
- next_task,
24271
+ text: compactJson({
24272
+ status: compactStatus(status),
24273
+ next_task: next_task ? compactTask(next_task, max_description_chars || 180) : null,
23858
24274
  overdue_count: overdue.length,
23859
- latest_handoff,
23860
- as_of: new Date().toISOString()
23861
- }, null, 2)
24275
+ latest_handoff: compactHandoff(latest_handoff),
24276
+ as_of: payload.as_of
24277
+ })
23862
24278
  }]
23863
24279
  };
23864
24280
  } catch (e) {
@@ -24168,14 +24584,37 @@ ${lines.join(`
24168
24584
  // src/mcp/tools/task-adv-tools.ts
24169
24585
  function registerTaskAdvTools(server, ctx) {
24170
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
+ }
24171
24607
  if (shouldRegisterTool("get_status")) {
24172
24608
  server.tool("get_status", "Get queue status summary, or pass task_id for a task's detailed status.", {
24173
24609
  task_id: exports_external.string().optional().describe("Task ID for task-specific status"),
24174
24610
  project_id: exports_external.string().optional().describe("Filter summary by project"),
24175
24611
  task_list_id: exports_external.string().optional().describe("Filter summary by task list"),
24176
24612
  agent_id: exports_external.string().optional().describe("Agent for next-task affinity"),
24177
- explain_blocked: exports_external.boolean().optional().describe("Include blocked task explanations in summary")
24178
- }, 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 }) => {
24179
24618
  try {
24180
24619
  if (!task_id) {
24181
24620
  const { getStatus: getStatus2 } = (init_tasks(), __toCommonJS(exports_tasks));
@@ -24185,21 +24624,51 @@ function registerTaskAdvTools(server, ctx) {
24185
24624
  if (task_list_id)
24186
24625
  filters.task_list_id = resolveId(task_list_id, "task_lists");
24187
24626
  const status = getStatus2(filters, agent_id, { explain_blocked });
24188
- 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)) }] };
24189
24628
  }
24190
24629
  const resolvedId = resolveId(task_id);
24191
24630
  const { getTask: getTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
24192
24631
  const task = getTask2(resolvedId);
24193
24632
  if (!task)
24194
24633
  throw new Error(`Task not found: ${task_id}`);
24195
- const { getTaskDependencies: getTaskDependencies3 } = (init_tasks(), __toCommonJS(exports_tasks));
24196
24634
  const { listComments: listComments2 } = (init_comments(), __toCommonJS(exports_comments));
24197
24635
  const { listTaskFiles: listTaskFiles2 } = (init_task_files(), __toCommonJS(exports_task_files));
24198
24636
  const [deps, comments, files] = await Promise.all([
24199
- Promise.resolve(getTaskDependencies3(resolvedId, "both")),
24637
+ Promise.resolve(getDependencyContext(resolvedId)),
24200
24638
  Promise.resolve(listComments2(resolvedId)),
24201
24639
  Promise.resolve(listTaskFiles2(resolvedId))
24202
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
+ }
24203
24672
  const lines = [
24204
24673
  `Status: ${task.status} | Priority: ${task.priority}`,
24205
24674
  task.assigned_to ? `Assigned: ${task.assigned_to}` : "Unassigned",
@@ -24223,29 +24692,81 @@ Files (${files.length}):` : null,
24223
24692
  });
24224
24693
  }
24225
24694
  if (shouldRegisterTool("task_context")) {
24226
- server.tool("task_context", "Get full context for a task: details, dependencies, relationships, comments, files, commits, time logs, and watchers.", {
24227
- task_id: exports_external.string().describe("Task ID")
24228
- }, 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 }) => {
24229
24703
  try {
24230
24704
  const resolvedId = resolveId(task_id);
24231
24705
  const { getTask: getTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
24232
24706
  const task = getTask2(resolvedId);
24233
24707
  if (!task)
24234
24708
  throw new Error(`Task not found: ${task_id}`);
24235
- const { getTaskDependencies: getTaskDependencies3 } = (init_tasks(), __toCommonJS(exports_tasks));
24236
24709
  const { getTaskRelationships: getTaskRelationships2 } = (init_task_relationships(), __toCommonJS(exports_task_relationships));
24237
24710
  const { listComments: listComments2 } = (init_comments(), __toCommonJS(exports_comments));
24238
24711
  const { listTaskFiles: listTaskFiles2 } = (init_task_files(), __toCommonJS(exports_task_files));
24239
24712
  const { getTaskCommits: getTaskCommits2 } = (init_task_commits(), __toCommonJS(exports_task_commits));
24240
24713
  const { getTaskWatchers: getTaskWatchers2 } = (init_tasks(), __toCommonJS(exports_tasks));
24241
24714
  const [deps, rels, comments, files, commits, watchers] = await Promise.all([
24242
- Promise.resolve(getTaskDependencies3(resolvedId, "both")),
24715
+ Promise.resolve(getDependencyContext(resolvedId)),
24243
24716
  Promise.resolve(getTaskRelationships2(resolvedId)),
24244
24717
  Promise.resolve(listComments2(resolvedId)),
24245
24718
  Promise.resolve(listTaskFiles2(resolvedId)),
24246
24719
  Promise.resolve(getTaskCommits2(resolvedId)),
24247
24720
  Promise.resolve(getTaskWatchers2(resolvedId))
24248
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
+ }
24249
24770
  const lines = [
24250
24771
  `== ${task.title} ====================`,
24251
24772
  `ID: ${task.id}`,
@@ -24411,15 +24932,20 @@ No blocked tasks.`,
24411
24932
  });
24412
24933
  }
24413
24934
  if (shouldRegisterTool("get_comments")) {
24414
- server.tool("get_comments", "Alias for list_comments.", {
24415
- task_id: exports_external.string().describe("Task ID")
24416
- }, 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 }) => {
24417
24940
  try {
24418
24941
  const { listComments: listComments2 } = (init_comments(), __toCommonJS(exports_comments));
24419
24942
  const comments = listComments2(resolveId(task_id));
24420
24943
  if (comments.length === 0)
24421
24944
  return { content: [{ type: "text", text: "No comments." }] };
24422
- 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).`);
24423
24949
  return { content: [{ type: "text", text: lines.join(`
24424
24950
 
24425
24951
  `) }] };
@@ -24486,7 +25012,7 @@ function registerTaskMetaTools(server, ctx) {
24486
25012
  const toolDocs = {
24487
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",
24488
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",
24489
- 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",
24490
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",
24491
25017
  delete_task: "delete_task \u2014 Delete a task. Params: task_id, force (skip child check)",
24492
25018
  start_task: "start_task \u2014 Mark task in_progress. Params: task_id, version",
@@ -24500,8 +25026,8 @@ function registerTaskMetaTools(server, ctx) {
24500
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",
24501
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",
24502
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",
24503
- get_context: "get_context \u2014 Get session start context. Params: agent_id, project_id, task_list_id, explain_blocked",
24504
- 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",
24505
25031
  standup: "standup \u2014 Get standup report. Params: agent_id, project_id",
24506
25032
  patrol_tasks: "patrol_tasks \u2014 Scan for task issues. Params: stuck_minutes, confidence_threshold, project_id",
24507
25033
  get_review_queue: "get_review_queue \u2014 Get tasks needing review. Params: project_id, limit",
@@ -24538,6 +25064,7 @@ function registerTaskMetaTools(server, ctx) {
24538
25064
  get_task_relationships: "get_task_relationships \u2014 Get all relationships. Params: task_id, relationship_type",
24539
25065
  create_comment: "create_comment \u2014 Add comment. Params: task_id, body, author",
24540
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",
24541
25068
  update_comment: "update_comment \u2014 Edit comment. Params: comment_id, body",
24542
25069
  delete_comment: "delete_comment \u2014 Delete comment. Params: comment_id",
24543
25070
  lock_task: "lock_task \u2014 Acquire exclusive lock. Params: task_id, agent_id, ttl_seconds",
@@ -26062,68 +26589,47 @@ ID: ${updated.id}${taskNote}`
26062
26589
  }
26063
26590
  }
26064
26591
 
26065
- // src/mcp/index.ts
26066
- function getMcpVersion() {
26592
+ // src/lib/package-version.ts
26593
+ import { existsSync as existsSync10, readFileSync as readFileSync6 } from "fs";
26594
+ import { dirname as dirname3, join as join10 } from "path";
26595
+ import { fileURLToPath } from "url";
26596
+ function getPackageVersion(fromUrl = import.meta.url) {
26067
26597
  try {
26068
- let dir = dirname3(fileURLToPath(import.meta.url));
26069
- for (let i = 0;i < 4; i++) {
26598
+ let dir = dirname3(fileURLToPath(fromUrl));
26599
+ for (let i = 0;i < 5; i++) {
26070
26600
  const pkgPath = join10(dir, "package.json");
26071
26601
  if (existsSync10(pkgPath)) {
26072
26602
  return JSON.parse(readFileSync6(pkgPath, "utf-8")).version || "0.0.0";
26073
26603
  }
26074
- dir = dirname3(dir);
26604
+ const parent = dirname3(dir);
26605
+ if (parent === dir)
26606
+ break;
26607
+ dir = parent;
26075
26608
  }
26076
26609
  } catch {
26077
26610
  return "0.0.0";
26078
26611
  }
26079
26612
  return "0.0.0";
26080
26613
  }
26614
+
26615
+ // src/mcp/index.ts
26616
+ function getMcpVersion() {
26617
+ return getPackageVersion(import.meta.url);
26618
+ }
26619
+ function hasVersionFlag() {
26620
+ return process.argv.includes("--version") || process.argv.includes("-V");
26621
+ }
26622
+ if (hasVersionFlag()) {
26623
+ console.log(getMcpVersion());
26624
+ process.exit(0);
26625
+ }
26081
26626
  var server = new McpServer({
26082
26627
  name: "todos",
26083
26628
  version: getMcpVersion()
26084
26629
  });
26085
- var TODOS_PROFILE = (process.env["TODOS_PROFILE"] || "full").toLowerCase();
26086
- var MINIMAL_TOOLS = new Set([
26087
- "claim_next_task",
26088
- "complete_task",
26089
- "fail_task",
26090
- "get_status",
26091
- "get_context",
26092
- "get_task",
26093
- "start_task",
26094
- "add_comment",
26095
- "get_next_task",
26096
- "bootstrap",
26097
- "get_tasks_changed_since",
26098
- "get_health",
26099
- "heartbeat",
26100
- "release_agent"
26101
- ]);
26102
- var STANDARD_EXCLUDED = new Set([
26103
- "rename_agent",
26104
- "delete_agent",
26105
- "unarchive_agent",
26106
- "create_webhook",
26107
- "list_webhooks",
26108
- "delete_webhook",
26109
- "create_template",
26110
- "list_templates",
26111
- "create_task_from_template",
26112
- "delete_template",
26113
- "update_template",
26114
- "init_templates",
26115
- "preview_template",
26116
- "export_template",
26117
- "import_template",
26118
- "template_history",
26119
- "approve_task"
26120
- ]);
26630
+ installMcpTokenTelemetry(server);
26121
26631
  function shouldRegisterTool(name) {
26122
- if (TODOS_PROFILE === "minimal")
26123
- return MINIMAL_TOOLS.has(name);
26124
- if (TODOS_PROFILE === "standard")
26125
- return !STANDARD_EXCLUDED.has(name);
26126
- return true;
26632
+ return shouldRegisterToolForProfile(name);
26127
26633
  }
26128
26634
  var agentFocusMap = new Map;
26129
26635
  function getAgentFocus(agentId) {