@tekmidian/pai 0.14.2 → 0.16.0

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.
Files changed (148) hide show
  1. package/dist/{auto-route-CLYcToZ5.mjs → auto-route-CdvjEWs5.mjs} +4 -4
  2. package/dist/{auto-route-CLYcToZ5.mjs.map → auto-route-CdvjEWs5.mjs.map} +1 -1
  3. package/dist/{kg-extraction-C8DEUHTS.mjs → checkpoint-block-Bl0HD3U2.mjs} +442 -13
  4. package/dist/checkpoint-block-Bl0HD3U2.mjs.map +1 -0
  5. package/dist/cli/index.mjs +14 -12
  6. package/dist/cli/index.mjs.map +1 -1
  7. package/dist/cli/program.d.mts.map +1 -1
  8. package/dist/cli/program.mjs +31 -16
  9. package/dist/cli/program.mjs.map +1 -1
  10. package/dist/{clusters-BYPw7vfW.mjs → clusters-CKWDwcMX.mjs} +2 -2
  11. package/dist/{clusters-BYPw7vfW.mjs.map → clusters-CKWDwcMX.mjs.map} +1 -1
  12. package/dist/{config-C8m-tPhP.mjs → config-DGOfoGfm.mjs} +1 -1
  13. package/dist/config-DGOfoGfm.mjs.map +1 -0
  14. package/dist/daemon/index.mjs +18 -16
  15. package/dist/daemon/index.mjs.map +1 -1
  16. package/dist/{daemon-COSvHTNu.mjs → daemon-1rKbN-fd.mjs} +31 -30
  17. package/dist/{daemon-COSvHTNu.mjs.map → daemon-1rKbN-fd.mjs.map} +1 -1
  18. package/dist/daemon-mcp/index.mjs +24 -2
  19. package/dist/daemon-mcp/index.mjs.map +1 -1
  20. package/dist/db-BtuN768f.mjs +206 -0
  21. package/dist/db-BtuN768f.mjs.map +1 -0
  22. package/dist/{db-CmYbAVCD.mjs → db-CYmBWcjh.mjs} +1 -1
  23. package/dist/{db-CmYbAVCD.mjs.map → db-CYmBWcjh.mjs.map} +1 -1
  24. package/dist/{detect-KjycLtXM.mjs → detect-Bf2z-oKB.mjs} +1 -1
  25. package/dist/{detect-KjycLtXM.mjs.map → detect-Bf2z-oKB.mjs.map} +1 -1
  26. package/dist/{detector-ZiOhHszd.mjs → detector-rq9JGTmS.mjs} +2 -2
  27. package/dist/{detector-ZiOhHszd.mjs.map → detector-rq9JGTmS.mjs.map} +1 -1
  28. package/dist/{embeddings-BJPOcbik.mjs → embeddings-DGRAPAYb.mjs} +1 -1
  29. package/dist/{embeddings-BJPOcbik.mjs.map → embeddings-DGRAPAYb.mjs.map} +1 -1
  30. package/dist/{factory-Q88X1bAN.mjs → factory-HcQsQqh0.mjs} +8 -5
  31. package/dist/{factory-Q88X1bAN.mjs.map → factory-HcQsQqh0.mjs.map} +1 -1
  32. package/dist/{helpers-IjZkXBhj.mjs → helpers-crDEr6S2.mjs} +1 -1
  33. package/dist/{helpers-IjZkXBhj.mjs.map → helpers-crDEr6S2.mjs.map} +1 -1
  34. package/dist/hooks/capture-all-events.mjs.map +1 -1
  35. package/dist/hooks/capture-session-summary.mjs.map +1 -1
  36. package/dist/hooks/capture-tool-output.mjs.map +1 -1
  37. package/dist/hooks/cleanup-session-files.mjs.map +1 -1
  38. package/dist/hooks/context-compression-hook.mjs +304 -48
  39. package/dist/hooks/context-compression-hook.mjs.map +4 -4
  40. package/dist/hooks/initialize-session.mjs.map +1 -1
  41. package/dist/hooks/inject-observations.mjs.map +1 -1
  42. package/dist/hooks/load-core-context.mjs.map +1 -1
  43. package/dist/hooks/load-project-context.mjs.map +1 -1
  44. package/dist/hooks/observe.mjs.map +1 -1
  45. package/dist/hooks/post-compact-inject.mjs.map +1 -1
  46. package/dist/hooks/security-validator.mjs.map +1 -1
  47. package/dist/hooks/session-commands.mjs.map +1 -1
  48. package/dist/hooks/stop-hook.mjs +299 -43
  49. package/dist/hooks/stop-hook.mjs.map +4 -4
  50. package/dist/hooks/subagent-stop-hook.mjs.map +1 -1
  51. package/dist/hooks/sync-todo-to-md.mjs.map +1 -1
  52. package/dist/hooks/update-tab-on-action.mjs.map +1 -1
  53. package/dist/hooks/update-tab-titles.mjs.map +1 -1
  54. package/dist/hooks/whisper-rules.mjs.map +1 -1
  55. package/dist/index.d.mts +0 -9
  56. package/dist/index.d.mts.map +1 -1
  57. package/dist/index.mjs +10 -9
  58. package/dist/indexer-AEcT8wHf.mjs +1 -0
  59. package/dist/{indexer-backend-Cm5RS7y0.mjs → indexer-backend-BQZXoarl.mjs} +3 -3
  60. package/dist/{indexer-backend-Cm5RS7y0.mjs.map → indexer-backend-BQZXoarl.mjs.map} +1 -1
  61. package/dist/{ipc-client-CoyUHPod.mjs → ipc-client-vO2GV325.mjs} +1 -1
  62. package/dist/{ipc-client-CoyUHPod.mjs.map → ipc-client-vO2GV325.mjs.map} +1 -1
  63. package/dist/{kg-entity-LXblD7LZ.mjs → kg-entity-DbOMPdF9.mjs} +1 -1
  64. package/dist/{kg-entity-LXblD7LZ.mjs.map → kg-entity-DbOMPdF9.mjs.map} +1 -1
  65. package/dist/{latent-ideas-RyC6eyqI.mjs → latent-ideas-C6IFFT0-.mjs} +4 -4
  66. package/dist/{latent-ideas-RyC6eyqI.mjs.map → latent-ideas-C6IFFT0-.mjs.map} +1 -1
  67. package/dist/{main-resolver-uFxNiDy7.mjs → main-resolver-DimnS2qP.mjs} +22 -22
  68. package/dist/{main-resolver-uFxNiDy7.mjs.map → main-resolver-DimnS2qP.mjs.map} +1 -1
  69. package/dist/{migrate-B02wDDgS.mjs → migrate-fLD6rAdO.mjs} +2 -2
  70. package/dist/{migrate-B02wDDgS.mjs.map → migrate-fLD6rAdO.mjs.map} +1 -1
  71. package/dist/{neighborhood-CklGIB8r.mjs → neighborhood-CzRNG2Oy.mjs} +2 -2
  72. package/dist/{neighborhood-CklGIB8r.mjs.map → neighborhood-CzRNG2Oy.mjs.map} +1 -1
  73. package/dist/{note-context-qvZlVXVO.mjs → note-context-CrfMbr4R.mjs} +1 -1
  74. package/dist/{note-context-qvZlVXVO.mjs.map → note-context-CrfMbr4R.mjs.map} +1 -1
  75. package/dist/{pai-marker-HVBwwBW-.mjs → pai-marker-B20KqhA8.mjs} +44 -2
  76. package/dist/pai-marker-B20KqhA8.mjs.map +1 -0
  77. package/dist/{pick-gne3kOJG.mjs → pick-B3zEYjin.mjs} +2316 -483
  78. package/dist/pick-B3zEYjin.mjs.map +1 -0
  79. package/dist/{postgres-B451Hnkm.mjs → postgres-zjKqPl4X.mjs} +2 -2
  80. package/dist/{postgres-B451Hnkm.mjs.map → postgres-zjKqPl4X.mjs.map} +1 -1
  81. package/dist/{query-feedback-fapbqRgh.mjs → query-feedback-BoY8_Dbb.mjs} +1 -1
  82. package/dist/{query-feedback-fapbqRgh.mjs.map → query-feedback-BoY8_Dbb.mjs.map} +1 -1
  83. package/dist/{reranker-C08R99zn.mjs → reranker-CMNZcfVx.mjs} +1 -1
  84. package/dist/{reranker-C08R99zn.mjs.map → reranker-CMNZcfVx.mjs.map} +1 -1
  85. package/dist/{search-HcdKtMla.mjs → search-i2nlQ-JM.mjs} +3 -3
  86. package/dist/{search-HcdKtMla.mjs.map → search-i2nlQ-JM.mjs.map} +1 -1
  87. package/dist/skills/End/SKILL.md +78 -0
  88. package/dist/skills/Pause/SKILL.md +77 -0
  89. package/dist/skills/Tasks/SKILL.md +22 -0
  90. package/dist/{sqlite-DQpY1Esi.mjs → sqlite-ChSCYwiC.mjs} +3 -3
  91. package/dist/{sqlite-DQpY1Esi.mjs.map → sqlite-ChSCYwiC.mjs.map} +1 -1
  92. package/dist/{state-BIlxNRUn.mjs → state-BXIdbxDs.mjs} +1 -1
  93. package/dist/{state-BIlxNRUn.mjs.map → state-BXIdbxDs.mjs.map} +1 -1
  94. package/dist/{stop-words-BwplsQ3z.mjs → stop-words-BaMEGVeY.mjs} +1 -1
  95. package/dist/{stop-words-BwplsQ3z.mjs.map → stop-words-BaMEGVeY.mjs.map} +1 -1
  96. package/dist/{sync-uPR4g438.mjs → sync-CmBKOL3K.mjs} +6 -205
  97. package/dist/sync-CmBKOL3K.mjs.map +1 -0
  98. package/dist/{themes-Cg8oG9Ra.mjs → themes-nvRM8v84.mjs} +3 -3
  99. package/dist/{themes-Cg8oG9Ra.mjs.map → themes-nvRM8v84.mjs.map} +1 -1
  100. package/dist/{tools-Op3C2Nm6.mjs → tools-CznjzfYz.mjs} +26 -26
  101. package/dist/{tools-Op3C2Nm6.mjs.map → tools-CznjzfYz.mjs.map} +1 -1
  102. package/dist/{trace-CLK-NPkb.mjs → trace-B8oz1ok0.mjs} +1 -1
  103. package/dist/{trace-CLK-NPkb.mjs.map → trace-B8oz1ok0.mjs.map} +1 -1
  104. package/dist/{utils-CqqgB0dH.mjs → utils-BAxjW3j8.mjs} +1 -1
  105. package/dist/{utils-CqqgB0dH.mjs.map → utils-BAxjW3j8.mjs.map} +1 -1
  106. package/dist/{vault-indexer-Dn_BccVK.mjs → vault-indexer-DrttVeEk.mjs} +2 -2
  107. package/dist/{vault-indexer-Dn_BccVK.mjs.map → vault-indexer-DrttVeEk.mjs.map} +1 -1
  108. package/dist/{work-queue-worker-BwouhVkb.mjs → work-queue-worker-BPXLkEik.mjs} +47 -30
  109. package/dist/work-queue-worker-BPXLkEik.mjs.map +1 -0
  110. package/dist/{zettelkasten-NwBD1NsT.mjs → zettelkasten-CTlt1dGv.mjs} +4 -4
  111. package/dist/{zettelkasten-NwBD1NsT.mjs.map → zettelkasten-CTlt1dGv.mjs.map} +1 -1
  112. package/docs/commands/README.md +17 -0
  113. package/docs/commands/backup.md +1 -1
  114. package/docs/commands/clear-names.md +1 -1
  115. package/docs/commands/daemon.md +1 -1
  116. package/docs/commands/db.md +1 -1
  117. package/docs/commands/end.md +4 -1
  118. package/docs/commands/help.md +1 -1
  119. package/docs/commands/kg.md +1 -1
  120. package/docs/commands/mcp.md +1 -1
  121. package/docs/commands/memory.md +1 -1
  122. package/docs/commands/notify.md +1 -1
  123. package/docs/commands/observation.md +1 -1
  124. package/docs/commands/obsidian.md +1 -1
  125. package/docs/commands/pause.md +4 -1
  126. package/docs/commands/project.md +1 -1
  127. package/docs/commands/projects.md +1 -1
  128. package/docs/commands/registry.md +18 -1
  129. package/docs/commands/restore.md +1 -1
  130. package/docs/commands/session.md +271 -0
  131. package/docs/commands/sessions.md +1 -1
  132. package/docs/commands/setup.md +1 -1
  133. package/docs/commands/shell-init.md +1 -1
  134. package/docs/commands/skill.md +1 -1
  135. package/docs/commands/task.md +2 -1
  136. package/docs/commands/topic.md +1 -1
  137. package/docs/commands/update.md +1 -1
  138. package/docs/commands/zettel.md +1 -1
  139. package/package.json +1 -1
  140. package/scripts/build-hooks.mjs +32 -3
  141. package/src/hooks/ts/lib/project-utils/todo.test.ts +132 -0
  142. package/src/hooks/ts/lib/project-utils/todo.ts +60 -34
  143. package/dist/config-C8m-tPhP.mjs.map +0 -1
  144. package/dist/kg-extraction-C8DEUHTS.mjs.map +0 -1
  145. package/dist/pai-marker-HVBwwBW-.mjs.map +0 -1
  146. package/dist/pick-gne3kOJG.mjs.map +0 -1
  147. package/dist/sync-uPR4g438.mjs.map +0 -1
  148. package/dist/work-queue-worker-BwouhVkb.mjs.map +0 -1
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/hooks/ts/stop/stop-hook.ts
4
- import { readFileSync as readFileSync5, writeFileSync as writeFileSync3, mkdirSync as mkdirSync4, existsSync as existsSync6, unlinkSync } from "fs";
5
- import { join as join6, basename as basename3, dirname } from "path";
4
+ import { readFileSync as readFileSync6, writeFileSync as writeFileSync4, mkdirSync as mkdirSync5, existsSync as existsSync7, unlinkSync } from "fs";
5
+ import { join as join7, basename as basename3, dirname } from "path";
6
6
  import { connect } from "net";
7
7
  import { randomUUID } from "crypto";
8
8
 
@@ -387,13 +387,260 @@ ${summary || "Session completed."}`
387
387
  }
388
388
 
389
389
  // src/hooks/ts/lib/project-utils/todo.ts
390
- import { existsSync as existsSync5, mkdirSync as mkdirSync3, readFileSync as readFileSync4, writeFileSync as writeFileSync2 } from "fs";
391
- import { join as join5 } from "path";
390
+ import { existsSync as existsSync6, mkdirSync as mkdirSync4, readFileSync as readFileSync5, writeFileSync as writeFileSync3 } from "fs";
391
+ import { join as join6 } from "path";
392
+
393
+ // src/session/checkpoint-block.ts
394
+ import {
395
+ existsSync as existsSync5,
396
+ readFileSync as readFileSync4,
397
+ writeFileSync as writeFileSync2,
398
+ readdirSync as readdirSync3,
399
+ renameSync as renameSync3,
400
+ mkdirSync as mkdirSync3,
401
+ realpathSync
402
+ } from "node:fs";
403
+ import { join as join5 } from "node:path";
404
+ var TODO_LOCATIONS = [
405
+ "Notes/TODO.md",
406
+ ".claude/Notes/TODO.md",
407
+ "tasks/todo.md",
408
+ "TODO.md"
409
+ ];
410
+ var MARKER_OPEN = "<!-- pai:checkpoint";
411
+ var MARKER_CLOSE = "<!-- /pai:checkpoint -->";
412
+ var CONTINUE_HEADING = "## Continue";
413
+ function findProjectTodo(rootPath) {
414
+ for (const rel of TODO_LOCATIONS) {
415
+ const full = join5(rootPath, rel);
416
+ if (existsSync5(full)) {
417
+ try {
418
+ return { path: full, content: readFileSync4(full, "utf8") };
419
+ } catch {
420
+ }
421
+ }
422
+ }
423
+ return null;
424
+ }
425
+ function resolveTodoTarget(rootPath, opts = {}) {
426
+ const found = findProjectTodo(rootPath);
427
+ if (found) return found;
428
+ const notesDir = join5(rootPath, "Notes");
429
+ if (opts.create !== false) {
430
+ try {
431
+ if (!existsSync5(notesDir)) mkdirSync3(notesDir, { recursive: true });
432
+ } catch {
433
+ return null;
434
+ }
435
+ }
436
+ return { path: join5(notesDir, "TODO.md"), content: "" };
437
+ }
438
+ function parseMarker(line) {
439
+ const trimmed = line.trim();
440
+ if (!trimmed.startsWith(MARKER_OPEN)) return null;
441
+ const attrs = {};
442
+ for (const m of trimmed.matchAll(/([a-zA-Z][\w-]*)="([^"]*)"/g)) {
443
+ attrs[m[1]] = m[2];
444
+ }
445
+ return {
446
+ authored: attrs.authored === "model" ? "model" : "auto",
447
+ session: attrs.session || void 0,
448
+ sessionId: attrs["session-id"] || void 0,
449
+ ts: attrs.ts || void 0
450
+ };
451
+ }
452
+ var BOILERPLATE_PATTERNS = [
453
+ /^##\s+Continue$/,
454
+ /^<!--\s*\/?pai:checkpoint/,
455
+ /^>\s*\*\*Last session:\*\*/,
456
+ /^>\s*\*\*Paused at:\*\*/,
457
+ /^>\s*Working directory:/,
458
+ /^>\s*Resume with:/,
459
+ /^>\s*_No checkpoint body was recorded/,
460
+ /^>?\s*$/,
461
+ /^-{3,}$/
462
+ ];
463
+ function isBoilerplateOnly(lines) {
464
+ return lines.every((line) => {
465
+ const t = line.trim();
466
+ return BOILERPLATE_PATTERNS.some((re) => re.test(t));
467
+ });
468
+ }
469
+ function extractSectionContent(lines) {
470
+ const kept = [];
471
+ for (const line of lines) {
472
+ const t = line.trim();
473
+ if (BOILERPLATE_PATTERNS.some((re) => re.test(t))) continue;
474
+ kept.push(line);
475
+ }
476
+ return kept.join("\n").trim();
477
+ }
478
+ function locateContinue(content) {
479
+ const lines = content.split("\n");
480
+ const startIdx = lines.findIndex((l) => l.trim() === CONTINUE_HEADING);
481
+ if (startIdx === -1) return null;
482
+ let meta = null;
483
+ let markerIdx = -1;
484
+ for (let i = startIdx + 1; i < Math.min(startIdx + 6, lines.length); i++) {
485
+ const parsed = parseMarker(lines[i]);
486
+ if (parsed) {
487
+ meta = parsed;
488
+ markerIdx = i;
489
+ break;
490
+ }
491
+ if (lines[i].trim() !== "") break;
492
+ }
493
+ let endIdx = lines.length;
494
+ if (markerIdx !== -1) {
495
+ const closeIdx = lines.findIndex(
496
+ (l, i) => i > markerIdx && l.trim() === MARKER_CLOSE
497
+ );
498
+ if (closeIdx !== -1) {
499
+ endIdx = closeIdx + 1;
500
+ } else {
501
+ endIdx = heuristicEnd(lines, startIdx);
502
+ }
503
+ } else {
504
+ endIdx = heuristicEnd(lines, startIdx);
505
+ }
506
+ let trailingEnd = endIdx;
507
+ while (trailingEnd < lines.length && lines[trailingEnd].trim() === "") {
508
+ trailingEnd += 1;
509
+ }
510
+ if (trailingEnd < lines.length && lines[trailingEnd].trim() === "---") {
511
+ trailingEnd += 1;
512
+ } else {
513
+ trailingEnd = endIdx;
514
+ }
515
+ return {
516
+ startIdx,
517
+ endIdx: trailingEnd,
518
+ meta,
519
+ lines: lines.slice(startIdx, trailingEnd)
520
+ };
521
+ }
522
+ function heuristicEnd(lines, startIdx) {
523
+ for (let i = startIdx + 1; i < lines.length; i++) {
524
+ const trimmed = lines[i].trim();
525
+ if (trimmed === "---" || /^##(?!#)/.test(trimmed) && trimmed !== CONTINUE_HEADING) {
526
+ return i;
527
+ }
528
+ }
529
+ return lines.length;
530
+ }
531
+ function stripContinue(content) {
532
+ const found = locateContinue(content);
533
+ if (!found) return content;
534
+ const lines = content.split("\n");
535
+ const before = lines.slice(0, found.startIdx);
536
+ const after = lines.slice(found.endIdx);
537
+ while (after.length > 0 && after[0].trim() === "") after.shift();
538
+ return [...before, ...after].join("\n");
539
+ }
540
+ function escapeAttr(value) {
541
+ return value.replace(/"/g, "'");
542
+ }
543
+ function buildContinueBlock(opts) {
544
+ const ts = opts.timestamp ?? (/* @__PURE__ */ new Date()).toISOString();
545
+ const attrs = [
546
+ `authored="${opts.authored}"`,
547
+ `session="${escapeAttr(opts.sessionLine)}"`,
548
+ opts.sessionId ? `session-id="${escapeAttr(opts.sessionId)}"` : null,
549
+ `ts="${ts}"`
550
+ ].filter(Boolean).join(" ");
551
+ const header = [
552
+ `> **Last session:** ${opts.sessionLine}`,
553
+ `> **Paused at:** ${ts}`,
554
+ ">",
555
+ `> Working directory: ${opts.cwd}`
556
+ ];
557
+ if (opts.sessionId) {
558
+ header.push(">", `> Resume with: \`claude --resume ${opts.sessionId}\``);
559
+ }
560
+ const body = (opts.body ?? "").trim();
561
+ const parts = [
562
+ CONTINUE_HEADING,
563
+ "",
564
+ `${MARKER_OPEN} ${attrs} -->`,
565
+ "",
566
+ ...header
567
+ ];
568
+ if (body) {
569
+ parts.push("", body);
570
+ } else {
571
+ parts.push(
572
+ ">",
573
+ "> _No checkpoint body was recorded \u2014 see the latest session note._"
574
+ );
575
+ }
576
+ parts.push("", MARKER_CLOSE, "", "---", "");
577
+ return parts.join("\n");
578
+ }
579
+ function applyContinue(opts) {
580
+ const target = resolveTodoTarget(opts.rootPath, { create: !opts.dryRun });
581
+ if (!target) {
582
+ return {
583
+ action: "failed",
584
+ path: null,
585
+ block: buildContinueBlock(opts),
586
+ error: "Could not resolve or create a TODO.md target"
587
+ };
588
+ }
589
+ const existing = locateContinue(target.content);
590
+ let carriedForward = false;
591
+ let effectiveBody = opts.body;
592
+ if (opts.authored === "auto" && existing) {
593
+ if (existing.meta?.authored === "model" && existing.meta.session === opts.sessionLine) {
594
+ return {
595
+ action: "preserved",
596
+ path: target.path,
597
+ block: buildContinueBlock(opts),
598
+ preservedMeta: existing.meta
599
+ };
600
+ }
601
+ if (!existing.meta && !isBoilerplateOnly(existing.lines)) {
602
+ const salvaged = extractSectionContent(existing.lines);
603
+ if (salvaged) {
604
+ effectiveBody = [
605
+ "_Carried forward from the previous checkpoint (author unknown \u2014 this",
606
+ "block predates checkpoint authorship markers):_",
607
+ "",
608
+ salvaged
609
+ ].join("\n");
610
+ carriedForward = true;
611
+ }
612
+ }
613
+ }
614
+ const block = buildContinueBlock({ ...opts, body: effectiveBody });
615
+ if (opts.dryRun) {
616
+ return { action: "written", path: target.path, block, carriedForward };
617
+ }
618
+ const newContent = block + stripContinue(target.content).trimStart();
619
+ const tmpPath = `${target.path}.continue.tmp`;
620
+ try {
621
+ writeFileSync2(tmpPath, newContent, "utf8");
622
+ renameSync3(tmpPath, target.path);
623
+ } catch (err) {
624
+ try {
625
+ if (existsSync5(tmpPath)) renameSync3(tmpPath, `${tmpPath}.dead`);
626
+ } catch {
627
+ }
628
+ return {
629
+ action: "failed",
630
+ path: target.path,
631
+ block,
632
+ error: String(err)
633
+ };
634
+ }
635
+ return { action: "written", path: target.path, block, carriedForward };
636
+ }
637
+
638
+ // src/hooks/ts/lib/project-utils/todo.ts
392
639
  function ensureTodoMd(cwd) {
393
640
  const todoPath = findTodoPath(cwd);
394
- if (!existsSync5(todoPath)) {
395
- const parentDir = join5(todoPath, "..");
396
- if (!existsSync5(parentDir)) mkdirSync3(parentDir, { recursive: true });
641
+ if (!existsSync6(todoPath)) {
642
+ const parentDir = join6(todoPath, "..");
643
+ if (!existsSync6(parentDir)) mkdirSync4(parentDir, { recursive: true });
397
644
  const content = `# TODO
398
645
 
399
646
  ## Current Session
@@ -408,43 +655,52 @@ function ensureTodoMd(cwd) {
408
655
 
409
656
  *Last updated: ${(/* @__PURE__ */ new Date()).toISOString()}*
410
657
  `;
411
- writeFileSync2(todoPath, content);
658
+ writeFileSync3(todoPath, content);
412
659
  console.error(`Created TODO.md: ${todoPath}`);
413
660
  }
414
661
  return todoPath;
415
662
  }
416
663
  function updateTodoContinue(cwd, noteFilename, state, tokenDisplay) {
417
- const todoPath = ensureTodoMd(cwd);
418
- let content = readFileSync4(todoPath, "utf-8");
419
- content = content.replace(/## Continue\n[\s\S]*?\n---\n+/, "");
420
- const now = (/* @__PURE__ */ new Date()).toISOString();
421
- const stateLines = state ? state.split("\n").filter((l) => l.trim()).slice(0, 10).map((l) => `> ${l}`).join("\n") : `> Working directory: ${cwd}. Check the latest session note for details.`;
422
- const continueSection = `## Continue
423
-
424
- > **Last session:** ${noteFilename.replace(".md", "")}
425
- > **Paused at:** ${now}
426
- >
427
- ${stateLines}
428
-
429
- ---
430
-
431
- `;
432
- content = content.replace(/^\s+/, "");
433
- const titleMatch = content.match(/^(# [^\n]+\n+)/);
434
- if (titleMatch) {
435
- content = titleMatch[1] + continueSection + content.substring(titleMatch[0].length);
436
- } else {
437
- content = continueSection + content;
664
+ ensureTodoMd(cwd);
665
+ const result = applyContinue({
666
+ rootPath: cwd,
667
+ authored: "auto",
668
+ // The hooks identify a session by its note filename; `pai pause` resolves
669
+ // the same string from the registry (and falls back to this filename when
670
+ // the registry has no session row). They must agree, because this string
671
+ // is the key that decides whether an authored checkpoint belongs to the
672
+ // current session and must be preserved.
673
+ sessionLine: noteFilename.replace(/\.md$/, ""),
674
+ cwd,
675
+ body: state?.trim() || void 0
676
+ });
677
+ if (result.action === "preserved") {
678
+ console.error(
679
+ "TODO.md ## Continue left intact \u2014 authored checkpoint for this session"
680
+ );
681
+ return;
682
+ }
683
+ if (result.action === "failed") {
684
+ console.error(`TODO.md ## Continue update failed: ${result.error}`);
685
+ return;
438
686
  }
439
- content = content.replace(/(\n---\s*)*(\n\*Last updated:.*\*\s*)+$/g, "");
440
- content = content.trimEnd() + `
687
+ try {
688
+ const todoPath = result.path;
689
+ const now = (/* @__PURE__ */ new Date()).toISOString();
690
+ let content = readFileSync5(todoPath, "utf-8");
691
+ content = content.replace(/(\n---\s*)*(\n\*Last updated:.*\*\s*)+$/g, "");
692
+ content = content.trimEnd() + `
441
693
 
442
694
  ---
443
695
 
444
696
  *Last updated: ${now}*
445
697
  `;
446
- writeFileSync2(todoPath, content);
447
- console.error("TODO.md ## Continue section updated");
698
+ writeFileSync3(todoPath, content);
699
+ } catch {
700
+ }
701
+ console.error(
702
+ result.carriedForward ? "TODO.md ## Continue section updated (previous content carried forward)" : "TODO.md ## Continue section updated"
703
+ );
448
704
  }
449
705
 
450
706
  // src/hooks/ts/stop/stop-hook.ts
@@ -458,7 +714,7 @@ var AUTO_SAVE_INTERVAL = (() => {
458
714
  }
459
715
  return 15;
460
716
  })();
461
- var SESSION_STATE_DIR = join6(
717
+ var SESSION_STATE_DIR = join7(
462
718
  process.env.HOME ?? "/tmp",
463
719
  ".config",
464
720
  "pai",
@@ -466,9 +722,9 @@ var SESSION_STATE_DIR = join6(
466
722
  );
467
723
  function readSessionState(sessionId) {
468
724
  try {
469
- const stateFile = join6(SESSION_STATE_DIR, `${sessionId}.json`);
470
- if (!existsSync6(stateFile)) return { humanMessageCount: 0 };
471
- const raw = readFileSync5(stateFile, "utf-8");
725
+ const stateFile = join7(SESSION_STATE_DIR, `${sessionId}.json`);
726
+ if (!existsSync7(stateFile)) return { humanMessageCount: 0 };
727
+ const raw = readFileSync6(stateFile, "utf-8");
472
728
  const parsed = JSON.parse(raw);
473
729
  return {
474
730
  humanMessageCount: typeof parsed.humanMessageCount === "number" ? parsed.humanMessageCount : 0
@@ -479,17 +735,17 @@ function readSessionState(sessionId) {
479
735
  }
480
736
  function writeSessionState(sessionId, state) {
481
737
  try {
482
- mkdirSync4(SESSION_STATE_DIR, { recursive: true });
483
- const stateFile = join6(SESSION_STATE_DIR, `${sessionId}.json`);
484
- writeFileSync3(stateFile, JSON.stringify(state, null, 2), "utf-8");
738
+ mkdirSync5(SESSION_STATE_DIR, { recursive: true });
739
+ const stateFile = join7(SESSION_STATE_DIR, `${sessionId}.json`);
740
+ writeFileSync4(stateFile, JSON.stringify(state, null, 2), "utf-8");
485
741
  } catch (e) {
486
742
  console.error(`STOP-HOOK: Could not write session state: ${e}`);
487
743
  }
488
744
  }
489
745
  function deleteSessionState(sessionId) {
490
746
  try {
491
- const stateFile = join6(SESSION_STATE_DIR, `${sessionId}.json`);
492
- if (existsSync6(stateFile)) {
747
+ const stateFile = join7(SESSION_STATE_DIR, `${sessionId}.json`);
748
+ if (existsSync7(stateFile)) {
493
749
  unlinkSync(stateFile);
494
750
  }
495
751
  } catch {
@@ -996,7 +1252,7 @@ STOP-HOOK TRIGGERED AT ${timestamp}`);
996
1252
  }
997
1253
  let transcript;
998
1254
  try {
999
- transcript = readFileSync5(transcriptPath, "utf-8");
1255
+ transcript = readFileSync6(transcriptPath, "utf-8");
1000
1256
  debug(`Transcript loaded: ${transcript.split("\n").length} lines`);
1001
1257
  } catch (e) {
1002
1258
  console.error(`Error reading transcript: ${e}`);