@hasna/todos 0.11.65 → 0.11.66

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/mcp/index.js CHANGED
@@ -9413,38 +9413,7 @@ var init_task_lists = __esm(() => {
9413
9413
  init_projects();
9414
9414
  });
9415
9415
 
9416
- // src/lib/shared-events.ts
9417
- function taskEventData(task, extra = {}) {
9418
- return {
9419
- id: task.id,
9420
- task_id: task.id,
9421
- short_id: task.short_id,
9422
- title: task.title,
9423
- description: task.description,
9424
- status: task.status,
9425
- priority: task.priority,
9426
- project_id: task.project_id,
9427
- parent_id: task.parent_id,
9428
- plan_id: task.plan_id,
9429
- task_list_id: task.task_list_id,
9430
- agent_id: task.agent_id,
9431
- assigned_to: task.assigned_to,
9432
- session_id: task.session_id,
9433
- working_dir: task.working_dir,
9434
- tags: task.tags,
9435
- metadata: task.metadata,
9436
- version: task.version,
9437
- created_at: task.created_at,
9438
- updated_at: task.updated_at,
9439
- started_at: task.started_at,
9440
- completed_at: task.completed_at,
9441
- due_at: task.due_at,
9442
- requires_approval: task.requires_approval,
9443
- approved_by: task.approved_by,
9444
- approved_at: task.approved_at,
9445
- ...extra
9446
- };
9447
- }
9416
+ // src/lib/task-route-contract.ts
9448
9417
  function booleanField(value) {
9449
9418
  if (typeof value === "boolean")
9450
9419
  return value;
@@ -9466,21 +9435,31 @@ function booleanField(value) {
9466
9435
  function objectField(value) {
9467
9436
  return value && typeof value === "object" && !Array.isArray(value) ? value : undefined;
9468
9437
  }
9469
- function firstBoolean(records, keys) {
9438
+ function collectBooleans(records, keys) {
9439
+ const values = [];
9470
9440
  for (const record of records) {
9441
+ if (!record)
9442
+ continue;
9471
9443
  for (const key of keys) {
9472
9444
  const value = booleanField(record[key]);
9473
9445
  if (value !== undefined)
9474
- return value;
9446
+ values.push(value);
9475
9447
  }
9476
9448
  }
9477
- return;
9449
+ return values;
9478
9450
  }
9479
- function routingAutomationMetadata(task) {
9480
- const automation = objectField(task.metadata.automation);
9481
- const records = [task.metadata];
9482
- if (automation)
9483
- records.push(automation);
9451
+ function mergedBoolean(records, keys, trueWins) {
9452
+ const values = collectBooleans(records, keys);
9453
+ if (values.length === 0)
9454
+ return;
9455
+ if (trueWins)
9456
+ return values.some(Boolean);
9457
+ return values.includes(false) ? false : true;
9458
+ }
9459
+ function routingAutomationMetadata(task, taskList) {
9460
+ const taskAutomation = objectField(task.metadata.automation);
9461
+ const taskListAutomation = taskList ? objectField(taskList.metadata.automation) : undefined;
9462
+ const records = [task.metadata, taskAutomation, taskList?.metadata, taskListAutomation];
9484
9463
  const result = {};
9485
9464
  const aliases = [
9486
9465
  ["allowed", ["allowed", "automation_allowed", "automationAllowed"]],
@@ -9491,7 +9470,7 @@ function routingAutomationMetadata(task) {
9491
9470
  ["approval_required", ["approval_required", "approvalRequired"]]
9492
9471
  ];
9493
9472
  for (const [canonical, keys] of aliases) {
9494
- const value = firstBoolean(records, keys);
9473
+ const value = mergedBoolean(records, keys, canonical !== "allowed");
9495
9474
  if (value !== undefined)
9496
9475
  result[canonical] = value;
9497
9476
  }
@@ -9499,23 +9478,88 @@ function routingAutomationMetadata(task) {
9499
9478
  result.requires_approval = true;
9500
9479
  return Object.keys(result).length > 0 ? result : undefined;
9501
9480
  }
9481
+ function routeEnabledForTask(task, taskList) {
9482
+ const explicit = booleanField(task.metadata.route_enabled);
9483
+ if (explicit !== undefined)
9484
+ return explicit;
9485
+ if (task.tags.includes("auto:route") || task.tags.includes("route:enabled"))
9486
+ return true;
9487
+ const taskListDefault = taskList ? booleanField(taskList.metadata.route_enabled) : undefined;
9488
+ if (taskListDefault !== undefined)
9489
+ return taskListDefault;
9490
+ return;
9491
+ }
9492
+ function stringField(value) {
9493
+ return typeof value === "string" && value.trim() ? value : undefined;
9494
+ }
9495
+ function workflowPointersFromMetadata(metadata) {
9496
+ const nested = objectField(metadata.workflow_invocation) ?? objectField(metadata.workflow) ?? {};
9497
+ return {
9498
+ current_workflow_invocation_id: stringField(metadata.current_workflow_invocation_id) ?? stringField(nested.current_workflow_invocation_id) ?? stringField(nested.invocation_id),
9499
+ current_run_id: stringField(metadata.current_run_id) ?? stringField(nested.current_run_id) ?? stringField(nested.run_id),
9500
+ latest_manifest_path: stringField(metadata.latest_manifest_path) ?? stringField(nested.latest_manifest_path) ?? stringField(nested.manifest_path),
9501
+ latest_evaluation_path: stringField(metadata.latest_evaluation_path) ?? stringField(nested.latest_evaluation_path) ?? stringField(nested.evaluation_path),
9502
+ workflow_state: stringField(metadata.workflow_state) ?? stringField(nested.workflow_state) ?? stringField(nested.state)
9503
+ };
9504
+ }
9505
+ function classifyProjectKind(path) {
9506
+ return path.includes("/hasna/opensource/") ? "open-source" : "unknown";
9507
+ }
9508
+ function isWorktreePath(path) {
9509
+ return path.includes("/.codewith/worktrees/") || path.includes("/.worktrees/");
9510
+ }
9511
+ function inferRootProjectId(project) {
9512
+ return isWorktreePath(project.path) ? null : project.id;
9513
+ }
9514
+ var TODOS_TASK_ROUTE_STATE_SCHEMA_VERSION = "todos.task_route_state.v1";
9515
+
9516
+ // src/lib/shared-events.ts
9517
+ function taskEventData(task, extra = {}) {
9518
+ return {
9519
+ id: task.id,
9520
+ task_id: task.id,
9521
+ short_id: task.short_id,
9522
+ title: task.title,
9523
+ description: task.description,
9524
+ status: task.status,
9525
+ priority: task.priority,
9526
+ project_id: task.project_id,
9527
+ parent_id: task.parent_id,
9528
+ plan_id: task.plan_id,
9529
+ task_list_id: task.task_list_id,
9530
+ agent_id: task.agent_id,
9531
+ assigned_to: task.assigned_to,
9532
+ session_id: task.session_id,
9533
+ working_dir: task.working_dir,
9534
+ tags: task.tags,
9535
+ metadata: task.metadata,
9536
+ version: task.version,
9537
+ created_at: task.created_at,
9538
+ updated_at: task.updated_at,
9539
+ started_at: task.started_at,
9540
+ completed_at: task.completed_at,
9541
+ due_at: task.due_at,
9542
+ requires_approval: task.requires_approval,
9543
+ approved_by: task.approved_by,
9544
+ approved_at: task.approved_at,
9545
+ ...extra
9546
+ };
9547
+ }
9502
9548
  function taskEventMetadata(task) {
9503
9549
  const metadata = {
9504
9550
  package: "@hasna/todos",
9505
9551
  todos_event_schema_version: 1,
9552
+ route_state_schema_version: TODOS_TASK_ROUTE_STATE_SCHEMA_VERSION,
9506
9553
  task_id: task.id,
9507
9554
  task_short_id: task.short_id,
9508
9555
  project_id: task.project_id,
9509
9556
  task_list_id: task.task_list_id,
9510
9557
  working_dir: task.working_dir
9511
9558
  };
9512
- const routeEnabled = booleanField(task.metadata.route_enabled);
9513
- if (routeEnabled !== undefined) {
9514
- metadata.route_enabled = routeEnabled;
9515
- }
9516
- const automation = routingAutomationMetadata(task);
9517
- if (automation) {
9518
- metadata.automation = automation;
9559
+ const pointers = workflowPointersFromMetadata(task.metadata);
9560
+ for (const [key, value] of Object.entries(pointers)) {
9561
+ if (value)
9562
+ metadata[key] = value;
9519
9563
  }
9520
9564
  try {
9521
9565
  const project = task.project_id ? getProject(task.project_id) : null;
@@ -9544,18 +9588,20 @@ function taskEventMetadata(task) {
9544
9588
  metadata.task_list_project_id = taskList.project_id;
9545
9589
  metadata.task_list_is_project_default = Boolean(project?.task_list_id && taskList.slug === project.task_list_id);
9546
9590
  }
9591
+ const routeEnabled = routeEnabledForTask(task, taskList);
9592
+ if (routeEnabled !== undefined) {
9593
+ metadata.route_enabled = routeEnabled;
9594
+ }
9595
+ const automation = routingAutomationMetadata(task, taskList);
9596
+ if (automation) {
9597
+ metadata.automation = automation;
9598
+ metadata.route_blocked_by_no_auto = automation.no_auto === true;
9599
+ metadata.route_blocked_by_manual = automation.manual === true || automation.manual_required === true;
9600
+ metadata.route_blocked_by_approval = (automation.requires_approval === true || automation.approval_required === true) && !task.approved_by;
9601
+ }
9547
9602
  } catch {}
9548
9603
  return metadata;
9549
9604
  }
9550
- function classifyProjectKind(path) {
9551
- return path.includes("/hasna/opensource/") ? "open-source" : "unknown";
9552
- }
9553
- function isWorktreePath(path) {
9554
- return path.includes("/.codewith/worktrees/") || path.includes("/.worktrees/");
9555
- }
9556
- function inferRootProjectId(project) {
9557
- return isWorktreePath(project.path) ? null : project.id;
9558
- }
9559
9605
  function readMachineLocalPath(project) {
9560
9606
  const machineId = process.env["TODOS_MACHINE_ID"];
9561
9607
  if (!machineId)
@@ -15999,7 +16045,19 @@ function bootstrapProject(options = {}, db) {
15999
16045
  }
16000
16046
  setMachineLocalPath(project.id, discovery.projectPath, d);
16001
16047
  const beforeTaskList = d.query("SELECT id FROM task_lists WHERE project_id = ? AND slug = ?").get(project.id, taskListSlug);
16002
- const taskList = ensureTaskList(`${project.name} Tasks`, taskListSlug, project.id, d);
16048
+ let taskList = ensureTaskList(`${project.name} Tasks`, taskListSlug, project.id, d);
16049
+ if (options.routeEnabled && taskList.metadata.route_enabled !== true) {
16050
+ taskList = updateTaskList(taskList.id, {
16051
+ metadata: {
16052
+ ...taskList.metadata,
16053
+ route_enabled: true,
16054
+ automation: {
16055
+ ...taskList.metadata.automation && typeof taskList.metadata.automation === "object" && !Array.isArray(taskList.metadata.automation) ? taskList.metadata.automation : {},
16056
+ no_auto: false
16057
+ }
16058
+ }
16059
+ }, d);
16060
+ }
16003
16061
  const createdSources = [];
16004
16062
  for (const source of [
16005
16063
  addSourceOnce(project.id, "local", "Project root", discovery.projectPath, { role: "project-root" }, d),
package/dist/registry.js CHANGED
@@ -7628,39 +7628,9 @@ function ensureTaskList(name, slug, projectId, db) {
7628
7628
  return createTaskList({ name, slug, project_id: projectId }, d);
7629
7629
  }
7630
7630
 
7631
- // src/lib/shared-events.ts
7632
- var SOURCE = "todos";
7633
- function taskEventData(task, extra = {}) {
7634
- return {
7635
- id: task.id,
7636
- task_id: task.id,
7637
- short_id: task.short_id,
7638
- title: task.title,
7639
- description: task.description,
7640
- status: task.status,
7641
- priority: task.priority,
7642
- project_id: task.project_id,
7643
- parent_id: task.parent_id,
7644
- plan_id: task.plan_id,
7645
- task_list_id: task.task_list_id,
7646
- agent_id: task.agent_id,
7647
- assigned_to: task.assigned_to,
7648
- session_id: task.session_id,
7649
- working_dir: task.working_dir,
7650
- tags: task.tags,
7651
- metadata: task.metadata,
7652
- version: task.version,
7653
- created_at: task.created_at,
7654
- updated_at: task.updated_at,
7655
- started_at: task.started_at,
7656
- completed_at: task.completed_at,
7657
- due_at: task.due_at,
7658
- requires_approval: task.requires_approval,
7659
- approved_by: task.approved_by,
7660
- approved_at: task.approved_at,
7661
- ...extra
7662
- };
7663
- }
7631
+ // src/lib/task-route-contract.ts
7632
+ var TODOS_TASK_ROUTE_STATE_SCHEMA_VERSION = "todos.task_route_state.v1";
7633
+ var TASK_WORKFLOW_POINTER_SCHEMA_VERSION = "todos.task_workflow_pointer.v1";
7664
7634
  function booleanField(value) {
7665
7635
  if (typeof value === "boolean")
7666
7636
  return value;
@@ -7682,21 +7652,31 @@ function booleanField(value) {
7682
7652
  function objectField(value) {
7683
7653
  return value && typeof value === "object" && !Array.isArray(value) ? value : undefined;
7684
7654
  }
7685
- function firstBoolean(records, keys) {
7655
+ function collectBooleans(records, keys) {
7656
+ const values = [];
7686
7657
  for (const record of records) {
7658
+ if (!record)
7659
+ continue;
7687
7660
  for (const key of keys) {
7688
7661
  const value = booleanField(record[key]);
7689
7662
  if (value !== undefined)
7690
- return value;
7663
+ values.push(value);
7691
7664
  }
7692
7665
  }
7693
- return;
7666
+ return values;
7694
7667
  }
7695
- function routingAutomationMetadata(task) {
7696
- const automation = objectField(task.metadata.automation);
7697
- const records = [task.metadata];
7698
- if (automation)
7699
- records.push(automation);
7668
+ function mergedBoolean(records, keys, trueWins) {
7669
+ const values = collectBooleans(records, keys);
7670
+ if (values.length === 0)
7671
+ return;
7672
+ if (trueWins)
7673
+ return values.some(Boolean);
7674
+ return values.includes(false) ? false : true;
7675
+ }
7676
+ function routingAutomationMetadata(task, taskList) {
7677
+ const taskAutomation = objectField(task.metadata.automation);
7678
+ const taskListAutomation = taskList ? objectField(taskList.metadata.automation) : undefined;
7679
+ const records = [task.metadata, taskAutomation, taskList?.metadata, taskListAutomation];
7700
7680
  const result = {};
7701
7681
  const aliases = [
7702
7682
  ["allowed", ["allowed", "automation_allowed", "automationAllowed"]],
@@ -7707,7 +7687,7 @@ function routingAutomationMetadata(task) {
7707
7687
  ["approval_required", ["approval_required", "approvalRequired"]]
7708
7688
  ];
7709
7689
  for (const [canonical, keys] of aliases) {
7710
- const value = firstBoolean(records, keys);
7690
+ const value = mergedBoolean(records, keys, canonical !== "allowed");
7711
7691
  if (value !== undefined)
7712
7692
  result[canonical] = value;
7713
7693
  }
@@ -7715,23 +7695,91 @@ function routingAutomationMetadata(task) {
7715
7695
  result.requires_approval = true;
7716
7696
  return Object.keys(result).length > 0 ? result : undefined;
7717
7697
  }
7698
+ function routeEnabledForTask(task, taskList) {
7699
+ const explicit = booleanField(task.metadata.route_enabled);
7700
+ if (explicit !== undefined)
7701
+ return explicit;
7702
+ if (task.tags.includes("auto:route") || task.tags.includes("route:enabled"))
7703
+ return true;
7704
+ const taskListDefault = taskList ? booleanField(taskList.metadata.route_enabled) : undefined;
7705
+ if (taskListDefault !== undefined)
7706
+ return taskListDefault;
7707
+ return;
7708
+ }
7709
+ function stringField(value) {
7710
+ return typeof value === "string" && value.trim() ? value : undefined;
7711
+ }
7712
+ function workflowPointersFromMetadata(metadata) {
7713
+ const nested = objectField(metadata.workflow_invocation) ?? objectField(metadata.workflow) ?? {};
7714
+ return {
7715
+ current_workflow_invocation_id: stringField(metadata.current_workflow_invocation_id) ?? stringField(nested.current_workflow_invocation_id) ?? stringField(nested.invocation_id),
7716
+ current_run_id: stringField(metadata.current_run_id) ?? stringField(nested.current_run_id) ?? stringField(nested.run_id),
7717
+ latest_manifest_path: stringField(metadata.latest_manifest_path) ?? stringField(nested.latest_manifest_path) ?? stringField(nested.manifest_path),
7718
+ latest_evaluation_path: stringField(metadata.latest_evaluation_path) ?? stringField(nested.latest_evaluation_path) ?? stringField(nested.evaluation_path),
7719
+ workflow_state: stringField(metadata.workflow_state) ?? stringField(nested.workflow_state) ?? stringField(nested.state)
7720
+ };
7721
+ }
7722
+ function compactWorkflowPointers(pointers) {
7723
+ return Object.fromEntries(Object.entries(pointers).filter(([, value]) => typeof value === "string" && value.length > 0));
7724
+ }
7725
+ function classifyProjectKind(path) {
7726
+ return path.includes("/hasna/opensource/") ? "open-source" : "unknown";
7727
+ }
7728
+ function isWorktreePath(path) {
7729
+ return path.includes("/.codewith/worktrees/") || path.includes("/.worktrees/");
7730
+ }
7731
+ function inferRootProjectId(project) {
7732
+ return isWorktreePath(project.path) ? null : project.id;
7733
+ }
7734
+
7735
+ // src/lib/shared-events.ts
7736
+ var SOURCE = "todos";
7737
+ function taskEventData(task, extra = {}) {
7738
+ return {
7739
+ id: task.id,
7740
+ task_id: task.id,
7741
+ short_id: task.short_id,
7742
+ title: task.title,
7743
+ description: task.description,
7744
+ status: task.status,
7745
+ priority: task.priority,
7746
+ project_id: task.project_id,
7747
+ parent_id: task.parent_id,
7748
+ plan_id: task.plan_id,
7749
+ task_list_id: task.task_list_id,
7750
+ agent_id: task.agent_id,
7751
+ assigned_to: task.assigned_to,
7752
+ session_id: task.session_id,
7753
+ working_dir: task.working_dir,
7754
+ tags: task.tags,
7755
+ metadata: task.metadata,
7756
+ version: task.version,
7757
+ created_at: task.created_at,
7758
+ updated_at: task.updated_at,
7759
+ started_at: task.started_at,
7760
+ completed_at: task.completed_at,
7761
+ due_at: task.due_at,
7762
+ requires_approval: task.requires_approval,
7763
+ approved_by: task.approved_by,
7764
+ approved_at: task.approved_at,
7765
+ ...extra
7766
+ };
7767
+ }
7718
7768
  function taskEventMetadata(task) {
7719
7769
  const metadata = {
7720
7770
  package: "@hasna/todos",
7721
7771
  todos_event_schema_version: 1,
7772
+ route_state_schema_version: TODOS_TASK_ROUTE_STATE_SCHEMA_VERSION,
7722
7773
  task_id: task.id,
7723
7774
  task_short_id: task.short_id,
7724
7775
  project_id: task.project_id,
7725
7776
  task_list_id: task.task_list_id,
7726
7777
  working_dir: task.working_dir
7727
7778
  };
7728
- const routeEnabled = booleanField(task.metadata.route_enabled);
7729
- if (routeEnabled !== undefined) {
7730
- metadata.route_enabled = routeEnabled;
7731
- }
7732
- const automation = routingAutomationMetadata(task);
7733
- if (automation) {
7734
- metadata.automation = automation;
7779
+ const pointers = workflowPointersFromMetadata(task.metadata);
7780
+ for (const [key, value] of Object.entries(pointers)) {
7781
+ if (value)
7782
+ metadata[key] = value;
7735
7783
  }
7736
7784
  try {
7737
7785
  const project = task.project_id ? getProject(task.project_id) : null;
@@ -7760,18 +7808,20 @@ function taskEventMetadata(task) {
7760
7808
  metadata.task_list_project_id = taskList.project_id;
7761
7809
  metadata.task_list_is_project_default = Boolean(project?.task_list_id && taskList.slug === project.task_list_id);
7762
7810
  }
7811
+ const routeEnabled = routeEnabledForTask(task, taskList);
7812
+ if (routeEnabled !== undefined) {
7813
+ metadata.route_enabled = routeEnabled;
7814
+ }
7815
+ const automation = routingAutomationMetadata(task, taskList);
7816
+ if (automation) {
7817
+ metadata.automation = automation;
7818
+ metadata.route_blocked_by_no_auto = automation.no_auto === true;
7819
+ metadata.route_blocked_by_manual = automation.manual === true || automation.manual_required === true;
7820
+ metadata.route_blocked_by_approval = (automation.requires_approval === true || automation.approval_required === true) && !task.approved_by;
7821
+ }
7763
7822
  } catch {}
7764
7823
  return metadata;
7765
7824
  }
7766
- function classifyProjectKind(path) {
7767
- return path.includes("/hasna/opensource/") ? "open-source" : "unknown";
7768
- }
7769
- function isWorktreePath(path) {
7770
- return path.includes("/.codewith/worktrees/") || path.includes("/.worktrees/");
7771
- }
7772
- function inferRootProjectId(project) {
7773
- return isWorktreePath(project.path) ? null : project.id;
7774
- }
7775
7825
  function readMachineLocalPath(project) {
7776
7826
  const machineId = process.env["TODOS_MACHINE_ID"];
7777
7827
  if (!machineId)
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "packageName": "@hasna/todos",
3
- "packageVersion": "0.11.65",
3
+ "packageVersion": "0.11.66",
4
4
  "repository": "https://github.com/hasna/todos.git",
5
- "gitCommit": "f0c20fab559134ebaebad350633d424296b429ab",
6
- "generatedAt": "2026-06-28T08:59:54.014Z"
5
+ "gitCommit": "d016ecf14209af05d9d2040ff52e8b69b27a0b6f",
6
+ "generatedAt": "2026-06-28T16:44:28.364Z"
7
7
  }