@hasna/todos 0.11.57 → 0.11.58

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.
@@ -4014,7 +4014,7 @@ var init_event_hooks = __esm(() => {
4014
4014
  VALID_TARGETS = new Set(["stdout", "file", "socket", "script"]);
4015
4015
  });
4016
4016
 
4017
- // node_modules/.bun/@hasna+events@0.1.7/node_modules/@hasna/events/dist/index.js
4017
+ // node_modules/.bun/@hasna+events@0.1.9/node_modules/@hasna/events/dist/index.js
4018
4018
  import { chmod, mkdir, readFile, rename, writeFile } from "fs/promises";
4019
4019
  import { existsSync as existsSync6 } from "fs";
4020
4020
  import { homedir } from "os";
@@ -4031,17 +4031,30 @@ function getPathValue(input, path) {
4031
4031
  return;
4032
4032
  }, input);
4033
4033
  }
4034
- function wildcardToRegExp(pattern) {
4035
- const escaped = pattern.replace(/[|\\{}()[\]^$+?.]/g, "\\$&").replace(/\*/g, ".*");
4036
- return new RegExp(`^${escaped}$`);
4034
+ function wildcardToRegExp(pattern, options = {}) {
4035
+ let body = "";
4036
+ for (let index = 0;index < pattern.length; index += 1) {
4037
+ const char = pattern[index];
4038
+ if (char === "*") {
4039
+ if (pattern[index + 1] === "*") {
4040
+ body += ".*";
4041
+ index += 1;
4042
+ } else {
4043
+ body += options.segmentSafe ? "[^/]*" : ".*";
4044
+ }
4045
+ } else {
4046
+ body += char.replace(/[|\\{}()[\]^$+?.]/g, "\\$&");
4047
+ }
4048
+ }
4049
+ return new RegExp(`^${body}$`);
4037
4050
  }
4038
- function matchString(value, matcher) {
4051
+ function matchString(value, matcher, options = {}) {
4039
4052
  if (matcher === undefined)
4040
4053
  return true;
4041
4054
  if (value === undefined)
4042
4055
  return false;
4043
4056
  const matchers = Array.isArray(matcher) ? matcher : [matcher];
4044
- return matchers.some((item) => wildcardToRegExp(item).test(value));
4057
+ return matchers.some((item) => wildcardToRegExp(item, options).test(value));
4045
4058
  }
4046
4059
  function matchRecord(input, matcher) {
4047
4060
  if (!matcher)
@@ -4049,7 +4062,9 @@ function matchRecord(input, matcher) {
4049
4062
  return Object.entries(matcher).every(([path, expected]) => {
4050
4063
  const actual = getPathValue(input, path);
4051
4064
  if (typeof expected === "string" || Array.isArray(expected)) {
4052
- return matchString(actual === undefined ? undefined : String(actual), expected);
4065
+ return matchString(actual === undefined ? undefined : String(actual), expected, {
4066
+ segmentSafe: path.endsWith("_path") || path.endsWith(".path")
4067
+ });
4053
4068
  }
4054
4069
  return actual === expected;
4055
4070
  });
@@ -4403,7 +4418,7 @@ class EventsClient {
4403
4418
  }
4404
4419
  return deliveries;
4405
4420
  }
4406
- async testChannel(id, input = {}) {
4421
+ async matchChannel(id, input = {}) {
4407
4422
  const channel = await this.store.getChannel(id);
4408
4423
  if (!channel)
4409
4424
  throw new Error(`Channel not found: ${id}`);
@@ -4420,6 +4435,34 @@ class EventsClient {
4420
4435
  time: input.time,
4421
4436
  id: input.id
4422
4437
  });
4438
+ const matched = channelMatchesEvent(channel, event);
4439
+ return {
4440
+ channelId: channel.id,
4441
+ matched,
4442
+ event,
4443
+ filters: channel.filters,
4444
+ reason: matched ? undefined : channel.enabled ? "event did not match channel filters" : "channel is disabled"
4445
+ };
4446
+ }
4447
+ async testChannel(id, input = {}, options = {}) {
4448
+ const channel = await this.store.getChannel(id);
4449
+ if (!channel)
4450
+ throw new Error(`Channel not found: ${id}`);
4451
+ const match = await this.matchChannel(id, input);
4452
+ const event = match.event;
4453
+ if (options.honorFilters && !match.matched) {
4454
+ const timestamp = new Date().toISOString();
4455
+ const result2 = createDeliveryResult(event, channel, [{
4456
+ attempt: 1,
4457
+ status: "skipped",
4458
+ startedAt: timestamp,
4459
+ completedAt: timestamp,
4460
+ error: match.reason
4461
+ }]);
4462
+ result2.metadata = { reason: "filter_mismatch" };
4463
+ await this.store.appendDelivery(result2);
4464
+ return result2;
4465
+ }
4423
4466
  const eventForChannel = await this.applyRedaction(event, channel);
4424
4467
  const result = await this.deliverWithRetry(eventForChannel, channel);
4425
4468
  await this.store.appendDelivery(result);
@@ -4523,6 +4566,90 @@ var init_dist = __esm(() => {
4523
4566
  DEFAULT_SIGNATURE_TOLERANCE_MS = 5 * 60 * 1000;
4524
4567
  });
4525
4568
 
4569
+ // src/db/task-lists.ts
4570
+ function rowToTaskList(row) {
4571
+ return {
4572
+ ...row,
4573
+ metadata: JSON.parse(row.metadata || "{}")
4574
+ };
4575
+ }
4576
+ function createTaskList(input, db) {
4577
+ const d = db || getDatabase();
4578
+ const id = uuid();
4579
+ const timestamp = now();
4580
+ const slug = input.slug || slugify(input.name);
4581
+ if (!input.project_id) {
4582
+ const existing = d.query("SELECT id FROM task_lists WHERE project_id IS NULL AND slug = ?").get(slug);
4583
+ if (existing) {
4584
+ throw new Error(`Standalone task list with slug "${slug}" already exists`);
4585
+ }
4586
+ }
4587
+ d.run(`INSERT INTO task_lists (id, project_id, slug, name, description, metadata, created_at, updated_at)
4588
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, [id, input.project_id || null, slug, input.name, input.description || null, JSON.stringify(input.metadata || {}), timestamp, timestamp]);
4589
+ return getTaskList(id, d);
4590
+ }
4591
+ function getTaskList(id, db) {
4592
+ const d = db || getDatabase();
4593
+ const row = d.query("SELECT * FROM task_lists WHERE id = ?").get(id);
4594
+ return row ? rowToTaskList(row) : null;
4595
+ }
4596
+ function getTaskListBySlug(slug, projectId, db) {
4597
+ const d = db || getDatabase();
4598
+ let row;
4599
+ if (projectId) {
4600
+ row = d.query("SELECT * FROM task_lists WHERE slug = ? AND project_id = ?").get(slug, projectId);
4601
+ } else {
4602
+ row = d.query("SELECT * FROM task_lists WHERE slug = ? AND project_id IS NULL").get(slug);
4603
+ }
4604
+ return row ? rowToTaskList(row) : null;
4605
+ }
4606
+ function listTaskLists(projectId, db) {
4607
+ const d = db || getDatabase();
4608
+ if (projectId) {
4609
+ return d.query("SELECT * FROM task_lists WHERE project_id = ? ORDER BY name").all(projectId).map(rowToTaskList);
4610
+ }
4611
+ return d.query("SELECT * FROM task_lists ORDER BY name").all().map(rowToTaskList);
4612
+ }
4613
+ function updateTaskList(id, input, db) {
4614
+ const d = db || getDatabase();
4615
+ const existing = getTaskList(id, d);
4616
+ if (!existing)
4617
+ throw new TaskListNotFoundError(id);
4618
+ const sets = ["updated_at = ?"];
4619
+ const params = [now()];
4620
+ if (input.name !== undefined) {
4621
+ sets.push("name = ?");
4622
+ params.push(input.name);
4623
+ }
4624
+ if (input.description !== undefined) {
4625
+ sets.push("description = ?");
4626
+ params.push(input.description);
4627
+ }
4628
+ if (input.metadata !== undefined) {
4629
+ sets.push("metadata = ?");
4630
+ params.push(JSON.stringify(input.metadata));
4631
+ }
4632
+ params.push(id);
4633
+ d.run(`UPDATE task_lists SET ${sets.join(", ")} WHERE id = ?`, params);
4634
+ return getTaskList(id, d);
4635
+ }
4636
+ function deleteTaskList(id, db) {
4637
+ const d = db || getDatabase();
4638
+ return d.run("DELETE FROM task_lists WHERE id = ?", [id]).changes > 0;
4639
+ }
4640
+ function ensureTaskList(name, slug, projectId, db) {
4641
+ const d = db || getDatabase();
4642
+ const existing = getTaskListBySlug(slug, projectId, d);
4643
+ if (existing)
4644
+ return existing;
4645
+ return createTaskList({ name, slug, project_id: projectId }, d);
4646
+ }
4647
+ var init_task_lists = __esm(() => {
4648
+ init_types();
4649
+ init_database();
4650
+ init_projects();
4651
+ });
4652
+
4526
4653
  // src/lib/shared-events.ts
4527
4654
  function taskEventData(task, extra = {}) {
4528
4655
  return {
@@ -4552,6 +4679,69 @@ function taskEventData(task, extra = {}) {
4552
4679
  ...extra
4553
4680
  };
4554
4681
  }
4682
+ function taskEventMetadata(task) {
4683
+ const metadata = {
4684
+ package: "@hasna/todos",
4685
+ todos_event_schema_version: 1,
4686
+ task_id: task.id,
4687
+ task_short_id: task.short_id,
4688
+ project_id: task.project_id,
4689
+ task_list_id: task.task_list_id,
4690
+ working_dir: task.working_dir
4691
+ };
4692
+ try {
4693
+ const project = task.project_id ? getProject(task.project_id) : null;
4694
+ const projectPath = project ? readMachineLocalPath(project) ?? project.path : task.working_dir;
4695
+ if (project) {
4696
+ metadata.project_id = project.id;
4697
+ metadata.project_name = project.name;
4698
+ metadata.project_path = projectPath;
4699
+ metadata.project_canonical_path = project.path;
4700
+ metadata.project_default_task_list_slug = project.task_list_id;
4701
+ metadata.root_project_id = inferRootProjectId(project);
4702
+ } else if (projectPath) {
4703
+ metadata.project_path = projectPath;
4704
+ metadata.project_canonical_path = projectPath;
4705
+ }
4706
+ if (projectPath) {
4707
+ metadata.project_kind = classifyProjectKind(projectPath);
4708
+ metadata.project_is_worktree = isWorktreePath(projectPath);
4709
+ if (typeof task.metadata.route_enabled === "boolean") {
4710
+ metadata.route_enabled = task.metadata.route_enabled;
4711
+ }
4712
+ metadata.working_dir = task.working_dir ?? projectPath;
4713
+ }
4714
+ const taskList = task.task_list_id ? getTaskList(task.task_list_id) ?? (project ? getTaskListBySlug(task.task_list_id, project.id) : null) : project?.task_list_id ? getTaskListBySlug(project.task_list_id, project.id) : null;
4715
+ if (taskList) {
4716
+ metadata.task_list_id = taskList.id;
4717
+ metadata.task_list_slug = taskList.slug;
4718
+ metadata.task_list_name = taskList.name;
4719
+ metadata.task_list_project_id = taskList.project_id;
4720
+ metadata.task_list_is_project_default = Boolean(project?.task_list_id && taskList.slug === project.task_list_id);
4721
+ }
4722
+ } catch {}
4723
+ return metadata;
4724
+ }
4725
+ function classifyProjectKind(path) {
4726
+ return path.includes("/hasna/opensource/") ? "open-source" : "unknown";
4727
+ }
4728
+ function isWorktreePath(path) {
4729
+ return path.includes("/.codewith/worktrees/") || path.includes("/.worktrees/");
4730
+ }
4731
+ function inferRootProjectId(project) {
4732
+ return isWorktreePath(project.path) ? null : project.id;
4733
+ }
4734
+ function readMachineLocalPath(project) {
4735
+ const machineId = process.env["TODOS_MACHINE_ID"];
4736
+ if (!machineId)
4737
+ return null;
4738
+ try {
4739
+ const row = getDatabase().query("SELECT path FROM project_machine_paths WHERE project_id = ? AND machine_id = ?").get(project.id, machineId);
4740
+ return row?.path ?? null;
4741
+ } catch {
4742
+ return null;
4743
+ }
4744
+ }
4555
4745
  async function emitSharedTaskEvent(input) {
4556
4746
  const data = taskEventData(input.task, input.data);
4557
4747
  await new EventsClient().emit({
@@ -4562,12 +4752,7 @@ async function emitSharedTaskEvent(input) {
4562
4752
  message: input.message ?? `${input.type}: ${input.task.title}`,
4563
4753
  data,
4564
4754
  dedupeKey: input.dedupeKey ?? `${input.type}:${input.task.id}:${input.task.version}`,
4565
- metadata: {
4566
- package: "@hasna/todos",
4567
- task_id: input.task.id,
4568
- project_id: input.task.project_id,
4569
- task_list_id: input.task.task_list_id
4570
- }
4755
+ metadata: taskEventMetadata(input.task)
4571
4756
  }, { deliver: true, dedupe: true });
4572
4757
  }
4573
4758
  function emitSharedTaskEventQuiet(input) {
@@ -4578,6 +4763,9 @@ function emitSharedTaskEventQuiet(input) {
4578
4763
  var SOURCE = "todos";
4579
4764
  var init_shared_events = __esm(() => {
4580
4765
  init_dist();
4766
+ init_database();
4767
+ init_projects();
4768
+ init_task_lists();
4581
4769
  });
4582
4770
 
4583
4771
  // src/lib/secret-redaction.ts
@@ -31823,90 +32011,6 @@ var init_dispatch = __esm(() => {
31823
32011
  init_tmux();
31824
32012
  });
31825
32013
 
31826
- // src/db/task-lists.ts
31827
- function rowToTaskList(row) {
31828
- return {
31829
- ...row,
31830
- metadata: JSON.parse(row.metadata || "{}")
31831
- };
31832
- }
31833
- function createTaskList(input, db) {
31834
- const d = db || getDatabase();
31835
- const id = uuid();
31836
- const timestamp = now();
31837
- const slug = input.slug || slugify(input.name);
31838
- if (!input.project_id) {
31839
- const existing = d.query("SELECT id FROM task_lists WHERE project_id IS NULL AND slug = ?").get(slug);
31840
- if (existing) {
31841
- throw new Error(`Standalone task list with slug "${slug}" already exists`);
31842
- }
31843
- }
31844
- d.run(`INSERT INTO task_lists (id, project_id, slug, name, description, metadata, created_at, updated_at)
31845
- VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, [id, input.project_id || null, slug, input.name, input.description || null, JSON.stringify(input.metadata || {}), timestamp, timestamp]);
31846
- return getTaskList(id, d);
31847
- }
31848
- function getTaskList(id, db) {
31849
- const d = db || getDatabase();
31850
- const row = d.query("SELECT * FROM task_lists WHERE id = ?").get(id);
31851
- return row ? rowToTaskList(row) : null;
31852
- }
31853
- function getTaskListBySlug(slug, projectId, db) {
31854
- const d = db || getDatabase();
31855
- let row;
31856
- if (projectId) {
31857
- row = d.query("SELECT * FROM task_lists WHERE slug = ? AND project_id = ?").get(slug, projectId);
31858
- } else {
31859
- row = d.query("SELECT * FROM task_lists WHERE slug = ? AND project_id IS NULL").get(slug);
31860
- }
31861
- return row ? rowToTaskList(row) : null;
31862
- }
31863
- function listTaskLists(projectId, db) {
31864
- const d = db || getDatabase();
31865
- if (projectId) {
31866
- return d.query("SELECT * FROM task_lists WHERE project_id = ? ORDER BY name").all(projectId).map(rowToTaskList);
31867
- }
31868
- return d.query("SELECT * FROM task_lists ORDER BY name").all().map(rowToTaskList);
31869
- }
31870
- function updateTaskList(id, input, db) {
31871
- const d = db || getDatabase();
31872
- const existing = getTaskList(id, d);
31873
- if (!existing)
31874
- throw new TaskListNotFoundError(id);
31875
- const sets = ["updated_at = ?"];
31876
- const params = [now()];
31877
- if (input.name !== undefined) {
31878
- sets.push("name = ?");
31879
- params.push(input.name);
31880
- }
31881
- if (input.description !== undefined) {
31882
- sets.push("description = ?");
31883
- params.push(input.description);
31884
- }
31885
- if (input.metadata !== undefined) {
31886
- sets.push("metadata = ?");
31887
- params.push(JSON.stringify(input.metadata));
31888
- }
31889
- params.push(id);
31890
- d.run(`UPDATE task_lists SET ${sets.join(", ")} WHERE id = ?`, params);
31891
- return getTaskList(id, d);
31892
- }
31893
- function deleteTaskList(id, db) {
31894
- const d = db || getDatabase();
31895
- return d.run("DELETE FROM task_lists WHERE id = ?", [id]).changes > 0;
31896
- }
31897
- function ensureTaskList(name, slug, projectId, db) {
31898
- const d = db || getDatabase();
31899
- const existing = getTaskListBySlug(slug, projectId, d);
31900
- if (existing)
31901
- return existing;
31902
- return createTaskList({ name, slug, project_id: projectId }, d);
31903
- }
31904
- var init_task_lists = __esm(() => {
31905
- init_types();
31906
- init_database();
31907
- init_projects();
31908
- });
31909
-
31910
32014
  // src/mcp/tools/dispatch.ts
31911
32015
  function registerDispatchTools(server, { shouldRegisterTool, resolveId, formatError: formatError2 }) {
31912
32016
  if (shouldRegisterTool("dispatch_tasks")) {