@hasna/todos 0.3.1 → 0.3.3

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
@@ -6688,7 +6688,12 @@ var init_mcp = __esm(() => {
6688
6688
  metadata: exports_external.record(exports_external.unknown()).optional().describe("Arbitrary metadata")
6689
6689
  }, async (params) => {
6690
6690
  try {
6691
- const task = createTask(params);
6691
+ const resolved = { ...params };
6692
+ if (resolved.project_id)
6693
+ resolved.project_id = resolveId(resolved.project_id, "projects");
6694
+ if (resolved.parent_id)
6695
+ resolved.parent_id = resolveId(resolved.parent_id);
6696
+ const task = createTask(resolved);
6692
6697
  return { content: [{ type: "text", text: `Task created:
6693
6698
  ${formatTask(task)}` }] };
6694
6699
  } catch (e) {
@@ -6709,7 +6714,10 @@ ${formatTask(task)}` }] };
6709
6714
  tags: exports_external.array(exports_external.string()).optional().describe("Filter by tags (any match)")
6710
6715
  }, async (params) => {
6711
6716
  try {
6712
- const tasks = listTasks(params);
6717
+ const resolved = { ...params };
6718
+ if (resolved.project_id)
6719
+ resolved.project_id = resolveId(resolved.project_id, "projects");
6720
+ const tasks = listTasks(resolved);
6713
6721
  if (tasks.length === 0) {
6714
6722
  return { content: [{ type: "text", text: "No tasks found." }] };
6715
6723
  }
@@ -6943,7 +6951,8 @@ ${text}` }] };
6943
6951
  project_id: exports_external.string().optional().describe("Limit to project")
6944
6952
  }, async ({ query, project_id }) => {
6945
6953
  try {
6946
- const tasks = searchTasks(query, project_id);
6954
+ const resolvedProjectId = project_id ? resolveId(project_id, "projects") : undefined;
6955
+ const tasks = searchTasks(query, resolvedProjectId);
6947
6956
  if (tasks.length === 0) {
6948
6957
  return { content: [{ type: "text", text: `No tasks matching "${query}".` }] };
6949
6958
  }
@@ -9080,12 +9089,15 @@ program2.command("export").description("Export tasks").option("-f, --format <for
9080
9089
  console.log(JSON.stringify(tasks, null, 2));
9081
9090
  }
9082
9091
  });
9083
- 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) => {
9092
+ function resolveClaudeTaskListId(explicit) {
9093
+ return explicit || process.env["TODOS_CLAUDE_TASK_LIST"] || process.env["CLAUDE_CODE_TASK_LIST_ID"] || process.env["CLAUDE_CODE_SESSION_ID"] || null;
9094
+ }
9095
+ program2.command("sync").description("Sync tasks with a Claude Code task list").option("--task-list <id>", "Task list ID (auto-detects from CLAUDE_CODE_TASK_LIST_ID or CLAUDE_CODE_SESSION_ID)").option("--push", "One-way: push SQLite tasks to Claude task list").option("--pull", "One-way: pull Claude task list into SQLite").action((opts) => {
9084
9096
  const globalOpts = program2.opts();
9085
9097
  const projectId = autoProject(globalOpts);
9086
- const taskListId = opts.taskList || process.env["TODOS_CLAUDE_TASK_LIST"];
9098
+ const taskListId = resolveClaudeTaskListId(opts.taskList);
9087
9099
  if (!taskListId) {
9088
- console.error(chalk.red("Task list ID required. Use --task-list <id> or set TODOS_CLAUDE_TASK_LIST."));
9100
+ console.error(chalk.red("Could not detect task list ID. Use --task-list <id>, or run inside a Claude Code session."));
9089
9101
  process.exit(1);
9090
9102
  }
9091
9103
  let result;
@@ -9112,12 +9124,7 @@ program2.command("sync").description("Sync tasks with a Claude Code task list").
9112
9124
  }
9113
9125
  });
9114
9126
  var hooks = program2.command("hooks").description("Manage Claude Code hook integration");
9115
- 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) => {
9116
- const taskListId = opts.taskList || process.env["TODOS_CLAUDE_TASK_LIST"];
9117
- if (!taskListId) {
9118
- console.error(chalk.red("Task list ID required. Use --task-list <id> or set TODOS_CLAUDE_TASK_LIST."));
9119
- process.exit(1);
9120
- }
9127
+ hooks.command("install").description("Install Claude Code hooks for auto-sync").action(() => {
9121
9128
  let todosBin = "todos";
9122
9129
  try {
9123
9130
  const p = execSync2("which todos", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
@@ -9130,11 +9137,17 @@ hooks.command("install").description("Install Claude Code hooks for auto-sync").
9130
9137
  const hookScript = `#!/usr/bin/env bash
9131
9138
  # Auto-generated by: todos hooks install
9132
9139
  # Syncs todos with Claude Code task list on tool use events.
9133
- # Reads hook JSON from stdin; determines sync direction from tool_name.
9140
+ # Auto-detects task list ID from Claude Code env vars.
9134
9141
 
9135
9142
  set -e
9136
9143
 
9137
- TASK_LIST="\${TODOS_CLAUDE_TASK_LIST:-${taskListId}}"
9144
+ # Auto-detect: explicit override > Claude task list > Claude session ID
9145
+ TASK_LIST="\${TODOS_CLAUDE_TASK_LIST:-\${CLAUDE_CODE_TASK_LIST_ID:-\${CLAUDE_CODE_SESSION_ID:-}}}"
9146
+
9147
+ if [ -z "$TASK_LIST" ]; then
9148
+ exit 0
9149
+ fi
9150
+
9138
9151
  TOOL_NAME=$(cat /dev/stdin | grep -o '"tool_name":"[^"]*"' | head -1 | cut -d'"' -f4 2>/dev/null || true)
9139
9152
 
9140
9153
  case "$TOOL_NAME" in
@@ -9144,10 +9157,6 @@ case "$TOOL_NAME" in
9144
9157
  mcp__todos__*)
9145
9158
  ${todosBin} sync --push --task-list "$TASK_LIST" 2>/dev/null || true
9146
9159
  ;;
9147
- *)
9148
- # Unknown tool, run bidirectional sync
9149
- ${todosBin} sync --task-list "$TASK_LIST" 2>/dev/null || true
9150
- ;;
9151
9160
  esac
9152
9161
 
9153
9162
  exit 0
@@ -9178,8 +9187,7 @@ exit 0
9178
9187
  hooksConfig["PostToolUse"] = filtered;
9179
9188
  writeJsonFile(settingsPath, settings);
9180
9189
  console.log(chalk.green(`Claude Code hooks configured in: ${settingsPath}`));
9181
- console.log(chalk.dim(`Task list ID: ${taskListId}`));
9182
- console.log(chalk.dim("Set TODOS_CLAUDE_TASK_LIST env var to override at runtime."));
9190
+ console.log(chalk.dim("Task list ID auto-detected from CLAUDE_CODE_TASK_LIST_ID or CLAUDE_CODE_SESSION_ID."));
9183
9191
  });
9184
9192
  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) => {
9185
9193
  if (opts.register) {
package/dist/mcp/index.js CHANGED
@@ -4597,7 +4597,12 @@ server.tool("create_task", "Create a new task", {
4597
4597
  metadata: exports_external.record(exports_external.unknown()).optional().describe("Arbitrary metadata")
4598
4598
  }, async (params) => {
4599
4599
  try {
4600
- const task = createTask(params);
4600
+ const resolved = { ...params };
4601
+ if (resolved.project_id)
4602
+ resolved.project_id = resolveId(resolved.project_id, "projects");
4603
+ if (resolved.parent_id)
4604
+ resolved.parent_id = resolveId(resolved.parent_id);
4605
+ const task = createTask(resolved);
4601
4606
  return { content: [{ type: "text", text: `Task created:
4602
4607
  ${formatTask(task)}` }] };
4603
4608
  } catch (e) {
@@ -4618,7 +4623,10 @@ server.tool("list_tasks", "List tasks with optional filters", {
4618
4623
  tags: exports_external.array(exports_external.string()).optional().describe("Filter by tags (any match)")
4619
4624
  }, async (params) => {
4620
4625
  try {
4621
- const tasks = listTasks(params);
4626
+ const resolved = { ...params };
4627
+ if (resolved.project_id)
4628
+ resolved.project_id = resolveId(resolved.project_id, "projects");
4629
+ const tasks = listTasks(resolved);
4622
4630
  if (tasks.length === 0) {
4623
4631
  return { content: [{ type: "text", text: "No tasks found." }] };
4624
4632
  }
@@ -4852,7 +4860,8 @@ server.tool("search_tasks", "Full-text search across task titles, descriptions,
4852
4860
  project_id: exports_external.string().optional().describe("Limit to project")
4853
4861
  }, async ({ query, project_id }) => {
4854
4862
  try {
4855
- const tasks = searchTasks(query, project_id);
4863
+ const resolvedProjectId = project_id ? resolveId(project_id, "projects") : undefined;
4864
+ const tasks = searchTasks(query, resolvedProjectId);
4856
4865
  if (tasks.length === 0) {
4857
4866
  return { content: [{ type: "text", text: `No tasks matching "${query}".` }] };
4858
4867
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/todos",
3
- "version": "0.3.1",
3
+ "version": "0.3.3",
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",