@hasna/todos 0.2.1 → 0.3.0
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 +334 -54
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -6975,27 +6975,27 @@ __export(exports_serve, {
|
|
|
6975
6975
|
startServer: () => startServer
|
|
6976
6976
|
});
|
|
6977
6977
|
import { execSync } from "child_process";
|
|
6978
|
-
import { existsSync as
|
|
6979
|
-
import { join as
|
|
6978
|
+
import { existsSync as existsSync3, readFileSync as readFileSync2 } from "fs";
|
|
6979
|
+
import { join as join3, dirname as dirname2, extname } from "path";
|
|
6980
6980
|
import { fileURLToPath } from "url";
|
|
6981
6981
|
function resolveDashboardDir() {
|
|
6982
6982
|
const candidates = [];
|
|
6983
6983
|
try {
|
|
6984
6984
|
const scriptDir = dirname2(fileURLToPath(import.meta.url));
|
|
6985
|
-
candidates.push(
|
|
6986
|
-
candidates.push(
|
|
6985
|
+
candidates.push(join3(scriptDir, "..", "dashboard", "dist"));
|
|
6986
|
+
candidates.push(join3(scriptDir, "..", "..", "dashboard", "dist"));
|
|
6987
6987
|
} catch {}
|
|
6988
6988
|
if (process.argv[1]) {
|
|
6989
6989
|
const mainDir = dirname2(process.argv[1]);
|
|
6990
|
-
candidates.push(
|
|
6991
|
-
candidates.push(
|
|
6990
|
+
candidates.push(join3(mainDir, "..", "dashboard", "dist"));
|
|
6991
|
+
candidates.push(join3(mainDir, "..", "..", "dashboard", "dist"));
|
|
6992
6992
|
}
|
|
6993
|
-
candidates.push(
|
|
6993
|
+
candidates.push(join3(process.cwd(), "dashboard", "dist"));
|
|
6994
6994
|
for (const candidate of candidates) {
|
|
6995
|
-
if (
|
|
6995
|
+
if (existsSync3(candidate))
|
|
6996
6996
|
return candidate;
|
|
6997
6997
|
}
|
|
6998
|
-
return
|
|
6998
|
+
return join3(process.cwd(), "dashboard", "dist");
|
|
6999
6999
|
}
|
|
7000
7000
|
function json(data, status = 200, port) {
|
|
7001
7001
|
return new Response(JSON.stringify(data), {
|
|
@@ -7009,14 +7009,14 @@ function json(data, status = 200, port) {
|
|
|
7009
7009
|
}
|
|
7010
7010
|
function getPackageVersion() {
|
|
7011
7011
|
try {
|
|
7012
|
-
const pkgPath =
|
|
7013
|
-
return JSON.parse(
|
|
7012
|
+
const pkgPath = join3(dirname2(fileURLToPath(import.meta.url)), "..", "..", "package.json");
|
|
7013
|
+
return JSON.parse(readFileSync2(pkgPath, "utf-8")).version || "0.0.0";
|
|
7014
7014
|
} catch {
|
|
7015
7015
|
return "0.0.0";
|
|
7016
7016
|
}
|
|
7017
7017
|
}
|
|
7018
7018
|
function serveStaticFile(filePath) {
|
|
7019
|
-
if (!
|
|
7019
|
+
if (!existsSync3(filePath))
|
|
7020
7020
|
return null;
|
|
7021
7021
|
const ext = extname(filePath);
|
|
7022
7022
|
const contentType = MIME_TYPES[ext] || "application/octet-stream";
|
|
@@ -7027,7 +7027,7 @@ function serveStaticFile(filePath) {
|
|
|
7027
7027
|
async function startServer(port, options) {
|
|
7028
7028
|
const shouldOpen = options?.open ?? true;
|
|
7029
7029
|
const dashboardDir = resolveDashboardDir();
|
|
7030
|
-
const dashboardExists =
|
|
7030
|
+
const dashboardExists = existsSync3(dashboardDir);
|
|
7031
7031
|
if (!dashboardExists) {
|
|
7032
7032
|
console.error(`
|
|
7033
7033
|
Dashboard not found at: ${dashboardDir}`);
|
|
@@ -7297,12 +7297,12 @@ Dashboard not found at: ${dashboardDir}`);
|
|
|
7297
7297
|
}
|
|
7298
7298
|
if (dashboardExists && (method === "GET" || method === "HEAD")) {
|
|
7299
7299
|
if (path !== "/") {
|
|
7300
|
-
const filePath =
|
|
7300
|
+
const filePath = join3(dashboardDir, path);
|
|
7301
7301
|
const res2 = serveStaticFile(filePath);
|
|
7302
7302
|
if (res2)
|
|
7303
7303
|
return res2;
|
|
7304
7304
|
}
|
|
7305
|
-
const indexPath =
|
|
7305
|
+
const indexPath = join3(dashboardDir, "index.html");
|
|
7306
7306
|
const res = serveStaticFile(indexPath);
|
|
7307
7307
|
if (res)
|
|
7308
7308
|
return res;
|
|
@@ -8433,13 +8433,187 @@ init_comments();
|
|
|
8433
8433
|
init_search();
|
|
8434
8434
|
import chalk from "chalk";
|
|
8435
8435
|
import { execSync as execSync2 } from "child_process";
|
|
8436
|
-
import { existsSync as
|
|
8437
|
-
import { basename, dirname as dirname3, join as
|
|
8436
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
|
|
8437
|
+
import { basename, dirname as dirname3, join as join4, resolve as resolve2 } from "path";
|
|
8438
8438
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
8439
|
+
|
|
8440
|
+
// src/lib/claude-tasks.ts
|
|
8441
|
+
init_tasks();
|
|
8442
|
+
import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync, readdirSync, writeFileSync } from "fs";
|
|
8443
|
+
import { join as join2 } from "path";
|
|
8444
|
+
var HOME = process.env["HOME"] || process.env["USERPROFILE"] || "~";
|
|
8445
|
+
function getTaskListDir(taskListId) {
|
|
8446
|
+
return join2(HOME, ".claude", "tasks", taskListId);
|
|
8447
|
+
}
|
|
8448
|
+
function readHighWaterMark(dir) {
|
|
8449
|
+
const path = join2(dir, ".highwatermark");
|
|
8450
|
+
if (!existsSync2(path))
|
|
8451
|
+
return 1;
|
|
8452
|
+
const val = parseInt(readFileSync(path, "utf-8").trim(), 10);
|
|
8453
|
+
return isNaN(val) ? 1 : val;
|
|
8454
|
+
}
|
|
8455
|
+
function writeHighWaterMark(dir, value) {
|
|
8456
|
+
writeFileSync(join2(dir, ".highwatermark"), String(value));
|
|
8457
|
+
}
|
|
8458
|
+
function readClaudeTask(dir, filename) {
|
|
8459
|
+
try {
|
|
8460
|
+
const content = readFileSync(join2(dir, filename), "utf-8");
|
|
8461
|
+
return JSON.parse(content);
|
|
8462
|
+
} catch {
|
|
8463
|
+
return null;
|
|
8464
|
+
}
|
|
8465
|
+
}
|
|
8466
|
+
function writeClaudeTask(dir, task) {
|
|
8467
|
+
writeFileSync(join2(dir, `${task.id}.json`), JSON.stringify(task, null, 2) + `
|
|
8468
|
+
`);
|
|
8469
|
+
}
|
|
8470
|
+
function toClaudeStatus(status) {
|
|
8471
|
+
if (status === "pending" || status === "in_progress" || status === "completed") {
|
|
8472
|
+
return status;
|
|
8473
|
+
}
|
|
8474
|
+
return "completed";
|
|
8475
|
+
}
|
|
8476
|
+
function toSqliteStatus(status) {
|
|
8477
|
+
return status;
|
|
8478
|
+
}
|
|
8479
|
+
function taskToClaudeTask(task, claudeTaskId) {
|
|
8480
|
+
return {
|
|
8481
|
+
id: claudeTaskId,
|
|
8482
|
+
subject: task.title,
|
|
8483
|
+
description: task.description || "",
|
|
8484
|
+
activeForm: "",
|
|
8485
|
+
status: toClaudeStatus(task.status),
|
|
8486
|
+
owner: task.assigned_to || task.agent_id || "",
|
|
8487
|
+
blocks: [],
|
|
8488
|
+
blockedBy: [],
|
|
8489
|
+
metadata: {
|
|
8490
|
+
todos_id: task.id,
|
|
8491
|
+
priority: task.priority
|
|
8492
|
+
}
|
|
8493
|
+
};
|
|
8494
|
+
}
|
|
8495
|
+
function pushToClaudeTaskList(taskListId, projectId) {
|
|
8496
|
+
const dir = getTaskListDir(taskListId);
|
|
8497
|
+
if (!existsSync2(dir))
|
|
8498
|
+
mkdirSync2(dir, { recursive: true });
|
|
8499
|
+
const filter = {};
|
|
8500
|
+
if (projectId)
|
|
8501
|
+
filter["project_id"] = projectId;
|
|
8502
|
+
const tasks = listTasks(filter);
|
|
8503
|
+
const existingByTodosId = new Map;
|
|
8504
|
+
const files = readdirSync(dir).filter((f) => f.endsWith(".json"));
|
|
8505
|
+
for (const f of files) {
|
|
8506
|
+
const ct = readClaudeTask(dir, f);
|
|
8507
|
+
if (ct?.metadata?.["todos_id"]) {
|
|
8508
|
+
existingByTodosId.set(ct.metadata["todos_id"], ct);
|
|
8509
|
+
}
|
|
8510
|
+
}
|
|
8511
|
+
let hwm = readHighWaterMark(dir);
|
|
8512
|
+
let pushed = 0;
|
|
8513
|
+
const errors = [];
|
|
8514
|
+
for (const task of tasks) {
|
|
8515
|
+
try {
|
|
8516
|
+
const existing = existingByTodosId.get(task.id);
|
|
8517
|
+
if (existing) {
|
|
8518
|
+
const updated = taskToClaudeTask(task, existing.id);
|
|
8519
|
+
updated.blocks = existing.blocks;
|
|
8520
|
+
updated.blockedBy = existing.blockedBy;
|
|
8521
|
+
updated.activeForm = existing.activeForm;
|
|
8522
|
+
writeClaudeTask(dir, updated);
|
|
8523
|
+
} else {
|
|
8524
|
+
const claudeId = String(hwm);
|
|
8525
|
+
hwm++;
|
|
8526
|
+
const ct = taskToClaudeTask(task, claudeId);
|
|
8527
|
+
writeClaudeTask(dir, ct);
|
|
8528
|
+
const current = getTask(task.id);
|
|
8529
|
+
if (current) {
|
|
8530
|
+
const newMeta = { ...current.metadata, claude_task_id: claudeId };
|
|
8531
|
+
updateTask(task.id, { version: current.version, metadata: newMeta });
|
|
8532
|
+
}
|
|
8533
|
+
}
|
|
8534
|
+
pushed++;
|
|
8535
|
+
} catch (e) {
|
|
8536
|
+
errors.push(`push ${task.id}: ${e instanceof Error ? e.message : String(e)}`);
|
|
8537
|
+
}
|
|
8538
|
+
}
|
|
8539
|
+
writeHighWaterMark(dir, hwm);
|
|
8540
|
+
return { pushed, pulled: 0, errors };
|
|
8541
|
+
}
|
|
8542
|
+
function pullFromClaudeTaskList(taskListId, projectId) {
|
|
8543
|
+
const dir = getTaskListDir(taskListId);
|
|
8544
|
+
if (!existsSync2(dir)) {
|
|
8545
|
+
return { pushed: 0, pulled: 0, errors: [`Task list directory not found: ${dir}`] };
|
|
8546
|
+
}
|
|
8547
|
+
const files = readdirSync(dir).filter((f) => f.endsWith(".json"));
|
|
8548
|
+
let pulled = 0;
|
|
8549
|
+
const errors = [];
|
|
8550
|
+
const filter = {};
|
|
8551
|
+
if (projectId)
|
|
8552
|
+
filter["project_id"] = projectId;
|
|
8553
|
+
const existingTasks = listTasks(filter);
|
|
8554
|
+
const byClaudeId = new Map;
|
|
8555
|
+
for (const t of existingTasks) {
|
|
8556
|
+
const cid = t.metadata["claude_task_id"];
|
|
8557
|
+
if (cid)
|
|
8558
|
+
byClaudeId.set(String(cid), t);
|
|
8559
|
+
}
|
|
8560
|
+
const byTodosId = new Map;
|
|
8561
|
+
for (const t of existingTasks) {
|
|
8562
|
+
byTodosId.set(t.id, t);
|
|
8563
|
+
}
|
|
8564
|
+
for (const f of files) {
|
|
8565
|
+
try {
|
|
8566
|
+
const ct = readClaudeTask(dir, f);
|
|
8567
|
+
if (!ct)
|
|
8568
|
+
continue;
|
|
8569
|
+
if (ct.metadata?.["_internal"])
|
|
8570
|
+
continue;
|
|
8571
|
+
const todosId = ct.metadata?.["todos_id"];
|
|
8572
|
+
const existingByMapping = byClaudeId.get(ct.id);
|
|
8573
|
+
const existingByTodos = todosId ? byTodosId.get(todosId) : undefined;
|
|
8574
|
+
const existing = existingByMapping || existingByTodos;
|
|
8575
|
+
if (existing) {
|
|
8576
|
+
updateTask(existing.id, {
|
|
8577
|
+
version: existing.version,
|
|
8578
|
+
title: ct.subject,
|
|
8579
|
+
description: ct.description || undefined,
|
|
8580
|
+
status: toSqliteStatus(ct.status),
|
|
8581
|
+
assigned_to: ct.owner || undefined,
|
|
8582
|
+
metadata: { ...existing.metadata, claude_task_id: ct.id }
|
|
8583
|
+
});
|
|
8584
|
+
} else {
|
|
8585
|
+
createTask({
|
|
8586
|
+
title: ct.subject,
|
|
8587
|
+
description: ct.description || undefined,
|
|
8588
|
+
status: toSqliteStatus(ct.status),
|
|
8589
|
+
assigned_to: ct.owner || undefined,
|
|
8590
|
+
project_id: projectId,
|
|
8591
|
+
metadata: { claude_task_id: ct.id },
|
|
8592
|
+
priority: ct.metadata?.["priority"] || "medium"
|
|
8593
|
+
});
|
|
8594
|
+
}
|
|
8595
|
+
pulled++;
|
|
8596
|
+
} catch (e) {
|
|
8597
|
+
errors.push(`pull ${f}: ${e instanceof Error ? e.message : String(e)}`);
|
|
8598
|
+
}
|
|
8599
|
+
}
|
|
8600
|
+
return { pushed: 0, pulled, errors };
|
|
8601
|
+
}
|
|
8602
|
+
function syncClaudeTaskList(taskListId, projectId) {
|
|
8603
|
+
const pullResult = pullFromClaudeTaskList(taskListId, projectId);
|
|
8604
|
+
const pushResult = pushToClaudeTaskList(taskListId, projectId);
|
|
8605
|
+
return {
|
|
8606
|
+
pushed: pushResult.pushed,
|
|
8607
|
+
pulled: pullResult.pulled,
|
|
8608
|
+
errors: [...pullResult.errors, ...pushResult.errors]
|
|
8609
|
+
};
|
|
8610
|
+
}
|
|
8611
|
+
|
|
8612
|
+
// src/cli/index.tsx
|
|
8439
8613
|
function getPackageVersion2() {
|
|
8440
8614
|
try {
|
|
8441
|
-
const pkgPath =
|
|
8442
|
-
return JSON.parse(
|
|
8615
|
+
const pkgPath = join4(dirname3(fileURLToPath2(import.meta.url)), "..", "..", "package.json");
|
|
8616
|
+
return JSON.parse(readFileSync3(pkgPath, "utf-8")).version || "0.0.0";
|
|
8443
8617
|
} catch {
|
|
8444
8618
|
return "0.0.0";
|
|
8445
8619
|
}
|
|
@@ -8909,81 +9083,187 @@ program2.command("export").description("Export tasks").option("-f, --format <for
|
|
|
8909
9083
|
console.log(JSON.stringify(tasks, null, 2));
|
|
8910
9084
|
}
|
|
8911
9085
|
});
|
|
8912
|
-
program2.command("
|
|
9086
|
+
program2.command("sync").description("Sync tasks with a Claude Code task list").option("--task-list <id>", "Claude Code task list ID (or env TODOS_CLAUDE_TASK_LIST)").option("--push", "One-way: push SQLite tasks to Claude task list").option("--pull", "One-way: pull Claude task list into SQLite").action((opts) => {
|
|
9087
|
+
const globalOpts = program2.opts();
|
|
9088
|
+
const projectId = autoProject(globalOpts);
|
|
9089
|
+
const taskListId = opts.taskList || process.env["TODOS_CLAUDE_TASK_LIST"];
|
|
9090
|
+
if (!taskListId) {
|
|
9091
|
+
console.error(chalk.red("Task list ID required. Use --task-list <id> or set TODOS_CLAUDE_TASK_LIST."));
|
|
9092
|
+
process.exit(1);
|
|
9093
|
+
}
|
|
9094
|
+
let result;
|
|
9095
|
+
if (opts.push && !opts.pull) {
|
|
9096
|
+
result = pushToClaudeTaskList(taskListId, projectId);
|
|
9097
|
+
} else if (opts.pull && !opts.push) {
|
|
9098
|
+
result = pullFromClaudeTaskList(taskListId, projectId);
|
|
9099
|
+
} else {
|
|
9100
|
+
result = syncClaudeTaskList(taskListId, projectId);
|
|
9101
|
+
}
|
|
9102
|
+
if (globalOpts.json) {
|
|
9103
|
+
output(result, true);
|
|
9104
|
+
return;
|
|
9105
|
+
}
|
|
9106
|
+
if (result.pulled > 0)
|
|
9107
|
+
console.log(chalk.green(`Pulled ${result.pulled} task(s) from Claude task list.`));
|
|
9108
|
+
if (result.pushed > 0)
|
|
9109
|
+
console.log(chalk.green(`Pushed ${result.pushed} task(s) to Claude task list.`));
|
|
9110
|
+
if (result.pulled === 0 && result.pushed === 0 && result.errors.length === 0) {
|
|
9111
|
+
console.log(chalk.dim("Nothing to sync."));
|
|
9112
|
+
}
|
|
9113
|
+
for (const err of result.errors) {
|
|
9114
|
+
console.error(chalk.red(` Error: ${err}`));
|
|
9115
|
+
}
|
|
9116
|
+
});
|
|
9117
|
+
var hooks = program2.command("hooks").description("Manage Claude Code hook integration");
|
|
9118
|
+
hooks.command("install").description("Install Claude Code hooks for auto-sync").option("--task-list <id>", "Claude Code task list ID (or env TODOS_CLAUDE_TASK_LIST)").action((opts) => {
|
|
9119
|
+
const taskListId = opts.taskList || process.env["TODOS_CLAUDE_TASK_LIST"];
|
|
9120
|
+
if (!taskListId) {
|
|
9121
|
+
console.error(chalk.red("Task list ID required. Use --task-list <id> or set TODOS_CLAUDE_TASK_LIST."));
|
|
9122
|
+
process.exit(1);
|
|
9123
|
+
}
|
|
9124
|
+
let todosBin = "todos";
|
|
9125
|
+
try {
|
|
9126
|
+
const p = execSync2("which todos", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
9127
|
+
if (p)
|
|
9128
|
+
todosBin = p;
|
|
9129
|
+
} catch {}
|
|
9130
|
+
const hooksDir = join4(process.cwd(), ".claude", "hooks");
|
|
9131
|
+
if (!existsSync4(hooksDir))
|
|
9132
|
+
mkdirSync3(hooksDir, { recursive: true });
|
|
9133
|
+
const hookScript = `#!/usr/bin/env bash
|
|
9134
|
+
# Auto-generated by: todos hooks install
|
|
9135
|
+
# Syncs todos with Claude Code task list on tool use events.
|
|
9136
|
+
# Reads hook JSON from stdin; determines sync direction from tool_name.
|
|
9137
|
+
|
|
9138
|
+
set -e
|
|
9139
|
+
|
|
9140
|
+
TASK_LIST="\${TODOS_CLAUDE_TASK_LIST:-${taskListId}}"
|
|
9141
|
+
TOOL_NAME=$(cat /dev/stdin | grep -o '"tool_name":"[^"]*"' | head -1 | cut -d'"' -f4 2>/dev/null || true)
|
|
9142
|
+
|
|
9143
|
+
case "$TOOL_NAME" in
|
|
9144
|
+
TaskCreate|TaskUpdate)
|
|
9145
|
+
${todosBin} sync --pull --task-list "$TASK_LIST" 2>/dev/null || true
|
|
9146
|
+
;;
|
|
9147
|
+
mcp__todos__*)
|
|
9148
|
+
${todosBin} sync --push --task-list "$TASK_LIST" 2>/dev/null || true
|
|
9149
|
+
;;
|
|
9150
|
+
*)
|
|
9151
|
+
# Unknown tool, run bidirectional sync
|
|
9152
|
+
${todosBin} sync --task-list "$TASK_LIST" 2>/dev/null || true
|
|
9153
|
+
;;
|
|
9154
|
+
esac
|
|
9155
|
+
|
|
9156
|
+
exit 0
|
|
9157
|
+
`;
|
|
9158
|
+
const hookPath = join4(hooksDir, "todos-sync.sh");
|
|
9159
|
+
writeFileSync2(hookPath, hookScript);
|
|
9160
|
+
execSync2(`chmod +x "${hookPath}"`);
|
|
9161
|
+
console.log(chalk.green(`Hook script created: ${hookPath}`));
|
|
9162
|
+
const settingsPath = join4(process.cwd(), ".claude", "settings.json");
|
|
9163
|
+
const settings = readJsonFile(settingsPath);
|
|
9164
|
+
if (!settings["hooks"]) {
|
|
9165
|
+
settings["hooks"] = {};
|
|
9166
|
+
}
|
|
9167
|
+
const hooksConfig = settings["hooks"];
|
|
9168
|
+
if (!hooksConfig["PostToolUse"]) {
|
|
9169
|
+
hooksConfig["PostToolUse"] = [];
|
|
9170
|
+
}
|
|
9171
|
+
const postToolUse = hooksConfig["PostToolUse"];
|
|
9172
|
+
const filtered = postToolUse.filter((h) => !(h["command"] || "").includes("todos-sync.sh"));
|
|
9173
|
+
filtered.push({
|
|
9174
|
+
matcher: "TaskCreate|TaskUpdate",
|
|
9175
|
+
command: hookPath
|
|
9176
|
+
});
|
|
9177
|
+
filtered.push({
|
|
9178
|
+
matcher: "mcp__todos__create_task|mcp__todos__update_task|mcp__todos__complete_task|mcp__todos__start_task",
|
|
9179
|
+
command: hookPath
|
|
9180
|
+
});
|
|
9181
|
+
hooksConfig["PostToolUse"] = filtered;
|
|
9182
|
+
writeJsonFile(settingsPath, settings);
|
|
9183
|
+
console.log(chalk.green(`Claude Code hooks configured in: ${settingsPath}`));
|
|
9184
|
+
console.log(chalk.dim(`Task list ID: ${taskListId}`));
|
|
9185
|
+
console.log(chalk.dim("Set TODOS_CLAUDE_TASK_LIST env var to override at runtime."));
|
|
9186
|
+
});
|
|
9187
|
+
program2.command("mcp").description("Start MCP server (stdio)").option("--register <agent>", "Register MCP server with an agent (claude, codex, gemini, all)").option("--unregister <agent>", "Unregister MCP server from an agent (claude, codex, gemini, all)").option("-g, --global", "Register/unregister globally (user-level) instead of project-level").action(async (opts) => {
|
|
8913
9188
|
if (opts.register) {
|
|
8914
|
-
registerMcp(opts.register);
|
|
9189
|
+
registerMcp(opts.register, opts.global);
|
|
8915
9190
|
return;
|
|
8916
9191
|
}
|
|
8917
9192
|
if (opts.unregister) {
|
|
8918
|
-
unregisterMcp(opts.unregister);
|
|
9193
|
+
unregisterMcp(opts.unregister, opts.global);
|
|
8919
9194
|
return;
|
|
8920
9195
|
}
|
|
8921
9196
|
await Promise.resolve().then(() => (init_mcp(), exports_mcp));
|
|
8922
9197
|
});
|
|
8923
|
-
var
|
|
9198
|
+
var HOME2 = process.env["HOME"] || process.env["USERPROFILE"] || "~";
|
|
8924
9199
|
function getMcpBinaryPath() {
|
|
8925
9200
|
try {
|
|
8926
9201
|
const p = execSync2("which todos-mcp", { encoding: "utf-8" }).trim();
|
|
8927
9202
|
if (p)
|
|
8928
9203
|
return p;
|
|
8929
9204
|
} catch {}
|
|
8930
|
-
const bunBin =
|
|
8931
|
-
if (
|
|
9205
|
+
const bunBin = join4(HOME2, ".bun", "bin", "todos-mcp");
|
|
9206
|
+
if (existsSync4(bunBin))
|
|
8932
9207
|
return bunBin;
|
|
8933
9208
|
return "todos-mcp";
|
|
8934
9209
|
}
|
|
8935
9210
|
function readJsonFile(path) {
|
|
8936
|
-
if (!
|
|
9211
|
+
if (!existsSync4(path))
|
|
8937
9212
|
return {};
|
|
8938
9213
|
try {
|
|
8939
|
-
return JSON.parse(
|
|
9214
|
+
return JSON.parse(readFileSync3(path, "utf-8"));
|
|
8940
9215
|
} catch {
|
|
8941
9216
|
return {};
|
|
8942
9217
|
}
|
|
8943
9218
|
}
|
|
8944
9219
|
function writeJsonFile(path, data) {
|
|
8945
9220
|
const dir = dirname3(path);
|
|
8946
|
-
if (!
|
|
8947
|
-
|
|
8948
|
-
|
|
9221
|
+
if (!existsSync4(dir))
|
|
9222
|
+
mkdirSync3(dir, { recursive: true });
|
|
9223
|
+
writeFileSync2(path, JSON.stringify(data, null, 2) + `
|
|
8949
9224
|
`);
|
|
8950
9225
|
}
|
|
8951
9226
|
function readTomlFile(path) {
|
|
8952
|
-
if (!
|
|
9227
|
+
if (!existsSync4(path))
|
|
8953
9228
|
return "";
|
|
8954
|
-
return
|
|
9229
|
+
return readFileSync3(path, "utf-8");
|
|
8955
9230
|
}
|
|
8956
9231
|
function writeTomlFile(path, content) {
|
|
8957
9232
|
const dir = dirname3(path);
|
|
8958
|
-
if (!
|
|
8959
|
-
|
|
8960
|
-
|
|
9233
|
+
if (!existsSync4(dir))
|
|
9234
|
+
mkdirSync3(dir, { recursive: true });
|
|
9235
|
+
writeFileSync2(path, content);
|
|
8961
9236
|
}
|
|
8962
|
-
function registerClaude(binPath) {
|
|
8963
|
-
const
|
|
8964
|
-
const configPath = join3(cwd, ".mcp.json");
|
|
9237
|
+
function registerClaude(binPath, global) {
|
|
9238
|
+
const configPath = global ? join4(HOME2, ".claude", ".mcp.json") : join4(process.cwd(), ".mcp.json");
|
|
8965
9239
|
const config = readJsonFile(configPath);
|
|
8966
|
-
config["
|
|
9240
|
+
if (!config["mcpServers"]) {
|
|
9241
|
+
config["mcpServers"] = {};
|
|
9242
|
+
}
|
|
9243
|
+
const servers = config["mcpServers"];
|
|
9244
|
+
servers["todos"] = {
|
|
8967
9245
|
command: binPath,
|
|
8968
9246
|
args: []
|
|
8969
9247
|
};
|
|
8970
9248
|
writeJsonFile(configPath, config);
|
|
8971
|
-
|
|
9249
|
+
const scope = global ? "global" : "project";
|
|
9250
|
+
console.log(chalk.green(`Claude Code (${scope}): registered in ${configPath}`));
|
|
8972
9251
|
}
|
|
8973
|
-
function unregisterClaude() {
|
|
8974
|
-
const
|
|
8975
|
-
const configPath = join3(cwd, ".mcp.json");
|
|
9252
|
+
function unregisterClaude(global) {
|
|
9253
|
+
const configPath = global ? join4(HOME2, ".claude", ".mcp.json") : join4(process.cwd(), ".mcp.json");
|
|
8976
9254
|
const config = readJsonFile(configPath);
|
|
8977
|
-
|
|
9255
|
+
const servers = config["mcpServers"];
|
|
9256
|
+
if (!servers || !("todos" in servers)) {
|
|
8978
9257
|
console.log(chalk.dim(`Claude Code: todos not found in ${configPath}`));
|
|
8979
9258
|
return;
|
|
8980
9259
|
}
|
|
8981
|
-
delete
|
|
9260
|
+
delete servers["todos"];
|
|
8982
9261
|
writeJsonFile(configPath, config);
|
|
8983
|
-
|
|
9262
|
+
const scope = global ? "global" : "project";
|
|
9263
|
+
console.log(chalk.green(`Claude Code (${scope}): unregistered from ${configPath}`));
|
|
8984
9264
|
}
|
|
8985
9265
|
function registerCodex(binPath) {
|
|
8986
|
-
const configPath =
|
|
9266
|
+
const configPath = join4(HOME2, ".codex", "config.toml");
|
|
8987
9267
|
let content = readTomlFile(configPath);
|
|
8988
9268
|
content = removeTomlBlock(content, "mcp_servers.todos");
|
|
8989
9269
|
const block = `
|
|
@@ -8997,7 +9277,7 @@ args = []
|
|
|
8997
9277
|
console.log(chalk.green(`Codex CLI: registered in ${configPath}`));
|
|
8998
9278
|
}
|
|
8999
9279
|
function unregisterCodex() {
|
|
9000
|
-
const configPath =
|
|
9280
|
+
const configPath = join4(HOME2, ".codex", "config.toml");
|
|
9001
9281
|
let content = readTomlFile(configPath);
|
|
9002
9282
|
if (!content.includes("[mcp_servers.todos]")) {
|
|
9003
9283
|
console.log(chalk.dim(`Codex CLI: todos not found in ${configPath}`));
|
|
@@ -9030,7 +9310,7 @@ function removeTomlBlock(content, blockName) {
|
|
|
9030
9310
|
`);
|
|
9031
9311
|
}
|
|
9032
9312
|
function registerGemini(binPath) {
|
|
9033
|
-
const configPath =
|
|
9313
|
+
const configPath = join4(HOME2, ".gemini", "settings.json");
|
|
9034
9314
|
const config = readJsonFile(configPath);
|
|
9035
9315
|
if (!config["mcpServers"]) {
|
|
9036
9316
|
config["mcpServers"] = {};
|
|
@@ -9044,7 +9324,7 @@ function registerGemini(binPath) {
|
|
|
9044
9324
|
console.log(chalk.green(`Gemini CLI: registered in ${configPath}`));
|
|
9045
9325
|
}
|
|
9046
9326
|
function unregisterGemini() {
|
|
9047
|
-
const configPath =
|
|
9327
|
+
const configPath = join4(HOME2, ".gemini", "settings.json");
|
|
9048
9328
|
const config = readJsonFile(configPath);
|
|
9049
9329
|
const servers = config["mcpServers"];
|
|
9050
9330
|
if (!servers || !("todos" in servers)) {
|
|
@@ -9055,13 +9335,13 @@ function unregisterGemini() {
|
|
|
9055
9335
|
writeJsonFile(configPath, config);
|
|
9056
9336
|
console.log(chalk.green(`Gemini CLI: unregistered from ${configPath}`));
|
|
9057
9337
|
}
|
|
9058
|
-
function registerMcp(agent) {
|
|
9338
|
+
function registerMcp(agent, global) {
|
|
9059
9339
|
const agents = agent === "all" ? ["claude", "codex", "gemini"] : [agent];
|
|
9060
9340
|
const binPath = getMcpBinaryPath();
|
|
9061
9341
|
for (const a of agents) {
|
|
9062
9342
|
switch (a) {
|
|
9063
9343
|
case "claude":
|
|
9064
|
-
registerClaude(binPath);
|
|
9344
|
+
registerClaude(binPath, global);
|
|
9065
9345
|
break;
|
|
9066
9346
|
case "codex":
|
|
9067
9347
|
registerCodex(binPath);
|
|
@@ -9074,12 +9354,12 @@ function registerMcp(agent) {
|
|
|
9074
9354
|
}
|
|
9075
9355
|
}
|
|
9076
9356
|
}
|
|
9077
|
-
function unregisterMcp(agent) {
|
|
9357
|
+
function unregisterMcp(agent, global) {
|
|
9078
9358
|
const agents = agent === "all" ? ["claude", "codex", "gemini"] : [agent];
|
|
9079
9359
|
for (const a of agents) {
|
|
9080
9360
|
switch (a) {
|
|
9081
9361
|
case "claude":
|
|
9082
|
-
unregisterClaude();
|
|
9362
|
+
unregisterClaude(global);
|
|
9083
9363
|
break;
|
|
9084
9364
|
case "codex":
|
|
9085
9365
|
unregisterCodex();
|