@hasna/todos 0.9.52 → 0.9.54

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli/index.js CHANGED
@@ -9154,6 +9154,18 @@ var init_zod = __esm(() => {
9154
9154
  var exports_mcp = {};
9155
9155
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
9156
9156
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
9157
+ import { readFileSync as readFileSync3 } from "fs";
9158
+ import { join as join6, dirname as dirname2 } from "path";
9159
+ import { fileURLToPath } from "url";
9160
+ function getMcpVersion() {
9161
+ try {
9162
+ const __dir = dirname2(fileURLToPath(import.meta.url));
9163
+ const pkgPath = join6(__dir, "..", "package.json");
9164
+ return JSON.parse(readFileSync3(pkgPath, "utf-8")).version || "0.0.0";
9165
+ } catch {
9166
+ return "0.0.0";
9167
+ }
9168
+ }
9157
9169
  function shouldRegisterTool(name) {
9158
9170
  if (TODOS_PROFILE === "minimal")
9159
9171
  return MINIMAL_TOOLS.has(name);
@@ -9273,7 +9285,7 @@ var init_mcp = __esm(() => {
9273
9285
  init_types();
9274
9286
  server = new McpServer({
9275
9287
  name: "todos",
9276
- version: "0.9.35"
9288
+ version: getMcpVersion()
9277
9289
  });
9278
9290
  TODOS_PROFILE = (process.env["TODOS_PROFILE"] || "full").toLowerCase();
9279
9291
  MINIMAL_TOOLS = new Set([
@@ -10331,8 +10343,15 @@ Retry task created: ${formatTask(result.retryTask)}`;
10331
10343
  }, async ({ agent_name }) => {
10332
10344
  try {
10333
10345
  const agent = registerAgent({ name: agent_name });
10334
- const tasks = listTasks({});
10335
- const myTasks = tasks.filter((t) => t.assigned_to === agent_name || t.assigned_to === agent.id || t.agent_id === agent.id || t.agent_id === agent_name);
10346
+ const byName = listTasks({ assigned_to: agent_name });
10347
+ const byId = listTasks({ agent_id: agent.id });
10348
+ const seen = new Set;
10349
+ const myTasks = [...byName, ...byId].filter((t) => {
10350
+ if (seen.has(t.id))
10351
+ return false;
10352
+ seen.add(t.id);
10353
+ return true;
10354
+ });
10336
10355
  const pending = myTasks.filter((t) => t.status === "pending");
10337
10356
  const inProgress = myTasks.filter((t) => t.status === "in_progress");
10338
10357
  const completed = myTasks.filter((t) => t.status === "completed");
@@ -11222,26 +11241,26 @@ __export(exports_serve, {
11222
11241
  startServer: () => startServer
11223
11242
  });
11224
11243
  import { existsSync as existsSync6 } from "fs";
11225
- import { join as join6, dirname as dirname2, extname, resolve as resolve2, sep } from "path";
11226
- import { fileURLToPath } from "url";
11244
+ import { join as join7, dirname as dirname3, extname, resolve as resolve2, sep } from "path";
11245
+ import { fileURLToPath as fileURLToPath2 } from "url";
11227
11246
  function resolveDashboardDir() {
11228
11247
  const candidates = [];
11229
11248
  try {
11230
- const scriptDir = dirname2(fileURLToPath(import.meta.url));
11231
- candidates.push(join6(scriptDir, "..", "dashboard", "dist"));
11232
- candidates.push(join6(scriptDir, "..", "..", "dashboard", "dist"));
11249
+ const scriptDir = dirname3(fileURLToPath2(import.meta.url));
11250
+ candidates.push(join7(scriptDir, "..", "dashboard", "dist"));
11251
+ candidates.push(join7(scriptDir, "..", "..", "dashboard", "dist"));
11233
11252
  } catch {}
11234
11253
  if (process.argv[1]) {
11235
- const mainDir = dirname2(process.argv[1]);
11236
- candidates.push(join6(mainDir, "..", "dashboard", "dist"));
11237
- candidates.push(join6(mainDir, "..", "..", "dashboard", "dist"));
11254
+ const mainDir = dirname3(process.argv[1]);
11255
+ candidates.push(join7(mainDir, "..", "dashboard", "dist"));
11256
+ candidates.push(join7(mainDir, "..", "..", "dashboard", "dist"));
11238
11257
  }
11239
- candidates.push(join6(process.cwd(), "dashboard", "dist"));
11258
+ candidates.push(join7(process.cwd(), "dashboard", "dist"));
11240
11259
  for (const candidate of candidates) {
11241
11260
  if (existsSync6(candidate))
11242
11261
  return candidate;
11243
11262
  }
11244
- return join6(process.cwd(), "dashboard", "dist");
11263
+ return join7(process.cwd(), "dashboard", "dist");
11245
11264
  }
11246
11265
  function json(data, status = 200, port) {
11247
11266
  return new Response(JSON.stringify(data), {
@@ -11641,6 +11660,19 @@ data: ${JSON.stringify({ type: "connected", agent_id: agentId, timestamp: new Da
11641
11660
  return json({ error: e instanceof Error ? e.message : "Failed to start task" }, 500, port);
11642
11661
  }
11643
11662
  }
11663
+ const failMatch = path.match(/^\/api\/tasks\/([^/]+)\/fail$/);
11664
+ if (failMatch && method === "POST") {
11665
+ const id = failMatch[1];
11666
+ try {
11667
+ const body = await req.json().catch(() => ({}));
11668
+ const { failTask: failTask2 } = await Promise.resolve().then(() => (init_tasks(), exports_tasks));
11669
+ const result = failTask2(id, body.agent_id, body.reason, { retry: body.retry, error_code: body.error_code });
11670
+ broadcastEvent({ type: "task", task_id: id, action: "failed", agent_id: body.agent_id || null });
11671
+ return json({ task: taskToSummary(result.task), retry_task: result.retryTask ? taskToSummary(result.retryTask) : null }, 200, port);
11672
+ } catch (e) {
11673
+ return json({ error: e instanceof Error ? e.message : "Failed to fail task" }, 500, port);
11674
+ }
11675
+ }
11644
11676
  const completeMatch = path.match(/^\/api\/tasks\/([^/]+)\/complete$/);
11645
11677
  if (completeMatch && method === "POST") {
11646
11678
  const id = completeMatch[1];
@@ -11953,7 +11985,7 @@ data: ${JSON.stringify({ type: "connected", agent_id: agentId, timestamp: new Da
11953
11985
  }
11954
11986
  if (dashboardExists && (method === "GET" || method === "HEAD")) {
11955
11987
  if (path !== "/") {
11956
- const filePath = join6(dashboardDir, path);
11988
+ const filePath = join7(dashboardDir, path);
11957
11989
  const resolvedFile = resolve2(filePath);
11958
11990
  const resolvedBase = resolve2(dashboardDir);
11959
11991
  if (!resolvedFile.startsWith(resolvedBase + sep) && resolvedFile !== resolvedBase) {
@@ -11963,7 +11995,7 @@ data: ${JSON.stringify({ type: "connected", agent_id: agentId, timestamp: new Da
11963
11995
  if (res2)
11964
11996
  return res2;
11965
11997
  }
11966
- const indexPath = join6(dashboardDir, "index.html");
11998
+ const indexPath = join7(dashboardDir, "index.html");
11967
11999
  const res = serveStaticFile(indexPath);
11968
12000
  if (res)
11969
12001
  return res;
@@ -13098,13 +13130,13 @@ init_sync();
13098
13130
  init_config();
13099
13131
  import chalk from "chalk";
13100
13132
  import { execSync } from "child_process";
13101
- import { existsSync as existsSync7, mkdirSync as mkdirSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
13102
- import { basename, dirname as dirname3, join as join7, resolve as resolve3 } from "path";
13103
- import { fileURLToPath as fileURLToPath2 } from "url";
13133
+ import { existsSync as existsSync7, mkdirSync as mkdirSync3, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
13134
+ import { basename, dirname as dirname4, join as join8, resolve as resolve3 } from "path";
13135
+ import { fileURLToPath as fileURLToPath3 } from "url";
13104
13136
  function getPackageVersion() {
13105
13137
  try {
13106
- const pkgPath = join7(dirname3(fileURLToPath2(import.meta.url)), "..", "..", "package.json");
13107
- return JSON.parse(readFileSync3(pkgPath, "utf-8")).version || "0.0.0";
13138
+ const pkgPath = join8(dirname4(fileURLToPath3(import.meta.url)), "..", "..", "package.json");
13139
+ return JSON.parse(readFileSync4(pkgPath, "utf-8")).version || "0.0.0";
13108
13140
  } catch {
13109
13141
  return "0.0.0";
13110
13142
  }
@@ -14009,7 +14041,7 @@ hooks.command("install").description("Install Claude Code hooks for auto-sync").
14009
14041
  if (p)
14010
14042
  todosBin = p;
14011
14043
  } catch {}
14012
- const hooksDir = join7(process.cwd(), ".claude", "hooks");
14044
+ const hooksDir = join8(process.cwd(), ".claude", "hooks");
14013
14045
  if (!existsSync7(hooksDir))
14014
14046
  mkdirSync3(hooksDir, { recursive: true });
14015
14047
  const hookScript = `#!/usr/bin/env bash
@@ -14035,11 +14067,11 @@ esac
14035
14067
 
14036
14068
  exit 0
14037
14069
  `;
14038
- const hookPath = join7(hooksDir, "todos-sync.sh");
14070
+ const hookPath = join8(hooksDir, "todos-sync.sh");
14039
14071
  writeFileSync3(hookPath, hookScript);
14040
14072
  execSync(`chmod +x "${hookPath}"`);
14041
14073
  console.log(chalk.green(`Hook script created: ${hookPath}`));
14042
- const settingsPath = join7(process.cwd(), ".claude", "settings.json");
14074
+ const settingsPath = join8(process.cwd(), ".claude", "settings.json");
14043
14075
  const settings = readJsonFile2(settingsPath);
14044
14076
  if (!settings["hooks"]) {
14045
14077
  settings["hooks"] = {};
@@ -14086,7 +14118,7 @@ function getMcpBinaryPath() {
14086
14118
  if (p)
14087
14119
  return p;
14088
14120
  } catch {}
14089
- const bunBin = join7(HOME2, ".bun", "bin", "todos-mcp");
14121
+ const bunBin = join8(HOME2, ".bun", "bin", "todos-mcp");
14090
14122
  if (existsSync7(bunBin))
14091
14123
  return bunBin;
14092
14124
  return "todos-mcp";
@@ -14095,13 +14127,13 @@ function readJsonFile2(path) {
14095
14127
  if (!existsSync7(path))
14096
14128
  return {};
14097
14129
  try {
14098
- return JSON.parse(readFileSync3(path, "utf-8"));
14130
+ return JSON.parse(readFileSync4(path, "utf-8"));
14099
14131
  } catch {
14100
14132
  return {};
14101
14133
  }
14102
14134
  }
14103
14135
  function writeJsonFile2(path, data) {
14104
- const dir = dirname3(path);
14136
+ const dir = dirname4(path);
14105
14137
  if (!existsSync7(dir))
14106
14138
  mkdirSync3(dir, { recursive: true });
14107
14139
  writeFileSync3(path, JSON.stringify(data, null, 2) + `
@@ -14110,10 +14142,10 @@ function writeJsonFile2(path, data) {
14110
14142
  function readTomlFile(path) {
14111
14143
  if (!existsSync7(path))
14112
14144
  return "";
14113
- return readFileSync3(path, "utf-8");
14145
+ return readFileSync4(path, "utf-8");
14114
14146
  }
14115
14147
  function writeTomlFile(path, content) {
14116
- const dir = dirname3(path);
14148
+ const dir = dirname4(path);
14117
14149
  if (!existsSync7(dir))
14118
14150
  mkdirSync3(dir, { recursive: true });
14119
14151
  writeFileSync3(path, content);
@@ -14141,7 +14173,7 @@ function unregisterClaude(_global) {
14141
14173
  }
14142
14174
  }
14143
14175
  function registerCodex(binPath) {
14144
- const configPath = join7(HOME2, ".codex", "config.toml");
14176
+ const configPath = join8(HOME2, ".codex", "config.toml");
14145
14177
  let content = readTomlFile(configPath);
14146
14178
  content = removeTomlBlock(content, "mcp_servers.todos");
14147
14179
  const block = `
@@ -14155,7 +14187,7 @@ args = []
14155
14187
  console.log(chalk.green(`Codex CLI: registered in ${configPath}`));
14156
14188
  }
14157
14189
  function unregisterCodex() {
14158
- const configPath = join7(HOME2, ".codex", "config.toml");
14190
+ const configPath = join8(HOME2, ".codex", "config.toml");
14159
14191
  let content = readTomlFile(configPath);
14160
14192
  if (!content.includes("[mcp_servers.todos]")) {
14161
14193
  console.log(chalk.dim(`Codex CLI: todos not found in ${configPath}`));
@@ -14188,7 +14220,7 @@ function removeTomlBlock(content, blockName) {
14188
14220
  `);
14189
14221
  }
14190
14222
  function registerGemini(binPath) {
14191
- const configPath = join7(HOME2, ".gemini", "settings.json");
14223
+ const configPath = join8(HOME2, ".gemini", "settings.json");
14192
14224
  const config = readJsonFile2(configPath);
14193
14225
  if (!config["mcpServers"]) {
14194
14226
  config["mcpServers"] = {};
@@ -14202,7 +14234,7 @@ function registerGemini(binPath) {
14202
14234
  console.log(chalk.green(`Gemini CLI: registered in ${configPath}`));
14203
14235
  }
14204
14236
  function unregisterGemini() {
14205
- const configPath = join7(HOME2, ".gemini", "settings.json");
14237
+ const configPath = join8(HOME2, ".gemini", "settings.json");
14206
14238
  const config = readJsonFile2(configPath);
14207
14239
  const servers = config["mcpServers"];
14208
14240
  if (!servers || !("todos" in servers)) {
@@ -14415,7 +14447,7 @@ Updated to ${latestVersion}!`));
14415
14447
  });
14416
14448
  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) => {
14417
14449
  const globalOpts = program2.opts();
14418
- const configPath = join7(process.env["HOME"] || "~", ".todos", "config.json");
14450
+ const configPath = join8(process.env["HOME"] || "~", ".todos", "config.json");
14419
14451
  if (opts.get) {
14420
14452
  const config2 = loadConfig();
14421
14453
  const keys = opts.get.split(".");
@@ -14441,7 +14473,7 @@ program2.command("config").description("View or update configuration").option("-
14441
14473
  }
14442
14474
  let config2 = {};
14443
14475
  try {
14444
- config2 = JSON.parse(readFileSync3(configPath, "utf-8"));
14476
+ config2 = JSON.parse(readFileSync4(configPath, "utf-8"));
14445
14477
  } catch {}
14446
14478
  const keys = key.split(".");
14447
14479
  let obj = config2;
@@ -14451,7 +14483,7 @@ program2.command("config").description("View or update configuration").option("-
14451
14483
  obj = obj[keys[i]];
14452
14484
  }
14453
14485
  obj[keys[keys.length - 1]] = parsedValue;
14454
- const dir = dirname3(configPath);
14486
+ const dir = dirname4(configPath);
14455
14487
  if (!existsSync7(dir))
14456
14488
  mkdirSync3(dir, { recursive: true });
14457
14489
  writeFileSync3(configPath, JSON.stringify(config2, null, 2));
package/dist/mcp/index.js CHANGED
@@ -6946,9 +6946,21 @@ function syncWithAgents(agents, taskListIdByAgent, projectId, direction = "both"
6946
6946
 
6947
6947
  // src/mcp/index.ts
6948
6948
  init_database();
6949
+ import { readFileSync as readFileSync3 } from "fs";
6950
+ import { join as join6, dirname as dirname2 } from "path";
6951
+ import { fileURLToPath } from "url";
6952
+ function getMcpVersion() {
6953
+ try {
6954
+ const __dir = dirname2(fileURLToPath(import.meta.url));
6955
+ const pkgPath = join6(__dir, "..", "package.json");
6956
+ return JSON.parse(readFileSync3(pkgPath, "utf-8")).version || "0.0.0";
6957
+ } catch {
6958
+ return "0.0.0";
6959
+ }
6960
+ }
6949
6961
  var server = new McpServer({
6950
6962
  name: "todos",
6951
- version: "0.9.35"
6963
+ version: getMcpVersion()
6952
6964
  });
6953
6965
  var TODOS_PROFILE = (process.env["TODOS_PROFILE"] || "full").toLowerCase();
6954
6966
  var MINIMAL_TOOLS = new Set([
@@ -8105,8 +8117,15 @@ if (shouldRegisterTool("get_my_tasks")) {
8105
8117
  }, async ({ agent_name }) => {
8106
8118
  try {
8107
8119
  const agent = registerAgent({ name: agent_name });
8108
- const tasks = listTasks({});
8109
- const myTasks = tasks.filter((t) => t.assigned_to === agent_name || t.assigned_to === agent.id || t.agent_id === agent.id || t.agent_id === agent_name);
8120
+ const byName = listTasks({ assigned_to: agent_name });
8121
+ const byId = listTasks({ agent_id: agent.id });
8122
+ const seen = new Set;
8123
+ const myTasks = [...byName, ...byId].filter((t) => {
8124
+ if (seen.has(t.id))
8125
+ return false;
8126
+ seen.add(t.id);
8127
+ return true;
8128
+ });
8110
8129
  const pending = myTasks.filter((t) => t.status === "pending");
8111
8130
  const inProgress = myTasks.filter((t) => t.status === "in_progress");
8112
8131
  const completed = myTasks.filter((t) => t.status === "completed");
@@ -2855,6 +2855,19 @@ data: ${JSON.stringify({ type: "connected", agent_id: agentId, timestamp: new Da
2855
2855
  return json({ error: e instanceof Error ? e.message : "Failed to start task" }, 500, port);
2856
2856
  }
2857
2857
  }
2858
+ const failMatch = path.match(/^\/api\/tasks\/([^/]+)\/fail$/);
2859
+ if (failMatch && method === "POST") {
2860
+ const id = failMatch[1];
2861
+ try {
2862
+ const body = await req.json().catch(() => ({}));
2863
+ const { failTask: failTask2 } = await Promise.resolve().then(() => (init_tasks(), exports_tasks));
2864
+ const result = failTask2(id, body.agent_id, body.reason, { retry: body.retry, error_code: body.error_code });
2865
+ broadcastEvent({ type: "task", task_id: id, action: "failed", agent_id: body.agent_id || null });
2866
+ return json({ task: taskToSummary(result.task), retry_task: result.retryTask ? taskToSummary(result.retryTask) : null }, 200, port);
2867
+ } catch (e) {
2868
+ return json({ error: e instanceof Error ? e.message : "Failed to fail task" }, 500, port);
2869
+ }
2870
+ }
2858
2871
  const completeMatch = path.match(/^\/api\/tasks\/([^/]+)\/complete$/);
2859
2872
  if (completeMatch && method === "POST") {
2860
2873
  const id = completeMatch[1];
@@ -1 +1 @@
1
- {"version":3,"file":"serve.d.ts","sourceRoot":"","sources":["../../src/server/serve.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAiHH,wBAAsB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;IAAE,IAAI,CAAC,EAAE,OAAO,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CA6vB1G"}
1
+ {"version":3,"file":"serve.d.ts","sourceRoot":"","sources":["../../src/server/serve.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAiHH,wBAAsB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;IAAE,IAAI,CAAC,EAAE,OAAO,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CA4wB1G"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/todos",
3
- "version": "0.9.52",
3
+ "version": "0.9.54",
4
4
  "description": "Universal task management for AI coding agents - CLI + MCP server + interactive TUI",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",