@hasna/todos 0.15.5 → 0.15.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.
@@ -1 +1 @@
1
- {"version":3,"file":"task-commands.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/task-commands.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAgEzC,oFAAoF;AACpF,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAQ5D;AAmUD,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,QAkzDpD"}
1
+ {"version":3,"file":"task-commands.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/task-commands.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAiEzC,oFAAoF;AACpF,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAQ5D;AA2ZD,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,QAwzDpD"}
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.15.5",
2126
+ version: "0.15.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",
@@ -19331,6 +19331,45 @@ var init_tasks = __esm(() => {
19331
19331
  init_calendar();
19332
19332
  });
19333
19333
 
19334
+ // src/lib/comment-cursor.ts
19335
+ function encodeCommentCursor(comment) {
19336
+ return Buffer.from(JSON.stringify({ created_at: comment.created_at, id: comment.id }), "utf8").toString("base64url");
19337
+ }
19338
+ function decodeCommentCursor(value) {
19339
+ if (value.length > MAX_COMMENT_CURSOR_LENGTH)
19340
+ throw new Error("invalid comment cursor");
19341
+ let parsed;
19342
+ try {
19343
+ parsed = JSON.parse(Buffer.from(value, "base64url").toString("utf8"));
19344
+ } catch {
19345
+ throw new Error("invalid comment cursor");
19346
+ }
19347
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
19348
+ throw new Error("invalid comment cursor");
19349
+ const cursor = parsed;
19350
+ if (typeof cursor["created_at"] !== "string" || cursor["created_at"].length > 64 || !Number.isFinite(Date.parse(cursor["created_at"])) || typeof cursor["id"] !== "string" || !cursor["id"] || cursor["id"].length > 256) {
19351
+ throw new Error("invalid comment cursor");
19352
+ }
19353
+ return { created_at: cursor["created_at"], id: cursor["id"] };
19354
+ }
19355
+ function isStrictlyOlder(comment, before) {
19356
+ return comment.created_at < before.created_at || comment.created_at === before.created_at && comment.id < before.id;
19357
+ }
19358
+ function pageComments(all, options) {
19359
+ const ascending = [...all].sort((left, right) => left.created_at.localeCompare(right.created_at) || left.id.localeCompare(right.id));
19360
+ const scoped = options.before ? ascending.filter((comment) => isStrictlyOlder(comment, options.before)) : ascending;
19361
+ const comments = scoped.slice(-options.limit);
19362
+ const hasMore = scoped.length > comments.length;
19363
+ return {
19364
+ comments,
19365
+ count: comments.length,
19366
+ has_more: hasMore,
19367
+ next_cursor: hasMore && comments[0] ? encodeCommentCursor(comments[0]) : null,
19368
+ limit: options.limit
19369
+ };
19370
+ }
19371
+ var MAX_COMMENT_CURSOR_LENGTH = 1024;
19372
+
19334
19373
  // src/lib/bulk-tags.ts
19335
19374
  function parseTagList(raw) {
19336
19375
  if (!raw)
@@ -20565,6 +20604,53 @@ function parseIntOption(value, flag) {
20565
20604
  }
20566
20605
  return n;
20567
20606
  }
20607
+ function commentPageOptions(opts) {
20608
+ const requested = opts.commentsLimit !== undefined || opts.commentsCursor !== undefined;
20609
+ let limit = DEFAULT_CLI_COMMENT_PAGE;
20610
+ if (opts.commentsLimit !== undefined) {
20611
+ const parsed = Number(opts.commentsLimit);
20612
+ if (!Number.isSafeInteger(parsed) || parsed < 1 || parsed > MAX_CLI_COMMENT_PAGE) {
20613
+ handleError(new Error(`--comments-limit must be an integer between 1 and ${MAX_CLI_COMMENT_PAGE}`));
20614
+ }
20615
+ limit = parsed;
20616
+ }
20617
+ let before;
20618
+ if (opts.commentsCursor !== undefined) {
20619
+ try {
20620
+ before = decodeCommentCursor(opts.commentsCursor);
20621
+ } catch {
20622
+ handleError(new Error("--comments-cursor is not a valid comment cursor; pass the value from comments_page.next_cursor"));
20623
+ }
20624
+ }
20625
+ return {
20626
+ request: {
20627
+ ...opts.commentsLimit !== undefined ? { limit } : {},
20628
+ ...opts.commentsCursor !== undefined ? { cursor: opts.commentsCursor } : {}
20629
+ },
20630
+ requested,
20631
+ limit,
20632
+ ...before ? { before } : {}
20633
+ };
20634
+ }
20635
+ function applyLocalCommentPage(task, page) {
20636
+ if (!task || !page.requested)
20637
+ return task;
20638
+ const paged = pageComments(task.comments, {
20639
+ limit: page.limit,
20640
+ ...page.before ? { before: page.before } : {}
20641
+ });
20642
+ return {
20643
+ ...task,
20644
+ comments: paged.comments,
20645
+ comments_page: {
20646
+ count: paged.count,
20647
+ limit: paged.limit,
20648
+ has_more: paged.has_more,
20649
+ next_cursor: paged.next_cursor,
20650
+ pagination_supported: true
20651
+ }
20652
+ };
20653
+ }
20568
20654
  function isPathLike(input) {
20569
20655
  return input.startsWith(".") || input.includes("/") || input.includes("\\");
20570
20656
  }
@@ -21161,13 +21247,14 @@ function registerTaskCommands(program2) {
21161
21247
  console.log(parts.join(" "));
21162
21248
  }
21163
21249
  });
21164
- program2.command("show <id>").description("Show full task details").action(async (id) => {
21250
+ program2.command("show <id>").description("Show full task details").option("--comments-limit <n>", `Comments per page, 1-${MAX_CLI_COMMENT_PAGE} (default ${DEFAULT_CLI_COMMENT_PAGE})`).option("--comments-cursor <cursor>", "Read the next OLDER page; pass comments_page.next_cursor").action(async (id, opts) => {
21165
21251
  const globalOpts = program2.opts();
21252
+ const page = commentPageOptions(opts);
21166
21253
  const cloud = getTodosCloudClient();
21167
21254
  let task2;
21168
21255
  if (cloud) {
21169
21256
  const remote = await cloudGetTask(cloud, await resolveTaskIdForCommand(id, cloud));
21170
- const commentPage = remote ? await cloudListComments(cloud, remote.id) : null;
21257
+ const commentPage = remote ? await cloudListComments(cloud, remote.id, page.request) : null;
21171
21258
  const relations = remote ? await cloudDetailRelations(cloud, remote.id) : null;
21172
21259
  task2 = remote ? {
21173
21260
  subtasks: [],
@@ -21187,7 +21274,7 @@ function registerTaskCommands(program2) {
21187
21274
  } : null;
21188
21275
  } else {
21189
21276
  const resolvedId = resolveTaskId(id);
21190
- task2 = getTaskWithRelations(resolvedId);
21277
+ task2 = applyLocalCommentPage(getTaskWithRelations(resolvedId), page);
21191
21278
  }
21192
21279
  if (!task2) {
21193
21280
  handleError(new Error(`Task not found: ${id}`));
@@ -21278,8 +21365,9 @@ function registerTaskCommands(program2) {
21278
21365
  }
21279
21366
  }
21280
21367
  });
21281
- program2.command("inspect [id]").description("Full orientation for a task \u2014 details, description, dependencies, blocker, files, commits, comments. If no ID given, shows current in-progress task for --agent.").action(async (id) => {
21368
+ program2.command("inspect [id]").description("Full orientation for a task \u2014 details, description, dependencies, blocker, files, commits, comments. If no ID given, shows current in-progress task for --agent.").option("--comments-limit <n>", `Comments per page, 1-${MAX_CLI_COMMENT_PAGE} (default ${DEFAULT_CLI_COMMENT_PAGE})`).option("--comments-cursor <cursor>", "Read the next OLDER page; pass comments_page.next_cursor").action(async (id, opts) => {
21282
21369
  const globalOpts = program2.opts();
21370
+ const page = commentPageOptions(opts);
21283
21371
  const cloud = getTodosCloudClient();
21284
21372
  let resolvedId = id ? await resolveTaskIdForCommand(id, cloud) : null;
21285
21373
  if (!resolvedId && globalOpts.agent && !cloud) {
@@ -21299,7 +21387,7 @@ function registerTaskCommands(program2) {
21299
21387
  let task2;
21300
21388
  if (cloud) {
21301
21389
  const remote = await cloudGetTask(cloud, resolvedId);
21302
- const commentPage = remote ? await cloudListComments(cloud, remote.id) : null;
21390
+ const commentPage = remote ? await cloudListComments(cloud, remote.id, page.request) : null;
21303
21391
  const relations = remote ? await cloudDetailRelations(cloud, remote.id) : null;
21304
21392
  task2 = remote ? {
21305
21393
  subtasks: [],
@@ -21319,7 +21407,7 @@ function registerTaskCommands(program2) {
21319
21407
  }
21320
21408
  } : null;
21321
21409
  } else {
21322
- task2 = getTaskWithRelations(resolvedId);
21410
+ task2 = applyLocalCommentPage(getTaskWithRelations(resolvedId), page);
21323
21411
  }
21324
21412
  if (!task2) {
21325
21413
  handleError(new Error(`Task not found: ${id || resolvedId}`));
@@ -21995,7 +22083,7 @@ ${chalk3.cyan(sid)} ${statusColor(task2.status)} ${prioColor(task2.priority)} ${
21995
22083
  }
21996
22084
  });
21997
22085
  }
21998
- var DEFAULT_LIST_SCAN_LIMIT = 1e4;
22086
+ var DEFAULT_LIST_SCAN_LIMIT = 1e4, DEFAULT_CLI_COMMENT_PAGE = 100, MAX_CLI_COMMENT_PAGE = 500;
21999
22087
  var init_task_commands = __esm(() => {
22000
22088
  init_database();
22001
22089
  init_projects();
@@ -38626,26 +38714,6 @@ function contextFromPrincipal(principal, body) {
38626
38714
  function redactComment3(comment) {
38627
38715
  return { ...comment, content: redactEvidenceText(comment.content) };
38628
38716
  }
38629
- function encodeCommentCursor(comment) {
38630
- return Buffer.from(JSON.stringify({ created_at: comment.created_at, id: comment.id }), "utf8").toString("base64url");
38631
- }
38632
- function decodeCommentCursor(value) {
38633
- if (value.length > 1024)
38634
- throw new Error("invalid comment cursor");
38635
- let parsed;
38636
- try {
38637
- parsed = JSON.parse(Buffer.from(value, "base64url").toString("utf8"));
38638
- } catch {
38639
- throw new Error("invalid comment cursor");
38640
- }
38641
- if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
38642
- throw new Error("invalid comment cursor");
38643
- const cursor = parsed;
38644
- if (typeof cursor["created_at"] !== "string" || cursor["created_at"].length > 64 || !Number.isFinite(Date.parse(cursor["created_at"])) || typeof cursor["id"] !== "string" || !cursor["id"] || cursor["id"].length > 256) {
38645
- throw new Error("invalid comment cursor");
38646
- }
38647
- return { created_at: cursor["created_at"], id: cursor["id"] };
38648
- }
38649
38717
  function normalizeImportSnapshot(raw) {
38650
38718
  const body = raw && typeof raw === "object" ? raw : {};
38651
38719
  const arr = (v) => Array.isArray(v) ? v : [];
package/dist/contracts.js CHANGED
@@ -12044,7 +12044,7 @@ var init_tasks = __esm(() => {
12044
12044
  // package.json
12045
12045
  var package_default = {
12046
12046
  name: "@hasna/todos",
12047
- version: "0.15.5",
12047
+ version: "0.15.6",
12048
12048
  description: "Universal task management for AI coding agents - CLI + MCP server + interactive TUI",
12049
12049
  type: "module",
12050
12050
  main: "dist/index.js",
package/dist/index.js CHANGED
@@ -12157,7 +12157,7 @@ var init_dispatches = __esm(() => {
12157
12157
  // package.json
12158
12158
  var package_default = {
12159
12159
  name: "@hasna/todos",
12160
- version: "0.15.5",
12160
+ version: "0.15.6",
12161
12161
  description: "Universal task management for AI coding agents - CLI + MCP server + interactive TUI",
12162
12162
  type: "module",
12163
12163
  main: "dist/index.js",
@@ -0,0 +1,40 @@
1
+ import type { TaskComment } from "../types/index.js";
2
+ /**
3
+ * Keyset cursor for the task-comment read model, and the pure pager that walks
4
+ * it. Both the `/v1` server and the CLI encode and decode the same cursor, so
5
+ * the codec lives here rather than beside either consumer — a second copy of
6
+ * this keyset logic is exactly how the two ends drift into disagreeing about
7
+ * what a cursor means.
8
+ *
9
+ * ORDERING CONTRACT, measured against the live deployment rather than assumed:
10
+ * a page carries the NEWEST `limit` comments in ASCENDING display order, so the
11
+ * newest comment is the LAST element. `next_cursor` encodes the FIRST (oldest)
12
+ * element of the page, and walking it moves toward OLDER history.
13
+ */
14
+ export interface CommentCursor {
15
+ created_at: string;
16
+ id: string;
17
+ }
18
+ export declare const MAX_COMMENT_CURSOR_LENGTH = 1024;
19
+ export declare function encodeCommentCursor(comment: Pick<TaskComment, "created_at" | "id">): string;
20
+ export declare function decodeCommentCursor(value: string): CommentCursor;
21
+ /** True when `comment` sorts strictly before `before` on the `(created_at, id)` keyset. */
22
+ export declare function isStrictlyOlder(comment: Pick<TaskComment, "created_at" | "id">, before: CommentCursor): boolean;
23
+ export interface CommentPageResult<T> {
24
+ comments: T[];
25
+ count: number;
26
+ has_more: boolean;
27
+ next_cursor: string | null;
28
+ limit: number;
29
+ }
30
+ /**
31
+ * Page an already-materialised, ascending comment list. Used by the local
32
+ * (SQLite) read path, where the whole history is in hand; the cloud path gets
33
+ * the identical shape from the server, which applies the same rule against the
34
+ * database.
35
+ */
36
+ export declare function pageComments<T extends Pick<TaskComment, "created_at" | "id">>(all: readonly T[], options: {
37
+ limit: number;
38
+ before?: CommentCursor;
39
+ }): CommentPageResult<T>;
40
+ //# sourceMappingURL=comment-cursor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"comment-cursor.d.ts","sourceRoot":"","sources":["../../src/lib/comment-cursor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD;;;;;;;;;;;GAWG;AAEH,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,eAAO,MAAM,yBAAyB,OAAQ,CAAC;AAE/C,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,IAAI,CAAC,WAAW,EAAE,YAAY,GAAG,IAAI,CAAC,GAAG,MAAM,CAG3F;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,aAAa,CAgBhE;AAED,2FAA2F;AAC3F,wBAAgB,eAAe,CAC7B,OAAO,EAAE,IAAI,CAAC,WAAW,EAAE,YAAY,GAAG,IAAI,CAAC,EAC/C,MAAM,EAAE,aAAa,GACpB,OAAO,CAGT;AAED,MAAM,WAAW,iBAAiB,CAAC,CAAC;IAClC,QAAQ,EAAE,CAAC,EAAE,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,OAAO,CAAC;IAClB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,CAAC,SAAS,IAAI,CAAC,WAAW,EAAE,YAAY,GAAG,IAAI,CAAC,EAC3E,GAAG,EAAE,SAAS,CAAC,EAAE,EACjB,OAAO,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,aAAa,CAAA;CAAE,GACjD,iBAAiB,CAAC,CAAC,CAAC,CAetB"}
package/dist/mcp/index.js CHANGED
@@ -34926,7 +34926,7 @@ var package_default;
34926
34926
  var init_package = __esm(() => {
34927
34927
  package_default = {
34928
34928
  name: "@hasna/todos",
34929
- version: "0.15.5",
34929
+ version: "0.15.6",
34930
34930
  description: "Universal task management for AI coding agents - CLI + MCP server + interactive TUI",
34931
34931
  type: "module",
34932
34932
  main: "dist/index.js",
@@ -50912,6 +50912,29 @@ var init_pr_groups = __esm(() => {
50912
50912
  JSON_HEADERS = { "Content-Type": "application/json" };
50913
50913
  });
50914
50914
 
50915
+ // src/lib/comment-cursor.ts
50916
+ function encodeCommentCursor(comment) {
50917
+ return Buffer.from(JSON.stringify({ created_at: comment.created_at, id: comment.id }), "utf8").toString("base64url");
50918
+ }
50919
+ function decodeCommentCursor(value) {
50920
+ if (value.length > MAX_COMMENT_CURSOR_LENGTH)
50921
+ throw new Error("invalid comment cursor");
50922
+ let parsed;
50923
+ try {
50924
+ parsed = JSON.parse(Buffer.from(value, "base64url").toString("utf8"));
50925
+ } catch {
50926
+ throw new Error("invalid comment cursor");
50927
+ }
50928
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
50929
+ throw new Error("invalid comment cursor");
50930
+ const cursor = parsed;
50931
+ if (typeof cursor["created_at"] !== "string" || cursor["created_at"].length > 64 || !Number.isFinite(Date.parse(cursor["created_at"])) || typeof cursor["id"] !== "string" || !cursor["id"] || cursor["id"].length > 256) {
50932
+ throw new Error("invalid comment cursor");
50933
+ }
50934
+ return { created_at: cursor["created_at"], id: cursor["id"] };
50935
+ }
50936
+ var MAX_COMMENT_CURSOR_LENGTH = 1024;
50937
+
50915
50938
  // src/server/v1.ts
50916
50939
  var exports_v1 = {};
50917
50940
  __export(exports_v1, {
@@ -51201,26 +51224,6 @@ function contextFromPrincipal(principal, body) {
51201
51224
  function redactComment3(comment) {
51202
51225
  return { ...comment, content: redactEvidenceText(comment.content) };
51203
51226
  }
51204
- function encodeCommentCursor(comment) {
51205
- return Buffer.from(JSON.stringify({ created_at: comment.created_at, id: comment.id }), "utf8").toString("base64url");
51206
- }
51207
- function decodeCommentCursor(value) {
51208
- if (value.length > 1024)
51209
- throw new Error("invalid comment cursor");
51210
- let parsed;
51211
- try {
51212
- parsed = JSON.parse(Buffer.from(value, "base64url").toString("utf8"));
51213
- } catch {
51214
- throw new Error("invalid comment cursor");
51215
- }
51216
- if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
51217
- throw new Error("invalid comment cursor");
51218
- const cursor = parsed;
51219
- if (typeof cursor["created_at"] !== "string" || cursor["created_at"].length > 64 || !Number.isFinite(Date.parse(cursor["created_at"])) || typeof cursor["id"] !== "string" || !cursor["id"] || cursor["id"].length > 256) {
51220
- throw new Error("invalid comment cursor");
51221
- }
51222
- return { created_at: cursor["created_at"], id: cursor["id"] };
51223
- }
51224
51227
  function normalizeImportSnapshot(raw) {
51225
51228
  const body = raw && typeof raw === "object" ? raw : {};
51226
51229
  const arr = (v) => Array.isArray(v) ? v : [];
package/dist/mcp.js CHANGED
@@ -41,7 +41,7 @@ var __require = import.meta.require;
41
41
  // package.json
42
42
  var package_default = {
43
43
  name: "@hasna/todos",
44
- version: "0.15.5",
44
+ version: "0.15.6",
45
45
  description: "Universal task management for AI coding agents - CLI + MCP server + interactive TUI",
46
46
  type: "module",
47
47
  main: "dist/index.js",
package/dist/registry.js CHANGED
@@ -12044,7 +12044,7 @@ var init_tasks = __esm(() => {
12044
12044
  // package.json
12045
12045
  var package_default = {
12046
12046
  name: "@hasna/todos",
12047
- version: "0.15.5",
12047
+ version: "0.15.6",
12048
12048
  description: "Universal task management for AI coding agents - CLI + MCP server + interactive TUI",
12049
12049
  type: "module",
12050
12050
  main: "dist/index.js",
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "packageName": "@hasna/todos",
3
- "packageVersion": "0.15.5",
3
+ "packageVersion": "0.15.6",
4
4
  "repository": "https://github.com/hasna/todos.git",
5
- "gitCommit": "f4b7416f32a052da6dc214930464e1844227013c",
6
- "gitTree": "c0b3579fad025c98669a980fc2686ac652348348",
7
- "sourceTreeSha256": "00ccce54628bd319f2cae61ba552bb5543b242b287e48b94000063d516427abe",
8
- "generatedAt": "2026-08-05T16:20:19.000Z"
5
+ "gitCommit": "912706df4217032e0fa328b8e63c631cbd2dbaaa",
6
+ "gitTree": "55ffcbf22946c2823608bf05b7db8c77c3d1837f",
7
+ "sourceTreeSha256": "6d97deaa19cf1aca86ae5d2ac810239a96079987884dffad727762b91dbbe660",
8
+ "generatedAt": "2026-08-06T09:35:27.000Z"
9
9
  }
@@ -70,7 +70,7 @@ var package_default;
70
70
  var init_package = __esm(() => {
71
71
  package_default = {
72
72
  name: "@hasna/todos",
73
- version: "0.15.5",
73
+ version: "0.15.6",
74
74
  description: "Universal task management for AI coding agents - CLI + MCP server + interactive TUI",
75
75
  type: "module",
76
76
  main: "dist/index.js",
@@ -22312,6 +22312,29 @@ var init_pr_groups = __esm(() => {
22312
22312
  JSON_HEADERS = { "Content-Type": "application/json" };
22313
22313
  });
22314
22314
 
22315
+ // src/lib/comment-cursor.ts
22316
+ function encodeCommentCursor(comment) {
22317
+ return Buffer.from(JSON.stringify({ created_at: comment.created_at, id: comment.id }), "utf8").toString("base64url");
22318
+ }
22319
+ function decodeCommentCursor(value) {
22320
+ if (value.length > MAX_COMMENT_CURSOR_LENGTH)
22321
+ throw new Error("invalid comment cursor");
22322
+ let parsed;
22323
+ try {
22324
+ parsed = JSON.parse(Buffer.from(value, "base64url").toString("utf8"));
22325
+ } catch {
22326
+ throw new Error("invalid comment cursor");
22327
+ }
22328
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
22329
+ throw new Error("invalid comment cursor");
22330
+ const cursor = parsed;
22331
+ if (typeof cursor["created_at"] !== "string" || cursor["created_at"].length > 64 || !Number.isFinite(Date.parse(cursor["created_at"])) || typeof cursor["id"] !== "string" || !cursor["id"] || cursor["id"].length > 256) {
22332
+ throw new Error("invalid comment cursor");
22333
+ }
22334
+ return { created_at: cursor["created_at"], id: cursor["id"] };
22335
+ }
22336
+ var MAX_COMMENT_CURSOR_LENGTH = 1024;
22337
+
22315
22338
  // src/server/v1.ts
22316
22339
  var exports_v1 = {};
22317
22340
  __export(exports_v1, {
@@ -22601,26 +22624,6 @@ function contextFromPrincipal(principal, body) {
22601
22624
  function redactComment2(comment) {
22602
22625
  return { ...comment, content: redactEvidenceText(comment.content) };
22603
22626
  }
22604
- function encodeCommentCursor(comment) {
22605
- return Buffer.from(JSON.stringify({ created_at: comment.created_at, id: comment.id }), "utf8").toString("base64url");
22606
- }
22607
- function decodeCommentCursor(value) {
22608
- if (value.length > 1024)
22609
- throw new Error("invalid comment cursor");
22610
- let parsed;
22611
- try {
22612
- parsed = JSON.parse(Buffer.from(value, "base64url").toString("utf8"));
22613
- } catch {
22614
- throw new Error("invalid comment cursor");
22615
- }
22616
- if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
22617
- throw new Error("invalid comment cursor");
22618
- const cursor = parsed;
22619
- if (typeof cursor["created_at"] !== "string" || cursor["created_at"].length > 64 || !Number.isFinite(Date.parse(cursor["created_at"])) || typeof cursor["id"] !== "string" || !cursor["id"] || cursor["id"].length > 256) {
22620
- throw new Error("invalid comment cursor");
22621
- }
22622
- return { created_at: cursor["created_at"], id: cursor["id"] };
22623
- }
22624
22627
  function normalizeImportSnapshot(raw) {
22625
22628
  const body = raw && typeof raw === "object" ? raw : {};
22626
22629
  const arr = (v) => Array.isArray(v) ? v : [];
@@ -1 +1 @@
1
- {"version":3,"file":"v1.d.ts","sourceRoot":"","sources":["../../src/server/v1.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAuB,oBAAoB,EAAmD,MAAM,0BAA0B,CAAC;AAC3I,OAAO,EAAE,qBAAqB,EAAE,sBAAsB,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAKhH,MAAM,WAAW,qBAAqB;IACpC,WAAW,CAAC,EAAE,OAAO,gBAAgB,CAAC;IACtC,YAAY,CAAC,EAAE,OAAO,iBAAiB,CAAC;IACxC,iBAAiB,CAAC,EAAE,OAAO,sBAAsB,CAAC;IAClD,gBAAgB,CAAC,EAAE,OAAO,qBAAqB,CAAC;CACjD;AA0TD;;;;;;;;GAQG;AACH,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,OAAO,GAAG,oBAAoB,CAiB1E;AAED,gFAAgF;AAChF,wBAAgB,oBAAoB,CAAC,CAAC,EAAE,oBAAoB,GAAG,MAAM,CAapE;AAED;;;GAGG;AACH,wBAAsB,eAAe,CACnC,GAAG,EAAE,OAAO,EACZ,GAAG,EAAE,GAAG,EACR,YAAY,GAAE,qBAA0B,GACvC,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAu6B1B"}
1
+ {"version":3,"file":"v1.d.ts","sourceRoot":"","sources":["../../src/server/v1.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAuB,oBAAoB,EAAmD,MAAM,0BAA0B,CAAC;AAC3I,OAAO,EAAE,qBAAqB,EAAE,sBAAsB,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAMhH,MAAM,WAAW,qBAAqB;IACpC,WAAW,CAAC,EAAE,OAAO,gBAAgB,CAAC;IACtC,YAAY,CAAC,EAAE,OAAO,iBAAiB,CAAC;IACxC,iBAAiB,CAAC,EAAE,OAAO,sBAAsB,CAAC;IAClD,gBAAgB,CAAC,EAAE,OAAO,qBAAqB,CAAC;CACjD;AAuSD;;;;;;;;GAQG;AACH,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,OAAO,GAAG,oBAAoB,CAiB1E;AAED,gFAAgF;AAChF,wBAAgB,oBAAoB,CAAC,CAAC,EAAE,oBAAoB,GAAG,MAAM,CAapE;AAED;;;GAGG;AACH,wBAAsB,eAAe,CACnC,GAAG,EAAE,OAAO,EACZ,GAAG,EAAE,GAAG,EACR,YAAY,GAAE,qBAA0B,GACvC,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAu6B1B"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/todos",
3
- "version": "0.15.5",
3
+ "version": "0.15.6",
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",