@hasna/todos 0.13.5 → 0.13.6

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
@@ -2123,7 +2123,7 @@ var package_default;
2123
2123
  var init_package = __esm(() => {
2124
2124
  package_default = {
2125
2125
  name: "@hasna/todos",
2126
- version: "0.13.5",
2126
+ version: "0.13.6",
2127
2127
  description: "Universal task management for AI coding agents - CLI + MCP server + interactive TUI",
2128
2128
  type: "module",
2129
2129
  main: "dist/index.js",
@@ -5871,6 +5871,7 @@ function unresolvedRelatedTask(id) {
5871
5871
  reason: null,
5872
5872
  spawned_from_session: null,
5873
5873
  assigned_by: null,
5874
+ created_by: null,
5874
5875
  assigned_from_project: null,
5875
5876
  task_type: null,
5876
5877
  cost_tokens: 0,
@@ -9035,6 +9036,7 @@ function ensureSchema(db) {
9035
9036
  ensureColumn("tasks", "reason", "TEXT");
9036
9037
  ensureColumn("tasks", "spawned_from_session", "TEXT");
9037
9038
  ensureColumn("tasks", "assigned_by", "TEXT");
9039
+ ensureColumn("tasks", "created_by", "TEXT");
9038
9040
  ensureColumn("tasks", "assigned_from_project", "TEXT");
9039
9041
  ensureColumn("tasks", "started_at", "TEXT");
9040
9042
  ensureColumn("tasks", "task_type", "TEXT");
@@ -9197,6 +9199,8 @@ function ensureSchema(db) {
9197
9199
  ensureIndex("CREATE INDEX IF NOT EXISTS idx_project_sources_project ON project_sources(project_id)");
9198
9200
  ensureIndex("CREATE INDEX IF NOT EXISTS idx_project_sources_type ON project_sources(type)");
9199
9201
  ensureIndex("CREATE INDEX IF NOT EXISTS idx_tasks_assigned_by ON tasks(assigned_by)");
9202
+ ensureIndex("CREATE INDEX IF NOT EXISTS idx_tasks_created_by ON tasks(created_by)");
9203
+ ensureIndex("CREATE INDEX IF NOT EXISTS idx_tasks_assigned_created ON tasks(assigned_to, created_by)");
9200
9204
  ensureIndex("CREATE INDEX IF NOT EXISTS idx_task_rel_source ON task_relationships(source_task_id)");
9201
9205
  ensureIndex("CREATE INDEX IF NOT EXISTS idx_task_rel_target ON task_relationships(target_task_id)");
9202
9206
  ensureIndex("CREATE INDEX IF NOT EXISTS idx_task_rel_type ON task_relationships(relationship_type)");
@@ -15346,13 +15350,14 @@ function createTask(input, db) {
15346
15350
  const timestamp2 = now();
15347
15351
  const tags = input.tags || [];
15348
15352
  const machineId = currentStorageMachineId(d);
15353
+ const createdBy = input.created_by || input.agent_id || null;
15349
15354
  const assignedBy = input.assigned_by || input.agent_id;
15350
15355
  const assignedFromProject = input.assigned_from_project || null;
15351
15356
  let id = uuid();
15352
15357
  for (let attempt = 0;attempt < 3; attempt++) {
15353
15358
  try {
15354
- d.run(`INSERT INTO tasks (id, short_id, project_id, parent_id, plan_id, task_list_id, cycle_id, title, description, status, priority, agent_id, assigned_to, session_id, working_dir, tags, metadata, version, created_at, updated_at, due_at, estimated_minutes, sla_minutes, confidence, retry_count, max_retries, retry_after, requires_approval, approved_by, approved_at, recurrence_rule, recurrence_parent_id, spawns_template_id, reason, spawned_from_session, assigned_by, assigned_from_project, task_type, machine_id)
15355
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
15359
+ d.run(`INSERT INTO tasks (id, short_id, project_id, parent_id, plan_id, task_list_id, cycle_id, title, description, status, priority, agent_id, assigned_to, session_id, working_dir, tags, metadata, version, created_at, updated_at, due_at, estimated_minutes, sla_minutes, confidence, retry_count, max_retries, retry_after, requires_approval, approved_by, approved_at, recurrence_rule, recurrence_parent_id, spawns_template_id, reason, spawned_from_session, assigned_by, created_by, assigned_from_project, task_type, machine_id)
15360
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
15356
15361
  id,
15357
15362
  null,
15358
15363
  input.project_id || null,
@@ -15388,6 +15393,7 @@ function createTask(input, db) {
15388
15393
  input.reason || null,
15389
15394
  input.spawned_from_session || null,
15390
15395
  assignedBy || null,
15396
+ createdBy,
15391
15397
  assignedFromProject || null,
15392
15398
  input.task_type || null,
15393
15399
  machineId
@@ -15497,6 +15503,14 @@ function listTasks(filter = {}, db) {
15497
15503
  conditions.push("agent_id = ?");
15498
15504
  params.push(filter.agent_id);
15499
15505
  }
15506
+ if (filter.created_by) {
15507
+ conditions.push("LOWER(created_by) = LOWER(?)");
15508
+ params.push(filter.created_by);
15509
+ }
15510
+ if (filter.not_created_by) {
15511
+ conditions.push("(created_by IS NULL OR LOWER(created_by) != LOWER(?))");
15512
+ params.push(filter.not_created_by);
15513
+ }
15500
15514
  if (filter.session_id) {
15501
15515
  conditions.push("session_id = ?");
15502
15516
  params.push(filter.session_id);
@@ -15650,6 +15664,14 @@ function countTasks(filter = {}, db) {
15650
15664
  conditions.push("agent_id = ?");
15651
15665
  params.push(filter.agent_id);
15652
15666
  }
15667
+ if (filter.created_by) {
15668
+ conditions.push("LOWER(created_by) = LOWER(?)");
15669
+ params.push(filter.created_by);
15670
+ }
15671
+ if (filter.not_created_by) {
15672
+ conditions.push("(created_by IS NULL OR LOWER(created_by) != LOWER(?))");
15673
+ params.push(filter.not_created_by);
15674
+ }
15653
15675
  if (filter.session_id) {
15654
15676
  conditions.push("session_id = ?");
15655
15677
  params.push(filter.session_id);
@@ -18810,6 +18832,73 @@ var init_tasks = __esm(() => {
18810
18832
  init_calendar();
18811
18833
  });
18812
18834
 
18835
+ // src/lib/creator-identity.ts
18836
+ import { existsSync as existsSync8, rmSync as rmSync2 } from "fs";
18837
+ import { join as join7 } from "path";
18838
+ function identityFilePath() {
18839
+ return join7(getTodosGlobalDir(), "identity.json");
18840
+ }
18841
+ function readPersistedIdentity() {
18842
+ const path = identityFilePath();
18843
+ if (!existsSync8(path))
18844
+ return null;
18845
+ const parsed = readJsonFile(path);
18846
+ if (!parsed || typeof parsed.agent_id !== "string" || !parsed.agent_id.trim())
18847
+ return null;
18848
+ return parsed;
18849
+ }
18850
+ function detectIdentityCollision(agentId, agentName) {
18851
+ const existing = readPersistedIdentity();
18852
+ if (!existing)
18853
+ return null;
18854
+ const sameId = canonicalAgentRef(existing.agent_id) === canonicalAgentRef(agentId);
18855
+ const sameName = Boolean(agentName) && canonicalAgentRef(existing.agent_name || "") === canonicalAgentRef(agentName);
18856
+ if (sameId || sameName)
18857
+ return null;
18858
+ return { existing };
18859
+ }
18860
+ function persistIdentity(identity) {
18861
+ const record = {
18862
+ agent_id: identity.agent_id,
18863
+ ...identity.agent_name ? { agent_name: identity.agent_name } : {},
18864
+ ...identity.session_id ? { session_id: identity.session_id } : {},
18865
+ registered_at: new Date().toISOString()
18866
+ };
18867
+ ensureDir(getTodosGlobalDir());
18868
+ writeJsonFile(identityFilePath(), record);
18869
+ return record;
18870
+ }
18871
+ function clearPersistedIdentity() {
18872
+ const path = identityFilePath();
18873
+ if (!existsSync8(path))
18874
+ return false;
18875
+ try {
18876
+ rmSync2(path);
18877
+ return true;
18878
+ } catch {
18879
+ return false;
18880
+ }
18881
+ }
18882
+ function canonicalAgentRef(value) {
18883
+ return value.trim().toLowerCase();
18884
+ }
18885
+ function resolveCreatorIdentity(explicit) {
18886
+ const fromExplicit = explicit?.trim();
18887
+ if (fromExplicit)
18888
+ return { agent_id: canonicalAgentRef(fromExplicit), source: "explicit" };
18889
+ const fromEnv = (process.env["TODOS_AGENT_ID"] || process.env["HASNA_TODOS_AGENT_ID"] || "").trim();
18890
+ if (fromEnv)
18891
+ return { agent_id: canonicalAgentRef(fromEnv), source: "env" };
18892
+ const persisted = readPersistedIdentity();
18893
+ if (persisted) {
18894
+ return { agent_id: canonicalAgentRef(persisted.agent_name || persisted.agent_id), source: "persisted" };
18895
+ }
18896
+ return { agent_id: null, source: "none" };
18897
+ }
18898
+ var init_creator_identity = __esm(() => {
18899
+ init_sync_utils();
18900
+ });
18901
+
18813
18902
  // src/cli/output-redaction.ts
18814
18903
  function redactBroadTask(task) {
18815
18904
  return redactValue(task);
@@ -18830,7 +18919,7 @@ __export(exports_task_routing, {
18830
18919
  setTaskWorkflowPointers: () => setTaskWorkflowPointers,
18831
18920
  getTaskRouteState: () => getTaskRouteState
18832
18921
  });
18833
- import { existsSync as existsSync8, statSync as statSync3 } from "fs";
18922
+ import { existsSync as existsSync9, statSync as statSync3 } from "fs";
18834
18923
  function machineLocalPath(project, db) {
18835
18924
  const machineId = process.env["TODOS_MACHINE_ID"];
18836
18925
  if (!machineId)
@@ -18870,7 +18959,7 @@ function routeConcurrencyKey(task, project, taskList, projectPath) {
18870
18959
  }
18871
18960
  function directoryExists(path) {
18872
18961
  try {
18873
- return existsSync8(path) && statSync3(path).isDirectory();
18962
+ return existsSync9(path) && statSync3(path).isDirectory();
18874
18963
  } catch {
18875
18964
  return false;
18876
18965
  }
@@ -19307,10 +19396,15 @@ function computeLocalReparent(current, opts) {
19307
19396
  return patch;
19308
19397
  }
19309
19398
  function registerTaskCommands(program2) {
19310
- program2.command("add <title>").description("Create a new task").option("-d, --description <text>", "Task description").option("-p, --priority <level>", "Priority: low, medium, high, critical").option("--parent <id>", "Parent task ID").option("-t, --tags <tags>", "Comma-separated tags").option("--tag <tags>", "Comma-separated tags (alias for --tags)").option("--plan <id>", "Assign to a plan").option("--assign <agent>", "Assign to agent").option("--status <status>", "Initial status").option("--list <id>", "Task list ID").option("--task-list <id>", "Task list ID (alias for --list)").option("--estimated <minutes>", "Estimated time in minutes").option("--sla-minutes <minutes>", "SLA minutes before unfinished work is escalated").option("--sla <minutes>", "Alias for --sla-minutes").option("--approval", "Require approval before completion").option("--recurrence <rule>", "Recurrence rule, e.g. 'every day', 'every weekday', 'every 2 weeks'").option("--due <date>", "Due date (ISO string or YYYY-MM-DD)").option("--reason <text>", "Why this task exists").option("--project <id>", "Assign to project by ID or slug (overrides auto-detect)").action(async (title, opts) => {
19399
+ program2.command("add <title>").description("Create a new task").option("-d, --description <text>", "Task description").option("-p, --priority <level>", "Priority: low, medium, high, critical").option("--parent <id>", "Parent task ID").option("-t, --tags <tags>", "Comma-separated tags").option("--tag <tags>", "Comma-separated tags (alias for --tags)").option("--plan <id>", "Assign to a plan").option("--assign <agent>", "Assign to agent").option("--status <status>", "Initial status").option("--list <id>", "Task list ID").option("--task-list <id>", "Task list ID (alias for --list)").option("--estimated <minutes>", "Estimated time in minutes").option("--sla-minutes <minutes>", "SLA minutes before unfinished work is escalated").option("--sla <minutes>", "Alias for --sla-minutes").option("--approval", "Require approval before completion").option("--recurrence <rule>", "Recurrence rule, e.g. 'every day', 'every weekday', 'every 2 weeks'").option("--due <date>", "Due date (ISO string or YYYY-MM-DD)").option("--reason <text>", "Why this task exists").option("--project <id>", "Assign to project by ID or slug (overrides auto-detect)").option("--unassigned", "Deliberately file this task with no assignee").option("--created-by <agent>", "Record a different filer than the resolved agent identity").action(async (title, opts) => {
19311
19400
  const globalOpts = program2.opts();
19312
19401
  opts.tags = opts.tags || opts.tag;
19313
19402
  opts.list = opts.list || opts.taskList;
19403
+ const creator = resolveCreatorIdentity(opts.createdBy || globalOpts.agent);
19404
+ const assignee = opts.assign || (opts.unassigned ? undefined : creator.agent_id || undefined);
19405
+ if (!assignee && !opts.unassigned) {
19406
+ console.error(chalk2.yellow("Warning: task is ownerless and unattributable \u2014 run `todos init <name>`, or pass --assign <agent> or --unassigned."));
19407
+ }
19314
19408
  const cloud = getTodosCloudClient();
19315
19409
  if (cloud) {
19316
19410
  let task3;
@@ -19332,10 +19426,11 @@ function registerTaskCommands(program2) {
19332
19426
  parent_id: opts.parent ? await resolveTaskIdForCommand(opts.parent, cloud) : undefined,
19333
19427
  tags: opts.tags ? opts.tags.split(",").map((t) => t.trim()) : undefined,
19334
19428
  plan_id: cloudPlan?.id,
19335
- assigned_to: opts.assign,
19429
+ assigned_to: assignee,
19336
19430
  status: parseStatus(opts.status),
19337
19431
  task_list_id: cloudTaskListId,
19338
- agent_id: globalOpts.agent,
19432
+ agent_id: globalOpts.agent || creator.agent_id || undefined,
19433
+ created_by: creator.agent_id || undefined,
19339
19434
  session_id: globalOpts.session,
19340
19435
  project_id: cloudProjectId,
19341
19436
  estimated_minutes: opts.estimated !== undefined ? parseIntOption(opts.estimated, "--estimated") : undefined,
@@ -19377,10 +19472,11 @@ function registerTaskCommands(program2) {
19377
19472
  parent_id: opts.parent ? resolveTaskId(opts.parent) : undefined,
19378
19473
  tags: opts.tags ? opts.tags.split(",").map((t) => t.trim()) : undefined,
19379
19474
  plan_id: opts.plan ? resolvePlanId(opts.plan) : undefined,
19380
- assigned_to: opts.assign,
19475
+ assigned_to: assignee,
19381
19476
  status: parseStatus(opts.status),
19382
19477
  task_list_id: taskListId,
19383
- agent_id: globalOpts.agent,
19478
+ agent_id: globalOpts.agent || creator.agent_id || undefined,
19479
+ created_by: creator.agent_id || undefined,
19384
19480
  session_id: globalOpts.session,
19385
19481
  project_id: projectId,
19386
19482
  working_dir: process.cwd(),
@@ -19534,14 +19630,14 @@ function registerTaskCommands(program2) {
19534
19630
  console.log(` ${chalk2.dim("Manifest:")} ${state.pointers.latest_manifest_path}`);
19535
19631
  }
19536
19632
  });
19537
- program2.command("list").description("List tasks").option("-s, --status <status>", "Filter by status").option("-p, --priority <priority>", "Filter by priority").option("--assigned <agent>", "Filter by assigned agent").option("--tags <tags>", "Filter by tags (comma-separated)").option("--tag <tags>", "Filter by tags (alias for --tags)").option("-a, --all", "Show all tasks (including completed/cancelled)").option("--list <ref>", "Filter by task list UUID, unique UUID prefix, or project-scoped slug").option("--task-list <ref>", "Filter by task list UUID, unique UUID prefix, or project-scoped slug (alias for --list)").option("--project-name <name>", "Filter by project name").option("--agent-name <name>", "Filter by agent name/assigned").option("--sort <field>", "Sort by: updated, created, priority, status").option("--format <fmt>", "Output format: table (default), compact, csv, json").option("--due-today", "Only tasks due today or earlier").option("--overdue", "Only overdue tasks (past due_at)").option("--recurring", "Only recurring tasks").option("--limit <n>", "Max tasks to return").action(async (opts) => {
19633
+ program2.command("list").description("List tasks").option("-s, --status <status>", "Filter by status").option("-p, --priority <priority>", "Filter by priority").option("--assigned <agent>", "Filter by assigned agent").option("--created-by <agent>", "Filter by the agent who FILED the task").option("--not-created-by <agent>", "Exclude tasks filed by this agent").option("--inbox", "Work assigned to my identity that a DIFFERENT agent filed").option("--tags <tags>", "Filter by tags (comma-separated)").option("--tag <tags>", "Filter by tags (alias for --tags)").option("-a, --all", "Show all tasks (including completed/cancelled)").option("--list <ref>", "Filter by task list UUID, unique UUID prefix, or project-scoped slug").option("--task-list <ref>", "Filter by task list UUID, unique UUID prefix, or project-scoped slug (alias for --list)").option("--project-name <name>", "Filter by project name").option("--agent-name <name>", "Filter by agent name/assigned").option("--sort <field>", "Sort by: updated, created, priority, status").option("--format <fmt>", "Output format: table (default), compact, csv, json").option("--due-today", "Only tasks due today or earlier").option("--overdue", "Only overdue tasks (past due_at)").option("--recurring", "Only recurring tasks").option("--limit <n>", "Max tasks to return").action(async (opts) => {
19538
19634
  const globalOpts = program2.opts();
19539
19635
  opts.tags = opts.tags || opts.tag;
19540
19636
  opts.list = opts.list || opts.taskList;
19541
19637
  const cloud = getTodosCloudClient();
19542
19638
  const cloudProjectRef = globalOpts.project || opts.projectName;
19543
19639
  const projectId = cloud && cloudProjectRef ? await cloudResolveProjectRef(cloud, cloudProjectRef) : cloud ? undefined : autoProject(globalOpts);
19544
- const hasAssignedFilter = Boolean(opts.assigned || opts.agentName);
19640
+ const hasAssignedFilter = Boolean(opts.assigned || opts.agentName || opts.inbox);
19545
19641
  const hasExplicitProjectFilter = Boolean(globalOpts.project || opts.projectName);
19546
19642
  const allowedSortFields = new Set(["updated", "created", "priority", "status"]);
19547
19643
  if (opts.sort && !allowedSortFields.has(opts.sort)) {
@@ -19574,6 +19670,19 @@ function registerTaskCommands(program2) {
19574
19670
  filter["priority"] = opts.priority;
19575
19671
  if (opts.assigned)
19576
19672
  filter["assigned_to"] = opts.assigned;
19673
+ if (opts.createdBy)
19674
+ filter["created_by"] = canonicalAgentRef(opts.createdBy);
19675
+ if (opts.notCreatedBy)
19676
+ filter["not_created_by"] = canonicalAgentRef(opts.notCreatedBy);
19677
+ if (opts.inbox) {
19678
+ const me = resolveCreatorIdentity(program2.opts().agent);
19679
+ if (!me.agent_id) {
19680
+ console.error(chalk2.red("--inbox needs an agent identity. Run `todos init <name>` or pass --agent <id>."));
19681
+ process.exit(1);
19682
+ }
19683
+ filter["assigned_to"] = me.agent_id;
19684
+ filter["not_created_by"] = me.agent_id;
19685
+ }
19577
19686
  if (opts.tags)
19578
19687
  filter["tags"] = opts.tags.split(",").map((t) => t.trim());
19579
19688
  if (opts.projectName && !cloud) {
@@ -19598,7 +19707,32 @@ function registerTaskCommands(program2) {
19598
19707
  }
19599
19708
  filter["limit"] = parsedLimit;
19600
19709
  }
19601
- let tasks = cloud ? await cloudListTasks(cloud, filter) : listTasks(filter);
19710
+ const creatorFilterActive = Boolean(filter["created_by"] || filter["not_created_by"]);
19711
+ const requestedLimit = filter["limit"];
19712
+ const serverFilter = creatorFilterActive && cloud && requestedLimit !== undefined ? (() => {
19713
+ const { limit: _dropped, ...rest } = filter;
19714
+ return rest;
19715
+ })() : filter;
19716
+ let tasks = cloud ? await cloudListTasks(cloud, serverFilter) : listTasks(serverFilter);
19717
+ if (cloud && creatorFilterActive) {
19718
+ if (tasks.length > 0 && tasks.every((t) => !("created_by" in t))) {
19719
+ console.error(chalk2.yellow(`Warning: this server does not record task authorship, so the creator filter matched nothing to exclude.
19720
+ ` + " Results are unfiltered. The API needs upgrading past the release that added created_by."));
19721
+ }
19722
+ const wantCreatedBy = filter["created_by"];
19723
+ const excludeCreatedBy = filter["not_created_by"];
19724
+ tasks = tasks.filter((t) => {
19725
+ const raw = t.created_by ?? null;
19726
+ const author = raw === null ? null : canonicalAgentRef(raw);
19727
+ if (wantCreatedBy && author !== canonicalAgentRef(wantCreatedBy))
19728
+ return false;
19729
+ if (excludeCreatedBy && author !== null && author === canonicalAgentRef(excludeCreatedBy))
19730
+ return false;
19731
+ return true;
19732
+ });
19733
+ if (requestedLimit !== undefined)
19734
+ tasks = tasks.slice(0, requestedLimit);
19735
+ }
19602
19736
  if (opts.dueToday) {
19603
19737
  const todayEnd = new Date;
19604
19738
  todayEnd.setHours(23, 59, 59, 999);
@@ -20482,14 +20616,15 @@ var init_task_commands = __esm(() => {
20482
20616
  init_tasks();
20483
20617
  init_task_lists();
20484
20618
  init_cloud_router();
20619
+ init_creator_identity();
20485
20620
  init_helpers();
20486
20621
  init_output_redaction();
20487
20622
  init_types();
20488
20623
  });
20489
20624
 
20490
20625
  // src/lib/plan-artifacts.ts
20491
- import { existsSync as existsSync9, mkdirSync as mkdirSync6, readFileSync as readFileSync4, writeFileSync as writeFileSync4 } from "fs";
20492
- import { join as join7, resolve as resolve10 } from "path";
20626
+ import { existsSync as existsSync10, mkdirSync as mkdirSync6, readFileSync as readFileSync4, writeFileSync as writeFileSync4 } from "fs";
20627
+ import { join as join8, resolve as resolve10 } from "path";
20493
20628
  function assertSafePathSegment(value, label) {
20494
20629
  const trimmed = value.trim();
20495
20630
  if (!trimmed || trimmed === "." || trimmed === ".." || trimmed.includes("/") || trimmed.includes("\\")) {
@@ -20550,7 +20685,7 @@ function resolvePlanArtifactPaths(input) {
20550
20685
  const project = resolvePlanArtifactProject(input);
20551
20686
  const projectId = assertSafePathSegment(project.id, "project id");
20552
20687
  const projectRoot = resolve10(project.path);
20553
- const directory = join7(projectRoot, ".hasna", "todos", "plans", projectId);
20688
+ const directory = join8(projectRoot, ".hasna", "todos", "plans", projectId);
20554
20689
  const planId = input.plan_id ? assertSafePathSegment(input.plan_id, "plan id") : null;
20555
20690
  const planSlug = input.plan_slug ? assertSafePathSegment(slugify(input.plan_slug), "plan slug") : null;
20556
20691
  const fileName = planId ? planSlug ? `${planSlug}--${planId.slice(0, 8)}.md` : `${planId}.md` : null;
@@ -20558,7 +20693,7 @@ function resolvePlanArtifactPaths(input) {
20558
20693
  project_id: project.id,
20559
20694
  project_root: projectRoot,
20560
20695
  directory,
20561
- file_path: fileName ? join7(directory, fileName) : directory
20696
+ file_path: fileName ? join8(directory, fileName) : directory
20562
20697
  };
20563
20698
  }
20564
20699
  function resolvePlanArtifactCandidatePaths(plan, db) {
@@ -20724,7 +20859,7 @@ function readPlanArtifact(plan, db) {
20724
20859
  return null;
20725
20860
  const d = db || getDatabase();
20726
20861
  const paths = resolvePlanArtifactCandidatePaths(plan, d);
20727
- const path = existsSync9(paths.primary.file_path) ? paths.primary.file_path : existsSync9(paths.legacy.file_path) ? paths.legacy.file_path : null;
20862
+ const path = existsSync10(paths.primary.file_path) ? paths.primary.file_path : existsSync10(paths.legacy.file_path) ? paths.legacy.file_path : null;
20728
20863
  if (!path)
20729
20864
  return null;
20730
20865
  const markdown = readFileSync4(path, "utf8");
@@ -20739,7 +20874,7 @@ function inspectPlanArtifact(plan, db) {
20739
20874
  return null;
20740
20875
  const d = db || getDatabase();
20741
20876
  const paths = resolvePlanArtifactCandidatePaths(plan, d);
20742
- const path = existsSync9(paths.primary.file_path) ? paths.primary.file_path : existsSync9(paths.legacy.file_path) ? paths.legacy.file_path : null;
20877
+ const path = existsSync10(paths.primary.file_path) ? paths.primary.file_path : existsSync10(paths.legacy.file_path) ? paths.legacy.file_path : null;
20743
20878
  if (!path) {
20744
20879
  return {
20745
20880
  path: paths.primary.file_path,
@@ -20819,7 +20954,7 @@ __export(exports_builtin_templates, {
20819
20954
  BUILTIN_TEMPLATES: () => BUILTIN_TEMPLATES
20820
20955
  });
20821
20956
  import { mkdirSync as mkdirSync7, writeFileSync as writeFileSync5 } from "fs";
20822
- import { join as join8 } from "path";
20957
+ import { join as join9 } from "path";
20823
20958
  function templateMetadata(template) {
20824
20959
  return {
20825
20960
  source: BUILTIN_TEMPLATE_LIBRARY_SOURCE,
@@ -20878,7 +21013,7 @@ function writeBuiltinTemplateFiles(directory) {
20878
21013
  mkdirSync7(directory, { recursive: true });
20879
21014
  const files = [];
20880
21015
  for (const entry of exportBuiltinTemplateFiles()) {
20881
- const path = join8(directory, entry.filename);
21016
+ const path = join9(directory, entry.filename);
20882
21017
  writeFileSync5(path, `${JSON.stringify(entry.template, null, 2)}
20883
21018
  `, "utf-8");
20884
21019
  files.push(path);
@@ -22650,16 +22785,16 @@ var init_saved_search_views = __esm(() => {
22650
22785
  });
22651
22786
 
22652
22787
  // src/lib/claude-tasks.ts
22653
- import { existsSync as existsSync10, readFileSync as readFileSync5, readdirSync as readdirSync2, writeFileSync as writeFileSync6 } from "fs";
22654
- import { join as join9 } from "path";
22788
+ import { existsSync as existsSync11, readFileSync as readFileSync5, readdirSync as readdirSync2, writeFileSync as writeFileSync6 } from "fs";
22789
+ import { join as join10 } from "path";
22655
22790
  function getTaskListDir(taskListId) {
22656
- return join9(HOME, ".claude", "tasks", taskListId);
22791
+ return join10(HOME, ".claude", "tasks", taskListId);
22657
22792
  }
22658
22793
  function readClaudeTask(dir, filename) {
22659
- return readJsonFile(join9(dir, filename));
22794
+ return readJsonFile(join10(dir, filename));
22660
22795
  }
22661
22796
  function writeClaudeTask(dir, task) {
22662
- writeJsonFile(join9(dir, `${task.id}.json`), task);
22797
+ writeJsonFile(join10(dir, `${task.id}.json`), task);
22663
22798
  }
22664
22799
  function toClaudeStatus(status) {
22665
22800
  if (status === "pending" || status === "in_progress" || status === "completed") {
@@ -22671,14 +22806,14 @@ function toSqliteStatus(status) {
22671
22806
  return status;
22672
22807
  }
22673
22808
  function readPrefixCounter(dir) {
22674
- const path = join9(dir, ".prefix-counter");
22675
- if (!existsSync10(path))
22809
+ const path = join10(dir, ".prefix-counter");
22810
+ if (!existsSync11(path))
22676
22811
  return 0;
22677
22812
  const val = parseInt(readFileSync5(path, "utf-8").trim(), 10);
22678
22813
  return isNaN(val) ? 0 : val;
22679
22814
  }
22680
22815
  function writePrefixCounter(dir, value) {
22681
- writeFileSync6(join9(dir, ".prefix-counter"), String(value));
22816
+ writeFileSync6(join10(dir, ".prefix-counter"), String(value));
22682
22817
  }
22683
22818
  function formatPrefixedSubject(title, prefix, counter) {
22684
22819
  const padded = String(counter).padStart(5, "0");
@@ -22705,7 +22840,7 @@ function taskToClaudeTask(task, claudeTaskId, existingMeta) {
22705
22840
  }
22706
22841
  function pushToClaudeTaskList(taskListId, projectId, options = {}) {
22707
22842
  const dir = getTaskListDir(taskListId);
22708
- if (!existsSync10(dir))
22843
+ if (!existsSync11(dir))
22709
22844
  ensureDir(dir);
22710
22845
  const filter = {};
22711
22846
  if (projectId)
@@ -22714,7 +22849,7 @@ function pushToClaudeTaskList(taskListId, projectId, options = {}) {
22714
22849
  const existingByTodosId = new Map;
22715
22850
  const files = listJsonFiles(dir);
22716
22851
  for (const f of files) {
22717
- const path = join9(dir, f);
22852
+ const path = join10(dir, f);
22718
22853
  const ct = readClaudeTask(dir, f);
22719
22854
  if (ct?.metadata?.["todos_id"]) {
22720
22855
  existingByTodosId.set(ct.metadata["todos_id"], { task: ct, mtimeMs: getFileMtimeMs(path) });
@@ -22801,7 +22936,7 @@ function pushToClaudeTaskList(taskListId, projectId, options = {}) {
22801
22936
  }
22802
22937
  function pullFromClaudeTaskList(taskListId, projectId, options = {}) {
22803
22938
  const dir = getTaskListDir(taskListId);
22804
- if (!existsSync10(dir)) {
22939
+ if (!existsSync11(dir)) {
22805
22940
  return { pushed: 0, pulled: 0, errors: [`Task list directory not found: ${dir}`] };
22806
22941
  }
22807
22942
  const files = readdirSync2(dir).filter((f) => f.endsWith(".json"));
@@ -22821,7 +22956,7 @@ function pullFromClaudeTaskList(taskListId, projectId, options = {}) {
22821
22956
  }
22822
22957
  for (const f of files) {
22823
22958
  try {
22824
- const filePath = join9(dir, f);
22959
+ const filePath = join10(dir, f);
22825
22960
  const ct = readClaudeTask(dir, f);
22826
22961
  if (!ct)
22827
22962
  continue;
@@ -22894,20 +23029,20 @@ var init_claude_tasks = __esm(() => {
22894
23029
  });
22895
23030
 
22896
23031
  // src/lib/agent-tasks.ts
22897
- import { existsSync as existsSync11 } from "fs";
22898
- import { join as join10 } from "path";
23032
+ import { existsSync as existsSync12 } from "fs";
23033
+ import { join as join11 } from "path";
22899
23034
  function agentBaseDir(agent) {
22900
23035
  const key = `TODOS_${agent.toUpperCase()}_TASKS_DIR`;
22901
- return process.env[key] || getAgentTasksDir(agent) || process.env["TODOS_AGENT_TASKS_DIR"] || join10(getTodosGlobalDir(), "agents");
23036
+ return process.env[key] || getAgentTasksDir(agent) || process.env["TODOS_AGENT_TASKS_DIR"] || join11(getTodosGlobalDir(), "agents");
22902
23037
  }
22903
23038
  function getTaskListDir2(agent, taskListId) {
22904
- return join10(agentBaseDir(agent), agent, taskListId);
23039
+ return join11(agentBaseDir(agent), agent, taskListId);
22905
23040
  }
22906
23041
  function readAgentTask(dir, filename) {
22907
- return readJsonFile(join10(dir, filename));
23042
+ return readJsonFile(join11(dir, filename));
22908
23043
  }
22909
23044
  function writeAgentTask(dir, task) {
22910
- writeJsonFile(join10(dir, `${task.id}.json`), task);
23045
+ writeJsonFile(join11(dir, `${task.id}.json`), task);
22911
23046
  }
22912
23047
  function taskToAgentTask(task, externalId, existingMeta) {
22913
23048
  return {
@@ -22932,7 +23067,7 @@ function metadataKey(agent) {
22932
23067
  }
22933
23068
  function pushToAgentTaskList(agent, taskListId, projectId, options = {}) {
22934
23069
  const dir = getTaskListDir2(agent, taskListId);
22935
- if (!existsSync11(dir))
23070
+ if (!existsSync12(dir))
22936
23071
  ensureDir(dir);
22937
23072
  const filter = {};
22938
23073
  if (projectId)
@@ -22941,7 +23076,7 @@ function pushToAgentTaskList(agent, taskListId, projectId, options = {}) {
22941
23076
  const existingByTodosId = new Map;
22942
23077
  const files = listJsonFiles(dir);
22943
23078
  for (const f of files) {
22944
- const path = join10(dir, f);
23079
+ const path = join11(dir, f);
22945
23080
  const at = readAgentTask(dir, f);
22946
23081
  if (at?.metadata?.["todos_id"]) {
22947
23082
  existingByTodosId.set(at.metadata["todos_id"], { task: at, mtimeMs: getFileMtimeMs(path) });
@@ -23015,7 +23150,7 @@ function pushToAgentTaskList(agent, taskListId, projectId, options = {}) {
23015
23150
  }
23016
23151
  function pullFromAgentTaskList(agent, taskListId, projectId, options = {}) {
23017
23152
  const dir = getTaskListDir2(agent, taskListId);
23018
- if (!existsSync11(dir)) {
23153
+ if (!existsSync12(dir)) {
23019
23154
  return { pushed: 0, pulled: 0, errors: [`Task list directory not found: ${dir}`] };
23020
23155
  }
23021
23156
  const files = listJsonFiles(dir);
@@ -23034,7 +23169,7 @@ function pullFromAgentTaskList(agent, taskListId, projectId, options = {}) {
23034
23169
  }
23035
23170
  for (const f of files) {
23036
23171
  try {
23037
- const filePath = join10(dir, f);
23172
+ const filePath = join11(dir, f);
23038
23173
  const at = readAgentTask(dir, f);
23039
23174
  if (!at)
23040
23175
  continue;
@@ -23187,7 +23322,7 @@ __export(exports_project_bootstrap, {
23187
23322
  discoverProjectWorkspace: () => discoverProjectWorkspace,
23188
23323
  bootstrapProject: () => bootstrapProject
23189
23324
  });
23190
- import { existsSync as existsSync12, readFileSync as readFileSync6, statSync as statSync4 } from "fs";
23325
+ import { existsSync as existsSync13, readFileSync as readFileSync6, statSync as statSync4 } from "fs";
23191
23326
  import { basename as basename4, dirname as dirname6, resolve as resolve11 } from "path";
23192
23327
  function safeStat(path) {
23193
23328
  try {
@@ -23206,7 +23341,7 @@ function canonicalPath(input) {
23206
23341
  function findUp(start, marker) {
23207
23342
  let current = canonicalPath(start);
23208
23343
  while (true) {
23209
- if (existsSync12(resolve11(current, marker)))
23344
+ if (existsSync13(resolve11(current, marker)))
23210
23345
  return current;
23211
23346
  const parent = dirname6(current);
23212
23347
  if (parent === current)
@@ -23218,7 +23353,7 @@ function readPackageJson(path) {
23218
23353
  if (!path)
23219
23354
  return null;
23220
23355
  const file = resolve11(path, "package.json");
23221
- if (!existsSync12(file))
23356
+ if (!existsSync13(file))
23222
23357
  return null;
23223
23358
  try {
23224
23359
  const parsed = JSON.parse(readFileSync6(file, "utf-8"));
@@ -23240,7 +23375,7 @@ function workspaceMarker(root, rootPackage) {
23240
23375
  if (rootPackage?.workspaces)
23241
23376
  markers.push("package.json#workspaces");
23242
23377
  for (const marker of ["pnpm-workspace.yaml", "turbo.json", "nx.json", "lerna.json", "rush.json", "bun.lock", "bun.lockb"]) {
23243
- if (existsSync12(resolve11(root, marker)))
23378
+ if (existsSync13(resolve11(root, marker)))
23244
23379
  markers.push(marker);
23245
23380
  }
23246
23381
  const kind = markers.find((marker) => marker !== "bun.lock" && marker !== "bun.lockb") ?? null;
@@ -23574,9 +23709,9 @@ __export(exports_extract, {
23574
23709
  buildCodebaseIndex: () => buildCodebaseIndex,
23575
23710
  EXTRACT_TAGS: () => EXTRACT_TAGS
23576
23711
  });
23577
- import { existsSync as existsSync13, readFileSync as readFileSync7, statSync as statSync5 } from "fs";
23712
+ import { existsSync as existsSync14, readFileSync as readFileSync7, statSync as statSync5 } from "fs";
23578
23713
  import { createHash as createHash4 } from "crypto";
23579
- import { relative as relative3, resolve as resolve12, join as join11 } from "path";
23714
+ import { relative as relative3, resolve as resolve12, join as join12 } from "path";
23580
23715
  function stableHash(value) {
23581
23716
  return createHash4("sha256").update(value).digest("hex");
23582
23717
  }
@@ -23585,8 +23720,8 @@ function normalizePathForMatch(value) {
23585
23720
  }
23586
23721
  function readGitignorePatterns(basePath) {
23587
23722
  const root = statSync5(basePath).isFile() ? resolve12(basePath, "..") : basePath;
23588
- const gitignorePath = join11(root, ".gitignore");
23589
- if (!existsSync13(gitignorePath))
23723
+ const gitignorePath = join12(root, ".gitignore");
23724
+ if (!existsSync14(gitignorePath))
23590
23725
  return [];
23591
23726
  try {
23592
23727
  return readFileSync7(gitignorePath, "utf-8").split(`
@@ -23728,7 +23863,7 @@ function buildCodebaseIndex(options) {
23728
23863
  const files = collectFiles(basePath, extensions, excludes, respectGitignore);
23729
23864
  const indexed = [];
23730
23865
  for (const file of files) {
23731
- const fullPath = statSync5(basePath).isFile() ? basePath : join11(basePath, file);
23866
+ const fullPath = statSync5(basePath).isFile() ? basePath : join12(basePath, file);
23732
23867
  try {
23733
23868
  const source = readFileSync7(fullPath, "utf-8");
23734
23869
  const relPath = statSync5(basePath).isFile() ? relative3(resolve12(basePath, ".."), fullPath) : file;
@@ -23759,7 +23894,7 @@ function extractTodos(options, db) {
23759
23894
  const files = collectFiles(basePath, extensions, excludes, respectGitignore);
23760
23895
  const allComments = [];
23761
23896
  for (const file of files) {
23762
- const fullPath = statSync5(basePath).isFile() ? basePath : join11(basePath, file);
23897
+ const fullPath = statSync5(basePath).isFile() ? basePath : join12(basePath, file);
23763
23898
  try {
23764
23899
  const source = readFileSync7(fullPath, "utf-8");
23765
23900
  const relPath = statSync5(basePath).isFile() ? relative3(resolve12(basePath, ".."), fullPath) : file;
@@ -26810,8 +26945,16 @@ function resolveCloudAgentByNameOrId(agents, nameOrId) {
26810
26945
  return null;
26811
26946
  return matches.reduce((freshest, candidate) => new Date(candidate.last_seen_at).getTime() > new Date(freshest.last_seen_at).getTime() ? candidate : freshest);
26812
26947
  }
26948
+ function clearIdentityIfMine(agentId, agentName) {
26949
+ const persisted = readPersistedIdentity();
26950
+ if (!persisted)
26951
+ return;
26952
+ if (persisted.agent_id === agentId || agentName && persisted.agent_name === agentName) {
26953
+ clearPersistedIdentity();
26954
+ }
26955
+ }
26813
26956
  function registerAgentCommands(program2) {
26814
- program2.command("init <name>").alias("register").description("Register an agent and get a short UUID (alias: register)").option("-d, --description <text>", "Agent description").action(async (name, opts) => {
26957
+ program2.command("init <name>").alias("register").description("Register an agent and get a short UUID (alias: register)").option("-d, --description <text>", "Agent description").option("--force", "Take over the machine-wide persisted identity even if another session holds it").action(async (name, opts) => {
26815
26958
  const globalOpts = program2.opts();
26816
26959
  try {
26817
26960
  const cloud = getTodosCloudClient();
@@ -26821,14 +26964,25 @@ function registerAgentCommands(program2) {
26821
26964
  console.error(chalk5.red("CONFLICT:"), result.message);
26822
26965
  process.exit(1);
26823
26966
  }
26967
+ const collision = detectIdentityCollision(result.id, result.name);
26968
+ if (collision && !opts.force) {
26969
+ const held = collision.existing.agent_name || collision.existing.agent_id;
26970
+ console.error(chalk5.red(`This machine already has a persisted todos identity: ${held} (registered ${collision.existing.registered_at}).`));
26971
+ console.error(chalk5.yellow(`Overwriting it would make that session attribute its tasks to you.
26972
+ ` + `For a concurrent session, set a per-process identity instead \u2014 it outranks the file and cannot collide:
26973
+ ` + ` export TODOS_AGENT_ID=${result.name}
26974
+ Or pass --force to take over the machine-wide identity.`));
26975
+ process.exit(2);
26976
+ }
26977
+ persistIdentity({ agent_id: result.id, agent_name: result.name, ...globalOpts.session ? { session_id: globalOpts.session } : {} });
26824
26978
  if (globalOpts.json) {
26825
- output(result, true);
26979
+ output({ ...result, identity_persisted: true }, true);
26826
26980
  } else {
26827
26981
  console.log(chalk5.green("Agent registered:"));
26828
26982
  console.log(` ${chalk5.dim("ID:")} ${result.id}`);
26829
26983
  console.log(` ${chalk5.dim("Name:")} ${result.name}`);
26830
26984
  console.log(`
26831
- Use ${chalk5.cyan(`--agent ${result.id}`)} on future commands.`);
26985
+ ${chalk5.dim("Identity saved \u2014 later commands attribute to this agent automatically.")}`);
26832
26986
  }
26833
26987
  } catch (e) {
26834
26988
  handleError(e);
@@ -26885,6 +27039,7 @@ Use ${chalk5.cyan(`--agent ${result.id}`)} on future commands.`);
26885
27039
  if (!result.released) {
26886
27040
  handleError(new Error("Release denied: session_id does not match agent's current session."));
26887
27041
  }
27042
+ clearIdentityIfMine(result.agent.id, result.agent.name);
26888
27043
  if (globalOpts.json) {
26889
27044
  console.log(JSON.stringify({ agent_id: result.agent.id, name: result.agent.name, released: true }));
26890
27045
  } else {
@@ -26901,6 +27056,7 @@ Use ${chalk5.cyan(`--agent ${result.id}`)} on future commands.`);
26901
27056
  if (!released) {
26902
27057
  handleError(new Error("Release denied: session_id does not match agent's current session."));
26903
27058
  }
27059
+ clearIdentityIfMine(a.id, a.name);
26904
27060
  if (globalOpts.json) {
26905
27061
  console.log(JSON.stringify({ agent_id: a.id, name: a.name, released: true }));
26906
27062
  } else {
@@ -27241,6 +27397,7 @@ var init_agent_commands = __esm(() => {
27241
27397
  init_task_lists();
27242
27398
  init_tasks();
27243
27399
  init_helpers();
27400
+ init_creator_identity();
27244
27401
  init_cloud_router();
27245
27402
  });
27246
27403
 
@@ -27283,8 +27440,8 @@ async function findFreePort(start) {
27283
27440
  var DEFAULT_PORT = 19427;
27284
27441
 
27285
27442
  // src/lib/db-backup.ts
27286
- import { existsSync as existsSync14, copyFileSync, mkdirSync as mkdirSync8, readFileSync as readFileSync8, renameSync, statSync as statSync6, writeFileSync as writeFileSync7, unlinkSync } from "fs";
27287
- import { dirname as dirname7, join as join12, resolve as resolve14 } from "path";
27443
+ import { existsSync as existsSync15, copyFileSync, mkdirSync as mkdirSync8, readFileSync as readFileSync8, renameSync, statSync as statSync6, writeFileSync as writeFileSync7, unlinkSync } from "fs";
27444
+ import { dirname as dirname7, join as join13, resolve as resolve14 } from "path";
27288
27445
  import { Database as Database2 } from "bun:sqlite";
27289
27446
  function resolveDbPath(dbPath) {
27290
27447
  if (dbPath)
@@ -27300,7 +27457,7 @@ function resolveDbPath(dbPath) {
27300
27457
  }
27301
27458
  function backupDatabase(outputPath, sourcePath) {
27302
27459
  const source = resolveDbPath(sourcePath);
27303
- if (!existsSync14(source))
27460
+ if (!existsSync15(source))
27304
27461
  throw new Error(`Database not found: ${source}`);
27305
27462
  mkdirSync8(dirname7(outputPath), { recursive: true });
27306
27463
  closeDatabase();
@@ -27325,7 +27482,7 @@ function backupDatabase(outputPath, sourcePath) {
27325
27482
  function checkDatabaseIntegrity(dbPath) {
27326
27483
  const path = dbPath ? resolve14(dbPath) : resolveDbPath();
27327
27484
  const errors = [];
27328
- if (!existsSync14(path)) {
27485
+ if (!existsSync15(path)) {
27329
27486
  return {
27330
27487
  schema_version: DB_BACKUP_SCHEMA,
27331
27488
  path,
@@ -27390,7 +27547,7 @@ function checkDatabaseIntegrity(dbPath) {
27390
27547
  function defaultBackupPath(dbPath) {
27391
27548
  const base = dbPath ? dirname7(resolve14(dbPath)) : dirname7(resolveDbPath());
27392
27549
  const stamp = new Date().toISOString().replace(/[:.]/g, "-");
27393
- return join12(base, "backups", `todos-${stamp}.db`);
27550
+ return join13(base, "backups", `todos-${stamp}.db`);
27394
27551
  }
27395
27552
  var DB_BACKUP_SCHEMA = "todos.db_backup.v1";
27396
27553
  var init_db_backup = __esm(() => {
@@ -27407,7 +27564,7 @@ __export(exports_evidence_redaction, {
27407
27564
  TODOS_EVIDENCE_REDACTION_CONFIRM: () => TODOS_EVIDENCE_REDACTION_CONFIRM
27408
27565
  });
27409
27566
  import { chmodSync } from "fs";
27410
- import { join as join13 } from "path";
27567
+ import { join as join14 } from "path";
27411
27568
  function unique4(values) {
27412
27569
  return Array.from(new Set((values ?? []).map((value) => value.trim()).filter(Boolean)));
27413
27570
  }
@@ -27754,7 +27911,7 @@ function redactEvidenceRows(options, db) {
27754
27911
  }
27755
27912
  function defaultEvidenceRedactionBackupPath(dbPath) {
27756
27913
  const base = defaultBackupPath(dbPath);
27757
- return join13(base.replace(/\.db$/, ""), "pre-redaction.db");
27914
+ return join14(base.replace(/\.db$/, ""), "pre-redaction.db");
27758
27915
  }
27759
27916
  var TODOS_EVIDENCE_REDACTION_SCHEMA = "todos.evidence_redaction.v1", TODOS_EVIDENCE_REDACTION_CONFIRM = "REDACT_TODOS_EVIDENCE";
27760
27917
  var init_evidence_redaction = __esm(() => {
@@ -27770,7 +27927,7 @@ __export(exports_retention_cleanup, {
27770
27927
  applyRetentionCleanup: () => applyRetentionCleanup,
27771
27928
  RETENTION_CLEANUP_CONFIRMATION: () => RETENTION_CLEANUP_CONFIRMATION
27772
27929
  });
27773
- import { existsSync as existsSync15, unlinkSync as unlinkSync2 } from "fs";
27930
+ import { existsSync as existsSync16, unlinkSync as unlinkSync2 } from "fs";
27774
27931
  function normalizeScopes(scopes) {
27775
27932
  if (!scopes || scopes.length === 0)
27776
27933
  return [...ALL_SCOPES];
@@ -27973,7 +28130,7 @@ function applyRetentionCleanup(input, db) {
27973
28130
  for (const artifact of report.candidates.artifact_files) {
27974
28131
  try {
27975
28132
  const path = artifactStorePath(artifact.relative_path);
27976
- if (!existsSync15(path)) {
28133
+ if (!existsSync16(path)) {
27977
28134
  report.warnings.push(`stored artifact already missing: ${artifact.relative_path}`);
27978
28135
  continue;
27979
28136
  }
@@ -28649,8 +28806,8 @@ __export(exports_local_extensions, {
28649
28806
  discoverLocalExtensions: () => discoverLocalExtensions
28650
28807
  });
28651
28808
  import { createHash as createHash6, createVerify } from "crypto";
28652
- import { existsSync as existsSync16, readdirSync as readdirSync3, readFileSync as readFileSync9, statSync as statSync7 } from "fs";
28653
- import { basename as basename6, join as join14, resolve as resolve15 } from "path";
28809
+ import { existsSync as existsSync17, readdirSync as readdirSync3, readFileSync as readFileSync9, statSync as statSync7 } from "fs";
28810
+ import { basename as basename6, join as join15, resolve as resolve15 } from "path";
28654
28811
  function isObject(value) {
28655
28812
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
28656
28813
  }
@@ -28909,10 +29066,10 @@ function verifyExtensionSignature(input) {
28909
29066
  }
28910
29067
  function inspectExtensionSource(source2) {
28911
29068
  const resolved = resolve15(source2);
28912
- if (!existsSync16(resolved))
29069
+ if (!existsSync17(resolved))
28913
29070
  throw new Error(`extension source not found: ${source2}`);
28914
29071
  const stat = statSync7(resolved);
28915
- const manifestPath = stat.isDirectory() ? [join14(resolved, "todos.extension.json"), join14(resolved, "extension.json")].find(existsSync16) : resolved;
29072
+ const manifestPath = stat.isDirectory() ? [join15(resolved, "todos.extension.json"), join15(resolved, "extension.json")].find(existsSync17) : resolved;
28916
29073
  if (!manifestPath)
28917
29074
  throw new Error(`extension directory ${source2} is missing todos.extension.json`);
28918
29075
  const raw = readFileSync9(manifestPath);
@@ -29008,20 +29165,20 @@ function projectExtensionSources(projectPath) {
29008
29165
  return [];
29009
29166
  const root = resolve15(projectPath);
29010
29167
  const candidates = [
29011
- join14(root, "todos.extension.json"),
29012
- join14(root, ".todos", "todos.extension.json")
29168
+ join15(root, "todos.extension.json"),
29169
+ join15(root, ".todos", "todos.extension.json")
29013
29170
  ];
29014
- const extensionDir = join14(root, ".todos", "extensions");
29015
- if (existsSync16(extensionDir)) {
29171
+ const extensionDir = join15(root, ".todos", "extensions");
29172
+ if (existsSync17(extensionDir)) {
29016
29173
  for (const entry of readdirSync3(extensionDir)) {
29017
29174
  if (entry.startsWith("."))
29018
29175
  continue;
29019
- const full = join14(extensionDir, entry);
29176
+ const full = join15(extensionDir, entry);
29020
29177
  if (statSync7(full).isDirectory() || entry.endsWith(".json"))
29021
29178
  candidates.push(full);
29022
29179
  }
29023
29180
  }
29024
- return candidates.filter(existsSync16);
29181
+ return candidates.filter(existsSync17);
29025
29182
  }
29026
29183
  function discoverLocalExtensions(options = {}) {
29027
29184
  const config = loadConfig();
@@ -31109,6 +31266,10 @@ class PostgresJsonRecordStore {
31109
31266
  conds.push(`payload->>'assigned_to' = ${p(filter.assigned_to)}`);
31110
31267
  if (filter.agent_id !== undefined)
31111
31268
  conds.push(`payload->>'agent_id' = ${p(filter.agent_id)}`);
31269
+ if (filter.created_by !== undefined)
31270
+ conds.push(`LOWER(payload->>'created_by') = LOWER(${p(filter.created_by)})`);
31271
+ if (filter.not_created_by !== undefined)
31272
+ conds.push(`(payload->>'created_by' IS NULL OR LOWER(payload->>'created_by') <> LOWER(${p(filter.not_created_by)}))`);
31112
31273
  if (filter.session_id !== undefined)
31113
31274
  conds.push(`payload->>'session_id' = ${p(filter.session_id)}`);
31114
31275
  if (filter.tags?.length) {
@@ -31622,7 +31783,7 @@ async function createTask2(input, store, context) {
31622
31783
  description: input.description ?? null,
31623
31784
  status: input.status ?? "pending",
31624
31785
  priority: input.priority ?? "medium",
31625
- agent_id: input.agent_id ?? null,
31786
+ agent_id: input.agent_id ?? context?.agentId ?? null,
31626
31787
  assigned_to: input.assigned_to ?? null,
31627
31788
  session_id: input.session_id ?? context?.sessionId ?? null,
31628
31789
  working_dir: input.working_dir ?? null,
@@ -31647,7 +31808,8 @@ async function createTask2(input, store, context) {
31647
31808
  confidence: input.confidence ?? null,
31648
31809
  reason: input.reason ?? null,
31649
31810
  spawned_from_session: input.spawned_from_session ?? null,
31650
- assigned_by: input.assigned_by ?? null,
31811
+ assigned_by: input.assigned_by ?? input.agent_id ?? context?.agentId ?? null,
31812
+ created_by: input.created_by ?? input.agent_id ?? context?.agentId ?? null,
31651
31813
  assigned_from_project: input.assigned_from_project ?? null,
31652
31814
  task_type: input.task_type ?? null,
31653
31815
  cost_tokens: 0,
@@ -31684,7 +31846,8 @@ async function updateTask2(id, input, store) {
31684
31846
  tags: input.tags ?? existing.tags,
31685
31847
  metadata: input.metadata ?? existing.metadata,
31686
31848
  requires_approval: input.requires_approval ?? existing.requires_approval,
31687
- task_list_id: input.task_list_id !== undefined ? input.task_list_id : existing.task_list_id
31849
+ task_list_id: input.task_list_id !== undefined ? input.task_list_id : existing.task_list_id,
31850
+ created_by: existing.created_by
31688
31851
  };
31689
31852
  await store.upsert("tasks", task);
31690
31853
  return task;
@@ -33412,8 +33575,8 @@ var exports_doctor = {};
33412
33575
  __export(exports_doctor, {
33413
33576
  runTodosDoctor: () => runTodosDoctor
33414
33577
  });
33415
- import { chmodSync as chmodSync2, copyFileSync as copyFileSync2, existsSync as existsSync17, mkdirSync as mkdirSync9, statSync as statSync8 } from "fs";
33416
- import { basename as basename7, dirname as dirname8, join as join15 } from "path";
33578
+ import { chmodSync as chmodSync2, copyFileSync as copyFileSync2, existsSync as existsSync18, mkdirSync as mkdirSync9, statSync as statSync8 } from "fs";
33579
+ import { basename as basename7, dirname as dirname8, join as join16 } from "path";
33417
33580
  function tableExists3(db, table) {
33418
33581
  return Boolean(db.query("SELECT name FROM sqlite_master WHERE type='table' AND name=?").get(table));
33419
33582
  }
@@ -33507,7 +33670,7 @@ function findMissingProjectRoots(db) {
33507
33670
  continue;
33508
33671
  if (!row.path.startsWith("/"))
33509
33672
  continue;
33510
- if (!existsSync17(row.path))
33673
+ if (!existsSync18(row.path))
33511
33674
  missing++;
33512
33675
  }
33513
33676
  return missing;
@@ -33567,16 +33730,16 @@ function databasePermissionsAreUnsafe(dbPath) {
33567
33730
  function createBackup(dbPath) {
33568
33731
  if (dbPath === ":memory:" || dbPath.startsWith("file::memory:"))
33569
33732
  return;
33570
- if (!existsSync17(dbPath))
33733
+ if (!existsSync18(dbPath))
33571
33734
  return;
33572
33735
  const stamp = now().replace(/[:.]/g, "-");
33573
- const backupDir = join15(dirname8(dbPath), `${basename7(dbPath)}.backup-${stamp}`);
33736
+ const backupDir = join16(dirname8(dbPath), `${basename7(dbPath)}.backup-${stamp}`);
33574
33737
  const files = [];
33575
33738
  mkdirSync9(backupDir, { recursive: true });
33576
33739
  for (const source2 of [dbPath, `${dbPath}-wal`, `${dbPath}-shm`]) {
33577
- if (!existsSync17(source2))
33740
+ if (!existsSync18(source2))
33578
33741
  continue;
33579
- const target = join15(backupDir, basename7(source2));
33742
+ const target = join16(backupDir, basename7(source2));
33580
33743
  copyFileSync2(source2, target);
33581
33744
  files.push(target);
33582
33745
  }
@@ -33835,7 +33998,7 @@ var init_doctor = __esm(() => {
33835
33998
  });
33836
33999
 
33837
34000
  // src/server/routes.ts
33838
- import { join as join16, resolve as resolve17, sep as sep4 } from "path";
34001
+ import { join as join17, resolve as resolve17, sep as sep4 } from "path";
33839
34002
  function parseFieldsParam(url) {
33840
34003
  const fieldsParam = url.searchParams.get("fields");
33841
34004
  return fieldsParam ? fieldsParam.split(",").map((f) => f.trim()).filter(Boolean) : undefined;
@@ -34016,11 +34179,15 @@ async function handleCreateTask(req, ctx, json2, taskToSummary2) {
34016
34179
  const body = await req.json();
34017
34180
  if (!body.title)
34018
34181
  return json2({ error: "Missing 'title'" }, 400);
34182
+ const createdBy = body.created_by ?? body.agent_id ?? "dashboard";
34019
34183
  const task = createTask({
34020
34184
  title: body.title,
34021
34185
  description: body.description,
34022
34186
  priority: body.priority,
34023
- project_id: body.project_id
34187
+ project_id: body.project_id,
34188
+ agent_id: body.agent_id ?? createdBy,
34189
+ created_by: createdBy,
34190
+ ...body.assigned_to ? { assigned_to: body.assigned_to } : {}
34024
34191
  });
34025
34192
  ctx.broadcastEvent({ type: "task", task_id: task.id, action: "created", agent_id: task.agent_id, project_id: task.project_id });
34026
34193
  return json2(taskToSummary2(task), 201);
@@ -34626,7 +34793,7 @@ function handleStaticFiles(path, method, ctx, json2, serveStaticFile2) {
34626
34793
  if (!ctx.dashboardExists || method !== "GET" && method !== "HEAD")
34627
34794
  return null;
34628
34795
  if (path !== "/") {
34629
- const filePath = join16(ctx.dashboardDir, path);
34796
+ const filePath = join17(ctx.dashboardDir, path);
34630
34797
  const resolvedFile = resolve17(filePath);
34631
34798
  const resolvedBase = resolve17(ctx.dashboardDir);
34632
34799
  if (!resolvedFile.startsWith(resolvedBase + sep4) && resolvedFile !== resolvedBase) {
@@ -34636,7 +34803,7 @@ function handleStaticFiles(path, method, ctx, json2, serveStaticFile2) {
34636
34803
  if (res2)
34637
34804
  return res2;
34638
34805
  }
34639
- const indexPath = join16(ctx.dashboardDir, "index.html");
34806
+ const indexPath = join17(ctx.dashboardDir, "index.html");
34640
34807
  const res = serveStaticFile2(indexPath);
34641
34808
  if (res)
34642
34809
  return res;
@@ -35124,7 +35291,11 @@ function createLocalSqliteTodosStorageAdapter(options = {}) {
35124
35291
  sync: true
35125
35292
  },
35126
35293
  tasks: {
35127
- create: (input) => createTask(input, database()),
35294
+ create: (input, context) => createTask({
35295
+ ...input,
35296
+ agent_id: input.agent_id ?? context?.agentId,
35297
+ created_by: input.created_by ?? input.agent_id ?? context?.agentId
35298
+ }, database()),
35128
35299
  get: (id) => getTask(id, database()),
35129
35300
  resolveRef: (ref) => resolveTaskRefLocal(database(), ref),
35130
35301
  list: (filter = {}) => listTasksMaybeSearch(filter, database()),
@@ -37740,6 +37911,8 @@ async function handleV1Request(req, url, dependencies = {}) {
37740
37911
  ...url.searchParams.get("task_list_id") ? { task_list_id: url.searchParams.get("task_list_id") } : {},
37741
37912
  ...url.searchParams.get("assigned_to") ? { assigned_to: url.searchParams.get("assigned_to") } : {},
37742
37913
  ...url.searchParams.get("agent_id") ? { agent_id: url.searchParams.get("agent_id") } : {},
37914
+ ...url.searchParams.get("created_by") ? { created_by: url.searchParams.get("created_by") } : {},
37915
+ ...url.searchParams.get("not_created_by") ? { not_created_by: url.searchParams.get("not_created_by") } : {},
37743
37916
  ...url.searchParams.get("tags") ? {
37744
37917
  tags: url.searchParams.get("tags").split(",").map((tag) => tag.trim()).filter(Boolean)
37745
37918
  } : {},
@@ -42985,6 +43158,8 @@ function registerTaskCrudTools(server, ctx) {
42985
43158
  project_id: exports_external.string().optional().describe("Project ID"),
42986
43159
  task_list_id: exports_external.string().optional().describe("Task list ID"),
42987
43160
  assigned_to: exports_external.string().optional().describe("Agent ID or name to assign to"),
43161
+ created_by: exports_external.string().optional().describe("Agent who FILED this task. Defaults to the ambient agent identity (todos init / TODOS_AGENT_ID)."),
43162
+ unassigned: exports_external.boolean().optional().describe("Deliberately file with no assignee. Without it, the task defaults to the filer."),
42988
43163
  depends_on: exports_external.array(exports_external.string()).optional().describe("Array of task IDs this task depends on"),
42989
43164
  short_id: exports_external.string().nullable().optional().describe("Short ID (auto-generated if not provided, disabled if null)"),
42990
43165
  tags: exports_external.array(exports_external.string()).optional().describe("Tags for the task"),
@@ -42995,12 +43170,18 @@ function registerTaskCrudTools(server, ctx) {
42995
43170
  retry_count: exports_external.number().optional().describe("Max retry count for agent failures")
42996
43171
  }, async (params) => {
42997
43172
  try {
42998
- const { depends_on, assigned_to, project_id, task_list_id, tags, estimate, confidence, retry_count, deadline, ...rest } = params;
43173
+ const { depends_on, assigned_to, project_id, task_list_id, tags, estimate, confidence, retry_count, deadline, created_by, unassigned, ...rest } = params;
43174
+ const creator = resolveCreatorIdentity(created_by);
43175
+ const assignee = assigned_to || (unassigned ? undefined : creator.agent_id || undefined);
42999
43176
  const cloud = getTodosCloudClient();
43000
43177
  if (cloud) {
43001
43178
  const payload = { ...rest };
43002
- if (assigned_to)
43003
- payload.assigned_to = assigned_to;
43179
+ if (creator.agent_id) {
43180
+ payload.created_by = creator.agent_id;
43181
+ payload.agent_id = payload.agent_id ?? creator.agent_id;
43182
+ }
43183
+ if (assignee)
43184
+ payload.assigned_to = assignee;
43004
43185
  if (project_id)
43005
43186
  payload.project_id = project_id;
43006
43187
  if (task_list_id)
@@ -43021,8 +43202,12 @@ function registerTaskCrudTools(server, ctx) {
43021
43202
  return { content: [{ type: "text", text: mutationTaskResponse(created) }] };
43022
43203
  }
43023
43204
  const resolved = { ...rest };
43024
- if (assigned_to)
43025
- resolved.assigned_to = resolveAssignee(assigned_to);
43205
+ if (creator.agent_id) {
43206
+ resolved.created_by = creator.agent_id;
43207
+ resolved.agent_id = resolved.agent_id ?? creator.agent_id;
43208
+ }
43209
+ if (assignee)
43210
+ resolved.assigned_to = resolveAssignee(assignee);
43026
43211
  if (project_id)
43027
43212
  resolved.project_id = resolveId(project_id, "projects");
43028
43213
  if (task_list_id)
@@ -43108,6 +43293,8 @@ function registerTaskCrudTools(server, ctx) {
43108
43293
  project_id: exports_external.string().optional().describe("Filter by project"),
43109
43294
  task_list_id: exports_external.string().optional().describe("Filter by task list"),
43110
43295
  assigned_to: exports_external.string().optional().describe("Filter by assignee (agent ID or name, empty string = unassigned)"),
43296
+ created_by: exports_external.string().optional().describe("Filter by the agent who FILED the task"),
43297
+ not_created_by: exports_external.string().optional().describe('Exclude tasks filed by this agent. With assigned_to=<me> this is the "my inbox, minus my own filings" query.'),
43111
43298
  tags: exports_external.array(exports_external.string()).optional().describe("Filter by tags (AND logic)"),
43112
43299
  created_after: exports_external.string().optional().describe("ISO date \u2014 tasks created after this date"),
43113
43300
  created_before: exports_external.string().optional().describe("ISO date \u2014 tasks created before this date"),
@@ -43300,6 +43487,7 @@ var init_task_crud2 = __esm(() => {
43300
43487
  init_tasks();
43301
43488
  init_types();
43302
43489
  init_token_utils();
43490
+ init_creator_identity();
43303
43491
  init_cloud_router();
43304
43492
  });
43305
43493
 
@@ -43496,8 +43684,8 @@ var exports_mention_resolver = {};
43496
43684
  __export(exports_mention_resolver, {
43497
43685
  resolveMentions: () => resolveMentions
43498
43686
  });
43499
- import { existsSync as existsSync18, readdirSync as readdirSync4, readFileSync as readFileSync10, statSync as statSync9 } from "fs";
43500
- import { basename as basename8, isAbsolute, join as join17, relative as relative5, resolve as resolve18, sep as sep5 } from "path";
43687
+ import { existsSync as existsSync19, readdirSync as readdirSync4, readFileSync as readFileSync10, statSync as statSync9 } from "fs";
43688
+ import { basename as basename8, isAbsolute, join as join18, relative as relative5, resolve as resolve18, sep as sep5 } from "path";
43501
43689
  function blankResolution(parsed) {
43502
43690
  return {
43503
43691
  input: parsed.input,
@@ -43595,7 +43783,7 @@ function resolveFile(parsed, workspace) {
43595
43783
  return resolution;
43596
43784
  }
43597
43785
  resolution.path = relPath;
43598
- if (!existsSync18(absolutePath)) {
43786
+ if (!existsSync19(absolutePath)) {
43599
43787
  resolution.warnings.push("file does not exist in the local workspace");
43600
43788
  return resolution;
43601
43789
  }
@@ -43628,7 +43816,7 @@ function walkSourceFiles(root, current = root, files = []) {
43628
43816
  if (SKIP_DIRS2.has(entry2.name))
43629
43817
  continue;
43630
43818
  }
43631
- const absolutePath = join17(current, entry2.name);
43819
+ const absolutePath = join18(current, entry2.name);
43632
43820
  if (entry2.isDirectory()) {
43633
43821
  if (!SKIP_DIRS2.has(entry2.name))
43634
43822
  walkSourceFiles(root, absolutePath, files);
@@ -46395,7 +46583,7 @@ __export(exports_release_compatibility, {
46395
46583
  LOCAL_RELEASE_COMPATIBILITY_SCHEMA_VERSION: () => LOCAL_RELEASE_COMPATIBILITY_SCHEMA_VERSION
46396
46584
  });
46397
46585
  import { readFileSync as readFileSync11 } from "fs";
46398
- import { join as join18, resolve as resolve19 } from "path";
46586
+ import { join as join19, resolve as resolve19 } from "path";
46399
46587
  import { Database as Database3 } from "bun:sqlite";
46400
46588
  function pass(id, message, details) {
46401
46589
  return { id, status: "passed", message, details };
@@ -46407,7 +46595,7 @@ function warn(id, message, details) {
46407
46595
  return { id, status: "warning", message, details };
46408
46596
  }
46409
46597
  function readPackageJson2(root) {
46410
- return JSON.parse(readFileSync11(join18(root, "package.json"), "utf8"));
46598
+ return JSON.parse(readFileSync11(join19(root, "package.json"), "utf8"));
46411
46599
  }
46412
46600
  function sortedKeys(value) {
46413
46601
  return Object.keys(value ?? {}).sort((left, right) => left.localeCompare(right));
@@ -52350,7 +52538,7 @@ __export(exports_verification_providers, {
52350
52538
  getVerificationRecord: () => getVerificationRecord,
52351
52539
  discoverVerificationProviderCapabilities: () => discoverVerificationProviderCapabilities
52352
52540
  });
52353
- import { existsSync as existsSync19, readFileSync as readFileSync12 } from "fs";
52541
+ import { existsSync as existsSync20, readFileSync as readFileSync12 } from "fs";
52354
52542
  function normalizeName6(name) {
52355
52543
  const normalized = name.trim().toLowerCase();
52356
52544
  if (!/^[a-z0-9][a-z0-9_-]{0,63}$/.test(normalized)) {
@@ -52502,7 +52690,7 @@ Timed out after ${provider.timeout_ms}ms`);
52502
52690
  };
52503
52691
  }
52504
52692
  function runCiLogProvider(input) {
52505
- const text = input.log_text ?? (input.log_path && existsSync19(input.log_path) ? readFileSync12(input.log_path, "utf-8") : "");
52693
+ const text = input.log_text ?? (input.log_path && existsSync20(input.log_path) ? readFileSync12(input.log_path, "utf-8") : "");
52506
52694
  return {
52507
52695
  status: classifyLog(text),
52508
52696
  attempts: 1,
@@ -52514,7 +52702,7 @@ function runBrowserProvider(input) {
52514
52702
  if (!input.artifact_path) {
52515
52703
  return { status: "unknown", attempts: 1, exit_code: null, output_summary: "browser provider needs a screenshot or artifact path" };
52516
52704
  }
52517
- if (!existsSync19(input.artifact_path)) {
52705
+ if (!existsSync20(input.artifact_path)) {
52518
52706
  return { status: "failed", attempts: 1, exit_code: null, output_summary: `artifact not found: ${input.artifact_path}` };
52519
52707
  }
52520
52708
  return {
@@ -55103,7 +55291,7 @@ __export(exports_onboarding_fixtures, {
55103
55291
  TODOS_ONBOARDING_FIXTURE_LIBRARY_VERSION: () => TODOS_ONBOARDING_FIXTURE_LIBRARY_VERSION
55104
55292
  });
55105
55293
  import { mkdirSync as mkdirSync11, writeFileSync as writeFileSync9 } from "fs";
55106
- import { join as join19 } from "path";
55294
+ import { join as join20 } from "path";
55107
55295
  function emptyData() {
55108
55296
  return {
55109
55297
  projects: [],
@@ -55171,6 +55359,7 @@ function task(input) {
55171
55359
  reason: "Bundled deterministic onboarding fixture",
55172
55360
  spawned_from_session: null,
55173
55361
  assigned_by: null,
55362
+ created_by: null,
55174
55363
  assigned_from_project: null,
55175
55364
  task_type: "onboarding",
55176
55365
  cost_tokens: 0,
@@ -55437,7 +55626,7 @@ function writeOnboardingFixtureFiles(directory) {
55437
55626
  mkdirSync11(directory, { recursive: true });
55438
55627
  const files = [];
55439
55628
  for (const fixture of allFixtures()) {
55440
- const path = join19(directory, `${fixture.summary.name}.bridge.json`);
55629
+ const path = join20(directory, `${fixture.summary.name}.bridge.json`);
55441
55630
  writeFileSync9(path, `${JSON.stringify(fixture.bundle, null, 2)}
55442
55631
  `, "utf-8");
55443
55632
  files.push(path);
@@ -61649,16 +61838,16 @@ __export(exports_environment_snapshots, {
61649
61838
  captureEnvironmentSnapshot: () => captureEnvironmentSnapshot
61650
61839
  });
61651
61840
  import { createHash as createHash13 } from "crypto";
61652
- import { existsSync as existsSync20, readFileSync as readFileSync15, statSync as statSync10 } from "fs";
61841
+ import { existsSync as existsSync21, readFileSync as readFileSync15, statSync as statSync10 } from "fs";
61653
61842
  import { hostname as hostname2, platform, arch } from "os";
61654
- import { dirname as dirname10, join as join20, resolve as resolve21 } from "path";
61843
+ import { dirname as dirname10, join as join21, resolve as resolve21 } from "path";
61655
61844
  import { tmpdir as tmpdir4 } from "os";
61656
61845
  function sha2567(value) {
61657
61846
  return createHash13("sha256").update(value).digest("hex");
61658
61847
  }
61659
61848
  function fileRecord(root, relativePath) {
61660
- const path = join20(root, relativePath);
61661
- if (!existsSync20(path))
61849
+ const path = join21(root, relativePath);
61850
+ if (!existsSync21(path))
61662
61851
  return null;
61663
61852
  const stat = statSync10(path);
61664
61853
  if (!stat.isFile())
@@ -61670,7 +61859,7 @@ function manifestRecord(root, relativePath) {
61670
61859
  const base = fileRecord(root, relativePath);
61671
61860
  if (!base)
61672
61861
  return null;
61673
- const parsed = readJsonFile(join20(root, relativePath));
61862
+ const parsed = readJsonFile(join21(root, relativePath));
61674
61863
  if (!parsed)
61675
61864
  return { ...base, redacted: {} };
61676
61865
  const redacted = redactValue({
@@ -61765,8 +61954,8 @@ function commandEnv(env, includeValues) {
61765
61954
  function defaultSnapshotDir() {
61766
61955
  const dbPath = getDatabasePath();
61767
61956
  if (dbPath === ":memory:" || dbPath.startsWith("file::memory:"))
61768
- return join20(tmpdir4(), "hasna-todos", "environment-snapshots");
61769
- return join20(dirname10(resolve21(dbPath)), "environment-snapshots");
61957
+ return join21(tmpdir4(), "hasna-todos", "environment-snapshots");
61958
+ return join21(dirname10(resolve21(dbPath)), "environment-snapshots");
61770
61959
  }
61771
61960
  function snapshotWithId(snapshot) {
61772
61961
  const digest = sha2567(JSON.stringify(snapshot)).slice(0, 24);
@@ -61813,7 +62002,7 @@ function captureEnvironmentSnapshot(input = {}) {
61813
62002
  });
61814
62003
  }
61815
62004
  function writeEnvironmentSnapshot(snapshot, outputPath) {
61816
- const path = outputPath ? resolve21(outputPath) : join20(defaultSnapshotDir(), `${snapshot.id}.json`);
62005
+ const path = outputPath ? resolve21(outputPath) : join21(defaultSnapshotDir(), `${snapshot.id}.json`);
61817
62006
  ensureDir(dirname10(path));
61818
62007
  writeJsonFile(path, snapshot);
61819
62008
  return path;
@@ -62621,27 +62810,27 @@ __export(exports_serve, {
62621
62810
  SECURITY_HEADERS: () => SECURITY_HEADERS,
62622
62811
  MIME_TYPES: () => MIME_TYPES
62623
62812
  });
62624
- import { existsSync as existsSync21 } from "fs";
62625
- import { join as join21, dirname as dirname11, extname } from "path";
62813
+ import { existsSync as existsSync22 } from "fs";
62814
+ import { join as join22, dirname as dirname11, extname } from "path";
62626
62815
  import { fileURLToPath } from "url";
62627
62816
  function resolveDashboardDir() {
62628
62817
  const candidates = [];
62629
62818
  try {
62630
62819
  const scriptDir = dirname11(fileURLToPath(import.meta.url));
62631
- candidates.push(join21(scriptDir, "..", "dashboard", "dist"));
62632
- candidates.push(join21(scriptDir, "..", "..", "dashboard", "dist"));
62820
+ candidates.push(join22(scriptDir, "..", "dashboard", "dist"));
62821
+ candidates.push(join22(scriptDir, "..", "..", "dashboard", "dist"));
62633
62822
  } catch {}
62634
62823
  if (process.argv[1]) {
62635
62824
  const mainDir = dirname11(process.argv[1]);
62636
- candidates.push(join21(mainDir, "..", "dashboard", "dist"));
62637
- candidates.push(join21(mainDir, "..", "..", "dashboard", "dist"));
62825
+ candidates.push(join22(mainDir, "..", "dashboard", "dist"));
62826
+ candidates.push(join22(mainDir, "..", "..", "dashboard", "dist"));
62638
62827
  }
62639
- candidates.push(join21(process.cwd(), "dashboard", "dist"));
62828
+ candidates.push(join22(process.cwd(), "dashboard", "dist"));
62640
62829
  for (const candidate of candidates) {
62641
- if (existsSync21(candidate))
62830
+ if (existsSync22(candidate))
62642
62831
  return candidate;
62643
62832
  }
62644
- return join21(process.cwd(), "dashboard", "dist");
62833
+ return join22(process.cwd(), "dashboard", "dist");
62645
62834
  }
62646
62835
  function getProvidedApiKey(req) {
62647
62836
  const headerKey = req.headers.get("x-api-key");
@@ -62713,7 +62902,7 @@ function json(data, status = 200, headers) {
62713
62902
  });
62714
62903
  }
62715
62904
  function serveStaticFile(filePath) {
62716
- if (!existsSync21(filePath))
62905
+ if (!existsSync22(filePath))
62717
62906
  return null;
62718
62907
  const ext = extname(filePath);
62719
62908
  const contentType = MIME_TYPES[ext] || "application/octet-stream";
@@ -62814,7 +63003,7 @@ data: ${data}
62814
63003
  filteredSseClients.delete(client);
62815
63004
  }
62816
63005
  const dashboardDir = resolveDashboardDir();
62817
- const dashboardExists = existsSync21(dashboardDir);
63006
+ const dashboardExists = existsSync22(dashboardDir);
62818
63007
  if (!dashboardExists) {
62819
63008
  console.error(`
62820
63009
  Dashboard not found at: ${dashboardDir}`);
@@ -64687,12 +64876,12 @@ __export(exports_config_serve_commands, {
64687
64876
  registerConfigServeCommands: () => registerConfigServeCommands
64688
64877
  });
64689
64878
  import chalk6 from "chalk";
64690
- import { existsSync as existsSync22, mkdirSync as mkdirSync12, readFileSync as readFileSync16, writeFileSync as writeFileSync10 } from "fs";
64691
- import { dirname as dirname12, join as join22 } from "path";
64879
+ import { existsSync as existsSync23, mkdirSync as mkdirSync12, readFileSync as readFileSync16, writeFileSync as writeFileSync10 } from "fs";
64880
+ import { dirname as dirname12, join as join23 } from "path";
64692
64881
  function registerConfigServeCommands(program2) {
64693
64882
  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) => {
64694
64883
  const globalOpts = program2.opts();
64695
- const configPath = join22(getTodosGlobalDir(), "config.json");
64884
+ const configPath = join23(getTodosGlobalDir(), "config.json");
64696
64885
  if (opts.get) {
64697
64886
  const config2 = loadConfig();
64698
64887
  const keys = opts.get.split(".");
@@ -64729,7 +64918,7 @@ function registerConfigServeCommands(program2) {
64729
64918
  }
64730
64919
  obj[keys[keys.length - 1]] = parsedValue;
64731
64920
  const dir = dirname12(configPath);
64732
- if (!existsSync22(dir))
64921
+ if (!existsSync23(dir))
64733
64922
  mkdirSync12(dir, { recursive: true });
64734
64923
  writeFileSync10(configPath, JSON.stringify(config2, null, 2));
64735
64924
  if (globalOpts.json) {
@@ -65909,8 +66098,8 @@ __export(exports_routing_doctor, {
65909
66098
  classifyTaskRouting: () => classifyTaskRouting,
65910
66099
  TODOS_ROUTING_DOCTOR_SCHEMA_VERSION: () => TODOS_ROUTING_DOCTOR_SCHEMA_VERSION
65911
66100
  });
65912
- import { copyFileSync as copyFileSync3, existsSync as existsSync23, mkdirSync as mkdirSync13, writeFileSync as writeFileSync11 } from "fs";
65913
- import { basename as basename9, dirname as dirname13, join as join23 } from "path";
66101
+ import { copyFileSync as copyFileSync3, existsSync as existsSync24, mkdirSync as mkdirSync13, writeFileSync as writeFileSync11 } from "fs";
66102
+ import { basename as basename9, dirname as dirname13, join as join24 } from "path";
65914
66103
  function normalizePath5(path) {
65915
66104
  if (!path)
65916
66105
  return null;
@@ -66212,16 +66401,16 @@ function evaluateTaskRouting(ctx) {
66212
66401
  function createBackup2(dbPath, generatedAt) {
66213
66402
  if (dbPath === ":memory:" || dbPath.startsWith("file::memory:"))
66214
66403
  return;
66215
- if (!existsSync23(dbPath))
66404
+ if (!existsSync24(dbPath))
66216
66405
  return;
66217
66406
  const stamp = generatedAt.replace(/[:.]/g, "-");
66218
- const backupDir = join23(dirname13(dbPath), `${basename9(dbPath)}.routing-doctor-backup-${stamp}`);
66407
+ const backupDir = join24(dirname13(dbPath), `${basename9(dbPath)}.routing-doctor-backup-${stamp}`);
66219
66408
  const files = [];
66220
66409
  mkdirSync13(backupDir, { recursive: true });
66221
66410
  for (const source3 of [dbPath, `${dbPath}-wal`, `${dbPath}-shm`]) {
66222
- if (!existsSync23(source3))
66411
+ if (!existsSync24(source3))
66223
66412
  continue;
66224
- const target = join23(backupDir, basename9(source3));
66413
+ const target = join24(backupDir, basename9(source3));
66225
66414
  copyFileSync3(source3, target);
66226
66415
  files.push(target);
66227
66416
  }
@@ -66325,7 +66514,7 @@ function runRoutingDoctor(options = {}) {
66325
66514
  }
66326
66515
  const applied = repairs.filter((r) => r.applied);
66327
66516
  if (applied.length > 0) {
66328
- const undoPath = options.undoRecordPath ?? join23(process.cwd(), `todos-routing-doctor-undo-${generatedAt.replace(/[:.]/g, "-")}.json`);
66517
+ const undoPath = options.undoRecordPath ?? join24(process.cwd(), `todos-routing-doctor-undo-${generatedAt.replace(/[:.]/g, "-")}.json`);
66329
66518
  const undoRecord = {
66330
66519
  schema_version: TODOS_ROUTING_DOCTOR_SCHEMA_VERSION,
66331
66520
  purpose: "Undo record for routing-doctor --apply. Restore each field with the prior value below.",
@@ -66424,8 +66613,8 @@ __export(exports_task_route_sources, {
66424
66613
  });
66425
66614
  import { Database as Database4 } from "bun:sqlite";
66426
66615
  import { createHash as createHash14 } from "crypto";
66427
- import { existsSync as existsSync24, readdirSync as readdirSync5, statSync as statSync11 } from "fs";
66428
- import { basename as basename10, dirname as dirname14, join as join24, resolve as resolve22 } from "path";
66616
+ import { existsSync as existsSync25, readdirSync as readdirSync5, statSync as statSync11 } from "fs";
66617
+ import { basename as basename10, dirname as dirname14, join as join25, resolve as resolve22 } from "path";
66429
66618
  function normalizePath6(input) {
66430
66619
  return resolve22(input);
66431
66620
  }
@@ -66489,8 +66678,8 @@ function discoverStoresUnderRoot(sourceRoot) {
66489
66678
  const rootPath = normalizePath6(sourceRoot);
66490
66679
  const errors2 = [];
66491
66680
  const stores = [];
66492
- if (!existsSync24(rootPath)) {
66493
- const ref = createStoreRef(join24(rootPath, TODO_STORE_RELATIVE_PATH));
66681
+ if (!existsSync25(rootPath)) {
66682
+ const ref = createStoreRef(join25(rootPath, TODO_STORE_RELATIVE_PATH));
66494
66683
  errors2.push({
66495
66684
  ...ref,
66496
66685
  code: "SOURCE_ROOT_MISSING",
@@ -66502,7 +66691,7 @@ function discoverStoresUnderRoot(sourceRoot) {
66502
66691
  try {
66503
66692
  rootStat = statSync11(rootPath);
66504
66693
  } catch (error2) {
66505
- const ref = createStoreRef(join24(rootPath, TODO_STORE_RELATIVE_PATH));
66694
+ const ref = createStoreRef(join25(rootPath, TODO_STORE_RELATIVE_PATH));
66506
66695
  errors2.push({
66507
66696
  ...ref,
66508
66697
  code: "SOURCE_ROOT_UNREADABLE",
@@ -66515,8 +66704,8 @@ function discoverStoresUnderRoot(sourceRoot) {
66515
66704
  return { stores, errors: errors2 };
66516
66705
  }
66517
66706
  function scanDirectory(dir, depth) {
66518
- const candidate = join24(dir, TODO_STORE_RELATIVE_PATH);
66519
- if (existsSync24(candidate)) {
66707
+ const candidate = join25(dir, TODO_STORE_RELATIVE_PATH);
66708
+ if (existsSync25(candidate)) {
66520
66709
  stores.push(createStoreRef(candidate));
66521
66710
  }
66522
66711
  if (depth >= ROOT_SCAN_MAX_DEPTH)
@@ -66536,7 +66725,7 @@ function discoverStoresUnderRoot(sourceRoot) {
66536
66725
  for (const entry2 of entries) {
66537
66726
  if (!entry2.isDirectory() || SKIPPED_SCAN_DIRS.has(entry2.name))
66538
66727
  continue;
66539
- scanDirectory(join24(dir, entry2.name), depth + 1);
66728
+ scanDirectory(join25(dir, entry2.name), depth + 1);
66540
66729
  }
66541
66730
  }
66542
66731
  scanDirectory(rootPath, 0);
@@ -66562,7 +66751,7 @@ function collectStoreRefs(input) {
66562
66751
  };
66563
66752
  }
66564
66753
  function openReadonlyStore(ref) {
66565
- if (!existsSync24(ref.source_db_path)) {
66754
+ if (!existsSync25(ref.source_db_path)) {
66566
66755
  throw Object.assign(new Error(`Store does not exist: ${ref.source_db_path}`), { code: "STORE_MISSING" });
66567
66756
  }
66568
66757
  return new Database4(ref.source_db_path, { readonly: true, create: false });
@@ -66736,7 +66925,7 @@ var init_task_route_sources = __esm(() => {
66736
66925
  init_task_crud();
66737
66926
  init_redaction();
66738
66927
  init_task_routing();
66739
- TODO_STORE_RELATIVE_PATH = join24(".hasna", "todos", "todos.db");
66928
+ TODO_STORE_RELATIVE_PATH = join25(".hasna", "todos", "todos.db");
66740
66929
  SKIPPED_SCAN_DIRS = new Set([
66741
66930
  ".git",
66742
66931
  ".hg",
@@ -68245,9 +68434,9 @@ Findings`));
68245
68434
  const db = getDatabase();
68246
68435
  const row = db.query("SELECT COUNT(*) as count FROM tasks").get();
68247
68436
  const { statSync: statSync12 } = await import("fs");
68248
- const { join: join25 } = await import("path");
68437
+ const { join: join26 } = await import("path");
68249
68438
  const home = process.env["HOME"] || process.env["USERPROFILE"] || "~";
68250
- const dbPath = process.env["HASNA_TODOS_DB_PATH"] || process.env["TODOS_DB_PATH"] || join25(home, ".hasna", "todos", "todos.db");
68439
+ const dbPath = process.env["HASNA_TODOS_DB_PATH"] || process.env["TODOS_DB_PATH"] || join26(home, ".hasna", "todos", "todos.db");
68251
68440
  let size = "unknown";
68252
68441
  try {
68253
68442
  size = `${(statSync12(dbPath).size / 1024 / 1024).toFixed(1)} MB`;
@@ -70161,21 +70350,21 @@ __export(exports_mcp_hooks_commands, {
70161
70350
  });
70162
70351
  import chalk9 from "chalk";
70163
70352
  import { execSync as execSync3 } from "child_process";
70164
- import { existsSync as existsSync25, readFileSync as readFileSync18, writeFileSync as writeFileSync13, mkdirSync as mkdirSync14, chmodSync as chmodSync3 } from "fs";
70165
- import { dirname as dirname15, join as join25 } from "path";
70353
+ import { existsSync as existsSync26, readFileSync as readFileSync18, writeFileSync as writeFileSync13, mkdirSync as mkdirSync14, chmodSync as chmodSync3 } from "fs";
70354
+ import { dirname as dirname15, join as join26 } from "path";
70166
70355
  function getMcpBinaryPath() {
70167
70356
  try {
70168
70357
  const p = execSync3("which todos-mcp", { encoding: "utf-8" }).trim();
70169
70358
  if (p)
70170
70359
  return p;
70171
70360
  } catch {}
70172
- const bunBin = join25(HOME2, ".bun", "bin", "todos-mcp");
70173
- if (existsSync25(bunBin))
70361
+ const bunBin = join26(HOME2, ".bun", "bin", "todos-mcp");
70362
+ if (existsSync26(bunBin))
70174
70363
  return bunBin;
70175
70364
  return "todos-mcp";
70176
70365
  }
70177
70366
  function readJsonFile2(path) {
70178
- if (!existsSync25(path))
70367
+ if (!existsSync26(path))
70179
70368
  return {};
70180
70369
  try {
70181
70370
  return JSON.parse(readFileSync18(path, "utf-8"));
@@ -70185,19 +70374,19 @@ function readJsonFile2(path) {
70185
70374
  }
70186
70375
  function writeJsonFile2(path, data) {
70187
70376
  const dir = dirname15(path);
70188
- if (!existsSync25(dir))
70377
+ if (!existsSync26(dir))
70189
70378
  mkdirSync14(dir, { recursive: true });
70190
70379
  writeFileSync13(path, JSON.stringify(data, null, 2) + `
70191
70380
  `);
70192
70381
  }
70193
70382
  function readTomlFile(path) {
70194
- if (!existsSync25(path))
70383
+ if (!existsSync26(path))
70195
70384
  return "";
70196
70385
  return readFileSync18(path, "utf-8");
70197
70386
  }
70198
70387
  function writeTomlFile(path, content) {
70199
70388
  const dir = dirname15(path);
70200
- if (!existsSync25(dir))
70389
+ if (!existsSync26(dir))
70201
70390
  mkdirSync14(dir, { recursive: true });
70202
70391
  writeFileSync13(path, content);
70203
70392
  }
@@ -70262,7 +70451,7 @@ function unregisterClaude(_global) {
70262
70451
  }
70263
70452
  }
70264
70453
  function registerCodex(binPath) {
70265
- const configPath = join25(HOME2, ".codex", "config.toml");
70454
+ const configPath = join26(HOME2, ".codex", "config.toml");
70266
70455
  let content = readTomlFile(configPath);
70267
70456
  content = removeTomlBlock(content, "mcp_servers.todos");
70268
70457
  const block = `
@@ -70276,7 +70465,7 @@ args = ["--stdio"]
70276
70465
  console.log(chalk9.green(`Codex CLI: registered in ${configPath}`));
70277
70466
  }
70278
70467
  function unregisterCodex() {
70279
- const configPath = join25(HOME2, ".codex", "config.toml");
70468
+ const configPath = join26(HOME2, ".codex", "config.toml");
70280
70469
  let content = readTomlFile(configPath);
70281
70470
  if (!content.includes("[mcp_servers.todos]")) {
70282
70471
  console.log(chalk9.dim(`Codex CLI: todos not found in ${configPath}`));
@@ -70288,7 +70477,7 @@ function unregisterCodex() {
70288
70477
  console.log(chalk9.green(`Codex CLI: unregistered from ${configPath}`));
70289
70478
  }
70290
70479
  function registerGemini(binPath) {
70291
- const configPath = join25(HOME2, ".gemini", "settings.json");
70480
+ const configPath = join26(HOME2, ".gemini", "settings.json");
70292
70481
  const config = readJsonFile2(configPath);
70293
70482
  if (!config["mcpServers"]) {
70294
70483
  config["mcpServers"] = {};
@@ -70302,7 +70491,7 @@ function registerGemini(binPath) {
70302
70491
  console.log(chalk9.green(`Gemini CLI: registered in ${configPath}`));
70303
70492
  }
70304
70493
  function unregisterGemini() {
70305
- const configPath = join25(HOME2, ".gemini", "settings.json");
70494
+ const configPath = join26(HOME2, ".gemini", "settings.json");
70306
70495
  const config = readJsonFile2(configPath);
70307
70496
  const servers = config["mcpServers"];
70308
70497
  if (!servers || !("todos" in servers)) {
@@ -70313,8 +70502,37 @@ function unregisterGemini() {
70313
70502
  writeJsonFile2(configPath, config);
70314
70503
  console.log(chalk9.green(`Gemini CLI: unregistered from ${configPath}`));
70315
70504
  }
70505
+ function cursorConfigPath(global) {
70506
+ return global ? join26(HOME2, ".cursor", "mcp.json") : join26(process.cwd(), ".cursor", "mcp.json");
70507
+ }
70508
+ function registerCursor(binPath, global) {
70509
+ const configPath = cursorConfigPath(global);
70510
+ const config = readJsonFile2(configPath);
70511
+ if (!config["mcpServers"]) {
70512
+ config["mcpServers"] = {};
70513
+ }
70514
+ const servers = config["mcpServers"];
70515
+ servers["todos"] = {
70516
+ command: binPath,
70517
+ args: ["--stdio"]
70518
+ };
70519
+ writeJsonFile2(configPath, config);
70520
+ console.log(chalk9.green(`Cursor (${global ? "user" : "project"}): registered in ${configPath}`));
70521
+ }
70522
+ function unregisterCursor(global) {
70523
+ const configPath = cursorConfigPath(global);
70524
+ const config = readJsonFile2(configPath);
70525
+ const servers = config["mcpServers"];
70526
+ if (!servers || !("todos" in servers)) {
70527
+ console.log(chalk9.dim(`Cursor: todos not found in ${configPath}`));
70528
+ return;
70529
+ }
70530
+ delete servers["todos"];
70531
+ writeJsonFile2(configPath, config);
70532
+ console.log(chalk9.green(`Cursor (${global ? "user" : "project"}): unregistered from ${configPath}`));
70533
+ }
70316
70534
  function registerMcp(agent, global) {
70317
- const agents = agent === "all" ? ["claude", "codex", "gemini"] : [agent];
70535
+ const agents = agent === "all" ? ["claude", "codex", "gemini", "cursor"] : [agent];
70318
70536
  const binPath = getMcpBinaryPath();
70319
70537
  for (const a of agents) {
70320
70538
  switch (a) {
@@ -70327,13 +70545,16 @@ function registerMcp(agent, global) {
70327
70545
  case "gemini":
70328
70546
  registerGemini(binPath);
70329
70547
  break;
70548
+ case "cursor":
70549
+ registerCursor(binPath, global);
70550
+ break;
70330
70551
  default:
70331
- console.error(chalk9.red(`Unknown agent: ${a}. Use: claude, codex, gemini, all`));
70552
+ console.error(chalk9.red(`Unknown agent: ${a}. Use: claude, codex, gemini, cursor, all`));
70332
70553
  }
70333
70554
  }
70334
70555
  }
70335
70556
  function unregisterMcp(agent, global) {
70336
- const agents = agent === "all" ? ["claude", "codex", "gemini"] : [agent];
70557
+ const agents = agent === "all" ? ["claude", "codex", "gemini", "cursor"] : [agent];
70337
70558
  for (const a of agents) {
70338
70559
  switch (a) {
70339
70560
  case "claude":
@@ -70345,8 +70566,11 @@ function unregisterMcp(agent, global) {
70345
70566
  case "gemini":
70346
70567
  unregisterGemini();
70347
70568
  break;
70569
+ case "cursor":
70570
+ unregisterCursor(global);
70571
+ break;
70348
70572
  default:
70349
- console.error(chalk9.red(`Unknown agent: ${a}. Use: claude, codex, gemini, all`));
70573
+ console.error(chalk9.red(`Unknown agent: ${a}. Use: claude, codex, gemini, cursor, all`));
70350
70574
  }
70351
70575
  }
70352
70576
  }
@@ -70359,8 +70583,8 @@ function registerMcpHooksCommands(program2) {
70359
70583
  if (p)
70360
70584
  todosBin = p;
70361
70585
  } catch {}
70362
- const hooksDir = join25(process.cwd(), ".claude", "hooks");
70363
- if (!existsSync25(hooksDir))
70586
+ const hooksDir = join26(process.cwd(), ".claude", "hooks");
70587
+ if (!existsSync26(hooksDir))
70364
70588
  mkdirSync14(hooksDir, { recursive: true });
70365
70589
  const hookScript = `#!/usr/bin/env bash
70366
70590
  # Auto-generated by: todos hooks install
@@ -70385,11 +70609,11 @@ esac
70385
70609
 
70386
70610
  exit 0
70387
70611
  `;
70388
- const hookPath = join25(hooksDir, "todos-sync.sh");
70612
+ const hookPath = join26(hooksDir, "todos-sync.sh");
70389
70613
  writeFileSync13(hookPath, hookScript);
70390
70614
  execSync3(`chmod +x "${hookPath}"`);
70391
70615
  console.log(chalk9.green(`Hook script created: ${hookPath}`));
70392
- const settingsPath = join25(process.cwd(), ".claude", "settings.json");
70616
+ const settingsPath = join26(process.cwd(), ".claude", "settings.json");
70393
70617
  const settings = readJsonFile2(settingsPath);
70394
70618
  if (!settings["hooks"]) {
70395
70619
  settings["hooks"] = {};
@@ -70418,7 +70642,7 @@ exit 0
70418
70642
  console.log(chalk9.green(`Claude Code hooks configured in: ${settingsPath}`));
70419
70643
  console.log(chalk9.dim("Task list ID auto-detected from project."));
70420
70644
  });
70421
- 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) => {
70645
+ program2.command("mcp").description("Start MCP server (stdio)").option("--register <agent>", "Register MCP server with an agent (claude, codex, gemini, cursor, all)").option("--unregister <agent>", "Unregister MCP server from an agent (claude, codex, gemini, cursor, all)").option("-g, --global", "Register/unregister globally (user-level) instead of project-level").action(async (opts) => {
70422
70646
  if (opts.register) {
70423
70647
  registerMcp(opts.register, opts.global);
70424
70648
  return;
@@ -71320,7 +71544,7 @@ Artifacts:`));
71320
71544
  const gitDir = execSync3("git rev-parse --git-dir", { encoding: "utf-8" }).trim();
71321
71545
  const hookPath = `${gitDir}/hooks/post-commit`;
71322
71546
  const marker = "# todos-auto-link";
71323
- if (existsSync25(hookPath)) {
71547
+ if (existsSync26(hookPath)) {
71324
71548
  const existing = readFileSync18(hookPath, "utf-8");
71325
71549
  if (existing.includes(marker)) {
71326
71550
  console.log(chalk9.yellow("Hook already installed."));
@@ -71347,7 +71571,7 @@ $(dirname "$0")/../../scripts/post-commit-hook.sh
71347
71571
  const gitDir = execSync3("git rev-parse --git-dir", { encoding: "utf-8" }).trim();
71348
71572
  const hookPath = `${gitDir}/hooks/post-commit`;
71349
71573
  const marker = "# todos-auto-link";
71350
- if (!existsSync25(hookPath)) {
71574
+ if (!existsSync26(hookPath)) {
71351
71575
  console.log(chalk9.dim("No post-commit hook found."));
71352
71576
  return;
71353
71577
  }
@@ -71533,7 +71757,7 @@ import chalk11 from "chalk";
71533
71757
  import { execSync as execSync4 } from "child_process";
71534
71758
  import { readFileSync as readFileSync19, unlinkSync as unlinkSync3, writeFileSync as writeFileSync14 } from "fs";
71535
71759
  import { tmpdir as tmpdir5 } from "os";
71536
- import { join as join26 } from "path";
71760
+ import { join as join27 } from "path";
71537
71761
  function getOrCreateLocalMachineName() {
71538
71762
  return process.env["TODOS_MACHINE_NAME"] || __require("os").hostname() || "unknown";
71539
71763
  }
@@ -71571,7 +71795,7 @@ function remoteTempPath(sshAddress) {
71571
71795
  }
71572
71796
  function readRemoteBridgeBundle(sshAddress) {
71573
71797
  const remotePath = remoteTempPath(sshAddress);
71574
- const localPath = join26(tmpdir5(), `todos-bridge-pull-${uuid()}.json`);
71798
+ const localPath = join27(tmpdir5(), `todos-bridge-pull-${uuid()}.json`);
71575
71799
  try {
71576
71800
  runSsh(sshAddress, `todos export --format bridge --allow-plaintext-sensitive --output ${shellQuote(remotePath)}`, 120000);
71577
71801
  scpFromRemote(sshAddress, remotePath, localPath);
@@ -71586,7 +71810,7 @@ function readRemoteBridgeBundle(sshAddress) {
71586
71810
  }
71587
71811
  }
71588
71812
  function writeLocalBridgeBundle() {
71589
- const localPath = join26(tmpdir5(), `todos-bridge-push-${uuid()}.json`);
71813
+ const localPath = join27(tmpdir5(), `todos-bridge-push-${uuid()}.json`);
71590
71814
  writeFileSync14(localPath, JSON.stringify(createLocalBridgeBundle(), null, 2));
71591
71815
  return localPath;
71592
71816
  }
@@ -76161,7 +76385,7 @@ __export(exports_sdk_integration_fixtures, {
76161
76385
  TODOS_SDK_INTEGRATION_FIXTURE_GENERATED_AT: () => TODOS_SDK_INTEGRATION_FIXTURE_GENERATED_AT
76162
76386
  });
76163
76387
  import { mkdirSync as mkdirSync15, writeFileSync as writeFileSync15 } from "fs";
76164
- import { join as join27 } from "path";
76388
+ import { join as join28 } from "path";
76165
76389
  function source5(version) {
76166
76390
  return {
76167
76391
  packageName: "@hasna/todos",
@@ -76268,7 +76492,7 @@ function writeSdkIntegrationFixtures(directory, options = {}) {
76268
76492
  ];
76269
76493
  const written = [];
76270
76494
  for (const [name, payload] of files) {
76271
- const file = join27(directory, name);
76495
+ const file = join28(directory, name);
76272
76496
  writeFileSync15(file, `${JSON.stringify(payload, null, 2)}
76273
76497
  `, "utf-8");
76274
76498
  written.push(file);