@hasna/todos 0.13.4 → 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.4",
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,
@@ -6362,41 +6363,103 @@ function isMetadataInvocation(args, invocation) {
6362
6363
  }
6363
6364
  return invocation.commandArgs.length === 1 && (HELP_FLAGS.has(invocation.commandArgs[0]) || VERSION_FLAGS.has(invocation.commandArgs[0]));
6364
6365
  }
6365
- function commandSupportsRemote(invocation) {
6366
+ function firstPresentOption(args, candidates) {
6367
+ const option = candidates.find((candidate) => hasOption(args, candidate));
6368
+ return option ? dropIt(option) : null;
6369
+ }
6370
+ function disqualifyingArgument(invocation) {
6366
6371
  const command = invocation.command;
6367
- if (!command || COMMAND_CAPABILITY_MATRIX.get(command) !== "remote-http")
6368
- return false;
6369
6372
  const args = invocation.commandArgs;
6370
6373
  switch (command) {
6371
6374
  case "task":
6372
- return positionalArgs(args)[0] === "upsert";
6375
+ return positionalArgs(args)[0] === "upsert" ? null : { blame: "any subcommand other than `upsert`", remedy: "use `todos task upsert`" };
6373
6376
  case "doctor":
6374
- return positionalArgs(args)[0] !== "routing" && !hasOption(args, "--apply") && !hasOption(args, "--fix");
6377
+ if (positionalArgs(args)[0] === "routing")
6378
+ return dropIt("the `routing` subcommand");
6379
+ return firstPresentOption(args, ["--apply", "--fix"]);
6375
6380
  case "projects":
6376
- return !hasOption(args, "--deregister") && !hasOption(args, "--path-prefix") && !hasOption(args, "--dry-run");
6381
+ return firstPresentOption(args, ["--deregister", "--path-prefix", "--dry-run"]);
6377
6382
  case "plans":
6378
- return !hasOption(args, "--artifact") && !hasOption(args, "--write-artifacts");
6383
+ return firstPresentOption(args, ["--artifact", "--write-artifacts"]);
6379
6384
  case "list":
6380
- return !hasOption(args, "--recurring");
6385
+ return firstPresentOption(args, ["--recurring"]);
6381
6386
  case "claim":
6382
- return !invocation.globalOptions.has("--project") && !hasOption(args, "--project") && !hasOption(args, "--stale-minutes") && !hasOption(args, "--steal-stale");
6387
+ if (invocation.globalOptions.has("--project"))
6388
+ return dropIt("--project");
6389
+ return firstPresentOption(args, ["--project", "--stale-minutes", "--steal-stale"]);
6383
6390
  case "status":
6384
- return !invocation.globalOptions.has("--agent") && !hasOption(args, "--agent");
6385
- case "deps":
6386
- return true;
6391
+ if (invocation.globalOptions.has("--agent"))
6392
+ return dropIt("--agent");
6393
+ return firstPresentOption(args, ["--agent"]);
6387
6394
  case "bulk": {
6388
6395
  const action = positionalArgs(args)[0];
6389
6396
  if (action === "plan" || action === "move-plan")
6390
- return true;
6391
- return Boolean(action && ["done", "complete", "start", "delete"].includes(action)) && !hasOption(args, "--plan") && !hasOption(args, "--clear-plan");
6397
+ return null;
6398
+ if (!action) {
6399
+ return { blame: "a missing action", remedy: "pass one of done, complete, start, delete, plan, move-plan" };
6400
+ }
6401
+ if (!["done", "complete", "start", "delete"].includes(action)) {
6402
+ return {
6403
+ blame: `the \`${action}\` action`,
6404
+ remedy: "use one of done, complete, start, delete, plan, move-plan"
6405
+ };
6406
+ }
6407
+ return firstPresentOption(args, ["--plan", "--clear-plan"]);
6392
6408
  }
6393
6409
  default:
6394
- return true;
6410
+ return null;
6395
6411
  }
6396
6412
  }
6413
+ function commandSupportsRemote(invocation) {
6414
+ const command = invocation.command;
6415
+ if (!command || COMMAND_CAPABILITY_MATRIX.get(command) !== "remote-http")
6416
+ return false;
6417
+ return disqualifyingArgument(invocation) === null;
6418
+ }
6419
+ function editDistance(a, b) {
6420
+ const rows = a.length + 1;
6421
+ const cols = b.length + 1;
6422
+ let twoBack = new Array(cols).fill(0);
6423
+ let previous = Array.from({ length: cols }, (_, index) => index);
6424
+ for (let row = 1;row < rows; row += 1) {
6425
+ const current = [row, ...new Array(cols - 1).fill(0)];
6426
+ for (let col = 1;col < cols; col += 1) {
6427
+ const substitution = previous[col - 1] + (a[row - 1] === b[col - 1] ? 0 : 1);
6428
+ current[col] = Math.min(current[col - 1] + 1, previous[col] + 1, substitution);
6429
+ if (row > 1 && col > 1 && a[row - 1] === b[col - 2] && a[row - 2] === b[col - 1]) {
6430
+ current[col] = Math.min(current[col], twoBack[col - 2] + 1);
6431
+ }
6432
+ }
6433
+ twoBack = previous;
6434
+ previous = current;
6435
+ }
6436
+ return previous[cols - 1];
6437
+ }
6438
+ function nearestCommands(command, limit = 3) {
6439
+ const threshold = command.length <= 4 ? 1 : command.length <= 8 ? 2 : 3;
6440
+ return [...COMMAND_CAPABILITY_MATRIX.entries()].filter(([, owner]) => owner !== "local-only").map(([candidate]) => candidate).map((candidate) => ({ candidate, distance: editDistance(command, candidate) })).filter(({ distance }) => distance <= threshold).sort((left, right) => left.distance - right.distance || left.candidate.localeCompare(right.candidate)).slice(0, limit).map(({ candidate }) => candidate);
6441
+ }
6397
6442
  function assertRemoteCommandSupported(invocation) {
6398
- if (invocation.invalidGlobalOption || invocation.unknownLeadingOption || !commandSupportsRemote(invocation)) {
6399
- throw new Error(`REMOTE_COMMAND_UNSUPPORTED: ${invocationLabel(invocation)} is not supported by the Todos /v1 CLI; ` + "local SQLite fallback is disabled");
6443
+ if (invocation.invalidGlobalOption) {
6444
+ throw new Error(`REMOTE_COMMAND_UNSUPPORTED: the global option ${invocation.invalidGlobalOption} was given without a value; ` + `pass one as \`${invocation.invalidGlobalOption} <value>\``);
6445
+ }
6446
+ if (invocation.unknownLeadingOption) {
6447
+ throw new Error(`REMOTE_COMMAND_UNSUPPORTED: unknown option ${invocation.unknownLeadingOption} before the command; ` + "run `todos --help` for the global options");
6448
+ }
6449
+ const command = invocation.command;
6450
+ const owner = command ? COMMAND_CAPABILITY_MATRIX.get(command) : undefined;
6451
+ if (command && !owner) {
6452
+ const suggestions = nearestCommands(command);
6453
+ const didYouMean = suggestions.length > 0 ? ` Did you mean: ${suggestions.join(", ")}?` : "";
6454
+ throw new Error(`UNKNOWN_COMMAND: \`${command}\` is not a built-in todos command on the /v1 route.${didYouMean} ` + "Run `todos --help` for the commands available here; verbs contributed by optional packages are local-only. " + "(This is not a connectivity, storage-mode or credential problem.)");
6455
+ }
6456
+ if (command && owner === "local-only") {
6457
+ throw new Error(`REMOTE_COMMAND_UNSUPPORTED: \`${command}\` is a local-only command and the Todos /v1 authority does not ` + "serve it; local SQLite fallback is disabled. Run `todos --help` to see the commands this route supports.");
6458
+ }
6459
+ if (!command || !commandSupportsRemote(invocation)) {
6460
+ const blame = command ? disqualifyingArgument(invocation) : null;
6461
+ const detail = blame ? `\`${command}\` is served by the Todos /v1 authority but ${blame.blame} is not; ${blame.remedy}` : `${invocationLabel(invocation)} is not supported by the Todos /v1 CLI; local SQLite fallback is disabled`;
6462
+ throw new Error(`REMOTE_COMMAND_UNSUPPORTED: ${detail}`);
6400
6463
  }
6401
6464
  }
6402
6465
  function initializeTodosCliAuthority(args = process.argv.slice(2), env = process.env) {
@@ -6415,7 +6478,7 @@ function initializeTodosCliAuthority(args = process.argv.slice(2), env = process
6415
6478
  }
6416
6479
  return { route: "remote-http", v1_base_url: client.baseUrl };
6417
6480
  }
6418
- var REGISTERED_CANONICAL_COMMANDS, TODOS_CLI_COMMAND_ALIASES, DIAGNOSTIC_COMMANDS, REMOTE_COMMANDS, COMMAND_CAPABILITY_MATRIX, GLOBAL_OPTIONS_WITH_VALUES, GLOBAL_FLAGS, HELP_FLAGS, VERSION_FLAGS;
6481
+ var REGISTERED_CANONICAL_COMMANDS, TODOS_CLI_COMMAND_ALIASES, DIAGNOSTIC_COMMANDS, REMOTE_COMMANDS, COMMAND_CAPABILITY_MATRIX, GLOBAL_OPTIONS_WITH_VALUES, GLOBAL_FLAGS, HELP_FLAGS, VERSION_FLAGS, dropIt = (blame) => ({ blame, remedy: "re-run without it" });
6419
6482
  var init_stage_a = __esm(() => {
6420
6483
  init_esm();
6421
6484
  init_cloud_router();
@@ -6594,6 +6657,8 @@ var init_stage_a = __esm(() => {
6594
6657
  "template-history": ["templates-history"],
6595
6658
  "agents-normalize": ["normalize-agents"],
6596
6659
  "agent-update": ["agents-update"],
6660
+ done: ["complete"],
6661
+ init: ["register"],
6597
6662
  upgrade: ["self-update"],
6598
6663
  roadmaps: ["roadmap"],
6599
6664
  "env-snapshot": ["environment-snapshot"],
@@ -8971,6 +9036,7 @@ function ensureSchema(db) {
8971
9036
  ensureColumn("tasks", "reason", "TEXT");
8972
9037
  ensureColumn("tasks", "spawned_from_session", "TEXT");
8973
9038
  ensureColumn("tasks", "assigned_by", "TEXT");
9039
+ ensureColumn("tasks", "created_by", "TEXT");
8974
9040
  ensureColumn("tasks", "assigned_from_project", "TEXT");
8975
9041
  ensureColumn("tasks", "started_at", "TEXT");
8976
9042
  ensureColumn("tasks", "task_type", "TEXT");
@@ -9133,6 +9199,8 @@ function ensureSchema(db) {
9133
9199
  ensureIndex("CREATE INDEX IF NOT EXISTS idx_project_sources_project ON project_sources(project_id)");
9134
9200
  ensureIndex("CREATE INDEX IF NOT EXISTS idx_project_sources_type ON project_sources(type)");
9135
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)");
9136
9204
  ensureIndex("CREATE INDEX IF NOT EXISTS idx_task_rel_source ON task_relationships(source_task_id)");
9137
9205
  ensureIndex("CREATE INDEX IF NOT EXISTS idx_task_rel_target ON task_relationships(target_task_id)");
9138
9206
  ensureIndex("CREATE INDEX IF NOT EXISTS idx_task_rel_type ON task_relationships(relationship_type)");
@@ -15282,13 +15350,14 @@ function createTask(input, db) {
15282
15350
  const timestamp2 = now();
15283
15351
  const tags = input.tags || [];
15284
15352
  const machineId = currentStorageMachineId(d);
15353
+ const createdBy = input.created_by || input.agent_id || null;
15285
15354
  const assignedBy = input.assigned_by || input.agent_id;
15286
15355
  const assignedFromProject = input.assigned_from_project || null;
15287
15356
  let id = uuid();
15288
15357
  for (let attempt = 0;attempt < 3; attempt++) {
15289
15358
  try {
15290
- 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)
15291
- 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, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
15292
15361
  id,
15293
15362
  null,
15294
15363
  input.project_id || null,
@@ -15324,6 +15393,7 @@ function createTask(input, db) {
15324
15393
  input.reason || null,
15325
15394
  input.spawned_from_session || null,
15326
15395
  assignedBy || null,
15396
+ createdBy,
15327
15397
  assignedFromProject || null,
15328
15398
  input.task_type || null,
15329
15399
  machineId
@@ -15433,6 +15503,14 @@ function listTasks(filter = {}, db) {
15433
15503
  conditions.push("agent_id = ?");
15434
15504
  params.push(filter.agent_id);
15435
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
+ }
15436
15514
  if (filter.session_id) {
15437
15515
  conditions.push("session_id = ?");
15438
15516
  params.push(filter.session_id);
@@ -15586,6 +15664,14 @@ function countTasks(filter = {}, db) {
15586
15664
  conditions.push("agent_id = ?");
15587
15665
  params.push(filter.agent_id);
15588
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
+ }
15589
15675
  if (filter.session_id) {
15590
15676
  conditions.push("session_id = ?");
15591
15677
  params.push(filter.session_id);
@@ -18746,6 +18832,73 @@ var init_tasks = __esm(() => {
18746
18832
  init_calendar();
18747
18833
  });
18748
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
+
18749
18902
  // src/cli/output-redaction.ts
18750
18903
  function redactBroadTask(task) {
18751
18904
  return redactValue(task);
@@ -18766,7 +18919,7 @@ __export(exports_task_routing, {
18766
18919
  setTaskWorkflowPointers: () => setTaskWorkflowPointers,
18767
18920
  getTaskRouteState: () => getTaskRouteState
18768
18921
  });
18769
- import { existsSync as existsSync8, statSync as statSync3 } from "fs";
18922
+ import { existsSync as existsSync9, statSync as statSync3 } from "fs";
18770
18923
  function machineLocalPath(project, db) {
18771
18924
  const machineId = process.env["TODOS_MACHINE_ID"];
18772
18925
  if (!machineId)
@@ -18806,7 +18959,7 @@ function routeConcurrencyKey(task, project, taskList, projectPath) {
18806
18959
  }
18807
18960
  function directoryExists(path) {
18808
18961
  try {
18809
- return existsSync8(path) && statSync3(path).isDirectory();
18962
+ return existsSync9(path) && statSync3(path).isDirectory();
18810
18963
  } catch {
18811
18964
  return false;
18812
18965
  }
@@ -19243,10 +19396,15 @@ function computeLocalReparent(current, opts) {
19243
19396
  return patch;
19244
19397
  }
19245
19398
  function registerTaskCommands(program2) {
19246
- 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) => {
19247
19400
  const globalOpts = program2.opts();
19248
19401
  opts.tags = opts.tags || opts.tag;
19249
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
+ }
19250
19408
  const cloud = getTodosCloudClient();
19251
19409
  if (cloud) {
19252
19410
  let task3;
@@ -19268,10 +19426,11 @@ function registerTaskCommands(program2) {
19268
19426
  parent_id: opts.parent ? await resolveTaskIdForCommand(opts.parent, cloud) : undefined,
19269
19427
  tags: opts.tags ? opts.tags.split(",").map((t) => t.trim()) : undefined,
19270
19428
  plan_id: cloudPlan?.id,
19271
- assigned_to: opts.assign,
19429
+ assigned_to: assignee,
19272
19430
  status: parseStatus(opts.status),
19273
19431
  task_list_id: cloudTaskListId,
19274
- agent_id: globalOpts.agent,
19432
+ agent_id: globalOpts.agent || creator.agent_id || undefined,
19433
+ created_by: creator.agent_id || undefined,
19275
19434
  session_id: globalOpts.session,
19276
19435
  project_id: cloudProjectId,
19277
19436
  estimated_minutes: opts.estimated !== undefined ? parseIntOption(opts.estimated, "--estimated") : undefined,
@@ -19313,10 +19472,11 @@ function registerTaskCommands(program2) {
19313
19472
  parent_id: opts.parent ? resolveTaskId(opts.parent) : undefined,
19314
19473
  tags: opts.tags ? opts.tags.split(",").map((t) => t.trim()) : undefined,
19315
19474
  plan_id: opts.plan ? resolvePlanId(opts.plan) : undefined,
19316
- assigned_to: opts.assign,
19475
+ assigned_to: assignee,
19317
19476
  status: parseStatus(opts.status),
19318
19477
  task_list_id: taskListId,
19319
- agent_id: globalOpts.agent,
19478
+ agent_id: globalOpts.agent || creator.agent_id || undefined,
19479
+ created_by: creator.agent_id || undefined,
19320
19480
  session_id: globalOpts.session,
19321
19481
  project_id: projectId,
19322
19482
  working_dir: process.cwd(),
@@ -19470,14 +19630,14 @@ function registerTaskCommands(program2) {
19470
19630
  console.log(` ${chalk2.dim("Manifest:")} ${state.pointers.latest_manifest_path}`);
19471
19631
  }
19472
19632
  });
19473
- 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) => {
19474
19634
  const globalOpts = program2.opts();
19475
19635
  opts.tags = opts.tags || opts.tag;
19476
19636
  opts.list = opts.list || opts.taskList;
19477
19637
  const cloud = getTodosCloudClient();
19478
19638
  const cloudProjectRef = globalOpts.project || opts.projectName;
19479
19639
  const projectId = cloud && cloudProjectRef ? await cloudResolveProjectRef(cloud, cloudProjectRef) : cloud ? undefined : autoProject(globalOpts);
19480
- const hasAssignedFilter = Boolean(opts.assigned || opts.agentName);
19640
+ const hasAssignedFilter = Boolean(opts.assigned || opts.agentName || opts.inbox);
19481
19641
  const hasExplicitProjectFilter = Boolean(globalOpts.project || opts.projectName);
19482
19642
  const allowedSortFields = new Set(["updated", "created", "priority", "status"]);
19483
19643
  if (opts.sort && !allowedSortFields.has(opts.sort)) {
@@ -19510,6 +19670,19 @@ function registerTaskCommands(program2) {
19510
19670
  filter["priority"] = opts.priority;
19511
19671
  if (opts.assigned)
19512
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
+ }
19513
19686
  if (opts.tags)
19514
19687
  filter["tags"] = opts.tags.split(",").map((t) => t.trim());
19515
19688
  if (opts.projectName && !cloud) {
@@ -19534,7 +19707,32 @@ function registerTaskCommands(program2) {
19534
19707
  }
19535
19708
  filter["limit"] = parsedLimit;
19536
19709
  }
19537
- 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
+ }
19538
19736
  if (opts.dueToday) {
19539
19737
  const todayEnd = new Date;
19540
19738
  todayEnd.setHours(23, 59, 59, 999);
@@ -20099,7 +20297,7 @@ ${chalk2.cyan(sid)} ${statusColor(task2.status)} ${prioColor(task2.priority)} ${
20099
20297
  console.log(formatTaskLine(task2));
20100
20298
  }
20101
20299
  });
20102
- program2.command("done <id>").description("Mark a task as completed").option("--attach-ids <ids>", "Comma-separated @hasna/attachments IDs to link as evidence").option("--files-changed <files>", "Comma-separated list of files changed").option("--test-results <results>", "Test results summary").option("--commit-hash <hash>", "Git commit hash").option("--notes <notes>", "Completion notes").option("--confidence <0-1>", "Agent's confidence 0.0-1.0 that the task is fully complete (default: 1.0, <0.7 flagged for review)").action(async (id, opts) => {
20300
+ program2.command("done <id>").alias("complete").description("Mark a task as completed (alias: complete)").option("--attach-ids <ids>", "Comma-separated @hasna/attachments IDs to link as evidence").option("--files-changed <files>", "Comma-separated list of files changed").option("--test-results <results>", "Test results summary").option("--commit-hash <hash>", "Git commit hash").option("--notes <notes>", "Completion notes").option("--confidence <0-1>", "Agent's confidence 0.0-1.0 that the task is fully complete (default: 1.0, <0.7 flagged for review)").action(async (id, opts) => {
20103
20301
  const globalOpts = program2.opts();
20104
20302
  const attachmentIds = opts.attachIds ? opts.attachIds.split(",").map((s) => s.trim()).filter(Boolean) : undefined;
20105
20303
  const filesChanged = opts.filesChanged ? opts.filesChanged.split(",").map((s) => s.trim()).filter(Boolean) : undefined;
@@ -20418,14 +20616,15 @@ var init_task_commands = __esm(() => {
20418
20616
  init_tasks();
20419
20617
  init_task_lists();
20420
20618
  init_cloud_router();
20619
+ init_creator_identity();
20421
20620
  init_helpers();
20422
20621
  init_output_redaction();
20423
20622
  init_types();
20424
20623
  });
20425
20624
 
20426
20625
  // src/lib/plan-artifacts.ts
20427
- import { existsSync as existsSync9, mkdirSync as mkdirSync6, readFileSync as readFileSync4, writeFileSync as writeFileSync4 } from "fs";
20428
- 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";
20429
20628
  function assertSafePathSegment(value, label) {
20430
20629
  const trimmed = value.trim();
20431
20630
  if (!trimmed || trimmed === "." || trimmed === ".." || trimmed.includes("/") || trimmed.includes("\\")) {
@@ -20486,7 +20685,7 @@ function resolvePlanArtifactPaths(input) {
20486
20685
  const project = resolvePlanArtifactProject(input);
20487
20686
  const projectId = assertSafePathSegment(project.id, "project id");
20488
20687
  const projectRoot = resolve10(project.path);
20489
- const directory = join7(projectRoot, ".hasna", "todos", "plans", projectId);
20688
+ const directory = join8(projectRoot, ".hasna", "todos", "plans", projectId);
20490
20689
  const planId = input.plan_id ? assertSafePathSegment(input.plan_id, "plan id") : null;
20491
20690
  const planSlug = input.plan_slug ? assertSafePathSegment(slugify(input.plan_slug), "plan slug") : null;
20492
20691
  const fileName = planId ? planSlug ? `${planSlug}--${planId.slice(0, 8)}.md` : `${planId}.md` : null;
@@ -20494,7 +20693,7 @@ function resolvePlanArtifactPaths(input) {
20494
20693
  project_id: project.id,
20495
20694
  project_root: projectRoot,
20496
20695
  directory,
20497
- file_path: fileName ? join7(directory, fileName) : directory
20696
+ file_path: fileName ? join8(directory, fileName) : directory
20498
20697
  };
20499
20698
  }
20500
20699
  function resolvePlanArtifactCandidatePaths(plan, db) {
@@ -20660,7 +20859,7 @@ function readPlanArtifact(plan, db) {
20660
20859
  return null;
20661
20860
  const d = db || getDatabase();
20662
20861
  const paths = resolvePlanArtifactCandidatePaths(plan, d);
20663
- 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;
20664
20863
  if (!path)
20665
20864
  return null;
20666
20865
  const markdown = readFileSync4(path, "utf8");
@@ -20675,7 +20874,7 @@ function inspectPlanArtifact(plan, db) {
20675
20874
  return null;
20676
20875
  const d = db || getDatabase();
20677
20876
  const paths = resolvePlanArtifactCandidatePaths(plan, d);
20678
- 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;
20679
20878
  if (!path) {
20680
20879
  return {
20681
20880
  path: paths.primary.file_path,
@@ -20755,7 +20954,7 @@ __export(exports_builtin_templates, {
20755
20954
  BUILTIN_TEMPLATES: () => BUILTIN_TEMPLATES
20756
20955
  });
20757
20956
  import { mkdirSync as mkdirSync7, writeFileSync as writeFileSync5 } from "fs";
20758
- import { join as join8 } from "path";
20957
+ import { join as join9 } from "path";
20759
20958
  function templateMetadata(template) {
20760
20959
  return {
20761
20960
  source: BUILTIN_TEMPLATE_LIBRARY_SOURCE,
@@ -20814,7 +21013,7 @@ function writeBuiltinTemplateFiles(directory) {
20814
21013
  mkdirSync7(directory, { recursive: true });
20815
21014
  const files = [];
20816
21015
  for (const entry of exportBuiltinTemplateFiles()) {
20817
- const path = join8(directory, entry.filename);
21016
+ const path = join9(directory, entry.filename);
20818
21017
  writeFileSync5(path, `${JSON.stringify(entry.template, null, 2)}
20819
21018
  `, "utf-8");
20820
21019
  files.push(path);
@@ -22586,16 +22785,16 @@ var init_saved_search_views = __esm(() => {
22586
22785
  });
22587
22786
 
22588
22787
  // src/lib/claude-tasks.ts
22589
- import { existsSync as existsSync10, readFileSync as readFileSync5, readdirSync as readdirSync2, writeFileSync as writeFileSync6 } from "fs";
22590
- 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";
22591
22790
  function getTaskListDir(taskListId) {
22592
- return join9(HOME, ".claude", "tasks", taskListId);
22791
+ return join10(HOME, ".claude", "tasks", taskListId);
22593
22792
  }
22594
22793
  function readClaudeTask(dir, filename) {
22595
- return readJsonFile(join9(dir, filename));
22794
+ return readJsonFile(join10(dir, filename));
22596
22795
  }
22597
22796
  function writeClaudeTask(dir, task) {
22598
- writeJsonFile(join9(dir, `${task.id}.json`), task);
22797
+ writeJsonFile(join10(dir, `${task.id}.json`), task);
22599
22798
  }
22600
22799
  function toClaudeStatus(status) {
22601
22800
  if (status === "pending" || status === "in_progress" || status === "completed") {
@@ -22607,14 +22806,14 @@ function toSqliteStatus(status) {
22607
22806
  return status;
22608
22807
  }
22609
22808
  function readPrefixCounter(dir) {
22610
- const path = join9(dir, ".prefix-counter");
22611
- if (!existsSync10(path))
22809
+ const path = join10(dir, ".prefix-counter");
22810
+ if (!existsSync11(path))
22612
22811
  return 0;
22613
22812
  const val = parseInt(readFileSync5(path, "utf-8").trim(), 10);
22614
22813
  return isNaN(val) ? 0 : val;
22615
22814
  }
22616
22815
  function writePrefixCounter(dir, value) {
22617
- writeFileSync6(join9(dir, ".prefix-counter"), String(value));
22816
+ writeFileSync6(join10(dir, ".prefix-counter"), String(value));
22618
22817
  }
22619
22818
  function formatPrefixedSubject(title, prefix, counter) {
22620
22819
  const padded = String(counter).padStart(5, "0");
@@ -22641,7 +22840,7 @@ function taskToClaudeTask(task, claudeTaskId, existingMeta) {
22641
22840
  }
22642
22841
  function pushToClaudeTaskList(taskListId, projectId, options = {}) {
22643
22842
  const dir = getTaskListDir(taskListId);
22644
- if (!existsSync10(dir))
22843
+ if (!existsSync11(dir))
22645
22844
  ensureDir(dir);
22646
22845
  const filter = {};
22647
22846
  if (projectId)
@@ -22650,7 +22849,7 @@ function pushToClaudeTaskList(taskListId, projectId, options = {}) {
22650
22849
  const existingByTodosId = new Map;
22651
22850
  const files = listJsonFiles(dir);
22652
22851
  for (const f of files) {
22653
- const path = join9(dir, f);
22852
+ const path = join10(dir, f);
22654
22853
  const ct = readClaudeTask(dir, f);
22655
22854
  if (ct?.metadata?.["todos_id"]) {
22656
22855
  existingByTodosId.set(ct.metadata["todos_id"], { task: ct, mtimeMs: getFileMtimeMs(path) });
@@ -22737,7 +22936,7 @@ function pushToClaudeTaskList(taskListId, projectId, options = {}) {
22737
22936
  }
22738
22937
  function pullFromClaudeTaskList(taskListId, projectId, options = {}) {
22739
22938
  const dir = getTaskListDir(taskListId);
22740
- if (!existsSync10(dir)) {
22939
+ if (!existsSync11(dir)) {
22741
22940
  return { pushed: 0, pulled: 0, errors: [`Task list directory not found: ${dir}`] };
22742
22941
  }
22743
22942
  const files = readdirSync2(dir).filter((f) => f.endsWith(".json"));
@@ -22757,7 +22956,7 @@ function pullFromClaudeTaskList(taskListId, projectId, options = {}) {
22757
22956
  }
22758
22957
  for (const f of files) {
22759
22958
  try {
22760
- const filePath = join9(dir, f);
22959
+ const filePath = join10(dir, f);
22761
22960
  const ct = readClaudeTask(dir, f);
22762
22961
  if (!ct)
22763
22962
  continue;
@@ -22830,20 +23029,20 @@ var init_claude_tasks = __esm(() => {
22830
23029
  });
22831
23030
 
22832
23031
  // src/lib/agent-tasks.ts
22833
- import { existsSync as existsSync11 } from "fs";
22834
- import { join as join10 } from "path";
23032
+ import { existsSync as existsSync12 } from "fs";
23033
+ import { join as join11 } from "path";
22835
23034
  function agentBaseDir(agent) {
22836
23035
  const key = `TODOS_${agent.toUpperCase()}_TASKS_DIR`;
22837
- 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");
22838
23037
  }
22839
23038
  function getTaskListDir2(agent, taskListId) {
22840
- return join10(agentBaseDir(agent), agent, taskListId);
23039
+ return join11(agentBaseDir(agent), agent, taskListId);
22841
23040
  }
22842
23041
  function readAgentTask(dir, filename) {
22843
- return readJsonFile(join10(dir, filename));
23042
+ return readJsonFile(join11(dir, filename));
22844
23043
  }
22845
23044
  function writeAgentTask(dir, task) {
22846
- writeJsonFile(join10(dir, `${task.id}.json`), task);
23045
+ writeJsonFile(join11(dir, `${task.id}.json`), task);
22847
23046
  }
22848
23047
  function taskToAgentTask(task, externalId, existingMeta) {
22849
23048
  return {
@@ -22868,7 +23067,7 @@ function metadataKey(agent) {
22868
23067
  }
22869
23068
  function pushToAgentTaskList(agent, taskListId, projectId, options = {}) {
22870
23069
  const dir = getTaskListDir2(agent, taskListId);
22871
- if (!existsSync11(dir))
23070
+ if (!existsSync12(dir))
22872
23071
  ensureDir(dir);
22873
23072
  const filter = {};
22874
23073
  if (projectId)
@@ -22877,7 +23076,7 @@ function pushToAgentTaskList(agent, taskListId, projectId, options = {}) {
22877
23076
  const existingByTodosId = new Map;
22878
23077
  const files = listJsonFiles(dir);
22879
23078
  for (const f of files) {
22880
- const path = join10(dir, f);
23079
+ const path = join11(dir, f);
22881
23080
  const at = readAgentTask(dir, f);
22882
23081
  if (at?.metadata?.["todos_id"]) {
22883
23082
  existingByTodosId.set(at.metadata["todos_id"], { task: at, mtimeMs: getFileMtimeMs(path) });
@@ -22951,7 +23150,7 @@ function pushToAgentTaskList(agent, taskListId, projectId, options = {}) {
22951
23150
  }
22952
23151
  function pullFromAgentTaskList(agent, taskListId, projectId, options = {}) {
22953
23152
  const dir = getTaskListDir2(agent, taskListId);
22954
- if (!existsSync11(dir)) {
23153
+ if (!existsSync12(dir)) {
22955
23154
  return { pushed: 0, pulled: 0, errors: [`Task list directory not found: ${dir}`] };
22956
23155
  }
22957
23156
  const files = listJsonFiles(dir);
@@ -22970,7 +23169,7 @@ function pullFromAgentTaskList(agent, taskListId, projectId, options = {}) {
22970
23169
  }
22971
23170
  for (const f of files) {
22972
23171
  try {
22973
- const filePath = join10(dir, f);
23172
+ const filePath = join11(dir, f);
22974
23173
  const at = readAgentTask(dir, f);
22975
23174
  if (!at)
22976
23175
  continue;
@@ -23123,7 +23322,7 @@ __export(exports_project_bootstrap, {
23123
23322
  discoverProjectWorkspace: () => discoverProjectWorkspace,
23124
23323
  bootstrapProject: () => bootstrapProject
23125
23324
  });
23126
- 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";
23127
23326
  import { basename as basename4, dirname as dirname6, resolve as resolve11 } from "path";
23128
23327
  function safeStat(path) {
23129
23328
  try {
@@ -23142,7 +23341,7 @@ function canonicalPath(input) {
23142
23341
  function findUp(start, marker) {
23143
23342
  let current = canonicalPath(start);
23144
23343
  while (true) {
23145
- if (existsSync12(resolve11(current, marker)))
23344
+ if (existsSync13(resolve11(current, marker)))
23146
23345
  return current;
23147
23346
  const parent = dirname6(current);
23148
23347
  if (parent === current)
@@ -23154,7 +23353,7 @@ function readPackageJson(path) {
23154
23353
  if (!path)
23155
23354
  return null;
23156
23355
  const file = resolve11(path, "package.json");
23157
- if (!existsSync12(file))
23356
+ if (!existsSync13(file))
23158
23357
  return null;
23159
23358
  try {
23160
23359
  const parsed = JSON.parse(readFileSync6(file, "utf-8"));
@@ -23176,7 +23375,7 @@ function workspaceMarker(root, rootPackage) {
23176
23375
  if (rootPackage?.workspaces)
23177
23376
  markers.push("package.json#workspaces");
23178
23377
  for (const marker of ["pnpm-workspace.yaml", "turbo.json", "nx.json", "lerna.json", "rush.json", "bun.lock", "bun.lockb"]) {
23179
- if (existsSync12(resolve11(root, marker)))
23378
+ if (existsSync13(resolve11(root, marker)))
23180
23379
  markers.push(marker);
23181
23380
  }
23182
23381
  const kind = markers.find((marker) => marker !== "bun.lock" && marker !== "bun.lockb") ?? null;
@@ -23510,9 +23709,9 @@ __export(exports_extract, {
23510
23709
  buildCodebaseIndex: () => buildCodebaseIndex,
23511
23710
  EXTRACT_TAGS: () => EXTRACT_TAGS
23512
23711
  });
23513
- 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";
23514
23713
  import { createHash as createHash4 } from "crypto";
23515
- 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";
23516
23715
  function stableHash(value) {
23517
23716
  return createHash4("sha256").update(value).digest("hex");
23518
23717
  }
@@ -23521,8 +23720,8 @@ function normalizePathForMatch(value) {
23521
23720
  }
23522
23721
  function readGitignorePatterns(basePath) {
23523
23722
  const root = statSync5(basePath).isFile() ? resolve12(basePath, "..") : basePath;
23524
- const gitignorePath = join11(root, ".gitignore");
23525
- if (!existsSync13(gitignorePath))
23723
+ const gitignorePath = join12(root, ".gitignore");
23724
+ if (!existsSync14(gitignorePath))
23526
23725
  return [];
23527
23726
  try {
23528
23727
  return readFileSync7(gitignorePath, "utf-8").split(`
@@ -23664,7 +23863,7 @@ function buildCodebaseIndex(options) {
23664
23863
  const files = collectFiles(basePath, extensions, excludes, respectGitignore);
23665
23864
  const indexed = [];
23666
23865
  for (const file of files) {
23667
- const fullPath = statSync5(basePath).isFile() ? basePath : join11(basePath, file);
23866
+ const fullPath = statSync5(basePath).isFile() ? basePath : join12(basePath, file);
23668
23867
  try {
23669
23868
  const source = readFileSync7(fullPath, "utf-8");
23670
23869
  const relPath = statSync5(basePath).isFile() ? relative3(resolve12(basePath, ".."), fullPath) : file;
@@ -23695,7 +23894,7 @@ function extractTodos(options, db) {
23695
23894
  const files = collectFiles(basePath, extensions, excludes, respectGitignore);
23696
23895
  const allComments = [];
23697
23896
  for (const file of files) {
23698
- const fullPath = statSync5(basePath).isFile() ? basePath : join11(basePath, file);
23897
+ const fullPath = statSync5(basePath).isFile() ? basePath : join12(basePath, file);
23699
23898
  try {
23700
23899
  const source = readFileSync7(fullPath, "utf-8");
23701
23900
  const relPath = statSync5(basePath).isFile() ? relative3(resolve12(basePath, ".."), fullPath) : file;
@@ -26746,8 +26945,16 @@ function resolveCloudAgentByNameOrId(agents, nameOrId) {
26746
26945
  return null;
26747
26946
  return matches.reduce((freshest, candidate) => new Date(candidate.last_seen_at).getTime() > new Date(freshest.last_seen_at).getTime() ? candidate : freshest);
26748
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
+ }
26749
26956
  function registerAgentCommands(program2) {
26750
- program2.command("init <name>").description("Register an agents and get a short UUID").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) => {
26751
26958
  const globalOpts = program2.opts();
26752
26959
  try {
26753
26960
  const cloud = getTodosCloudClient();
@@ -26757,14 +26964,25 @@ function registerAgentCommands(program2) {
26757
26964
  console.error(chalk5.red("CONFLICT:"), result.message);
26758
26965
  process.exit(1);
26759
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 } : {} });
26760
26978
  if (globalOpts.json) {
26761
- output(result, true);
26979
+ output({ ...result, identity_persisted: true }, true);
26762
26980
  } else {
26763
26981
  console.log(chalk5.green("Agent registered:"));
26764
26982
  console.log(` ${chalk5.dim("ID:")} ${result.id}`);
26765
26983
  console.log(` ${chalk5.dim("Name:")} ${result.name}`);
26766
26984
  console.log(`
26767
- Use ${chalk5.cyan(`--agent ${result.id}`)} on future commands.`);
26985
+ ${chalk5.dim("Identity saved \u2014 later commands attribute to this agent automatically.")}`);
26768
26986
  }
26769
26987
  } catch (e) {
26770
26988
  handleError(e);
@@ -26821,6 +27039,7 @@ Use ${chalk5.cyan(`--agent ${result.id}`)} on future commands.`);
26821
27039
  if (!result.released) {
26822
27040
  handleError(new Error("Release denied: session_id does not match agent's current session."));
26823
27041
  }
27042
+ clearIdentityIfMine(result.agent.id, result.agent.name);
26824
27043
  if (globalOpts.json) {
26825
27044
  console.log(JSON.stringify({ agent_id: result.agent.id, name: result.agent.name, released: true }));
26826
27045
  } else {
@@ -26837,6 +27056,7 @@ Use ${chalk5.cyan(`--agent ${result.id}`)} on future commands.`);
26837
27056
  if (!released) {
26838
27057
  handleError(new Error("Release denied: session_id does not match agent's current session."));
26839
27058
  }
27059
+ clearIdentityIfMine(a.id, a.name);
26840
27060
  if (globalOpts.json) {
26841
27061
  console.log(JSON.stringify({ agent_id: a.id, name: a.name, released: true }));
26842
27062
  } else {
@@ -27177,6 +27397,7 @@ var init_agent_commands = __esm(() => {
27177
27397
  init_task_lists();
27178
27398
  init_tasks();
27179
27399
  init_helpers();
27400
+ init_creator_identity();
27180
27401
  init_cloud_router();
27181
27402
  });
27182
27403
 
@@ -27219,8 +27440,8 @@ async function findFreePort(start) {
27219
27440
  var DEFAULT_PORT = 19427;
27220
27441
 
27221
27442
  // src/lib/db-backup.ts
27222
- import { existsSync as existsSync14, copyFileSync, mkdirSync as mkdirSync8, readFileSync as readFileSync8, renameSync, statSync as statSync6, writeFileSync as writeFileSync7, unlinkSync } from "fs";
27223
- 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";
27224
27445
  import { Database as Database2 } from "bun:sqlite";
27225
27446
  function resolveDbPath(dbPath) {
27226
27447
  if (dbPath)
@@ -27236,7 +27457,7 @@ function resolveDbPath(dbPath) {
27236
27457
  }
27237
27458
  function backupDatabase(outputPath, sourcePath) {
27238
27459
  const source = resolveDbPath(sourcePath);
27239
- if (!existsSync14(source))
27460
+ if (!existsSync15(source))
27240
27461
  throw new Error(`Database not found: ${source}`);
27241
27462
  mkdirSync8(dirname7(outputPath), { recursive: true });
27242
27463
  closeDatabase();
@@ -27261,7 +27482,7 @@ function backupDatabase(outputPath, sourcePath) {
27261
27482
  function checkDatabaseIntegrity(dbPath) {
27262
27483
  const path = dbPath ? resolve14(dbPath) : resolveDbPath();
27263
27484
  const errors = [];
27264
- if (!existsSync14(path)) {
27485
+ if (!existsSync15(path)) {
27265
27486
  return {
27266
27487
  schema_version: DB_BACKUP_SCHEMA,
27267
27488
  path,
@@ -27326,7 +27547,7 @@ function checkDatabaseIntegrity(dbPath) {
27326
27547
  function defaultBackupPath(dbPath) {
27327
27548
  const base = dbPath ? dirname7(resolve14(dbPath)) : dirname7(resolveDbPath());
27328
27549
  const stamp = new Date().toISOString().replace(/[:.]/g, "-");
27329
- return join12(base, "backups", `todos-${stamp}.db`);
27550
+ return join13(base, "backups", `todos-${stamp}.db`);
27330
27551
  }
27331
27552
  var DB_BACKUP_SCHEMA = "todos.db_backup.v1";
27332
27553
  var init_db_backup = __esm(() => {
@@ -27343,7 +27564,7 @@ __export(exports_evidence_redaction, {
27343
27564
  TODOS_EVIDENCE_REDACTION_CONFIRM: () => TODOS_EVIDENCE_REDACTION_CONFIRM
27344
27565
  });
27345
27566
  import { chmodSync } from "fs";
27346
- import { join as join13 } from "path";
27567
+ import { join as join14 } from "path";
27347
27568
  function unique4(values) {
27348
27569
  return Array.from(new Set((values ?? []).map((value) => value.trim()).filter(Boolean)));
27349
27570
  }
@@ -27690,7 +27911,7 @@ function redactEvidenceRows(options, db) {
27690
27911
  }
27691
27912
  function defaultEvidenceRedactionBackupPath(dbPath) {
27692
27913
  const base = defaultBackupPath(dbPath);
27693
- return join13(base.replace(/\.db$/, ""), "pre-redaction.db");
27914
+ return join14(base.replace(/\.db$/, ""), "pre-redaction.db");
27694
27915
  }
27695
27916
  var TODOS_EVIDENCE_REDACTION_SCHEMA = "todos.evidence_redaction.v1", TODOS_EVIDENCE_REDACTION_CONFIRM = "REDACT_TODOS_EVIDENCE";
27696
27917
  var init_evidence_redaction = __esm(() => {
@@ -27706,7 +27927,7 @@ __export(exports_retention_cleanup, {
27706
27927
  applyRetentionCleanup: () => applyRetentionCleanup,
27707
27928
  RETENTION_CLEANUP_CONFIRMATION: () => RETENTION_CLEANUP_CONFIRMATION
27708
27929
  });
27709
- import { existsSync as existsSync15, unlinkSync as unlinkSync2 } from "fs";
27930
+ import { existsSync as existsSync16, unlinkSync as unlinkSync2 } from "fs";
27710
27931
  function normalizeScopes(scopes) {
27711
27932
  if (!scopes || scopes.length === 0)
27712
27933
  return [...ALL_SCOPES];
@@ -27909,7 +28130,7 @@ function applyRetentionCleanup(input, db) {
27909
28130
  for (const artifact of report.candidates.artifact_files) {
27910
28131
  try {
27911
28132
  const path = artifactStorePath(artifact.relative_path);
27912
- if (!existsSync15(path)) {
28133
+ if (!existsSync16(path)) {
27913
28134
  report.warnings.push(`stored artifact already missing: ${artifact.relative_path}`);
27914
28135
  continue;
27915
28136
  }
@@ -28585,8 +28806,8 @@ __export(exports_local_extensions, {
28585
28806
  discoverLocalExtensions: () => discoverLocalExtensions
28586
28807
  });
28587
28808
  import { createHash as createHash6, createVerify } from "crypto";
28588
- import { existsSync as existsSync16, readdirSync as readdirSync3, readFileSync as readFileSync9, statSync as statSync7 } from "fs";
28589
- 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";
28590
28811
  function isObject(value) {
28591
28812
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
28592
28813
  }
@@ -28845,10 +29066,10 @@ function verifyExtensionSignature(input) {
28845
29066
  }
28846
29067
  function inspectExtensionSource(source2) {
28847
29068
  const resolved = resolve15(source2);
28848
- if (!existsSync16(resolved))
29069
+ if (!existsSync17(resolved))
28849
29070
  throw new Error(`extension source not found: ${source2}`);
28850
29071
  const stat = statSync7(resolved);
28851
- 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;
28852
29073
  if (!manifestPath)
28853
29074
  throw new Error(`extension directory ${source2} is missing todos.extension.json`);
28854
29075
  const raw = readFileSync9(manifestPath);
@@ -28944,20 +29165,20 @@ function projectExtensionSources(projectPath) {
28944
29165
  return [];
28945
29166
  const root = resolve15(projectPath);
28946
29167
  const candidates = [
28947
- join14(root, "todos.extension.json"),
28948
- join14(root, ".todos", "todos.extension.json")
29168
+ join15(root, "todos.extension.json"),
29169
+ join15(root, ".todos", "todos.extension.json")
28949
29170
  ];
28950
- const extensionDir = join14(root, ".todos", "extensions");
28951
- if (existsSync16(extensionDir)) {
29171
+ const extensionDir = join15(root, ".todos", "extensions");
29172
+ if (existsSync17(extensionDir)) {
28952
29173
  for (const entry of readdirSync3(extensionDir)) {
28953
29174
  if (entry.startsWith("."))
28954
29175
  continue;
28955
- const full = join14(extensionDir, entry);
29176
+ const full = join15(extensionDir, entry);
28956
29177
  if (statSync7(full).isDirectory() || entry.endsWith(".json"))
28957
29178
  candidates.push(full);
28958
29179
  }
28959
29180
  }
28960
- return candidates.filter(existsSync16);
29181
+ return candidates.filter(existsSync17);
28961
29182
  }
28962
29183
  function discoverLocalExtensions(options = {}) {
28963
29184
  const config = loadConfig();
@@ -31045,6 +31266,10 @@ class PostgresJsonRecordStore {
31045
31266
  conds.push(`payload->>'assigned_to' = ${p(filter.assigned_to)}`);
31046
31267
  if (filter.agent_id !== undefined)
31047
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)}))`);
31048
31273
  if (filter.session_id !== undefined)
31049
31274
  conds.push(`payload->>'session_id' = ${p(filter.session_id)}`);
31050
31275
  if (filter.tags?.length) {
@@ -31558,7 +31783,7 @@ async function createTask2(input, store, context) {
31558
31783
  description: input.description ?? null,
31559
31784
  status: input.status ?? "pending",
31560
31785
  priority: input.priority ?? "medium",
31561
- agent_id: input.agent_id ?? null,
31786
+ agent_id: input.agent_id ?? context?.agentId ?? null,
31562
31787
  assigned_to: input.assigned_to ?? null,
31563
31788
  session_id: input.session_id ?? context?.sessionId ?? null,
31564
31789
  working_dir: input.working_dir ?? null,
@@ -31583,7 +31808,8 @@ async function createTask2(input, store, context) {
31583
31808
  confidence: input.confidence ?? null,
31584
31809
  reason: input.reason ?? null,
31585
31810
  spawned_from_session: input.spawned_from_session ?? null,
31586
- 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,
31587
31813
  assigned_from_project: input.assigned_from_project ?? null,
31588
31814
  task_type: input.task_type ?? null,
31589
31815
  cost_tokens: 0,
@@ -31620,7 +31846,8 @@ async function updateTask2(id, input, store) {
31620
31846
  tags: input.tags ?? existing.tags,
31621
31847
  metadata: input.metadata ?? existing.metadata,
31622
31848
  requires_approval: input.requires_approval ?? existing.requires_approval,
31623
- 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
31624
31851
  };
31625
31852
  await store.upsert("tasks", task);
31626
31853
  return task;
@@ -33348,8 +33575,8 @@ var exports_doctor = {};
33348
33575
  __export(exports_doctor, {
33349
33576
  runTodosDoctor: () => runTodosDoctor
33350
33577
  });
33351
- import { chmodSync as chmodSync2, copyFileSync as copyFileSync2, existsSync as existsSync17, mkdirSync as mkdirSync9, statSync as statSync8 } from "fs";
33352
- 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";
33353
33580
  function tableExists3(db, table) {
33354
33581
  return Boolean(db.query("SELECT name FROM sqlite_master WHERE type='table' AND name=?").get(table));
33355
33582
  }
@@ -33443,7 +33670,7 @@ function findMissingProjectRoots(db) {
33443
33670
  continue;
33444
33671
  if (!row.path.startsWith("/"))
33445
33672
  continue;
33446
- if (!existsSync17(row.path))
33673
+ if (!existsSync18(row.path))
33447
33674
  missing++;
33448
33675
  }
33449
33676
  return missing;
@@ -33503,16 +33730,16 @@ function databasePermissionsAreUnsafe(dbPath) {
33503
33730
  function createBackup(dbPath) {
33504
33731
  if (dbPath === ":memory:" || dbPath.startsWith("file::memory:"))
33505
33732
  return;
33506
- if (!existsSync17(dbPath))
33733
+ if (!existsSync18(dbPath))
33507
33734
  return;
33508
33735
  const stamp = now().replace(/[:.]/g, "-");
33509
- const backupDir = join15(dirname8(dbPath), `${basename7(dbPath)}.backup-${stamp}`);
33736
+ const backupDir = join16(dirname8(dbPath), `${basename7(dbPath)}.backup-${stamp}`);
33510
33737
  const files = [];
33511
33738
  mkdirSync9(backupDir, { recursive: true });
33512
33739
  for (const source2 of [dbPath, `${dbPath}-wal`, `${dbPath}-shm`]) {
33513
- if (!existsSync17(source2))
33740
+ if (!existsSync18(source2))
33514
33741
  continue;
33515
- const target = join15(backupDir, basename7(source2));
33742
+ const target = join16(backupDir, basename7(source2));
33516
33743
  copyFileSync2(source2, target);
33517
33744
  files.push(target);
33518
33745
  }
@@ -33771,7 +33998,7 @@ var init_doctor = __esm(() => {
33771
33998
  });
33772
33999
 
33773
34000
  // src/server/routes.ts
33774
- 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";
33775
34002
  function parseFieldsParam(url) {
33776
34003
  const fieldsParam = url.searchParams.get("fields");
33777
34004
  return fieldsParam ? fieldsParam.split(",").map((f) => f.trim()).filter(Boolean) : undefined;
@@ -33952,11 +34179,15 @@ async function handleCreateTask(req, ctx, json2, taskToSummary2) {
33952
34179
  const body = await req.json();
33953
34180
  if (!body.title)
33954
34181
  return json2({ error: "Missing 'title'" }, 400);
34182
+ const createdBy = body.created_by ?? body.agent_id ?? "dashboard";
33955
34183
  const task = createTask({
33956
34184
  title: body.title,
33957
34185
  description: body.description,
33958
34186
  priority: body.priority,
33959
- 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 } : {}
33960
34191
  });
33961
34192
  ctx.broadcastEvent({ type: "task", task_id: task.id, action: "created", agent_id: task.agent_id, project_id: task.project_id });
33962
34193
  return json2(taskToSummary2(task), 201);
@@ -34562,7 +34793,7 @@ function handleStaticFiles(path, method, ctx, json2, serveStaticFile2) {
34562
34793
  if (!ctx.dashboardExists || method !== "GET" && method !== "HEAD")
34563
34794
  return null;
34564
34795
  if (path !== "/") {
34565
- const filePath = join16(ctx.dashboardDir, path);
34796
+ const filePath = join17(ctx.dashboardDir, path);
34566
34797
  const resolvedFile = resolve17(filePath);
34567
34798
  const resolvedBase = resolve17(ctx.dashboardDir);
34568
34799
  if (!resolvedFile.startsWith(resolvedBase + sep4) && resolvedFile !== resolvedBase) {
@@ -34572,7 +34803,7 @@ function handleStaticFiles(path, method, ctx, json2, serveStaticFile2) {
34572
34803
  if (res2)
34573
34804
  return res2;
34574
34805
  }
34575
- const indexPath = join16(ctx.dashboardDir, "index.html");
34806
+ const indexPath = join17(ctx.dashboardDir, "index.html");
34576
34807
  const res = serveStaticFile2(indexPath);
34577
34808
  if (res)
34578
34809
  return res;
@@ -35060,7 +35291,11 @@ function createLocalSqliteTodosStorageAdapter(options = {}) {
35060
35291
  sync: true
35061
35292
  },
35062
35293
  tasks: {
35063
- 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()),
35064
35299
  get: (id) => getTask(id, database()),
35065
35300
  resolveRef: (ref) => resolveTaskRefLocal(database(), ref),
35066
35301
  list: (filter = {}) => listTasksMaybeSearch(filter, database()),
@@ -37676,6 +37911,8 @@ async function handleV1Request(req, url, dependencies = {}) {
37676
37911
  ...url.searchParams.get("task_list_id") ? { task_list_id: url.searchParams.get("task_list_id") } : {},
37677
37912
  ...url.searchParams.get("assigned_to") ? { assigned_to: url.searchParams.get("assigned_to") } : {},
37678
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") } : {},
37679
37916
  ...url.searchParams.get("tags") ? {
37680
37917
  tags: url.searchParams.get("tags").split(",").map((tag) => tag.trim()).filter(Boolean)
37681
37918
  } : {},
@@ -42921,6 +43158,8 @@ function registerTaskCrudTools(server, ctx) {
42921
43158
  project_id: exports_external.string().optional().describe("Project ID"),
42922
43159
  task_list_id: exports_external.string().optional().describe("Task list ID"),
42923
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."),
42924
43163
  depends_on: exports_external.array(exports_external.string()).optional().describe("Array of task IDs this task depends on"),
42925
43164
  short_id: exports_external.string().nullable().optional().describe("Short ID (auto-generated if not provided, disabled if null)"),
42926
43165
  tags: exports_external.array(exports_external.string()).optional().describe("Tags for the task"),
@@ -42931,12 +43170,18 @@ function registerTaskCrudTools(server, ctx) {
42931
43170
  retry_count: exports_external.number().optional().describe("Max retry count for agent failures")
42932
43171
  }, async (params) => {
42933
43172
  try {
42934
- 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);
42935
43176
  const cloud = getTodosCloudClient();
42936
43177
  if (cloud) {
42937
43178
  const payload = { ...rest };
42938
- if (assigned_to)
42939
- 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;
42940
43185
  if (project_id)
42941
43186
  payload.project_id = project_id;
42942
43187
  if (task_list_id)
@@ -42957,8 +43202,12 @@ function registerTaskCrudTools(server, ctx) {
42957
43202
  return { content: [{ type: "text", text: mutationTaskResponse(created) }] };
42958
43203
  }
42959
43204
  const resolved = { ...rest };
42960
- if (assigned_to)
42961
- 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);
42962
43211
  if (project_id)
42963
43212
  resolved.project_id = resolveId(project_id, "projects");
42964
43213
  if (task_list_id)
@@ -43044,6 +43293,8 @@ function registerTaskCrudTools(server, ctx) {
43044
43293
  project_id: exports_external.string().optional().describe("Filter by project"),
43045
43294
  task_list_id: exports_external.string().optional().describe("Filter by task list"),
43046
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.'),
43047
43298
  tags: exports_external.array(exports_external.string()).optional().describe("Filter by tags (AND logic)"),
43048
43299
  created_after: exports_external.string().optional().describe("ISO date \u2014 tasks created after this date"),
43049
43300
  created_before: exports_external.string().optional().describe("ISO date \u2014 tasks created before this date"),
@@ -43236,6 +43487,7 @@ var init_task_crud2 = __esm(() => {
43236
43487
  init_tasks();
43237
43488
  init_types();
43238
43489
  init_token_utils();
43490
+ init_creator_identity();
43239
43491
  init_cloud_router();
43240
43492
  });
43241
43493
 
@@ -43432,8 +43684,8 @@ var exports_mention_resolver = {};
43432
43684
  __export(exports_mention_resolver, {
43433
43685
  resolveMentions: () => resolveMentions
43434
43686
  });
43435
- import { existsSync as existsSync18, readdirSync as readdirSync4, readFileSync as readFileSync10, statSync as statSync9 } from "fs";
43436
- 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";
43437
43689
  function blankResolution(parsed) {
43438
43690
  return {
43439
43691
  input: parsed.input,
@@ -43531,7 +43783,7 @@ function resolveFile(parsed, workspace) {
43531
43783
  return resolution;
43532
43784
  }
43533
43785
  resolution.path = relPath;
43534
- if (!existsSync18(absolutePath)) {
43786
+ if (!existsSync19(absolutePath)) {
43535
43787
  resolution.warnings.push("file does not exist in the local workspace");
43536
43788
  return resolution;
43537
43789
  }
@@ -43564,7 +43816,7 @@ function walkSourceFiles(root, current = root, files = []) {
43564
43816
  if (SKIP_DIRS2.has(entry2.name))
43565
43817
  continue;
43566
43818
  }
43567
- const absolutePath = join17(current, entry2.name);
43819
+ const absolutePath = join18(current, entry2.name);
43568
43820
  if (entry2.isDirectory()) {
43569
43821
  if (!SKIP_DIRS2.has(entry2.name))
43570
43822
  walkSourceFiles(root, absolutePath, files);
@@ -46331,7 +46583,7 @@ __export(exports_release_compatibility, {
46331
46583
  LOCAL_RELEASE_COMPATIBILITY_SCHEMA_VERSION: () => LOCAL_RELEASE_COMPATIBILITY_SCHEMA_VERSION
46332
46584
  });
46333
46585
  import { readFileSync as readFileSync11 } from "fs";
46334
- import { join as join18, resolve as resolve19 } from "path";
46586
+ import { join as join19, resolve as resolve19 } from "path";
46335
46587
  import { Database as Database3 } from "bun:sqlite";
46336
46588
  function pass(id, message, details) {
46337
46589
  return { id, status: "passed", message, details };
@@ -46343,7 +46595,7 @@ function warn(id, message, details) {
46343
46595
  return { id, status: "warning", message, details };
46344
46596
  }
46345
46597
  function readPackageJson2(root) {
46346
- return JSON.parse(readFileSync11(join18(root, "package.json"), "utf8"));
46598
+ return JSON.parse(readFileSync11(join19(root, "package.json"), "utf8"));
46347
46599
  }
46348
46600
  function sortedKeys(value) {
46349
46601
  return Object.keys(value ?? {}).sort((left, right) => left.localeCompare(right));
@@ -52286,7 +52538,7 @@ __export(exports_verification_providers, {
52286
52538
  getVerificationRecord: () => getVerificationRecord,
52287
52539
  discoverVerificationProviderCapabilities: () => discoverVerificationProviderCapabilities
52288
52540
  });
52289
- import { existsSync as existsSync19, readFileSync as readFileSync12 } from "fs";
52541
+ import { existsSync as existsSync20, readFileSync as readFileSync12 } from "fs";
52290
52542
  function normalizeName6(name) {
52291
52543
  const normalized = name.trim().toLowerCase();
52292
52544
  if (!/^[a-z0-9][a-z0-9_-]{0,63}$/.test(normalized)) {
@@ -52438,7 +52690,7 @@ Timed out after ${provider.timeout_ms}ms`);
52438
52690
  };
52439
52691
  }
52440
52692
  function runCiLogProvider(input) {
52441
- 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") : "");
52442
52694
  return {
52443
52695
  status: classifyLog(text),
52444
52696
  attempts: 1,
@@ -52450,7 +52702,7 @@ function runBrowserProvider(input) {
52450
52702
  if (!input.artifact_path) {
52451
52703
  return { status: "unknown", attempts: 1, exit_code: null, output_summary: "browser provider needs a screenshot or artifact path" };
52452
52704
  }
52453
- if (!existsSync19(input.artifact_path)) {
52705
+ if (!existsSync20(input.artifact_path)) {
52454
52706
  return { status: "failed", attempts: 1, exit_code: null, output_summary: `artifact not found: ${input.artifact_path}` };
52455
52707
  }
52456
52708
  return {
@@ -55039,7 +55291,7 @@ __export(exports_onboarding_fixtures, {
55039
55291
  TODOS_ONBOARDING_FIXTURE_LIBRARY_VERSION: () => TODOS_ONBOARDING_FIXTURE_LIBRARY_VERSION
55040
55292
  });
55041
55293
  import { mkdirSync as mkdirSync11, writeFileSync as writeFileSync9 } from "fs";
55042
- import { join as join19 } from "path";
55294
+ import { join as join20 } from "path";
55043
55295
  function emptyData() {
55044
55296
  return {
55045
55297
  projects: [],
@@ -55107,6 +55359,7 @@ function task(input) {
55107
55359
  reason: "Bundled deterministic onboarding fixture",
55108
55360
  spawned_from_session: null,
55109
55361
  assigned_by: null,
55362
+ created_by: null,
55110
55363
  assigned_from_project: null,
55111
55364
  task_type: "onboarding",
55112
55365
  cost_tokens: 0,
@@ -55373,7 +55626,7 @@ function writeOnboardingFixtureFiles(directory) {
55373
55626
  mkdirSync11(directory, { recursive: true });
55374
55627
  const files = [];
55375
55628
  for (const fixture of allFixtures()) {
55376
- const path = join19(directory, `${fixture.summary.name}.bridge.json`);
55629
+ const path = join20(directory, `${fixture.summary.name}.bridge.json`);
55377
55630
  writeFileSync9(path, `${JSON.stringify(fixture.bundle, null, 2)}
55378
55631
  `, "utf-8");
55379
55632
  files.push(path);
@@ -61585,16 +61838,16 @@ __export(exports_environment_snapshots, {
61585
61838
  captureEnvironmentSnapshot: () => captureEnvironmentSnapshot
61586
61839
  });
61587
61840
  import { createHash as createHash13 } from "crypto";
61588
- 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";
61589
61842
  import { hostname as hostname2, platform, arch } from "os";
61590
- 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";
61591
61844
  import { tmpdir as tmpdir4 } from "os";
61592
61845
  function sha2567(value) {
61593
61846
  return createHash13("sha256").update(value).digest("hex");
61594
61847
  }
61595
61848
  function fileRecord(root, relativePath) {
61596
- const path = join20(root, relativePath);
61597
- if (!existsSync20(path))
61849
+ const path = join21(root, relativePath);
61850
+ if (!existsSync21(path))
61598
61851
  return null;
61599
61852
  const stat = statSync10(path);
61600
61853
  if (!stat.isFile())
@@ -61606,7 +61859,7 @@ function manifestRecord(root, relativePath) {
61606
61859
  const base = fileRecord(root, relativePath);
61607
61860
  if (!base)
61608
61861
  return null;
61609
- const parsed = readJsonFile(join20(root, relativePath));
61862
+ const parsed = readJsonFile(join21(root, relativePath));
61610
61863
  if (!parsed)
61611
61864
  return { ...base, redacted: {} };
61612
61865
  const redacted = redactValue({
@@ -61701,8 +61954,8 @@ function commandEnv(env, includeValues) {
61701
61954
  function defaultSnapshotDir() {
61702
61955
  const dbPath = getDatabasePath();
61703
61956
  if (dbPath === ":memory:" || dbPath.startsWith("file::memory:"))
61704
- return join20(tmpdir4(), "hasna-todos", "environment-snapshots");
61705
- return join20(dirname10(resolve21(dbPath)), "environment-snapshots");
61957
+ return join21(tmpdir4(), "hasna-todos", "environment-snapshots");
61958
+ return join21(dirname10(resolve21(dbPath)), "environment-snapshots");
61706
61959
  }
61707
61960
  function snapshotWithId(snapshot) {
61708
61961
  const digest = sha2567(JSON.stringify(snapshot)).slice(0, 24);
@@ -61749,7 +62002,7 @@ function captureEnvironmentSnapshot(input = {}) {
61749
62002
  });
61750
62003
  }
61751
62004
  function writeEnvironmentSnapshot(snapshot, outputPath) {
61752
- const path = outputPath ? resolve21(outputPath) : join20(defaultSnapshotDir(), `${snapshot.id}.json`);
62005
+ const path = outputPath ? resolve21(outputPath) : join21(defaultSnapshotDir(), `${snapshot.id}.json`);
61753
62006
  ensureDir(dirname10(path));
61754
62007
  writeJsonFile(path, snapshot);
61755
62008
  return path;
@@ -62557,27 +62810,27 @@ __export(exports_serve, {
62557
62810
  SECURITY_HEADERS: () => SECURITY_HEADERS,
62558
62811
  MIME_TYPES: () => MIME_TYPES
62559
62812
  });
62560
- import { existsSync as existsSync21 } from "fs";
62561
- 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";
62562
62815
  import { fileURLToPath } from "url";
62563
62816
  function resolveDashboardDir() {
62564
62817
  const candidates = [];
62565
62818
  try {
62566
62819
  const scriptDir = dirname11(fileURLToPath(import.meta.url));
62567
- candidates.push(join21(scriptDir, "..", "dashboard", "dist"));
62568
- candidates.push(join21(scriptDir, "..", "..", "dashboard", "dist"));
62820
+ candidates.push(join22(scriptDir, "..", "dashboard", "dist"));
62821
+ candidates.push(join22(scriptDir, "..", "..", "dashboard", "dist"));
62569
62822
  } catch {}
62570
62823
  if (process.argv[1]) {
62571
62824
  const mainDir = dirname11(process.argv[1]);
62572
- candidates.push(join21(mainDir, "..", "dashboard", "dist"));
62573
- candidates.push(join21(mainDir, "..", "..", "dashboard", "dist"));
62825
+ candidates.push(join22(mainDir, "..", "dashboard", "dist"));
62826
+ candidates.push(join22(mainDir, "..", "..", "dashboard", "dist"));
62574
62827
  }
62575
- candidates.push(join21(process.cwd(), "dashboard", "dist"));
62828
+ candidates.push(join22(process.cwd(), "dashboard", "dist"));
62576
62829
  for (const candidate of candidates) {
62577
- if (existsSync21(candidate))
62830
+ if (existsSync22(candidate))
62578
62831
  return candidate;
62579
62832
  }
62580
- return join21(process.cwd(), "dashboard", "dist");
62833
+ return join22(process.cwd(), "dashboard", "dist");
62581
62834
  }
62582
62835
  function getProvidedApiKey(req) {
62583
62836
  const headerKey = req.headers.get("x-api-key");
@@ -62649,7 +62902,7 @@ function json(data, status = 200, headers) {
62649
62902
  });
62650
62903
  }
62651
62904
  function serveStaticFile(filePath) {
62652
- if (!existsSync21(filePath))
62905
+ if (!existsSync22(filePath))
62653
62906
  return null;
62654
62907
  const ext = extname(filePath);
62655
62908
  const contentType = MIME_TYPES[ext] || "application/octet-stream";
@@ -62750,7 +63003,7 @@ data: ${data}
62750
63003
  filteredSseClients.delete(client);
62751
63004
  }
62752
63005
  const dashboardDir = resolveDashboardDir();
62753
- const dashboardExists = existsSync21(dashboardDir);
63006
+ const dashboardExists = existsSync22(dashboardDir);
62754
63007
  if (!dashboardExists) {
62755
63008
  console.error(`
62756
63009
  Dashboard not found at: ${dashboardDir}`);
@@ -64623,12 +64876,12 @@ __export(exports_config_serve_commands, {
64623
64876
  registerConfigServeCommands: () => registerConfigServeCommands
64624
64877
  });
64625
64878
  import chalk6 from "chalk";
64626
- import { existsSync as existsSync22, mkdirSync as mkdirSync12, readFileSync as readFileSync16, writeFileSync as writeFileSync10 } from "fs";
64627
- 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";
64628
64881
  function registerConfigServeCommands(program2) {
64629
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) => {
64630
64883
  const globalOpts = program2.opts();
64631
- const configPath = join22(getTodosGlobalDir(), "config.json");
64884
+ const configPath = join23(getTodosGlobalDir(), "config.json");
64632
64885
  if (opts.get) {
64633
64886
  const config2 = loadConfig();
64634
64887
  const keys = opts.get.split(".");
@@ -64665,7 +64918,7 @@ function registerConfigServeCommands(program2) {
64665
64918
  }
64666
64919
  obj[keys[keys.length - 1]] = parsedValue;
64667
64920
  const dir = dirname12(configPath);
64668
- if (!existsSync22(dir))
64921
+ if (!existsSync23(dir))
64669
64922
  mkdirSync12(dir, { recursive: true });
64670
64923
  writeFileSync10(configPath, JSON.stringify(config2, null, 2));
64671
64924
  if (globalOpts.json) {
@@ -65845,8 +66098,8 @@ __export(exports_routing_doctor, {
65845
66098
  classifyTaskRouting: () => classifyTaskRouting,
65846
66099
  TODOS_ROUTING_DOCTOR_SCHEMA_VERSION: () => TODOS_ROUTING_DOCTOR_SCHEMA_VERSION
65847
66100
  });
65848
- import { copyFileSync as copyFileSync3, existsSync as existsSync23, mkdirSync as mkdirSync13, writeFileSync as writeFileSync11 } from "fs";
65849
- 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";
65850
66103
  function normalizePath5(path) {
65851
66104
  if (!path)
65852
66105
  return null;
@@ -66148,16 +66401,16 @@ function evaluateTaskRouting(ctx) {
66148
66401
  function createBackup2(dbPath, generatedAt) {
66149
66402
  if (dbPath === ":memory:" || dbPath.startsWith("file::memory:"))
66150
66403
  return;
66151
- if (!existsSync23(dbPath))
66404
+ if (!existsSync24(dbPath))
66152
66405
  return;
66153
66406
  const stamp = generatedAt.replace(/[:.]/g, "-");
66154
- const backupDir = join23(dirname13(dbPath), `${basename9(dbPath)}.routing-doctor-backup-${stamp}`);
66407
+ const backupDir = join24(dirname13(dbPath), `${basename9(dbPath)}.routing-doctor-backup-${stamp}`);
66155
66408
  const files = [];
66156
66409
  mkdirSync13(backupDir, { recursive: true });
66157
66410
  for (const source3 of [dbPath, `${dbPath}-wal`, `${dbPath}-shm`]) {
66158
- if (!existsSync23(source3))
66411
+ if (!existsSync24(source3))
66159
66412
  continue;
66160
- const target = join23(backupDir, basename9(source3));
66413
+ const target = join24(backupDir, basename9(source3));
66161
66414
  copyFileSync3(source3, target);
66162
66415
  files.push(target);
66163
66416
  }
@@ -66261,7 +66514,7 @@ function runRoutingDoctor(options = {}) {
66261
66514
  }
66262
66515
  const applied = repairs.filter((r) => r.applied);
66263
66516
  if (applied.length > 0) {
66264
- 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`);
66265
66518
  const undoRecord = {
66266
66519
  schema_version: TODOS_ROUTING_DOCTOR_SCHEMA_VERSION,
66267
66520
  purpose: "Undo record for routing-doctor --apply. Restore each field with the prior value below.",
@@ -66360,8 +66613,8 @@ __export(exports_task_route_sources, {
66360
66613
  });
66361
66614
  import { Database as Database4 } from "bun:sqlite";
66362
66615
  import { createHash as createHash14 } from "crypto";
66363
- import { existsSync as existsSync24, readdirSync as readdirSync5, statSync as statSync11 } from "fs";
66364
- 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";
66365
66618
  function normalizePath6(input) {
66366
66619
  return resolve22(input);
66367
66620
  }
@@ -66425,8 +66678,8 @@ function discoverStoresUnderRoot(sourceRoot) {
66425
66678
  const rootPath = normalizePath6(sourceRoot);
66426
66679
  const errors2 = [];
66427
66680
  const stores = [];
66428
- if (!existsSync24(rootPath)) {
66429
- const ref = createStoreRef(join24(rootPath, TODO_STORE_RELATIVE_PATH));
66681
+ if (!existsSync25(rootPath)) {
66682
+ const ref = createStoreRef(join25(rootPath, TODO_STORE_RELATIVE_PATH));
66430
66683
  errors2.push({
66431
66684
  ...ref,
66432
66685
  code: "SOURCE_ROOT_MISSING",
@@ -66438,7 +66691,7 @@ function discoverStoresUnderRoot(sourceRoot) {
66438
66691
  try {
66439
66692
  rootStat = statSync11(rootPath);
66440
66693
  } catch (error2) {
66441
- const ref = createStoreRef(join24(rootPath, TODO_STORE_RELATIVE_PATH));
66694
+ const ref = createStoreRef(join25(rootPath, TODO_STORE_RELATIVE_PATH));
66442
66695
  errors2.push({
66443
66696
  ...ref,
66444
66697
  code: "SOURCE_ROOT_UNREADABLE",
@@ -66451,8 +66704,8 @@ function discoverStoresUnderRoot(sourceRoot) {
66451
66704
  return { stores, errors: errors2 };
66452
66705
  }
66453
66706
  function scanDirectory(dir, depth) {
66454
- const candidate = join24(dir, TODO_STORE_RELATIVE_PATH);
66455
- if (existsSync24(candidate)) {
66707
+ const candidate = join25(dir, TODO_STORE_RELATIVE_PATH);
66708
+ if (existsSync25(candidate)) {
66456
66709
  stores.push(createStoreRef(candidate));
66457
66710
  }
66458
66711
  if (depth >= ROOT_SCAN_MAX_DEPTH)
@@ -66472,7 +66725,7 @@ function discoverStoresUnderRoot(sourceRoot) {
66472
66725
  for (const entry2 of entries) {
66473
66726
  if (!entry2.isDirectory() || SKIPPED_SCAN_DIRS.has(entry2.name))
66474
66727
  continue;
66475
- scanDirectory(join24(dir, entry2.name), depth + 1);
66728
+ scanDirectory(join25(dir, entry2.name), depth + 1);
66476
66729
  }
66477
66730
  }
66478
66731
  scanDirectory(rootPath, 0);
@@ -66498,7 +66751,7 @@ function collectStoreRefs(input) {
66498
66751
  };
66499
66752
  }
66500
66753
  function openReadonlyStore(ref) {
66501
- if (!existsSync24(ref.source_db_path)) {
66754
+ if (!existsSync25(ref.source_db_path)) {
66502
66755
  throw Object.assign(new Error(`Store does not exist: ${ref.source_db_path}`), { code: "STORE_MISSING" });
66503
66756
  }
66504
66757
  return new Database4(ref.source_db_path, { readonly: true, create: false });
@@ -66672,7 +66925,7 @@ var init_task_route_sources = __esm(() => {
66672
66925
  init_task_crud();
66673
66926
  init_redaction();
66674
66927
  init_task_routing();
66675
- TODO_STORE_RELATIVE_PATH = join24(".hasna", "todos", "todos.db");
66928
+ TODO_STORE_RELATIVE_PATH = join25(".hasna", "todos", "todos.db");
66676
66929
  SKIPPED_SCAN_DIRS = new Set([
66677
66930
  ".git",
66678
66931
  ".hg",
@@ -68181,9 +68434,9 @@ Findings`));
68181
68434
  const db = getDatabase();
68182
68435
  const row = db.query("SELECT COUNT(*) as count FROM tasks").get();
68183
68436
  const { statSync: statSync12 } = await import("fs");
68184
- const { join: join25 } = await import("path");
68437
+ const { join: join26 } = await import("path");
68185
68438
  const home = process.env["HOME"] || process.env["USERPROFILE"] || "~";
68186
- 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");
68187
68440
  let size = "unknown";
68188
68441
  try {
68189
68442
  size = `${(statSync12(dbPath).size / 1024 / 1024).toFixed(1)} MB`;
@@ -70097,21 +70350,21 @@ __export(exports_mcp_hooks_commands, {
70097
70350
  });
70098
70351
  import chalk9 from "chalk";
70099
70352
  import { execSync as execSync3 } from "child_process";
70100
- import { existsSync as existsSync25, readFileSync as readFileSync18, writeFileSync as writeFileSync13, mkdirSync as mkdirSync14, chmodSync as chmodSync3 } from "fs";
70101
- 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";
70102
70355
  function getMcpBinaryPath() {
70103
70356
  try {
70104
70357
  const p = execSync3("which todos-mcp", { encoding: "utf-8" }).trim();
70105
70358
  if (p)
70106
70359
  return p;
70107
70360
  } catch {}
70108
- const bunBin = join25(HOME2, ".bun", "bin", "todos-mcp");
70109
- if (existsSync25(bunBin))
70361
+ const bunBin = join26(HOME2, ".bun", "bin", "todos-mcp");
70362
+ if (existsSync26(bunBin))
70110
70363
  return bunBin;
70111
70364
  return "todos-mcp";
70112
70365
  }
70113
70366
  function readJsonFile2(path) {
70114
- if (!existsSync25(path))
70367
+ if (!existsSync26(path))
70115
70368
  return {};
70116
70369
  try {
70117
70370
  return JSON.parse(readFileSync18(path, "utf-8"));
@@ -70121,19 +70374,19 @@ function readJsonFile2(path) {
70121
70374
  }
70122
70375
  function writeJsonFile2(path, data) {
70123
70376
  const dir = dirname15(path);
70124
- if (!existsSync25(dir))
70377
+ if (!existsSync26(dir))
70125
70378
  mkdirSync14(dir, { recursive: true });
70126
70379
  writeFileSync13(path, JSON.stringify(data, null, 2) + `
70127
70380
  `);
70128
70381
  }
70129
70382
  function readTomlFile(path) {
70130
- if (!existsSync25(path))
70383
+ if (!existsSync26(path))
70131
70384
  return "";
70132
70385
  return readFileSync18(path, "utf-8");
70133
70386
  }
70134
70387
  function writeTomlFile(path, content) {
70135
70388
  const dir = dirname15(path);
70136
- if (!existsSync25(dir))
70389
+ if (!existsSync26(dir))
70137
70390
  mkdirSync14(dir, { recursive: true });
70138
70391
  writeFileSync13(path, content);
70139
70392
  }
@@ -70198,7 +70451,7 @@ function unregisterClaude(_global) {
70198
70451
  }
70199
70452
  }
70200
70453
  function registerCodex(binPath) {
70201
- const configPath = join25(HOME2, ".codex", "config.toml");
70454
+ const configPath = join26(HOME2, ".codex", "config.toml");
70202
70455
  let content = readTomlFile(configPath);
70203
70456
  content = removeTomlBlock(content, "mcp_servers.todos");
70204
70457
  const block = `
@@ -70212,7 +70465,7 @@ args = ["--stdio"]
70212
70465
  console.log(chalk9.green(`Codex CLI: registered in ${configPath}`));
70213
70466
  }
70214
70467
  function unregisterCodex() {
70215
- const configPath = join25(HOME2, ".codex", "config.toml");
70468
+ const configPath = join26(HOME2, ".codex", "config.toml");
70216
70469
  let content = readTomlFile(configPath);
70217
70470
  if (!content.includes("[mcp_servers.todos]")) {
70218
70471
  console.log(chalk9.dim(`Codex CLI: todos not found in ${configPath}`));
@@ -70224,7 +70477,7 @@ function unregisterCodex() {
70224
70477
  console.log(chalk9.green(`Codex CLI: unregistered from ${configPath}`));
70225
70478
  }
70226
70479
  function registerGemini(binPath) {
70227
- const configPath = join25(HOME2, ".gemini", "settings.json");
70480
+ const configPath = join26(HOME2, ".gemini", "settings.json");
70228
70481
  const config = readJsonFile2(configPath);
70229
70482
  if (!config["mcpServers"]) {
70230
70483
  config["mcpServers"] = {};
@@ -70238,7 +70491,7 @@ function registerGemini(binPath) {
70238
70491
  console.log(chalk9.green(`Gemini CLI: registered in ${configPath}`));
70239
70492
  }
70240
70493
  function unregisterGemini() {
70241
- const configPath = join25(HOME2, ".gemini", "settings.json");
70494
+ const configPath = join26(HOME2, ".gemini", "settings.json");
70242
70495
  const config = readJsonFile2(configPath);
70243
70496
  const servers = config["mcpServers"];
70244
70497
  if (!servers || !("todos" in servers)) {
@@ -70249,8 +70502,37 @@ function unregisterGemini() {
70249
70502
  writeJsonFile2(configPath, config);
70250
70503
  console.log(chalk9.green(`Gemini CLI: unregistered from ${configPath}`));
70251
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
+ }
70252
70534
  function registerMcp(agent, global) {
70253
- const agents = agent === "all" ? ["claude", "codex", "gemini"] : [agent];
70535
+ const agents = agent === "all" ? ["claude", "codex", "gemini", "cursor"] : [agent];
70254
70536
  const binPath = getMcpBinaryPath();
70255
70537
  for (const a of agents) {
70256
70538
  switch (a) {
@@ -70263,13 +70545,16 @@ function registerMcp(agent, global) {
70263
70545
  case "gemini":
70264
70546
  registerGemini(binPath);
70265
70547
  break;
70548
+ case "cursor":
70549
+ registerCursor(binPath, global);
70550
+ break;
70266
70551
  default:
70267
- 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`));
70268
70553
  }
70269
70554
  }
70270
70555
  }
70271
70556
  function unregisterMcp(agent, global) {
70272
- const agents = agent === "all" ? ["claude", "codex", "gemini"] : [agent];
70557
+ const agents = agent === "all" ? ["claude", "codex", "gemini", "cursor"] : [agent];
70273
70558
  for (const a of agents) {
70274
70559
  switch (a) {
70275
70560
  case "claude":
@@ -70281,8 +70566,11 @@ function unregisterMcp(agent, global) {
70281
70566
  case "gemini":
70282
70567
  unregisterGemini();
70283
70568
  break;
70569
+ case "cursor":
70570
+ unregisterCursor(global);
70571
+ break;
70284
70572
  default:
70285
- 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`));
70286
70574
  }
70287
70575
  }
70288
70576
  }
@@ -70295,8 +70583,8 @@ function registerMcpHooksCommands(program2) {
70295
70583
  if (p)
70296
70584
  todosBin = p;
70297
70585
  } catch {}
70298
- const hooksDir = join25(process.cwd(), ".claude", "hooks");
70299
- if (!existsSync25(hooksDir))
70586
+ const hooksDir = join26(process.cwd(), ".claude", "hooks");
70587
+ if (!existsSync26(hooksDir))
70300
70588
  mkdirSync14(hooksDir, { recursive: true });
70301
70589
  const hookScript = `#!/usr/bin/env bash
70302
70590
  # Auto-generated by: todos hooks install
@@ -70321,11 +70609,11 @@ esac
70321
70609
 
70322
70610
  exit 0
70323
70611
  `;
70324
- const hookPath = join25(hooksDir, "todos-sync.sh");
70612
+ const hookPath = join26(hooksDir, "todos-sync.sh");
70325
70613
  writeFileSync13(hookPath, hookScript);
70326
70614
  execSync3(`chmod +x "${hookPath}"`);
70327
70615
  console.log(chalk9.green(`Hook script created: ${hookPath}`));
70328
- const settingsPath = join25(process.cwd(), ".claude", "settings.json");
70616
+ const settingsPath = join26(process.cwd(), ".claude", "settings.json");
70329
70617
  const settings = readJsonFile2(settingsPath);
70330
70618
  if (!settings["hooks"]) {
70331
70619
  settings["hooks"] = {};
@@ -70354,7 +70642,7 @@ exit 0
70354
70642
  console.log(chalk9.green(`Claude Code hooks configured in: ${settingsPath}`));
70355
70643
  console.log(chalk9.dim("Task list ID auto-detected from project."));
70356
70644
  });
70357
- 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) => {
70358
70646
  if (opts.register) {
70359
70647
  registerMcp(opts.register, opts.global);
70360
70648
  return;
@@ -71256,7 +71544,7 @@ Artifacts:`));
71256
71544
  const gitDir = execSync3("git rev-parse --git-dir", { encoding: "utf-8" }).trim();
71257
71545
  const hookPath = `${gitDir}/hooks/post-commit`;
71258
71546
  const marker = "# todos-auto-link";
71259
- if (existsSync25(hookPath)) {
71547
+ if (existsSync26(hookPath)) {
71260
71548
  const existing = readFileSync18(hookPath, "utf-8");
71261
71549
  if (existing.includes(marker)) {
71262
71550
  console.log(chalk9.yellow("Hook already installed."));
@@ -71283,7 +71571,7 @@ $(dirname "$0")/../../scripts/post-commit-hook.sh
71283
71571
  const gitDir = execSync3("git rev-parse --git-dir", { encoding: "utf-8" }).trim();
71284
71572
  const hookPath = `${gitDir}/hooks/post-commit`;
71285
71573
  const marker = "# todos-auto-link";
71286
- if (!existsSync25(hookPath)) {
71574
+ if (!existsSync26(hookPath)) {
71287
71575
  console.log(chalk9.dim("No post-commit hook found."));
71288
71576
  return;
71289
71577
  }
@@ -71469,7 +71757,7 @@ import chalk11 from "chalk";
71469
71757
  import { execSync as execSync4 } from "child_process";
71470
71758
  import { readFileSync as readFileSync19, unlinkSync as unlinkSync3, writeFileSync as writeFileSync14 } from "fs";
71471
71759
  import { tmpdir as tmpdir5 } from "os";
71472
- import { join as join26 } from "path";
71760
+ import { join as join27 } from "path";
71473
71761
  function getOrCreateLocalMachineName() {
71474
71762
  return process.env["TODOS_MACHINE_NAME"] || __require("os").hostname() || "unknown";
71475
71763
  }
@@ -71507,7 +71795,7 @@ function remoteTempPath(sshAddress) {
71507
71795
  }
71508
71796
  function readRemoteBridgeBundle(sshAddress) {
71509
71797
  const remotePath = remoteTempPath(sshAddress);
71510
- const localPath = join26(tmpdir5(), `todos-bridge-pull-${uuid()}.json`);
71798
+ const localPath = join27(tmpdir5(), `todos-bridge-pull-${uuid()}.json`);
71511
71799
  try {
71512
71800
  runSsh(sshAddress, `todos export --format bridge --allow-plaintext-sensitive --output ${shellQuote(remotePath)}`, 120000);
71513
71801
  scpFromRemote(sshAddress, remotePath, localPath);
@@ -71522,7 +71810,7 @@ function readRemoteBridgeBundle(sshAddress) {
71522
71810
  }
71523
71811
  }
71524
71812
  function writeLocalBridgeBundle() {
71525
- const localPath = join26(tmpdir5(), `todos-bridge-push-${uuid()}.json`);
71813
+ const localPath = join27(tmpdir5(), `todos-bridge-push-${uuid()}.json`);
71526
71814
  writeFileSync14(localPath, JSON.stringify(createLocalBridgeBundle(), null, 2));
71527
71815
  return localPath;
71528
71816
  }
@@ -76097,7 +76385,7 @@ __export(exports_sdk_integration_fixtures, {
76097
76385
  TODOS_SDK_INTEGRATION_FIXTURE_GENERATED_AT: () => TODOS_SDK_INTEGRATION_FIXTURE_GENERATED_AT
76098
76386
  });
76099
76387
  import { mkdirSync as mkdirSync15, writeFileSync as writeFileSync15 } from "fs";
76100
- import { join as join27 } from "path";
76388
+ import { join as join28 } from "path";
76101
76389
  function source5(version) {
76102
76390
  return {
76103
76391
  packageName: "@hasna/todos",
@@ -76204,7 +76492,7 @@ function writeSdkIntegrationFixtures(directory, options = {}) {
76204
76492
  ];
76205
76493
  const written = [];
76206
76494
  for (const [name, payload] of files) {
76207
- const file = join27(directory, name);
76495
+ const file = join28(directory, name);
76208
76496
  writeFileSync15(file, `${JSON.stringify(payload, null, 2)}
76209
76497
  `, "utf-8");
76210
76498
  written.push(file);