@hasna/todos 0.3.2 → 0.3.4

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.
Files changed (2) hide show
  1. package/dist/cli/index.js +29 -22
  2. package/package.json +1 -1
package/dist/cli/index.js CHANGED
@@ -9089,12 +9089,15 @@ program2.command("export").description("Export tasks").option("-f, --format <for
9089
9089
  console.log(JSON.stringify(tasks, null, 2));
9090
9090
  }
9091
9091
  });
9092
- 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) => {
9093
9096
  const globalOpts = program2.opts();
9094
9097
  const projectId = autoProject(globalOpts);
9095
- const taskListId = opts.taskList || process.env["TODOS_CLAUDE_TASK_LIST"];
9098
+ const taskListId = resolveClaudeTaskListId(opts.taskList);
9096
9099
  if (!taskListId) {
9097
- 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."));
9098
9101
  process.exit(1);
9099
9102
  }
9100
9103
  let result;
@@ -9121,12 +9124,7 @@ program2.command("sync").description("Sync tasks with a Claude Code task list").
9121
9124
  }
9122
9125
  });
9123
9126
  var hooks = program2.command("hooks").description("Manage Claude Code hook integration");
9124
- 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) => {
9125
- const taskListId = opts.taskList || process.env["TODOS_CLAUDE_TASK_LIST"];
9126
- if (!taskListId) {
9127
- console.error(chalk.red("Task list ID required. Use --task-list <id> or set TODOS_CLAUDE_TASK_LIST."));
9128
- process.exit(1);
9129
- }
9127
+ hooks.command("install").description("Install Claude Code hooks for auto-sync").action(() => {
9130
9128
  let todosBin = "todos";
9131
9129
  try {
9132
9130
  const p = execSync2("which todos", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
@@ -9139,12 +9137,21 @@ hooks.command("install").description("Install Claude Code hooks for auto-sync").
9139
9137
  const hookScript = `#!/usr/bin/env bash
9140
9138
  # Auto-generated by: todos hooks install
9141
9139
  # Syncs todos with Claude Code task list on tool use events.
9142
- # Reads hook JSON from stdin; determines sync direction from tool_name.
9140
+ # Reads session_id and tool_name from the hook JSON stdin.
9141
+
9142
+ INPUT=$(cat)
9143
9143
 
9144
- set -e
9144
+ # Extract session_id from stdin JSON (hooks always receive this)
9145
+ SESSION_ID=$(echo "$INPUT" | grep -o '"session_id":"[^"]*"' | head -1 | cut -d'"' -f4 2>/dev/null || true)
9145
9146
 
9146
- TASK_LIST="\${TODOS_CLAUDE_TASK_LIST:-${taskListId}}"
9147
- TOOL_NAME=$(cat /dev/stdin | grep -o '"tool_name":"[^"]*"' | head -1 | cut -d'"' -f4 2>/dev/null || true)
9147
+ # Task list priority: env override > session ID from hook input
9148
+ TASK_LIST="\${TODOS_CLAUDE_TASK_LIST:-\${CLAUDE_CODE_TASK_LIST_ID:-$SESSION_ID}}"
9149
+
9150
+ if [ -z "$TASK_LIST" ]; then
9151
+ exit 0
9152
+ fi
9153
+
9154
+ TOOL_NAME=$(echo "$INPUT" | grep -o '"tool_name":"[^"]*"' | head -1 | cut -d'"' -f4 2>/dev/null || true)
9148
9155
 
9149
9156
  case "$TOOL_NAME" in
9150
9157
  TaskCreate|TaskUpdate)
@@ -9153,10 +9160,6 @@ case "$TOOL_NAME" in
9153
9160
  mcp__todos__*)
9154
9161
  ${todosBin} sync --push --task-list "$TASK_LIST" 2>/dev/null || true
9155
9162
  ;;
9156
- *)
9157
- # Unknown tool, run bidirectional sync
9158
- ${todosBin} sync --task-list "$TASK_LIST" 2>/dev/null || true
9159
- ;;
9160
9163
  esac
9161
9164
 
9162
9165
  exit 0
@@ -9175,20 +9178,24 @@ exit 0
9175
9178
  hooksConfig["PostToolUse"] = [];
9176
9179
  }
9177
9180
  const postToolUse = hooksConfig["PostToolUse"];
9178
- const filtered = postToolUse.filter((h) => !(h["command"] || "").includes("todos-sync.sh"));
9181
+ const filtered = postToolUse.filter((group) => {
9182
+ const groupHooks = group["hooks"];
9183
+ if (!groupHooks)
9184
+ return true;
9185
+ return !groupHooks.some((h) => (h["command"] || "").includes("todos-sync.sh"));
9186
+ });
9179
9187
  filtered.push({
9180
9188
  matcher: "TaskCreate|TaskUpdate",
9181
- command: hookPath
9189
+ hooks: [{ type: "command", command: hookPath }]
9182
9190
  });
9183
9191
  filtered.push({
9184
9192
  matcher: "mcp__todos__create_task|mcp__todos__update_task|mcp__todos__complete_task|mcp__todos__start_task",
9185
- command: hookPath
9193
+ hooks: [{ type: "command", command: hookPath }]
9186
9194
  });
9187
9195
  hooksConfig["PostToolUse"] = filtered;
9188
9196
  writeJsonFile(settingsPath, settings);
9189
9197
  console.log(chalk.green(`Claude Code hooks configured in: ${settingsPath}`));
9190
- console.log(chalk.dim(`Task list ID: ${taskListId}`));
9191
- console.log(chalk.dim("Set TODOS_CLAUDE_TASK_LIST env var to override at runtime."));
9198
+ console.log(chalk.dim("Task list ID auto-detected from hook stdin session_id."));
9192
9199
  });
9193
9200
  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) => {
9194
9201
  if (opts.register) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/todos",
3
- "version": "0.3.2",
3
+ "version": "0.3.4",
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",