@hasna/todos 0.15.41 → 0.15.46

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/storage.js CHANGED
@@ -8651,7 +8651,7 @@ function getTaskGraph(taskId, direction = "both", db) {
8651
8651
  const deps = getTaskDependencies(t.id, d);
8652
8652
  const hasUnfinishedDeps = deps.some((dep) => {
8653
8653
  const depTask = getTask(dep.depends_on, d);
8654
- return depTask && depTask.status !== "completed";
8654
+ return depTask && isBlockingDependencyStatus(depTask.status);
8655
8655
  });
8656
8656
  return { id: t.id, short_id: t.short_id, title: t.title, status: t.status, priority: t.priority, is_blocked: hasUnfinishedDeps };
8657
8657
  }
@@ -8911,7 +8911,7 @@ function getBlockingDeps(id, db) {
8911
8911
  const blocking = [];
8912
8912
  for (const dep of deps) {
8913
8913
  const task = getTask(dep.depends_on, d);
8914
- if (task && task.status !== "completed")
8914
+ if (task && isBlockingDependencyStatus(task.status))
8915
8915
  blocking.push(task);
8916
8916
  }
8917
8917
  return blocking;
@@ -9235,7 +9235,7 @@ function getNextTask(agentId, filters, db) {
9235
9235
  conditions.push(`id IN (SELECT task_id FROM task_tags WHERE tag IN (${placeholders}))`);
9236
9236
  params.push(...filters.tags);
9237
9237
  }
9238
- conditions.push("id NOT IN (SELECT td.task_id FROM task_dependencies td JOIN tasks dep ON dep.id = td.depends_on WHERE dep.status != 'completed')");
9238
+ conditions.push("id NOT IN (SELECT td.task_id FROM task_dependencies td JOIN tasks dep ON dep.id = td.depends_on WHERE dep.status NOT IN ('completed', 'cancelled'))");
9239
9239
  const where = conditions.join(" AND ");
9240
9240
  let recentProjectIds = [];
9241
9241
  const assignedAliasParams = [];
@@ -9279,7 +9279,7 @@ function getActiveWork(filters, db) {
9279
9279
  }
9280
9280
  function getTasksChangedSince(since, filters, db) {
9281
9281
  const d = db || getDatabase();
9282
- const conditions = ["updated_at > ?"];
9282
+ const conditions = ["(julianday(updated_at) IS NULL OR julianday(updated_at) > julianday(?))"];
9283
9283
  const params = [since];
9284
9284
  if (filters?.project_id) {
9285
9285
  conditions.push("project_id = ?");
@@ -14696,7 +14696,7 @@ function matchesExtraFilters(task, filter) {
14696
14696
  }
14697
14697
  if (filter.tags?.length) {
14698
14698
  const taskTags = new Set(task.tags ?? []);
14699
- if (!filter.tags.every((tag) => taskTags.has(tag)))
14699
+ if (!filter.tags.some((tag) => taskTags.has(tag)))
14700
14700
  return false;
14701
14701
  }
14702
14702
  if (filter.include_subtasks !== true && filter.parent_id === undefined && task.parent_id)
@@ -14860,6 +14860,59 @@ function createLocalSqliteTodosStorageAdapter(options = {}) {
14860
14860
  init_types();
14861
14861
  import { randomUUID as randomUUID3 } from "crypto";
14862
14862
  init_creator_identity();
14863
+
14864
+ // src/lib/instant-compare.ts
14865
+ var SQLITE_STAMP = /^(\d{4})-(\d{2})-(\d{2})(?:[ T](\d{2}):(\d{2})(?::(\d{2})(?:\.(\d+))?)?(?:Z|([+-])(\d{2}):(\d{2}))?)?$/;
14866
+ var DAYS_IN_MONTH = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
14867
+ function isLeapYear(year) {
14868
+ return year % 4 === 0 && year % 100 !== 0 || year % 400 === 0;
14869
+ }
14870
+ function sqliteJulianDay(value) {
14871
+ const m = SQLITE_STAMP.exec(value);
14872
+ if (!m)
14873
+ return null;
14874
+ const year = Number(m[1]);
14875
+ const month = Number(m[2]);
14876
+ const day = Number(m[3]);
14877
+ const hour = m[4] === undefined ? 0 : Number(m[4]);
14878
+ const minute = m[5] === undefined ? 0 : Number(m[5]);
14879
+ const second = m[6] === undefined ? 0 : Number(m[6]);
14880
+ const frac = m[7];
14881
+ const sign = m[8];
14882
+ const offsetHour = m[9];
14883
+ const offsetMinute = m[10];
14884
+ if (month < 1 || month > 12)
14885
+ return null;
14886
+ if (hour > 23 || minute > 59 || second > 59)
14887
+ return null;
14888
+ const maxDay = (DAYS_IN_MONTH[month - 1] ?? 0) + (month === 2 && isLeapYear(year) ? 1 : 0);
14889
+ if (day < 1 || day > maxDay)
14890
+ return null;
14891
+ let offsetMinutes = 0;
14892
+ if (sign !== undefined) {
14893
+ const oh = Number(offsetHour ?? "0");
14894
+ const om = Number(offsetMinute ?? "0");
14895
+ if (oh > 23 || om > 59)
14896
+ return null;
14897
+ offsetMinutes = oh * 60 + om;
14898
+ if (sign === "-")
14899
+ offsetMinutes = -offsetMinutes;
14900
+ }
14901
+ const micros = frac === undefined ? 0 : Number((frac + "000000").slice(0, 6));
14902
+ const ms = Date.UTC(year, month - 1, day, hour, minute, second);
14903
+ return (ms + micros / 1000) / 86400000 + 2440587.5 - offsetMinutes / 1440;
14904
+ }
14905
+ function changedSinceStampNewer(stamp, since) {
14906
+ const stampJd = sqliteJulianDay(stamp);
14907
+ if (stampJd === null)
14908
+ return true;
14909
+ const sinceJd = sqliteJulianDay(since);
14910
+ if (sinceJd === null)
14911
+ return false;
14912
+ return stampJd > sinceJd;
14913
+ }
14914
+
14915
+ // src/storage/postgres-adapter.ts
14863
14916
  init_stale_lock_handoff();
14864
14917
 
14865
14918
  // src/storage/postgres-sync.ts
@@ -15474,11 +15527,16 @@ class PostgresJsonRecordStore {
15474
15527
  return context?.requestId ?? this.sourceMachineId ?? null;
15475
15528
  }
15476
15529
  async ensureSchema() {
15477
- this.schemaReady ??= (async () => {
15478
- for (const sql of postgresTodosSyncSchemaSql(this.tableName, this.cursorTableName)) {
15479
- await this.options.client.query(sql);
15480
- }
15481
- })();
15530
+ if (!this.schemaReady) {
15531
+ this.schemaReady = (async () => {
15532
+ for (const sql of postgresTodosSyncSchemaSql(this.tableName, this.cursorTableName)) {
15533
+ await this.options.client.query(sql);
15534
+ }
15535
+ })().catch((error) => {
15536
+ this.schemaReady = null;
15537
+ throw error;
15538
+ });
15539
+ }
15482
15540
  await this.schemaReady;
15483
15541
  }
15484
15542
  async get(type, id) {
@@ -17243,7 +17301,7 @@ async function getActiveWork2(filters, store) {
17243
17301
  }));
17244
17302
  }
17245
17303
  async function getChangedSince(since, filters, store) {
17246
- return (await listTasks2(filters ?? {}, store)).filter((task) => task.updated_at > since);
17304
+ return (await listTasks2(filters ?? {}, store)).filter((task) => changedSinceStampNewer(task.updated_at ?? "", since));
17247
17305
  }
17248
17306
  async function createProject2(input, store, context) {
17249
17307
  const timestamp2 = new Date().toISOString();
@@ -1 +1 @@
1
- {"version":3,"file":"postgres.d.ts","sourceRoot":"","sources":["../../src/task-manifest/postgres.ts"],"names":[],"mappings":"AAEA,OAAO,EAEL,KAAK,sBAAsB,EAC3B,KAAK,0BAA0B,EAE/B,KAAK,wBAAwB,EAC9B,MAAM,cAAc,CAAC;AAItB,OAAO,EAEL,KAAK,yCAAyC,EAE9C,KAAK,4BAA4B,EACjC,KAAK,kCAAkC,EACvC,KAAK,mCAAmC,EAExC,KAAK,+BAA+B,EACpC,KAAK,wBAAwB,EAE9B,MAAM,YAAY,CAAC;AAgKpB,qBAAa,gCAAiC,YAAW,wBAAwB;IAQ7E,OAAO,CAAC,QAAQ,CAAC,MAAM;IAPzB,QAAQ,CAAC,IAAI,EAAG,YAAY,CAAU;IACtC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,WAAW,CAA8B;gBAG9B,MAAM,EAAE,+BAA+B,EACxD,OAAO,GAAE,yCAA8C;YAO3C,YAAY;YAQZ,UAAU;YAcV,eAAe;IAyCvB,KAAK,CAAC,KAAK,EAAE,sBAAsB,EAAE,MAAM,EAAE,0BAA0B,GAAG,OAAO,CAAC,4BAA4B,CAAC;IAwJ/G,SAAS,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,4BAA4B,CAAC;IAenE,qBAAqB,CAAC,MAAM,EAAE,MAAM;IA8BpC,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA0DzE,UAAU,CACd,KAAK,EAAE,kCAAkC,EACzC,OAAO,EAAE,wBAAwB,EACjC,qBAAqB,EAAE,MAAM,EAC7B,aAAa,EAAE,MAAM,EACrB,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,mCAAmC,CAAC;YAyPjC,QAAQ;CAcvB"}
1
+ {"version":3,"file":"postgres.d.ts","sourceRoot":"","sources":["../../src/task-manifest/postgres.ts"],"names":[],"mappings":"AAEA,OAAO,EAEL,KAAK,sBAAsB,EAC3B,KAAK,0BAA0B,EAE/B,KAAK,wBAAwB,EAC9B,MAAM,cAAc,CAAC;AAItB,OAAO,EAEL,KAAK,yCAAyC,EAE9C,KAAK,4BAA4B,EACjC,KAAK,kCAAkC,EACvC,KAAK,mCAAmC,EAExC,KAAK,+BAA+B,EACpC,KAAK,wBAAwB,EAE9B,MAAM,YAAY,CAAC;AAgKpB,qBAAa,gCAAiC,YAAW,wBAAwB;IAQ7E,OAAO,CAAC,QAAQ,CAAC,MAAM;IAPzB,QAAQ,CAAC,IAAI,EAAG,YAAY,CAAU;IACtC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,WAAW,CAA8B;gBAG9B,MAAM,EAAE,+BAA+B,EACxD,OAAO,GAAE,yCAA8C;YAO3C,YAAY;YAqBZ,UAAU;YAcV,eAAe;IAyCvB,KAAK,CAAC,KAAK,EAAE,sBAAsB,EAAE,MAAM,EAAE,0BAA0B,GAAG,OAAO,CAAC,4BAA4B,CAAC;IAwJ/G,SAAS,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,4BAA4B,CAAC;IAenE,qBAAqB,CAAC,MAAM,EAAE,MAAM;IA8BpC,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA0DzE,UAAU,CACd,KAAK,EAAE,kCAAkC,EACzC,OAAO,EAAE,wBAAwB,EACjC,qBAAqB,EAAE,MAAM,EAC7B,aAAa,EAAE,MAAM,EACrB,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,mCAAmC,CAAC;YAyPjC,QAAQ;CAcvB"}
@@ -6613,12 +6613,21 @@ class PostgresTodosTaskManifestBackend {
6613
6613
  this.tenantId = options.tenantId ?? "default";
6614
6614
  }
6615
6615
  async ensureSchema() {
6616
- this.schemaReady ??= (async () => {
6617
- for (const sql of postgresTodosSyncSchemaSql(this.tableName))
6618
- await this.client.query(sql);
6619
- for (const sql of postgresTodosTaskManifestSchemaSql(this.tenantId))
6620
- await this.client.query(sql);
6621
- })();
6616
+ if (this.schemaReady === null) {
6617
+ const attempt = (async () => {
6618
+ for (const sql of postgresTodosSyncSchemaSql(this.tableName))
6619
+ await this.client.query(sql);
6620
+ for (const sql of postgresTodosTaskManifestSchemaSql(this.tenantId))
6621
+ await this.client.query(sql);
6622
+ })();
6623
+ this.schemaReady = attempt;
6624
+ try {
6625
+ await attempt;
6626
+ } catch (error) {
6627
+ this.schemaReady = null;
6628
+ throw error;
6629
+ }
6630
+ }
6622
6631
  await this.schemaReady;
6623
6632
  }
6624
6633
  async insertSync(tx, objectType2, objectId, payload, now) {
@@ -1 +1 @@
1
- {"version":3,"file":"postgres.d.ts","sourceRoot":"","sources":["../../src/task-subtree-transfer/postgres.ts"],"names":[],"mappings":"AAcA,OAAO,EAGL,KAAK,gDAAgD,EACrD,KAAK,oCAAoC,EACzC,KAAK,wCAAwC,EAE7C,KAAK,sCAAsC,EAC3C,KAAK,kCAAkC,EAEvC,KAAK,8BAA8B,EACnC,KAAK,uCAAuC,EAC7C,MAAM,YAAY,CAAC;AACpB,OAAO,KAAK,EAAE,+BAA+B,EAAE,MAAM,cAAc,CAAC;AA6CpE,qBAAa,uCAAwC,YAAW,+BAA+B;IAQ3F,OAAO,CAAC,QAAQ,CAAC,MAAM;IAPzB,QAAQ,CAAC,IAAI,EAAG,YAAY,CAAU;IACtC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,WAAW,CAA8B;gBAG9B,MAAM,EAAE,OAAO,YAAY,EAAE,sCAAsC,EACpF,OAAO,GAAE,gDAAqD;YAOlD,YAAY;YAQZ,QAAQ;IAoDhB,OAAO,CAAC,KAAK,EAAE,sCAAsC,GAAG,OAAO,CAAC,kCAAkC,CAAC;YAK3F,aAAa;IAoCrB,KAAK,CAAC,KAAK,EAAE,oCAAoC,EAAE,OAAO,EAAE,wCAAwC,GAAG,OAAO,CAAC,8BAA8B,CAAC;IAuH9I,SAAS,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,8BAA8B,CAAC;IAUrE,QAAQ,CAAC,KAAK,EAAE,uCAAuC,EAAE,OAAO,EAAE,wCAAwC,GAAG,OAAO,CAAC,8BAA8B,CAAC;CAgH3J"}
1
+ {"version":3,"file":"postgres.d.ts","sourceRoot":"","sources":["../../src/task-subtree-transfer/postgres.ts"],"names":[],"mappings":"AAcA,OAAO,EAGL,KAAK,gDAAgD,EACrD,KAAK,oCAAoC,EACzC,KAAK,wCAAwC,EAE7C,KAAK,sCAAsC,EAC3C,KAAK,kCAAkC,EAEvC,KAAK,8BAA8B,EACnC,KAAK,uCAAuC,EAC7C,MAAM,YAAY,CAAC;AACpB,OAAO,KAAK,EAAE,+BAA+B,EAAE,MAAM,cAAc,CAAC;AA6CpE,qBAAa,uCAAwC,YAAW,+BAA+B;IAQ3F,OAAO,CAAC,QAAQ,CAAC,MAAM;IAPzB,QAAQ,CAAC,IAAI,EAAG,YAAY,CAAU;IACtC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,WAAW,CAA8B;gBAG9B,MAAM,EAAE,OAAO,YAAY,EAAE,sCAAsC,EACpF,OAAO,GAAE,gDAAqD;YAOlD,YAAY;YAqBZ,QAAQ;IAoDhB,OAAO,CAAC,KAAK,EAAE,sCAAsC,GAAG,OAAO,CAAC,kCAAkC,CAAC;YAK3F,aAAa;IAoCrB,KAAK,CAAC,KAAK,EAAE,oCAAoC,EAAE,OAAO,EAAE,wCAAwC,GAAG,OAAO,CAAC,8BAA8B,CAAC;IAuH9I,SAAS,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,8BAA8B,CAAC;IAUrE,QAAQ,CAAC,KAAK,EAAE,uCAAuC,EAAE,OAAO,EAAE,wCAAwC,GAAG,OAAO,CAAC,8BAA8B,CAAC;CAgH3J"}
@@ -835,12 +835,21 @@ class PostgresTodosTaskSubtreeTransferBackend {
835
835
  this.tenantId = options.tenantId ?? "default";
836
836
  }
837
837
  async ensureSchema() {
838
- this.schemaReady ??= (async () => {
839
- for (const sql of postgresTodosSyncSchemaSql(this.tableName))
840
- await this.client.query(sql);
841
- for (const sql of postgresTodosTaskSubtreeTransferSchemaSql())
842
- await this.client.query(sql);
843
- })();
838
+ if (this.schemaReady === null) {
839
+ const attempt = (async () => {
840
+ for (const sql of postgresTodosSyncSchemaSql(this.tableName))
841
+ await this.client.query(sql);
842
+ for (const sql of postgresTodosTaskSubtreeTransferSchemaSql())
843
+ await this.client.query(sql);
844
+ })();
845
+ this.schemaReady = attempt;
846
+ try {
847
+ await attempt;
848
+ } catch (error) {
849
+ this.schemaReady = null;
850
+ throw error;
851
+ }
852
+ }
844
853
  await this.schemaReady;
845
854
  }
846
855
  async snapshot(client, input, forUpdate = false) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/todos",
3
- "version": "0.15.41",
3
+ "version": "0.15.46",
4
4
  "description": "Universal task management for AI coding agents - CLI + MCP server + interactive TUI",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -118,7 +118,7 @@
118
118
  "author": "Andrei Hasna <andrei@hasna.com>",
119
119
  "license": "Apache-2.0",
120
120
  "dependencies": {
121
- "@hasna/contracts": "0.13.3",
121
+ "@hasna/contracts": "0.13.4",
122
122
  "@hasna/events": "^0.1.11",
123
123
  "@modelcontextprotocol/sdk": "^1.12.1",
124
124
  "chalk": "^5.4.1",