@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/README.md +16 -1
- package/dist/cli/helpers.d.ts +2 -1
- package/dist/cli/helpers.d.ts.map +1 -1
- package/dist/cli/index.js +694 -158
- package/dist/index.js +18 -17
- package/dist/lib/package-version.d.ts +2 -0
- package/dist/lib/package-version.d.ts.map +1 -0
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +585 -79
- package/dist/mcp/token-utils.d.ts +15 -0
- package/dist/mcp/token-utils.d.ts.map +1 -0
- package/dist/mcp/tools/task-adv-tools.d.ts.map +1 -1
- package/dist/mcp/tools/task-crud.d.ts.map +1 -1
- package/dist/mcp/tools/task-meta-tools.d.ts.map +1 -1
- package/dist/mcp/tools/task-project-tools.d.ts.map +1 -1
- package/dist/mcp/tools/task-workflow-tools.d.ts.map +1 -1
- package/dist/sdk/client.d.ts +30 -8
- package/dist/sdk/client.d.ts.map +1 -1
- package/dist/sdk/types.d.ts +6 -0
- package/dist/sdk/types.d.ts.map +1 -1
- package/dist/server/index.js +118 -56
- package/dist/server/routes.d.ts +3 -3
- package/dist/server/routes.d.ts.map +1 -1
- package/package.json +1 -1
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 {
|
|
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
|
|
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 (!
|
|
4226
|
+
if (!existsSync3(dir))
|
|
4212
4227
|
mkdirSync2(dir, { recursive: true });
|
|
4213
4228
|
}
|
|
4214
4229
|
function listJsonFiles(dir) {
|
|
4215
|
-
if (!
|
|
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 (!
|
|
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
|
|
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 (!
|
|
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 (!
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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 (
|
|
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 (!
|
|
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 =
|
|
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
|
|
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 (!
|
|
12046
|
+
if (!existsSync9(legacyDir))
|
|
12000
12047
|
return [];
|
|
12001
|
-
if (
|
|
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
|
|
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
|
|
26958
|
-
task_id: exports_external2.string().describe("Task ID (full or short)")
|
|
26959
|
-
|
|
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
|
|
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
|
-
|
|
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:
|
|
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:
|
|
28036
|
-
}
|
|
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
|
-
|
|
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(
|
|
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
|
|
28535
|
-
task_id: exports_external2.string().describe("Task ID")
|
|
28536
|
-
|
|
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(
|
|
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", "
|
|
28723
|
-
task_id: exports_external2.string().describe("Task ID")
|
|
28724
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
31702
|
-
|
|
31703
|
-
|
|
31704
|
-
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
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
|
|
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 (!
|
|
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 (!
|
|
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 (!
|
|
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
|
|
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 (!
|
|
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 (!
|
|
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 (!
|
|
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
|
|
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 = !
|
|
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 (!
|
|
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
|
|
36060
|
-
import { dirname as
|
|
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 =
|
|
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(
|
|
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 =
|
|
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
|
|
36628
|
+
return readFileSync7(path, "utf-8");
|
|
36093
36629
|
}
|
|
36094
36630
|
function writeTomlFile(path, content) {
|
|
36095
|
-
const dir =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
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 =
|
|
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))"'`;
|