@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/cli/index.js CHANGED
@@ -4063,6 +4063,30 @@ var init_projects = __esm(() => {
4063
4063
  init_machines();
4064
4064
  });
4065
4065
 
4066
+ // src/lib/package-version.ts
4067
+ import { existsSync as existsSync2, readFileSync } from "fs";
4068
+ import { dirname as dirname2, join as join2 } from "path";
4069
+ import { fileURLToPath } from "url";
4070
+ function getPackageVersion(fromUrl = import.meta.url) {
4071
+ try {
4072
+ let dir = dirname2(fileURLToPath(fromUrl));
4073
+ for (let i = 0;i < 5; i++) {
4074
+ const pkgPath = join2(dir, "package.json");
4075
+ if (existsSync2(pkgPath)) {
4076
+ return JSON.parse(readFileSync(pkgPath, "utf-8")).version || "0.0.0";
4077
+ }
4078
+ const parent = dirname2(dir);
4079
+ if (parent === dir)
4080
+ break;
4081
+ dir = parent;
4082
+ }
4083
+ } catch {
4084
+ return "0.0.0";
4085
+ }
4086
+ return "0.0.0";
4087
+ }
4088
+ var init_package_version = () => {};
4089
+
4066
4090
  // src/cli/helpers.ts
4067
4091
  var exports_helpers = {};
4068
4092
  __export(exports_helpers, {
@@ -4082,17 +4106,7 @@ __export(exports_helpers, {
4082
4106
  });
4083
4107
  import chalk from "chalk";
4084
4108
  import { execSync } from "child_process";
4085
- import { readFileSync } from "fs";
4086
- import { dirname as dirname2, join as join2, resolve as resolve2 } from "path";
4087
- import { fileURLToPath } from "url";
4088
- function getPackageVersion() {
4089
- try {
4090
- const pkgPath = join2(dirname2(fileURLToPath(import.meta.url)), "..", "..", "package.json");
4091
- return JSON.parse(readFileSync(pkgPath, "utf-8")).version || "0.0.0";
4092
- } catch {
4093
- return "0.0.0";
4094
- }
4095
- }
4109
+ import { resolve as resolve2 } from "path";
4096
4110
  function handleError(e) {
4097
4111
  let jsonMode = false;
4098
4112
  try {
@@ -4189,6 +4203,7 @@ var programForOptions = null, statusColors, priorityColors;
4189
4203
  var init_helpers = __esm(() => {
4190
4204
  init_database();
4191
4205
  init_projects();
4206
+ init_package_version();
4192
4207
  statusColors = {
4193
4208
  pending: chalk.yellow,
4194
4209
  in_progress: chalk.blue,
@@ -4205,14 +4220,14 @@ var init_helpers = __esm(() => {
4205
4220
  });
4206
4221
 
4207
4222
  // src/lib/sync-utils.ts
4208
- import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, readdirSync, statSync, writeFileSync } from "fs";
4223
+ import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync2, readdirSync, statSync, writeFileSync } from "fs";
4209
4224
  import { join as join3 } from "path";
4210
4225
  function ensureDir2(dir) {
4211
- if (!existsSync2(dir))
4226
+ if (!existsSync3(dir))
4212
4227
  mkdirSync2(dir, { recursive: true });
4213
4228
  }
4214
4229
  function listJsonFiles(dir) {
4215
- if (!existsSync2(dir))
4230
+ if (!existsSync3(dir))
4216
4231
  return [];
4217
4232
  return readdirSync(dir).filter((f) => f.endsWith(".json"));
4218
4233
  }
@@ -4229,7 +4244,7 @@ function writeJsonFile(path, data) {
4229
4244
  }
4230
4245
  function readHighWaterMark(dir) {
4231
4246
  const path = join3(dir, ".highwatermark");
4232
- if (!existsSync2(path))
4247
+ if (!existsSync3(path))
4233
4248
  return 1;
4234
4249
  const val = parseInt(readFileSync2(path, "utf-8").trim(), 10);
4235
4250
  return isNaN(val) ? 1 : val;
@@ -4272,7 +4287,7 @@ __export(exports_config, {
4272
4287
  getAgentTaskListId: () => getAgentTaskListId,
4273
4288
  getAgentPoolForProject: () => getAgentPoolForProject
4274
4289
  });
4275
- import { existsSync as existsSync3 } from "fs";
4290
+ import { existsSync as existsSync4 } from "fs";
4276
4291
  import { join as join4 } from "path";
4277
4292
  function getTodosGlobalDir() {
4278
4293
  const home = process.env["HOME"] || HOME;
@@ -4280,7 +4295,7 @@ function getTodosGlobalDir() {
4280
4295
  const legacyDir = join4(home, ".todos");
4281
4296
  const newConfig = join4(newDir, "config.json");
4282
4297
  const legacyConfig = join4(legacyDir, "config.json");
4283
- if (!existsSync3(newConfig) && existsSync3(legacyConfig))
4298
+ if (!existsSync4(newConfig) && existsSync4(legacyConfig))
4284
4299
  return legacyDir;
4285
4300
  return newDir;
4286
4301
  }
@@ -4296,7 +4311,7 @@ function normalizeAgent(agent) {
4296
4311
  function loadConfig() {
4297
4312
  if (cached)
4298
4313
  return cached;
4299
- if (!existsSync3(getConfigPath())) {
4314
+ if (!existsSync4(getConfigPath())) {
4300
4315
  cached = {};
4301
4316
  return cached;
4302
4317
  }
@@ -8275,6 +8290,18 @@ var init_orgs = __esm(() => {
8275
8290
 
8276
8291
  // src/server/routes.ts
8277
8292
  import { join as join8, resolve as resolve5, sep } from "path";
8293
+ function parseFieldsParam(url) {
8294
+ const fieldsParam = url.searchParams.get("fields");
8295
+ return fieldsParam ? fieldsParam.split(",").map((f) => f.trim()).filter(Boolean) : undefined;
8296
+ }
8297
+ function parseBoundedLimit(value, fallback, max) {
8298
+ if (!value)
8299
+ return fallback;
8300
+ const parsed = parseInt(value, 10);
8301
+ if (!Number.isFinite(parsed) || parsed <= 0)
8302
+ return fallback;
8303
+ return Math.min(parsed, max);
8304
+ }
8278
8305
  function handleSseEvents(_req, url, ctx) {
8279
8306
  const agentId = url.searchParams.get("agent_id") || undefined;
8280
8307
  const projectId = url.searchParams.get("project_id") || undefined;
@@ -8387,8 +8414,7 @@ async function handleListTasks(_req, url, _ctx, json2, taskToSummary2) {
8387
8414
  const agentId = url.searchParams.get("agent_id") || undefined;
8388
8415
  const limitParam = url.searchParams.get("limit");
8389
8416
  const offsetParam = url.searchParams.get("offset");
8390
- const fieldsParam = url.searchParams.get("fields");
8391
- const fields = fieldsParam ? fieldsParam.split(",").map((f) => f.trim()).filter(Boolean) : undefined;
8417
+ const fields = parseFieldsParam(url);
8392
8418
  const tasks = listTasks({
8393
8419
  status,
8394
8420
  project_id: projectId,
@@ -8489,8 +8515,9 @@ function handleTasksNext(_req, url, _ctx, json2, taskToSummary2) {
8489
8515
  try {
8490
8516
  const projectId = url.searchParams.get("project_id") || undefined;
8491
8517
  const agentId = url.searchParams.get("agent_id") || undefined;
8518
+ const fields = parseFieldsParam(url);
8492
8519
  const task = getNextTask(agentId, projectId ? { project_id: projectId } : undefined);
8493
- return json2({ task: task ? taskToSummary2(task) : null });
8520
+ return json2({ task: task ? taskToSummary2(task, fields) : null });
8494
8521
  } catch (e) {
8495
8522
  return json2({ error: e instanceof Error ? e.message : "Failed" }, 500);
8496
8523
  }
@@ -8508,8 +8535,9 @@ function handleTasksStale(_req, url, _ctx, json2, taskToSummary2) {
8508
8535
  try {
8509
8536
  const projectId = url.searchParams.get("project_id") || undefined;
8510
8537
  const minutes = parseInt(url.searchParams.get("minutes") || "30", 10);
8538
+ const fields = parseFieldsParam(url);
8511
8539
  const tasks = getStaleTasks(minutes, projectId ? { project_id: projectId } : undefined);
8512
- return json2({ tasks: tasks.map((t) => taskToSummary2(t)), count: tasks.length });
8540
+ return json2({ tasks: tasks.map((t) => taskToSummary2(t, fields)), count: tasks.length });
8513
8541
  } catch (e) {
8514
8542
  return json2({ error: e instanceof Error ? e.message : "Failed" }, 500);
8515
8543
  }
@@ -8520,8 +8548,9 @@ function handleTasksChanged(_req, url, _ctx, json2, taskToSummary2) {
8520
8548
  if (!since)
8521
8549
  return json2({ error: "since parameter required (ISO date string)" }, 400);
8522
8550
  const projectId = url.searchParams.get("project_id") || undefined;
8551
+ const fields = parseFieldsParam(url);
8523
8552
  const tasks = getTasksChangedSince(since, projectId ? { project_id: projectId } : undefined);
8524
- return json2({ tasks: tasks.map((t) => taskToSummary2(t)), count: tasks.length, since });
8553
+ return json2({ tasks: tasks.map((t) => taskToSummary2(t, fields)), count: tasks.length, since });
8525
8554
  } catch (e) {
8526
8555
  return json2({ error: e instanceof Error ? e.message : "Failed" }, 500);
8527
8556
  }
@@ -8530,11 +8559,12 @@ function handleTasksContext(_req, url, _ctx, json2, taskToSummary2) {
8530
8559
  const agentId = url.searchParams.get("agent_id") || undefined;
8531
8560
  const projectId = url.searchParams.get("project_id") || undefined;
8532
8561
  const format = url.searchParams.get("format") || "text";
8562
+ const fields = parseFieldsParam(url);
8533
8563
  const filters = projectId ? { project_id: projectId } : undefined;
8534
8564
  const status = getStatus(filters, agentId);
8535
8565
  const next = getNextTask(agentId, filters);
8536
8566
  if (format === "json") {
8537
- return json2({ status, next_task: next ? taskToSummary2(next) : null });
8567
+ return json2({ status, next_task: next ? taskToSummary2(next, fields) : null });
8538
8568
  }
8539
8569
  const lines = [];
8540
8570
  lines.push(`Tasks: ${status.pending} pending | ${status.in_progress} active | ${status.completed} done`);
@@ -8559,7 +8589,7 @@ function handleTaskAttachments(id, _ctx, json2) {
8559
8589
  const attachmentIds = evidence.attachments || [];
8560
8590
  return json2({ task_id: id, short_id: task.short_id, attachment_ids: attachmentIds, count: attachmentIds.length, files_changed: evidence.files_changed, commit_hash: evidence.commit_hash, notes: evidence.notes });
8561
8591
  }
8562
- async function handleTaskProgress(id, req, method, _ctx, json2) {
8592
+ async function handleTaskProgress(id, req, method, _ctx, json2, url) {
8563
8593
  const task = getTask(id);
8564
8594
  if (!task)
8565
8595
  return json2({ error: "Task not found" }, 404);
@@ -8567,7 +8597,21 @@ async function handleTaskProgress(id, req, method, _ctx, json2) {
8567
8597
  const all = listComments(id);
8568
8598
  const progress = all.filter((c) => c.type === "progress");
8569
8599
  const latest = progress[progress.length - 1] || null;
8570
- return json2({ task_id: id, progress_entries: progress, latest, count: progress.length });
8600
+ const format = url?.searchParams.get("format") || "compact";
8601
+ const limit = parseBoundedLimit(url?.searchParams.get("limit") || null, 20, 200);
8602
+ const progressEntries = format === "full" ? progress : progress.slice(-limit);
8603
+ return json2({
8604
+ task_id: id,
8605
+ progress_entries: progressEntries,
8606
+ latest,
8607
+ count: progress.length,
8608
+ summary: {
8609
+ total: progress.length,
8610
+ returned: progressEntries.length,
8611
+ omitted: Math.max(0, progress.length - progressEntries.length),
8612
+ format
8613
+ }
8614
+ });
8571
8615
  }
8572
8616
  if (method === "POST") {
8573
8617
  try {
@@ -8582,11 +8626,11 @@ async function handleTaskProgress(id, req, method, _ctx, json2) {
8582
8626
  }
8583
8627
  return null;
8584
8628
  }
8585
- function handleGetTask(id, _ctx, json2, taskToSummary2) {
8629
+ function handleGetTask(id, _ctx, json2, taskToSummary2, url) {
8586
8630
  const task = getTask(id);
8587
8631
  if (!task)
8588
8632
  return json2({ error: "Task not found" }, 404);
8589
- return json2(taskToSummary2(task));
8633
+ return json2(taskToSummary2(task, url ? parseFieldsParam(url) : undefined));
8590
8634
  }
8591
8635
  async function handlePatchTask(id, req, _ctx, json2, taskToSummary2) {
8592
8636
  try {
@@ -8849,8 +8893,11 @@ function handleActivity(_req, url, _ctx, json2) {
8849
8893
  const limit = parseInt(url.searchParams.get("limit") || "50", 10);
8850
8894
  return json2(getRecentActivity(limit));
8851
8895
  }
8852
- function handleTaskHistory(id, _ctx, json2) {
8853
- return json2(getTaskHistory(id));
8896
+ function handleTaskHistory(id, _ctx, json2, url) {
8897
+ const history = getTaskHistory(id);
8898
+ const format = url?.searchParams.get("format") || "compact";
8899
+ const limit = parseBoundedLimit(url?.searchParams.get("limit") || null, 20, 500);
8900
+ return json2(format === "full" ? history : history.slice(0, limit));
8854
8901
  }
8855
8902
  function handleListWebhooks(_ctx, json2) {
8856
8903
  return json2(listWebhooks());
@@ -8990,7 +9037,7 @@ __export(exports_serve, {
8990
9037
  SECURITY_HEADERS: () => SECURITY_HEADERS,
8991
9038
  MIME_TYPES: () => MIME_TYPES
8992
9039
  });
8993
- import { existsSync as existsSync6 } from "fs";
9040
+ import { existsSync as existsSync7 } from "fs";
8994
9041
  import { join as join9, dirname as dirname3, extname } from "path";
8995
9042
  import { fileURLToPath as fileURLToPath2 } from "url";
8996
9043
  function resolveDashboardDir() {
@@ -9007,7 +9054,7 @@ function resolveDashboardDir() {
9007
9054
  }
9008
9055
  candidates.push(join9(process.cwd(), "dashboard", "dist"));
9009
9056
  for (const candidate of candidates) {
9010
- if (existsSync6(candidate))
9057
+ if (existsSync7(candidate))
9011
9058
  return candidate;
9012
9059
  }
9013
9060
  return join9(process.cwd(), "dashboard", "dist");
@@ -9060,7 +9107,7 @@ function json(data, status = 200, headers) {
9060
9107
  });
9061
9108
  }
9062
9109
  function serveStaticFile(filePath) {
9063
- if (!existsSync6(filePath))
9110
+ if (!existsSync7(filePath))
9064
9111
  return null;
9065
9112
  const ext = extname(filePath);
9066
9113
  const contentType = MIME_TYPES[ext] || "application/octet-stream";
@@ -9139,7 +9186,7 @@ data: ${data}
9139
9186
  filteredSseClients.delete(client);
9140
9187
  }
9141
9188
  const dashboardDir = resolveDashboardDir();
9142
- const dashboardExists = existsSync6(dashboardDir);
9189
+ const dashboardExists = existsSync7(dashboardDir);
9143
9190
  if (!dashboardExists) {
9144
9191
  console.error(`
9145
9192
  Dashboard not found at: ${dashboardDir}`);
@@ -9245,7 +9292,7 @@ Dashboard not found at: ${dashboardDir}`);
9245
9292
  }
9246
9293
  const progressMatch = path.match(/^\/api\/tasks\/([^/]+)\/progress$/);
9247
9294
  if (progressMatch) {
9248
- const res = await handleTaskProgress(progressMatch[1], req, method, ctx, json);
9295
+ const res = await handleTaskProgress(progressMatch[1], req, method, ctx, json, url);
9249
9296
  if (res !== null)
9250
9297
  return res;
9251
9298
  }
@@ -9253,7 +9300,7 @@ Dashboard not found at: ${dashboardDir}`);
9253
9300
  if (taskMatch) {
9254
9301
  const id = taskMatch[1];
9255
9302
  if (method === "GET") {
9256
- return handleGetTask(id, ctx, jsonWithCors, taskToSummary);
9303
+ return handleGetTask(id, ctx, jsonWithCors, taskToSummary, url);
9257
9304
  }
9258
9305
  if (method === "PATCH") {
9259
9306
  return handlePatchTask(id, req, ctx, jsonWithCors, taskToSummary);
@@ -9344,7 +9391,7 @@ Dashboard not found at: ${dashboardDir}`);
9344
9391
  }
9345
9392
  const historyMatch = path.match(/^\/api\/tasks\/([^/]+)\/history$/);
9346
9393
  if (historyMatch && method === "GET") {
9347
- return handleTaskHistory(historyMatch[1], ctx, json);
9394
+ return handleTaskHistory(historyMatch[1], ctx, json, url);
9348
9395
  }
9349
9396
  if (path === "/api/webhooks" && method === "GET") {
9350
9397
  return handleListWebhooks(ctx, json);
@@ -11082,7 +11129,7 @@ __export(exports_dist, {
11082
11129
  import { createRequire } from "module";
11083
11130
  import { Database as Database2 } from "bun:sqlite";
11084
11131
  import {
11085
- existsSync as existsSync8,
11132
+ existsSync as existsSync9,
11086
11133
  mkdirSync as mkdirSync4,
11087
11134
  readdirSync as readdirSync3,
11088
11135
  copyFileSync
@@ -11996,9 +12043,9 @@ function getDbPath2(serviceName) {
11996
12043
  function migrateDotfile(serviceName) {
11997
12044
  const legacyDir = join11(homedir(), `.${serviceName}`);
11998
12045
  const newDir = join11(HASNA_DIR, serviceName);
11999
- if (!existsSync8(legacyDir))
12046
+ if (!existsSync9(legacyDir))
12000
12047
  return [];
12001
- if (existsSync8(newDir))
12048
+ if (existsSync9(newDir))
12002
12049
  return [];
12003
12050
  mkdirSync4(newDir, { recursive: true });
12004
12051
  const migrated = [];
@@ -12020,7 +12067,7 @@ function copyDirRecursive(src, dest, root, migrated) {
12020
12067
  }
12021
12068
  }
12022
12069
  function hasLegacyDotfile(serviceName) {
12023
- return existsSync8(join11(homedir(), `.${serviceName}`));
12070
+ return existsSync9(join11(homedir(), `.${serviceName}`));
12024
12071
  }
12025
12072
  function getHasnaDir() {
12026
12073
  mkdirSync4(HASNA_DIR, { recursive: true });
@@ -26864,6 +26911,384 @@ var init_dispatch2 = __esm(() => {
26864
26911
  init_database();
26865
26912
  });
26866
26913
 
26914
+ // src/mcp/token-utils.ts
26915
+ function splitTokens(value) {
26916
+ return (value || "").split(",").map((item) => item.trim().toLowerCase()).filter(Boolean);
26917
+ }
26918
+ function addGroupTools(toolNames, groupName) {
26919
+ const tools = MCP_TOOL_GROUPS[groupName];
26920
+ if (!tools)
26921
+ return false;
26922
+ for (const tool of tools)
26923
+ toolNames.add(tool);
26924
+ return true;
26925
+ }
26926
+ function shouldRegisterToolForProfile(name, profileValue = process.env["TODOS_PROFILE"], groupValue = process.env["TODOS_TOOL_GROUPS"]) {
26927
+ const profileTokens = splitTokens(profileValue || "minimal");
26928
+ const groupTokens = splitTokens(groupValue);
26929
+ if (profileTokens.includes("full") || profileTokens.includes("all"))
26930
+ return true;
26931
+ const tools = new Set;
26932
+ let matchedProfile = false;
26933
+ for (const token of profileTokens) {
26934
+ const profileGroups = MCP_PROFILE_GROUPS[token];
26935
+ if (profileGroups) {
26936
+ matchedProfile = true;
26937
+ for (const group of profileGroups)
26938
+ addGroupTools(tools, group);
26939
+ continue;
26940
+ }
26941
+ if (addGroupTools(tools, token)) {
26942
+ matchedProfile = true;
26943
+ continue;
26944
+ }
26945
+ if (token.startsWith("tool:")) {
26946
+ matchedProfile = true;
26947
+ tools.add(token.slice("tool:".length));
26948
+ }
26949
+ }
26950
+ if (!matchedProfile) {
26951
+ addGroupTools(tools, "core");
26952
+ }
26953
+ for (const token of groupTokens) {
26954
+ if (token === "full" || token === "all")
26955
+ return true;
26956
+ if (!addGroupTools(tools, token) && token.startsWith("tool:")) {
26957
+ tools.add(token.slice("tool:".length));
26958
+ }
26959
+ }
26960
+ return tools.has(name);
26961
+ }
26962
+ function truncateText(value, maxChars = 240) {
26963
+ if (!value)
26964
+ return null;
26965
+ if (maxChars <= 0)
26966
+ return "";
26967
+ if (value.length <= maxChars)
26968
+ return value;
26969
+ return `${value.slice(0, Math.max(0, maxChars - 3))}...`;
26970
+ }
26971
+ function compactTask(task, maxDescriptionChars = 180) {
26972
+ const summary = {
26973
+ id: task.id,
26974
+ short_id: task.short_id || task.id.slice(0, 8),
26975
+ title: task.title,
26976
+ status: task.status,
26977
+ priority: task.priority,
26978
+ assigned_to: task.assigned_to,
26979
+ project_id: task.project_id,
26980
+ due_at: task.due_at,
26981
+ updated_at: task.updated_at
26982
+ };
26983
+ if (task.tags?.length)
26984
+ summary["tags"] = task.tags.slice(0, 8);
26985
+ const description = truncateText(task.description, maxDescriptionChars);
26986
+ if (description)
26987
+ summary["description"] = description;
26988
+ return summary;
26989
+ }
26990
+ function compactStatus(status) {
26991
+ const compact = {};
26992
+ for (const key of [
26993
+ "total",
26994
+ "pending",
26995
+ "in_progress",
26996
+ "completed",
26997
+ "failed",
26998
+ "cancelled",
26999
+ "stale_count",
27000
+ "overdue_recurring",
27001
+ "blocked_count"
27002
+ ]) {
27003
+ if (status[key] !== undefined)
27004
+ compact[key] = status[key];
27005
+ }
27006
+ const activeWork = status["active_work"];
27007
+ if (Array.isArray(activeWork)) {
27008
+ compact["active_work"] = activeWork.slice(0, 5).map((task) => {
27009
+ if (!task || typeof task !== "object")
27010
+ return task;
27011
+ const record = task;
27012
+ return {
27013
+ id: record["id"],
27014
+ short_id: record["short_id"],
27015
+ title: record["title"],
27016
+ priority: record["priority"],
27017
+ assigned_to: record["assigned_to"],
27018
+ updated_at: record["updated_at"]
27019
+ };
27020
+ });
27021
+ if (activeWork.length > 5)
27022
+ compact["active_work_omitted"] = activeWork.length - 5;
27023
+ }
27024
+ const blocked = status["blocked"];
27025
+ if (Array.isArray(blocked)) {
27026
+ compact["blocked"] = blocked.slice(0, 5);
27027
+ if (blocked.length > 5)
27028
+ compact["blocked_omitted"] = blocked.length - 5;
27029
+ }
27030
+ return compact;
27031
+ }
27032
+ function compactHandoff(handoff) {
27033
+ if (!handoff || typeof handoff !== "object")
27034
+ return handoff ?? null;
27035
+ const record = handoff;
27036
+ return {
27037
+ id: record["id"],
27038
+ from_agent: record["from_agent"],
27039
+ to_agent: record["to_agent"],
27040
+ summary: truncateText(typeof record["summary"] === "string" ? record["summary"] : null, 240),
27041
+ created_at: record["created_at"]
27042
+ };
27043
+ }
27044
+ function compactJson(value) {
27045
+ return JSON.stringify(value);
27046
+ }
27047
+ function tokenTelemetryEnabled() {
27048
+ return /^(1|true|yes|on)$/i.test(process.env["TODOS_MCP_TOKEN_TELEMETRY"] || "");
27049
+ }
27050
+ function withMcpTokenTelemetry(result, toolName, enabled = tokenTelemetryEnabled()) {
27051
+ if (!enabled || !result || typeof result !== "object")
27052
+ return result;
27053
+ const response = result;
27054
+ if (!Array.isArray(response.content))
27055
+ return result;
27056
+ const content = response.content;
27057
+ const textChars = content.reduce((sum, item) => item["type"] === "text" && typeof item["text"] === "string" ? sum + item["text"].length : sum, 0);
27058
+ const telemetry = `[mcp-token-telemetry tool=${toolName} chars=${textChars} approx_tokens=${Math.max(1, Math.ceil(textChars / 4))}]`;
27059
+ const nextContent = [...content];
27060
+ let lastTextIndex = -1;
27061
+ for (let index = nextContent.length - 1;index >= 0; index -= 1) {
27062
+ const item = nextContent[index];
27063
+ if (item["type"] === "text" && typeof item["text"] === "string") {
27064
+ lastTextIndex = index;
27065
+ break;
27066
+ }
27067
+ }
27068
+ if (lastTextIndex >= 0) {
27069
+ const item = nextContent[lastTextIndex];
27070
+ nextContent[lastTextIndex] = { ...item, text: `${item["text"]}
27071
+
27072
+ ${telemetry}` };
27073
+ } else {
27074
+ nextContent.push({ type: "text", text: telemetry });
27075
+ }
27076
+ return { ...response, content: nextContent };
27077
+ }
27078
+ function installMcpTokenTelemetry(server) {
27079
+ const originalTool = server.tool.bind(server);
27080
+ server.tool = (...args) => {
27081
+ const name = String(args[0]);
27082
+ const last = args[args.length - 1];
27083
+ if (typeof last !== "function")
27084
+ return originalTool(...args);
27085
+ const wrapped = async (...handlerArgs) => {
27086
+ const result = await last(...handlerArgs);
27087
+ return withMcpTokenTelemetry(result, name);
27088
+ };
27089
+ return originalTool(...args.slice(0, -1), wrapped);
27090
+ };
27091
+ }
27092
+ var CORE_MCP_TOOLS, MCP_TOOL_GROUPS, MCP_PROFILE_GROUPS;
27093
+ var init_token_utils = __esm(() => {
27094
+ CORE_MCP_TOOLS = new Set([
27095
+ "add_comment",
27096
+ "bootstrap",
27097
+ "claim_next_task",
27098
+ "complete_task",
27099
+ "create_task",
27100
+ "fail_task",
27101
+ "get_context",
27102
+ "get_health",
27103
+ "get_next_task",
27104
+ "get_status",
27105
+ "get_task",
27106
+ "get_tasks_changed_since",
27107
+ "heartbeat",
27108
+ "list_agents",
27109
+ "list_tasks",
27110
+ "register_agent",
27111
+ "release_agent",
27112
+ "start_task",
27113
+ "suggest_agent_name"
27114
+ ]);
27115
+ MCP_TOOL_GROUPS = {
27116
+ core: [...CORE_MCP_TOOLS],
27117
+ tasks: [
27118
+ "archive_completed",
27119
+ "approve_task",
27120
+ "bulk_create_tasks",
27121
+ "bulk_delete_tasks",
27122
+ "bulk_update_tasks",
27123
+ "cancel_task",
27124
+ "claim_task",
27125
+ "clone_task",
27126
+ "delete_task",
27127
+ "extend_task",
27128
+ "get_active_work",
27129
+ "get_archived_tasks",
27130
+ "get_blocked_tasks",
27131
+ "get_blocking_tasks",
27132
+ "get_my_tasks",
27133
+ "get_review_queue",
27134
+ "get_stale_tasks",
27135
+ "list_my_tasks",
27136
+ "move_task",
27137
+ "patrol_tasks",
27138
+ "prioritize_task",
27139
+ "reassign_task",
27140
+ "release_task",
27141
+ "reschedule_task",
27142
+ "search_tasks",
27143
+ "standup",
27144
+ "task_context",
27145
+ "unarchive_task",
27146
+ "update_task"
27147
+ ],
27148
+ projects: [
27149
+ "create_plan",
27150
+ "create_project",
27151
+ "create_task_list",
27152
+ "delete_plan",
27153
+ "delete_project",
27154
+ "delete_task_list",
27155
+ "get_focus",
27156
+ "get_plan",
27157
+ "get_project",
27158
+ "get_task_list",
27159
+ "list_plans",
27160
+ "list_projects",
27161
+ "list_task_lists",
27162
+ "set_focus",
27163
+ "unfocus",
27164
+ "update_plan",
27165
+ "update_project",
27166
+ "update_task_list"
27167
+ ],
27168
+ resources: [
27169
+ "add_task_dependency",
27170
+ "add_task_file",
27171
+ "add_task_relationship",
27172
+ "bulk_find_tasks_by_files",
27173
+ "check_file_lock",
27174
+ "create_comment",
27175
+ "delete_comment",
27176
+ "detect_file_relationships",
27177
+ "find_path",
27178
+ "find_task_by_commit",
27179
+ "find_tasks_by_file",
27180
+ "get_comments",
27181
+ "get_critical_path",
27182
+ "get_file_heat_map",
27183
+ "get_impact_analysis",
27184
+ "get_latest_handoff",
27185
+ "get_related_entities",
27186
+ "get_task_commits",
27187
+ "get_task_dependencies",
27188
+ "get_task_relationships",
27189
+ "get_task_watchers",
27190
+ "link_task_to_commit",
27191
+ "list_active_files",
27192
+ "list_comments",
27193
+ "list_file_locks",
27194
+ "list_task_files",
27195
+ "lock_file",
27196
+ "log_time",
27197
+ "remove_task_dependency",
27198
+ "remove_task_relationship",
27199
+ "sync_kg",
27200
+ "unlock_file",
27201
+ "unwatch_task",
27202
+ "update_comment",
27203
+ "watch_task"
27204
+ ],
27205
+ agents: [
27206
+ "auto_assign_task",
27207
+ "delete_agent",
27208
+ "get_agent",
27209
+ "get_agent_metrics",
27210
+ "get_capable_agents",
27211
+ "get_leaderboard",
27212
+ "get_my_workload",
27213
+ "get_org_chart",
27214
+ "get_project_org_chart",
27215
+ "get_time_report",
27216
+ "list_project_agent_roles",
27217
+ "rebalance_workload",
27218
+ "rename_agent",
27219
+ "set_project_agent_role",
27220
+ "set_reports_to",
27221
+ "unarchive_agent",
27222
+ "update_agent"
27223
+ ],
27224
+ metadata: [
27225
+ "create_label",
27226
+ "create_tag",
27227
+ "delete_label",
27228
+ "delete_tag",
27229
+ "get_label",
27230
+ "get_recent_activity",
27231
+ "get_tag",
27232
+ "get_task_graph",
27233
+ "get_task_history",
27234
+ "get_task_stats",
27235
+ "list_labels",
27236
+ "list_tags",
27237
+ "search_tools",
27238
+ "describe_tools",
27239
+ "update_label",
27240
+ "update_tag"
27241
+ ],
27242
+ dispatch: [
27243
+ "cancel_dispatch",
27244
+ "dispatch_task_list",
27245
+ "dispatch_tasks",
27246
+ "dispatch_to_multiple",
27247
+ "list_dispatches",
27248
+ "run_due_dispatches"
27249
+ ],
27250
+ templates: [
27251
+ "create_task_from_template",
27252
+ "create_template",
27253
+ "delete_template",
27254
+ "export_template",
27255
+ "import_template",
27256
+ "init_templates",
27257
+ "list_templates",
27258
+ "preview_template",
27259
+ "template_history",
27260
+ "update_template"
27261
+ ],
27262
+ webhooks: ["create_webhook", "delete_webhook", "list_webhooks"],
27263
+ machines: [
27264
+ "machines_archive",
27265
+ "machines_delete",
27266
+ "machines_list",
27267
+ "machines_register",
27268
+ "machines_set_primary",
27269
+ "machines_unarchive"
27270
+ ],
27271
+ cloud: [
27272
+ "sync_all",
27273
+ "todos_cloud_conflicts",
27274
+ "todos_cloud_feedback",
27275
+ "todos_cloud_pull",
27276
+ "todos_cloud_push",
27277
+ "todos_cloud_status",
27278
+ "todos_inbox",
27279
+ "todos_retro"
27280
+ ],
27281
+ maintenance: ["extract_todos", "migrate_pg", "notify_upcoming_deadlines", "score_task"]
27282
+ };
27283
+ MCP_PROFILE_GROUPS = {
27284
+ minimal: ["core"],
27285
+ core: ["core"],
27286
+ standard: ["core", "tasks", "projects", "resources", "agents", "metadata"],
27287
+ agent: ["core", "tasks", "projects", "resources"],
27288
+ maintainer: ["core", "tasks", "projects", "resources", "agents", "metadata", "dispatch", "maintenance"]
27289
+ };
27290
+ });
27291
+
26867
27292
  // src/mcp/tools/task-crud.ts
26868
27293
  function registerTaskCrudTools(server, ctx) {
26869
27294
  const { shouldRegisterTool, resolveId, formatError, formatTask } = ctx;
@@ -26954,15 +27379,31 @@ function registerTaskCrudTools(server, ctx) {
26954
27379
  });
26955
27380
  }
26956
27381
  if (shouldRegisterTool("get_task")) {
26957
- server.tool("get_task", "Get full details for a task.", {
26958
- task_id: exports_external2.string().describe("Task ID (full or short)")
26959
- }, async ({ task_id }) => {
27382
+ server.tool("get_task", "Get compact task details by default. Pass detail=full for the full multi-line view.", {
27383
+ task_id: exports_external2.string().describe("Task ID (full or short)"),
27384
+ detail: exports_external2.enum(["compact", "full"]).optional().describe("Response detail (default: compact)"),
27385
+ max_description_chars: exports_external2.number().optional().describe("Max description chars in compact mode (default: 240)"),
27386
+ include_metadata: exports_external2.boolean().optional().describe("Include metadata in compact mode")
27387
+ }, async ({ task_id, detail, max_description_chars, include_metadata }) => {
26960
27388
  try {
26961
27389
  const resolvedId = resolveId(task_id);
26962
27390
  const task = getTask(resolvedId);
26963
27391
  if (!task)
26964
27392
  throw new TaskNotFoundError(task_id);
26965
27393
  const focus = ctx.getAgentFocus(task.assigned_to || "");
27394
+ if (detail !== "full") {
27395
+ const compact = compactTask(task, max_description_chars || 240);
27396
+ compact["version"] = task.version;
27397
+ compact["created_at"] = task.created_at;
27398
+ compact["focus"] = focus ? { agent_id: focus.agent_id, project_id: focus.project_id || null } : null;
27399
+ if (include_metadata && task.metadata && Object.keys(task.metadata).length > 0) {
27400
+ compact["metadata"] = task.metadata;
27401
+ }
27402
+ if (task.description && !compact["description"]) {
27403
+ compact["description"] = truncateText(task.description, max_description_chars || 240);
27404
+ }
27405
+ return { content: [{ type: "text", text: compactJson(compact) }] };
27406
+ }
26966
27407
  const lines = [
26967
27408
  `ID: ${task.id}`,
26968
27409
  `Short ID: ${task.short_id || "(none)"}`,
@@ -27061,6 +27502,7 @@ var init_task_crud2 = __esm(() => {
27061
27502
  init_zod2();
27062
27503
  init_tasks();
27063
27504
  init_types();
27505
+ init_token_utils();
27064
27506
  });
27065
27507
 
27066
27508
  // src/mcp/tools/task-project-tools.ts
@@ -27212,8 +27654,17 @@ function registerTaskProjectTools(server, ctx) {
27212
27654
  direction: exports_external2.enum(["upstream", "downstream", "both"]).optional().describe("Upstream = tasks this task depends on; downstream = tasks depending on this")
27213
27655
  }, async ({ task_id, direction }) => {
27214
27656
  try {
27215
- const { getTaskDependencies: getTaskDependencies3 } = (init_tasks(), __toCommonJS(exports_tasks));
27216
- const deps = getTaskDependencies3(resolveId(task_id), direction);
27657
+ const { getTaskDependencies: getTaskDependencies3, getTaskDependents: getTaskDependents2, getTask: getTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
27658
+ const resolvedId = resolveId(task_id);
27659
+ const upstream = direction !== "downstream" ? getTaskDependencies3(resolvedId).map((dep) => {
27660
+ const dependency = getTask2(dep.depends_on);
27661
+ return { direction: "upstream", task_id: dep.depends_on, status: dependency?.status || "unknown" };
27662
+ }) : [];
27663
+ const downstream = direction !== "upstream" ? getTaskDependents2(resolvedId).map((dep) => {
27664
+ const dependent = getTask2(dep.task_id);
27665
+ return { direction: "downstream", task_id: dep.task_id, status: dependent?.status || "unknown" };
27666
+ }) : [];
27667
+ const deps = [...upstream, ...downstream];
27217
27668
  if (deps.length === 0)
27218
27669
  return { content: [{ type: "text", text: "No dependencies." }] };
27219
27670
  const lines = deps.map((d) => `[${d.direction}] ${d.task_id.slice(0, 8)} (${d.status})`);
@@ -28010,8 +28461,10 @@ function registerTaskWorkflowTools(server, ctx) {
28010
28461
  agent_id: exports_external2.string().optional().describe("Agent ID or name"),
28011
28462
  project_id: exports_external2.string().optional().describe("Filter by project"),
28012
28463
  task_list_id: exports_external2.string().optional().describe("Filter by task list"),
28013
- explain_blocked: exports_external2.boolean().optional().describe("Include blocked task details")
28014
- }, async ({ agent_id, project_id, task_list_id, explain_blocked }) => {
28464
+ explain_blocked: exports_external2.boolean().optional().describe("Include blocked task details"),
28465
+ detail: exports_external2.enum(["compact", "full"]).optional().describe("Response detail (default: compact)"),
28466
+ max_description_chars: exports_external2.number().optional().describe("Max task description chars in compact mode (default: 180)")
28467
+ }, async ({ agent_id, project_id, task_list_id, explain_blocked, detail, max_description_chars }) => {
28015
28468
  try {
28016
28469
  const { getStatus: getStatus2, getNextTask: getNextTask2, getOverdueTasks: getOverdueTasks2 } = (init_tasks(), __toCommonJS(exports_tasks));
28017
28470
  const { getLatestHandoff: getLatestHandoff2 } = (init_handoffs(), __toCommonJS(exports_handoffs));
@@ -28024,16 +28477,31 @@ function registerTaskWorkflowTools(server, ctx) {
28024
28477
  const next_task = getNextTask2(agent_id, filters);
28025
28478
  const overdue = getOverdueTasks2(filters.project_id);
28026
28479
  const latest_handoff = getLatestHandoff2(agent_id, filters.project_id);
28480
+ const payload = {
28481
+ status,
28482
+ next_task,
28483
+ overdue_count: overdue.length,
28484
+ latest_handoff,
28485
+ as_of: new Date().toISOString()
28486
+ };
28487
+ if (detail === "full") {
28488
+ return {
28489
+ content: [{
28490
+ type: "text",
28491
+ text: JSON.stringify(payload, null, 2)
28492
+ }]
28493
+ };
28494
+ }
28027
28495
  return {
28028
28496
  content: [{
28029
28497
  type: "text",
28030
- text: JSON.stringify({
28031
- status,
28032
- next_task,
28498
+ text: compactJson({
28499
+ status: compactStatus(status),
28500
+ next_task: next_task ? compactTask(next_task, max_description_chars || 180) : null,
28033
28501
  overdue_count: overdue.length,
28034
- latest_handoff,
28035
- as_of: new Date().toISOString()
28036
- }, null, 2)
28502
+ latest_handoff: compactHandoff(latest_handoff),
28503
+ as_of: payload.as_of
28504
+ })
28037
28505
  }]
28038
28506
  };
28039
28507
  } catch (e) {
@@ -28097,6 +28565,7 @@ function registerTaskWorkflowTools(server, ctx) {
28097
28565
  var init_task_workflow_tools = __esm(() => {
28098
28566
  init_zod2();
28099
28567
  init_types();
28568
+ init_token_utils();
28100
28569
  });
28101
28570
 
28102
28571
  // src/mcp/tools/task-auto-tools.ts
@@ -28476,14 +28945,37 @@ var init_task_relationships = __esm(() => {
28476
28945
  // src/mcp/tools/task-adv-tools.ts
28477
28946
  function registerTaskAdvTools(server, ctx) {
28478
28947
  const { shouldRegisterTool, resolveId, formatError, formatTask, formatTaskDetail } = ctx;
28948
+ function getDependencyContext(taskId) {
28949
+ const { getTaskDependencies: getTaskDependencies3, getTaskDependents: getTaskDependents2, getTask: getTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
28950
+ const upstream = getTaskDependencies3(taskId).map((dep) => {
28951
+ const dependency = getTask2(dep.depends_on);
28952
+ return {
28953
+ direction: "upstream",
28954
+ task_id: dep.depends_on,
28955
+ status: dependency?.status || "unknown"
28956
+ };
28957
+ });
28958
+ const downstream = getTaskDependents2(taskId).map((dep) => {
28959
+ const dependent = getTask2(dep.task_id);
28960
+ return {
28961
+ direction: "downstream",
28962
+ task_id: dep.task_id,
28963
+ status: dependent?.status || "unknown"
28964
+ };
28965
+ });
28966
+ return [...upstream, ...downstream];
28967
+ }
28479
28968
  if (shouldRegisterTool("get_status")) {
28480
28969
  server.tool("get_status", "Get queue status summary, or pass task_id for a task's detailed status.", {
28481
28970
  task_id: exports_external2.string().optional().describe("Task ID for task-specific status"),
28482
28971
  project_id: exports_external2.string().optional().describe("Filter summary by project"),
28483
28972
  task_list_id: exports_external2.string().optional().describe("Filter summary by task list"),
28484
28973
  agent_id: exports_external2.string().optional().describe("Agent for next-task affinity"),
28485
- explain_blocked: exports_external2.boolean().optional().describe("Include blocked task explanations in summary")
28486
- }, async ({ task_id, project_id, task_list_id, agent_id, explain_blocked }) => {
28974
+ explain_blocked: exports_external2.boolean().optional().describe("Include blocked task explanations in summary"),
28975
+ detail: exports_external2.enum(["compact", "full"]).optional().describe("Response detail (default: compact)"),
28976
+ comment_limit: exports_external2.number().optional().describe("Max recent comments for task status (default: 5)"),
28977
+ file_limit: exports_external2.number().optional().describe("Max files for task status (default: 10)")
28978
+ }, async ({ task_id, project_id, task_list_id, agent_id, explain_blocked, detail, comment_limit, file_limit }) => {
28487
28979
  try {
28488
28980
  if (!task_id) {
28489
28981
  const { getStatus: getStatus2 } = (init_tasks(), __toCommonJS(exports_tasks));
@@ -28493,21 +28985,51 @@ function registerTaskAdvTools(server, ctx) {
28493
28985
  if (task_list_id)
28494
28986
  filters.task_list_id = resolveId(task_list_id, "task_lists");
28495
28987
  const status = getStatus2(filters, agent_id, { explain_blocked });
28496
- return { content: [{ type: "text", text: JSON.stringify(status, null, 2) }] };
28988
+ return { content: [{ type: "text", text: detail === "full" ? JSON.stringify(status, null, 2) : compactJson(compactStatus(status)) }] };
28497
28989
  }
28498
28990
  const resolvedId = resolveId(task_id);
28499
28991
  const { getTask: getTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
28500
28992
  const task = getTask2(resolvedId);
28501
28993
  if (!task)
28502
28994
  throw new Error(`Task not found: ${task_id}`);
28503
- const { getTaskDependencies: getTaskDependencies3 } = (init_tasks(), __toCommonJS(exports_tasks));
28504
28995
  const { listComments: listComments2 } = (init_comments(), __toCommonJS(exports_comments));
28505
28996
  const { listTaskFiles: listTaskFiles2 } = (init_task_files(), __toCommonJS(exports_task_files));
28506
28997
  const [deps, comments, files] = await Promise.all([
28507
- Promise.resolve(getTaskDependencies3(resolvedId, "both")),
28998
+ Promise.resolve(getDependencyContext(resolvedId)),
28508
28999
  Promise.resolve(listComments2(resolvedId)),
28509
29000
  Promise.resolve(listTaskFiles2(resolvedId))
28510
29001
  ]);
29002
+ if (detail !== "full") {
29003
+ const commentsShown = comments.slice(-(comment_limit || 5));
29004
+ const filesShown = files.slice(0, file_limit || 10);
29005
+ return {
29006
+ content: [{
29007
+ type: "text",
29008
+ text: compactJson({
29009
+ task: compactTask(task, 160),
29010
+ dependencies: {
29011
+ count: deps.length,
29012
+ items: deps.slice(0, 10).map((d) => ({ direction: d.direction, task_id: d.task_id, status: d.status })),
29013
+ omitted: Math.max(0, deps.length - 10)
29014
+ },
29015
+ comments: {
29016
+ count: comments.length,
29017
+ recent: commentsShown.map((c) => ({
29018
+ agent_id: c.agent_id || null,
29019
+ created_at: c.created_at,
29020
+ content: truncateText(c.content, 120)
29021
+ })),
29022
+ omitted: Math.max(0, comments.length - commentsShown.length)
29023
+ },
29024
+ files: {
29025
+ count: files.length,
29026
+ items: filesShown.map((f) => ({ status: f.status, path: f.path })),
29027
+ omitted: Math.max(0, files.length - filesShown.length)
29028
+ }
29029
+ })
29030
+ }]
29031
+ };
29032
+ }
28511
29033
  const lines = [
28512
29034
  `Status: ${task.status} | Priority: ${task.priority}`,
28513
29035
  task.assigned_to ? `Assigned: ${task.assigned_to}` : "Unassigned",
@@ -28531,29 +29053,81 @@ Files (${files.length}):` : null,
28531
29053
  });
28532
29054
  }
28533
29055
  if (shouldRegisterTool("task_context")) {
28534
- server.tool("task_context", "Get full context for a task: details, dependencies, relationships, comments, files, commits, time logs, and watchers.", {
28535
- task_id: exports_external2.string().describe("Task ID")
28536
- }, async ({ task_id }) => {
29056
+ 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.", {
29057
+ task_id: exports_external2.string().describe("Task ID"),
29058
+ detail: exports_external2.enum(["compact", "full"]).optional().describe("Response detail (default: compact)"),
29059
+ comment_limit: exports_external2.number().optional().describe("Max recent comments in compact mode (default: 5)"),
29060
+ file_limit: exports_external2.number().optional().describe("Max files in compact mode (default: 10)"),
29061
+ commit_limit: exports_external2.number().optional().describe("Max commits in compact mode (default: 10)"),
29062
+ max_description_chars: exports_external2.number().optional().describe("Max description chars in compact mode (default: 240)")
29063
+ }, async ({ task_id, detail, comment_limit, file_limit, commit_limit, max_description_chars }) => {
28537
29064
  try {
28538
29065
  const resolvedId = resolveId(task_id);
28539
29066
  const { getTask: getTask2 } = (init_tasks(), __toCommonJS(exports_tasks));
28540
29067
  const task = getTask2(resolvedId);
28541
29068
  if (!task)
28542
29069
  throw new Error(`Task not found: ${task_id}`);
28543
- const { getTaskDependencies: getTaskDependencies3 } = (init_tasks(), __toCommonJS(exports_tasks));
28544
29070
  const { getTaskRelationships: getTaskRelationships2 } = (init_task_relationships(), __toCommonJS(exports_task_relationships));
28545
29071
  const { listComments: listComments2 } = (init_comments(), __toCommonJS(exports_comments));
28546
29072
  const { listTaskFiles: listTaskFiles2 } = (init_task_files(), __toCommonJS(exports_task_files));
28547
29073
  const { getTaskCommits: getTaskCommits2 } = (init_task_commits(), __toCommonJS(exports_task_commits));
28548
29074
  const { getTaskWatchers: getTaskWatchers2 } = (init_tasks(), __toCommonJS(exports_tasks));
28549
29075
  const [deps, rels, comments, files, commits, watchers] = await Promise.all([
28550
- Promise.resolve(getTaskDependencies3(resolvedId, "both")),
29076
+ Promise.resolve(getDependencyContext(resolvedId)),
28551
29077
  Promise.resolve(getTaskRelationships2(resolvedId)),
28552
29078
  Promise.resolve(listComments2(resolvedId)),
28553
29079
  Promise.resolve(listTaskFiles2(resolvedId)),
28554
29080
  Promise.resolve(getTaskCommits2(resolvedId)),
28555
29081
  Promise.resolve(getTaskWatchers2(resolvedId))
28556
29082
  ]);
29083
+ if (detail !== "full") {
29084
+ const commentsShown = comments.slice(-(comment_limit || 5));
29085
+ const filesShown = files.slice(0, file_limit || 10);
29086
+ const commitsShown = commits.slice(0, commit_limit || 10);
29087
+ return {
29088
+ content: [{
29089
+ type: "text",
29090
+ text: compactJson({
29091
+ task: compactTask(task, max_description_chars || 240),
29092
+ dependencies: {
29093
+ count: deps.length,
29094
+ items: deps.slice(0, 10).map((d) => ({ direction: d.direction, task_id: d.task_id, status: d.status })),
29095
+ omitted: Math.max(0, deps.length - 10)
29096
+ },
29097
+ relationships: {
29098
+ count: rels.length,
29099
+ items: rels.slice(0, 10).map((r) => ({
29100
+ source_task_id: r.source_task_id,
29101
+ relationship_type: r.relationship_type,
29102
+ target_task_id: r.target_task_id
29103
+ })),
29104
+ omitted: Math.max(0, rels.length - 10)
29105
+ },
29106
+ comments: {
29107
+ count: comments.length,
29108
+ recent: commentsShown.map((c) => ({
29109
+ agent_id: c.agent_id || null,
29110
+ created_at: c.created_at,
29111
+ content: truncateText(c.content, 160)
29112
+ })),
29113
+ omitted: Math.max(0, comments.length - commentsShown.length)
29114
+ },
29115
+ files: {
29116
+ count: files.length,
29117
+ items: filesShown.map((f) => ({ status: f.status, path: f.path })),
29118
+ omitted: Math.max(0, files.length - filesShown.length)
29119
+ },
29120
+ commits: {
29121
+ count: commits.length,
29122
+ items: commitsShown.map((c) => ({ sha: c.sha, message: truncateText(c.message || "(no message)", 120) })),
29123
+ omitted: Math.max(0, commits.length - commitsShown.length)
29124
+ },
29125
+ watchers: { count: watchers.length },
29126
+ metadata_keys: task.metadata ? Object.keys(task.metadata) : []
29127
+ })
29128
+ }]
29129
+ };
29130
+ }
28557
29131
  const lines = [
28558
29132
  `== ${task.title} ====================`,
28559
29133
  `ID: ${task.id}`,
@@ -28719,15 +29293,20 @@ No blocked tasks.`,
28719
29293
  });
28720
29294
  }
28721
29295
  if (shouldRegisterTool("get_comments")) {
28722
- server.tool("get_comments", "Alias for list_comments.", {
28723
- task_id: exports_external2.string().describe("Task ID")
28724
- }, async ({ task_id }) => {
29296
+ server.tool("get_comments", "List recent comments for a task. Compact by default; pass detail=full for all comment text.", {
29297
+ task_id: exports_external2.string().describe("Task ID"),
29298
+ detail: exports_external2.enum(["compact", "full"]).optional().describe("Response detail (default: compact)"),
29299
+ limit: exports_external2.number().optional().describe("Max recent comments in compact mode (default: 20)")
29300
+ }, async ({ task_id, detail, limit }) => {
28725
29301
  try {
28726
29302
  const { listComments: listComments2 } = (init_comments(), __toCommonJS(exports_comments));
28727
29303
  const comments = listComments2(resolveId(task_id));
28728
29304
  if (comments.length === 0)
28729
29305
  return { content: [{ type: "text", text: "No comments." }] };
28730
- const lines = comments.map((c) => `[${c.agent_id || "?"}] ${c.created_at?.slice(0, 16)}: ${c.content}`);
29306
+ const shown = detail === "full" ? comments : comments.slice(-(limit || 20));
29307
+ const lines = shown.map((c) => `[${c.agent_id || "?"}] ${c.created_at?.slice(0, 16)}: ${detail === "full" ? c.content : truncateText(c.content, 180)}`);
29308
+ if (shown.length < comments.length)
29309
+ lines.unshift(`Showing ${shown.length} of ${comments.length} comments (${comments.length - shown.length} older omitted).`);
28731
29310
  return { content: [{ type: "text", text: lines.join(`
28732
29311
 
28733
29312
  `) }] };
@@ -28767,6 +29346,7 @@ No blocked tasks.`,
28767
29346
  }
28768
29347
  var init_task_adv_tools = __esm(() => {
28769
29348
  init_zod2();
29349
+ init_token_utils();
28770
29350
  });
28771
29351
 
28772
29352
  // src/mcp/tools/task-meta-tools.ts
@@ -28797,7 +29377,7 @@ function registerTaskMetaTools(server, ctx) {
28797
29377
  const toolDocs = {
28798
29378
  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",
28799
29379
  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",
28800
- get_task: "get_task \u2014 Get full details for a task. Params: task_id",
29380
+ get_task: "get_task \u2014 Get compact task details by default. Params: task_id, detail=compact|full, max_description_chars, include_metadata",
28801
29381
  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",
28802
29382
  delete_task: "delete_task \u2014 Delete a task. Params: task_id, force (skip child check)",
28803
29383
  start_task: "start_task \u2014 Mark task in_progress. Params: task_id, version",
@@ -28811,8 +29391,8 @@ function registerTaskMetaTools(server, ctx) {
28811
29391
  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",
28812
29392
  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",
28813
29393
  get_tasks_changed_since: "get_tasks_changed_since \u2014 List tasks changed since an ISO timestamp. Params: since, project_id, task_list_id, limit",
28814
- get_context: "get_context \u2014 Get session start context. Params: agent_id, project_id, task_list_id, explain_blocked",
28815
- bootstrap: "bootstrap \u2014 Bootstrap an agent session with queue context. Params: agent_id, project_id, task_list_id, explain_blocked",
29394
+ 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",
29395
+ 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",
28816
29396
  standup: "standup \u2014 Get standup report. Params: agent_id, project_id",
28817
29397
  patrol_tasks: "patrol_tasks \u2014 Scan for task issues. Params: stuck_minutes, confidence_threshold, project_id",
28818
29398
  get_review_queue: "get_review_queue \u2014 Get tasks needing review. Params: project_id, limit",
@@ -28849,6 +29429,7 @@ function registerTaskMetaTools(server, ctx) {
28849
29429
  get_task_relationships: "get_task_relationships \u2014 Get all relationships. Params: task_id, relationship_type",
28850
29430
  create_comment: "create_comment \u2014 Add comment. Params: task_id, body, author",
28851
29431
  list_comments: "list_comments \u2014 List comments. Params: task_id",
29432
+ get_comments: "get_comments \u2014 List recent comments by default. Params: task_id, detail=compact|full, limit",
28852
29433
  update_comment: "update_comment \u2014 Edit comment. Params: comment_id, body",
28853
29434
  delete_comment: "delete_comment \u2014 Delete comment. Params: comment_id",
28854
29435
  lock_task: "lock_task \u2014 Acquire exclusive lock. Params: task_id, agent_id, ttl_seconds",
@@ -31694,30 +32275,14 @@ __export(exports_mcp, {
31694
32275
  });
31695
32276
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
31696
32277
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
31697
- import { existsSync as existsSync9, readFileSync as readFileSync7 } from "fs";
31698
- import { join as join12, dirname as dirname6 } from "path";
31699
- import { fileURLToPath as fileURLToPath3 } from "url";
31700
32278
  function getMcpVersion() {
31701
- try {
31702
- let dir = dirname6(fileURLToPath3(import.meta.url));
31703
- for (let i = 0;i < 4; i++) {
31704
- const pkgPath = join12(dir, "package.json");
31705
- if (existsSync9(pkgPath)) {
31706
- return JSON.parse(readFileSync7(pkgPath, "utf-8")).version || "0.0.0";
31707
- }
31708
- dir = dirname6(dir);
31709
- }
31710
- } catch {
31711
- return "0.0.0";
31712
- }
31713
- return "0.0.0";
32279
+ return getPackageVersion(import.meta.url);
32280
+ }
32281
+ function hasVersionFlag() {
32282
+ return process.argv.includes("--version") || process.argv.includes("-V");
31714
32283
  }
31715
32284
  function shouldRegisterTool(name) {
31716
- if (TODOS_PROFILE === "minimal")
31717
- return MINIMAL_TOOLS.has(name);
31718
- if (TODOS_PROFILE === "standard")
31719
- return !STANDARD_EXCLUDED.has(name);
31720
- return true;
32285
+ return shouldRegisterToolForProfile(name);
31721
32286
  }
31722
32287
  function getAgentFocus(agentId) {
31723
32288
  const sessionFocus = agentFocusMap.get(agentId);
@@ -31854,7 +32419,7 @@ async function main() {
31854
32419
  const transport = new StdioServerTransport;
31855
32420
  await server.connect(transport);
31856
32421
  }
31857
- var server, TODOS_PROFILE, MINIMAL_TOOLS, STANDARD_EXCLUDED, agentFocusMap, toolContext;
32422
+ var server, agentFocusMap, toolContext;
31858
32423
  var init_mcp = __esm(() => {
31859
32424
  init_cloud();
31860
32425
  init_agents();
@@ -31872,46 +32437,17 @@ var init_mcp = __esm(() => {
31872
32437
  init_code_tools();
31873
32438
  init_machines2();
31874
32439
  init_agents2();
32440
+ init_package_version();
32441
+ init_token_utils();
32442
+ if (hasVersionFlag()) {
32443
+ console.log(getMcpVersion());
32444
+ process.exit(0);
32445
+ }
31875
32446
  server = new McpServer({
31876
32447
  name: "todos",
31877
32448
  version: getMcpVersion()
31878
32449
  });
31879
- TODOS_PROFILE = (process.env["TODOS_PROFILE"] || "full").toLowerCase();
31880
- MINIMAL_TOOLS = new Set([
31881
- "claim_next_task",
31882
- "complete_task",
31883
- "fail_task",
31884
- "get_status",
31885
- "get_context",
31886
- "get_task",
31887
- "start_task",
31888
- "add_comment",
31889
- "get_next_task",
31890
- "bootstrap",
31891
- "get_tasks_changed_since",
31892
- "get_health",
31893
- "heartbeat",
31894
- "release_agent"
31895
- ]);
31896
- STANDARD_EXCLUDED = new Set([
31897
- "rename_agent",
31898
- "delete_agent",
31899
- "unarchive_agent",
31900
- "create_webhook",
31901
- "list_webhooks",
31902
- "delete_webhook",
31903
- "create_template",
31904
- "list_templates",
31905
- "create_task_from_template",
31906
- "delete_template",
31907
- "update_template",
31908
- "init_templates",
31909
- "preview_template",
31910
- "export_template",
31911
- "import_template",
31912
- "template_history",
31913
- "approve_task"
31914
- ]);
32450
+ installMcpTokenTelemetry(server);
31915
32451
  agentFocusMap = new Map;
31916
32452
  toolContext = {
31917
32453
  shouldRegisterTool,
@@ -33034,7 +33570,7 @@ import { basename as basename2, resolve as resolve4 } from "path";
33034
33570
  init_tasks();
33035
33571
  init_config();
33036
33572
  init_sync_utils();
33037
- import { existsSync as existsSync4, readFileSync as readFileSync3, readdirSync as readdirSync2, writeFileSync as writeFileSync2 } from "fs";
33573
+ import { existsSync as existsSync5, readFileSync as readFileSync3, readdirSync as readdirSync2, writeFileSync as writeFileSync2 } from "fs";
33038
33574
  import { join as join5 } from "path";
33039
33575
  function getTaskListDir(taskListId) {
33040
33576
  return join5(HOME, ".claude", "tasks", taskListId);
@@ -33056,7 +33592,7 @@ function toSqliteStatus(status) {
33056
33592
  }
33057
33593
  function readPrefixCounter(dir) {
33058
33594
  const path = join5(dir, ".prefix-counter");
33059
- if (!existsSync4(path))
33595
+ if (!existsSync5(path))
33060
33596
  return 0;
33061
33597
  const val = parseInt(readFileSync3(path, "utf-8").trim(), 10);
33062
33598
  return isNaN(val) ? 0 : val;
@@ -33089,7 +33625,7 @@ function taskToClaudeTask(task, claudeTaskId, existingMeta) {
33089
33625
  }
33090
33626
  function pushToClaudeTaskList(taskListId, projectId, options = {}) {
33091
33627
  const dir = getTaskListDir(taskListId);
33092
- if (!existsSync4(dir))
33628
+ if (!existsSync5(dir))
33093
33629
  ensureDir2(dir);
33094
33630
  const filter = {};
33095
33631
  if (projectId)
@@ -33185,7 +33721,7 @@ function pushToClaudeTaskList(taskListId, projectId, options = {}) {
33185
33721
  }
33186
33722
  function pullFromClaudeTaskList(taskListId, projectId, options = {}) {
33187
33723
  const dir = getTaskListDir(taskListId);
33188
- if (!existsSync4(dir)) {
33724
+ if (!existsSync5(dir)) {
33189
33725
  return { pushed: 0, pulled: 0, errors: [`Task list directory not found: ${dir}`] };
33190
33726
  }
33191
33727
  const files = readdirSync2(dir).filter((f) => f.endsWith(".json"));
@@ -33276,12 +33812,12 @@ function syncClaudeTaskList(taskListId, projectId, options = {}) {
33276
33812
  init_tasks();
33277
33813
  init_sync_utils();
33278
33814
  init_config();
33279
- import { existsSync as existsSync5 } from "fs";
33815
+ import { existsSync as existsSync6 } from "fs";
33280
33816
  import { join as join6 } from "path";
33281
33817
  function getTodosGlobalDir2() {
33282
33818
  const newDir = join6(HOME, ".hasna", "todos");
33283
33819
  const legacyDir = join6(HOME, ".todos");
33284
- if (!existsSync5(newDir) && existsSync5(legacyDir))
33820
+ if (!existsSync6(newDir) && existsSync6(legacyDir))
33285
33821
  return legacyDir;
33286
33822
  return newDir;
33287
33823
  }
@@ -33321,7 +33857,7 @@ function metadataKey(agent) {
33321
33857
  }
33322
33858
  function pushToAgentTaskList(agent, taskListId, projectId, options = {}) {
33323
33859
  const dir = getTaskListDir2(agent, taskListId);
33324
- if (!existsSync5(dir))
33860
+ if (!existsSync6(dir))
33325
33861
  ensureDir2(dir);
33326
33862
  const filter = {};
33327
33863
  if (projectId)
@@ -33404,7 +33940,7 @@ function pushToAgentTaskList(agent, taskListId, projectId, options = {}) {
33404
33940
  }
33405
33941
  function pullFromAgentTaskList(agent, taskListId, projectId, options = {}) {
33406
33942
  const dir = getTaskListDir2(agent, taskListId);
33407
- if (!existsSync5(dir)) {
33943
+ if (!existsSync6(dir)) {
33408
33944
  return { pushed: 0, pulled: 0, errors: [`Task list directory not found: ${dir}`] };
33409
33945
  }
33410
33946
  const files = listJsonFiles(dir);
@@ -34298,7 +34834,7 @@ init_tasks();
34298
34834
  init_config();
34299
34835
  init_helpers();
34300
34836
  import chalk6 from "chalk";
34301
- import { existsSync as existsSync7, mkdirSync as mkdirSync3, readFileSync as readFileSync5, writeFileSync as writeFileSync3 } from "fs";
34837
+ import { existsSync as existsSync8, mkdirSync as mkdirSync3, readFileSync as readFileSync5, writeFileSync as writeFileSync3 } from "fs";
34302
34838
  import { dirname as dirname4, join as join10 } from "path";
34303
34839
  function registerConfigServeCommands(program2) {
34304
34840
  program2.command("config").description("View or update configuration").option("--get <key>", "Get a config value").option("--set <key=value>", "Set a config value (e.g. completion_guard.enabled=true)").action((opts) => {
@@ -34306,7 +34842,7 @@ function registerConfigServeCommands(program2) {
34306
34842
  const home = process.env["HOME"] || "~";
34307
34843
  const newPath = join10(home, ".hasna", "todos", "config.json");
34308
34844
  const legacyPath = join10(home, ".todos", "config.json");
34309
- const configPath = !existsSync7(newPath) && existsSync7(legacyPath) ? legacyPath : newPath;
34845
+ const configPath = !existsSync8(newPath) && existsSync8(legacyPath) ? legacyPath : newPath;
34310
34846
  if (opts.get) {
34311
34847
  const config2 = loadConfig();
34312
34848
  const keys = opts.get.split(".");
@@ -34343,7 +34879,7 @@ function registerConfigServeCommands(program2) {
34343
34879
  }
34344
34880
  obj[keys[keys.length - 1]] = parsedValue;
34345
34881
  const dir = dirname4(configPath);
34346
- if (!existsSync7(dir))
34882
+ if (!existsSync8(dir))
34347
34883
  mkdirSync3(dir, { recursive: true });
34348
34884
  writeFileSync3(configPath, JSON.stringify(config2, null, 2));
34349
34885
  if (globalOpts.json) {
@@ -36056,8 +36592,8 @@ init_tasks();
36056
36592
  init_helpers();
36057
36593
  import chalk9 from "chalk";
36058
36594
  import { execSync as execSync3 } from "child_process";
36059
- import { existsSync as existsSync10, readFileSync as readFileSync8, writeFileSync as writeFileSync5, mkdirSync as mkdirSync5, chmodSync } from "fs";
36060
- import { dirname as dirname7, join as join13 } from "path";
36595
+ import { existsSync as existsSync10, readFileSync as readFileSync7, writeFileSync as writeFileSync5, mkdirSync as mkdirSync5, chmodSync } from "fs";
36596
+ import { dirname as dirname6, join as join12 } from "path";
36061
36597
  var HOME2 = process.env["HOME"] || process.env["USERPROFILE"] || "~";
36062
36598
  function getMcpBinaryPath() {
36063
36599
  try {
@@ -36065,7 +36601,7 @@ function getMcpBinaryPath() {
36065
36601
  if (p)
36066
36602
  return p;
36067
36603
  } catch {}
36068
- const bunBin = join13(HOME2, ".bun", "bin", "todos-mcp");
36604
+ const bunBin = join12(HOME2, ".bun", "bin", "todos-mcp");
36069
36605
  if (existsSync10(bunBin))
36070
36606
  return bunBin;
36071
36607
  return "todos-mcp";
@@ -36074,13 +36610,13 @@ function readJsonFile2(path) {
36074
36610
  if (!existsSync10(path))
36075
36611
  return {};
36076
36612
  try {
36077
- return JSON.parse(readFileSync8(path, "utf-8"));
36613
+ return JSON.parse(readFileSync7(path, "utf-8"));
36078
36614
  } catch {
36079
36615
  return {};
36080
36616
  }
36081
36617
  }
36082
36618
  function writeJsonFile2(path, data) {
36083
- const dir = dirname7(path);
36619
+ const dir = dirname6(path);
36084
36620
  if (!existsSync10(dir))
36085
36621
  mkdirSync5(dir, { recursive: true });
36086
36622
  writeFileSync5(path, JSON.stringify(data, null, 2) + `
@@ -36089,10 +36625,10 @@ function writeJsonFile2(path, data) {
36089
36625
  function readTomlFile(path) {
36090
36626
  if (!existsSync10(path))
36091
36627
  return "";
36092
- return readFileSync8(path, "utf-8");
36628
+ return readFileSync7(path, "utf-8");
36093
36629
  }
36094
36630
  function writeTomlFile(path, content) {
36095
- const dir = dirname7(path);
36631
+ const dir = dirname6(path);
36096
36632
  if (!existsSync10(dir))
36097
36633
  mkdirSync5(dir, { recursive: true });
36098
36634
  writeFileSync5(path, content);
@@ -36139,7 +36675,7 @@ function unregisterClaude(_global) {
36139
36675
  }
36140
36676
  }
36141
36677
  function registerCodex(binPath) {
36142
- const configPath = join13(HOME2, ".codex", "config.toml");
36678
+ const configPath = join12(HOME2, ".codex", "config.toml");
36143
36679
  let content = readTomlFile(configPath);
36144
36680
  content = removeTomlBlock(content, "mcp_servers.todos");
36145
36681
  const block = `
@@ -36153,7 +36689,7 @@ args = []
36153
36689
  console.log(chalk9.green(`Codex CLI: registered in ${configPath}`));
36154
36690
  }
36155
36691
  function unregisterCodex() {
36156
- const configPath = join13(HOME2, ".codex", "config.toml");
36692
+ const configPath = join12(HOME2, ".codex", "config.toml");
36157
36693
  let content = readTomlFile(configPath);
36158
36694
  if (!content.includes("[mcp_servers.todos]")) {
36159
36695
  console.log(chalk9.dim(`Codex CLI: todos not found in ${configPath}`));
@@ -36165,7 +36701,7 @@ function unregisterCodex() {
36165
36701
  console.log(chalk9.green(`Codex CLI: unregistered from ${configPath}`));
36166
36702
  }
36167
36703
  function registerGemini(binPath) {
36168
- const configPath = join13(HOME2, ".gemini", "settings.json");
36704
+ const configPath = join12(HOME2, ".gemini", "settings.json");
36169
36705
  const config = readJsonFile2(configPath);
36170
36706
  if (!config["mcpServers"]) {
36171
36707
  config["mcpServers"] = {};
@@ -36179,7 +36715,7 @@ function registerGemini(binPath) {
36179
36715
  console.log(chalk9.green(`Gemini CLI: registered in ${configPath}`));
36180
36716
  }
36181
36717
  function unregisterGemini() {
36182
- const configPath = join13(HOME2, ".gemini", "settings.json");
36718
+ const configPath = join12(HOME2, ".gemini", "settings.json");
36183
36719
  const config = readJsonFile2(configPath);
36184
36720
  const servers = config["mcpServers"];
36185
36721
  if (!servers || !("todos" in servers)) {
@@ -36236,7 +36772,7 @@ function registerMcpHooksCommands(program2) {
36236
36772
  if (p)
36237
36773
  todosBin = p;
36238
36774
  } catch {}
36239
- const hooksDir = join13(process.cwd(), ".claude", "hooks");
36775
+ const hooksDir = join12(process.cwd(), ".claude", "hooks");
36240
36776
  if (!existsSync10(hooksDir))
36241
36777
  mkdirSync5(hooksDir, { recursive: true });
36242
36778
  const hookScript = `#!/usr/bin/env bash
@@ -36262,11 +36798,11 @@ esac
36262
36798
 
36263
36799
  exit 0
36264
36800
  `;
36265
- const hookPath = join13(hooksDir, "todos-sync.sh");
36801
+ const hookPath = join12(hooksDir, "todos-sync.sh");
36266
36802
  writeFileSync5(hookPath, hookScript);
36267
36803
  execSync3(`chmod +x "${hookPath}"`);
36268
36804
  console.log(chalk9.green(`Hook script created: ${hookPath}`));
36269
- const settingsPath = join13(process.cwd(), ".claude", "settings.json");
36805
+ const settingsPath = join12(process.cwd(), ".claude", "settings.json");
36270
36806
  const settings = readJsonFile2(settingsPath);
36271
36807
  if (!settings["hooks"]) {
36272
36808
  settings["hooks"] = {};
@@ -36360,7 +36896,7 @@ exit 0
36360
36896
  const hookPath = `${gitDir}/hooks/post-commit`;
36361
36897
  const marker = "# todos-auto-link";
36362
36898
  if (existsSync10(hookPath)) {
36363
- const existing = readFileSync8(hookPath, "utf-8");
36899
+ const existing = readFileSync7(hookPath, "utf-8");
36364
36900
  if (existing.includes(marker)) {
36365
36901
  console.log(chalk9.yellow("Hook already installed."));
36366
36902
  return;
@@ -36391,7 +36927,7 @@ $(dirname "$0")/../../scripts/post-commit-hook.sh
36391
36927
  console.log(chalk9.dim("No post-commit hook found."));
36392
36928
  return;
36393
36929
  }
36394
- const content = readFileSync8(hookPath, "utf-8");
36930
+ const content = readFileSync7(hookPath, "utf-8");
36395
36931
  if (!content.includes(marker)) {
36396
36932
  console.log(chalk9.dim("Hook not managed by todos."));
36397
36933
  return;
@@ -36555,7 +37091,7 @@ import chalk11 from "chalk";
36555
37091
  import { execSync as execSync4 } from "child_process";
36556
37092
  import { writeFileSync as writeFileSync6 } from "fs";
36557
37093
  import { tmpdir } from "os";
36558
- import { join as join14 } from "path";
37094
+ import { join as join13 } from "path";
36559
37095
  function getOrCreateLocalMachineName() {
36560
37096
  return process.env["TODOS_MACHINE_NAME"] || __require("os").hostname() || "unknown";
36561
37097
  }
@@ -36749,7 +37285,7 @@ Warning: No primary machine set.`));
36749
37285
  if (opts.push) {
36750
37286
  try {
36751
37287
  const localTasks = listTasks3();
36752
- const tmpFile = join14(tmpdir(), `todos-export-${uuid()}.json`);
37288
+ const tmpFile = join13(tmpdir(), `todos-export-${uuid()}.json`);
36753
37289
  writeFileSync6(tmpFile, JSON.stringify(localTasks, null, 2));
36754
37290
  execSync4(`scp ${tmpFile} ${ssh}:/tmp/todos-import.json`, { timeout: 15000 });
36755
37291
  const importCmd = `ssh ${ssh} 'node -e "const fs=require(\\'fs\\');const tasks=JSON.parse(fs.readFileSync(\\'/tmp/todos-import.json\\',\\'utf-8\\'));console.log(JSON.stringify(tasks.length))"'`;