@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/pre-compact/context-compression-hook.ts
4
- import { existsSync as existsSync7, readFileSync as readFileSync6, writeFileSync as writeFileSync3 } from "fs";
5
- import { basename as basename3, dirname, join as join6 } from "path";
4
+ import { existsSync as existsSync8, readFileSync as readFileSync7, writeFileSync as writeFileSync4 } from "fs";
5
+ import { basename as basename3, dirname, join as join7 } from "path";
6
6
  import { tmpdir } from "os";
7
7
  import { connect } from "net";
8
8
  import { randomUUID } from "crypto";
@@ -420,13 +420,260 @@ function calculateSessionTokens(jsonlPath) {
420
420
  }
421
421
 
422
422
  // src/hooks/ts/lib/project-utils/todo.ts
423
- import { existsSync as existsSync6, mkdirSync as mkdirSync3, readFileSync as readFileSync5, writeFileSync as writeFileSync2 } from "fs";
424
- import { join as join5 } from "path";
423
+ import { existsSync as existsSync7, mkdirSync as mkdirSync4, readFileSync as readFileSync6, writeFileSync as writeFileSync3 } from "fs";
424
+ import { join as join6 } from "path";
425
+
426
+ // src/session/checkpoint-block.ts
427
+ import {
428
+ existsSync as existsSync6,
429
+ readFileSync as readFileSync5,
430
+ writeFileSync as writeFileSync2,
431
+ readdirSync as readdirSync3,
432
+ renameSync as renameSync3,
433
+ mkdirSync as mkdirSync3,
434
+ realpathSync
435
+ } from "node:fs";
436
+ import { join as join5 } from "node:path";
437
+ var TODO_LOCATIONS = [
438
+ "Notes/TODO.md",
439
+ ".claude/Notes/TODO.md",
440
+ "tasks/todo.md",
441
+ "TODO.md"
442
+ ];
443
+ var MARKER_OPEN = "<!-- pai:checkpoint";
444
+ var MARKER_CLOSE = "<!-- /pai:checkpoint -->";
445
+ var CONTINUE_HEADING = "## Continue";
446
+ function findProjectTodo(rootPath) {
447
+ for (const rel of TODO_LOCATIONS) {
448
+ const full = join5(rootPath, rel);
449
+ if (existsSync6(full)) {
450
+ try {
451
+ return { path: full, content: readFileSync5(full, "utf8") };
452
+ } catch {
453
+ }
454
+ }
455
+ }
456
+ return null;
457
+ }
458
+ function resolveTodoTarget(rootPath, opts = {}) {
459
+ const found = findProjectTodo(rootPath);
460
+ if (found) return found;
461
+ const notesDir = join5(rootPath, "Notes");
462
+ if (opts.create !== false) {
463
+ try {
464
+ if (!existsSync6(notesDir)) mkdirSync3(notesDir, { recursive: true });
465
+ } catch {
466
+ return null;
467
+ }
468
+ }
469
+ return { path: join5(notesDir, "TODO.md"), content: "" };
470
+ }
471
+ function parseMarker(line) {
472
+ const trimmed = line.trim();
473
+ if (!trimmed.startsWith(MARKER_OPEN)) return null;
474
+ const attrs = {};
475
+ for (const m of trimmed.matchAll(/([a-zA-Z][\w-]*)="([^"]*)"/g)) {
476
+ attrs[m[1]] = m[2];
477
+ }
478
+ return {
479
+ authored: attrs.authored === "model" ? "model" : "auto",
480
+ session: attrs.session || void 0,
481
+ sessionId: attrs["session-id"] || void 0,
482
+ ts: attrs.ts || void 0
483
+ };
484
+ }
485
+ var BOILERPLATE_PATTERNS = [
486
+ /^##\s+Continue$/,
487
+ /^<!--\s*\/?pai:checkpoint/,
488
+ /^>\s*\*\*Last session:\*\*/,
489
+ /^>\s*\*\*Paused at:\*\*/,
490
+ /^>\s*Working directory:/,
491
+ /^>\s*Resume with:/,
492
+ /^>\s*_No checkpoint body was recorded/,
493
+ /^>?\s*$/,
494
+ /^-{3,}$/
495
+ ];
496
+ function isBoilerplateOnly(lines) {
497
+ return lines.every((line) => {
498
+ const t = line.trim();
499
+ return BOILERPLATE_PATTERNS.some((re) => re.test(t));
500
+ });
501
+ }
502
+ function extractSectionContent(lines) {
503
+ const kept = [];
504
+ for (const line of lines) {
505
+ const t = line.trim();
506
+ if (BOILERPLATE_PATTERNS.some((re) => re.test(t))) continue;
507
+ kept.push(line);
508
+ }
509
+ return kept.join("\n").trim();
510
+ }
511
+ function locateContinue(content) {
512
+ const lines = content.split("\n");
513
+ const startIdx = lines.findIndex((l) => l.trim() === CONTINUE_HEADING);
514
+ if (startIdx === -1) return null;
515
+ let meta = null;
516
+ let markerIdx = -1;
517
+ for (let i = startIdx + 1; i < Math.min(startIdx + 6, lines.length); i++) {
518
+ const parsed = parseMarker(lines[i]);
519
+ if (parsed) {
520
+ meta = parsed;
521
+ markerIdx = i;
522
+ break;
523
+ }
524
+ if (lines[i].trim() !== "") break;
525
+ }
526
+ let endIdx = lines.length;
527
+ if (markerIdx !== -1) {
528
+ const closeIdx = lines.findIndex(
529
+ (l, i) => i > markerIdx && l.trim() === MARKER_CLOSE
530
+ );
531
+ if (closeIdx !== -1) {
532
+ endIdx = closeIdx + 1;
533
+ } else {
534
+ endIdx = heuristicEnd(lines, startIdx);
535
+ }
536
+ } else {
537
+ endIdx = heuristicEnd(lines, startIdx);
538
+ }
539
+ let trailingEnd = endIdx;
540
+ while (trailingEnd < lines.length && lines[trailingEnd].trim() === "") {
541
+ trailingEnd += 1;
542
+ }
543
+ if (trailingEnd < lines.length && lines[trailingEnd].trim() === "---") {
544
+ trailingEnd += 1;
545
+ } else {
546
+ trailingEnd = endIdx;
547
+ }
548
+ return {
549
+ startIdx,
550
+ endIdx: trailingEnd,
551
+ meta,
552
+ lines: lines.slice(startIdx, trailingEnd)
553
+ };
554
+ }
555
+ function heuristicEnd(lines, startIdx) {
556
+ for (let i = startIdx + 1; i < lines.length; i++) {
557
+ const trimmed = lines[i].trim();
558
+ if (trimmed === "---" || /^##(?!#)/.test(trimmed) && trimmed !== CONTINUE_HEADING) {
559
+ return i;
560
+ }
561
+ }
562
+ return lines.length;
563
+ }
564
+ function stripContinue(content) {
565
+ const found = locateContinue(content);
566
+ if (!found) return content;
567
+ const lines = content.split("\n");
568
+ const before = lines.slice(0, found.startIdx);
569
+ const after = lines.slice(found.endIdx);
570
+ while (after.length > 0 && after[0].trim() === "") after.shift();
571
+ return [...before, ...after].join("\n");
572
+ }
573
+ function escapeAttr(value) {
574
+ return value.replace(/"/g, "'");
575
+ }
576
+ function buildContinueBlock(opts) {
577
+ const ts = opts.timestamp ?? (/* @__PURE__ */ new Date()).toISOString();
578
+ const attrs = [
579
+ `authored="${opts.authored}"`,
580
+ `session="${escapeAttr(opts.sessionLine)}"`,
581
+ opts.sessionId ? `session-id="${escapeAttr(opts.sessionId)}"` : null,
582
+ `ts="${ts}"`
583
+ ].filter(Boolean).join(" ");
584
+ const header = [
585
+ `> **Last session:** ${opts.sessionLine}`,
586
+ `> **Paused at:** ${ts}`,
587
+ ">",
588
+ `> Working directory: ${opts.cwd}`
589
+ ];
590
+ if (opts.sessionId) {
591
+ header.push(">", `> Resume with: \`claude --resume ${opts.sessionId}\``);
592
+ }
593
+ const body = (opts.body ?? "").trim();
594
+ const parts = [
595
+ CONTINUE_HEADING,
596
+ "",
597
+ `${MARKER_OPEN} ${attrs} -->`,
598
+ "",
599
+ ...header
600
+ ];
601
+ if (body) {
602
+ parts.push("", body);
603
+ } else {
604
+ parts.push(
605
+ ">",
606
+ "> _No checkpoint body was recorded \u2014 see the latest session note._"
607
+ );
608
+ }
609
+ parts.push("", MARKER_CLOSE, "", "---", "");
610
+ return parts.join("\n");
611
+ }
612
+ function applyContinue(opts) {
613
+ const target = resolveTodoTarget(opts.rootPath, { create: !opts.dryRun });
614
+ if (!target) {
615
+ return {
616
+ action: "failed",
617
+ path: null,
618
+ block: buildContinueBlock(opts),
619
+ error: "Could not resolve or create a TODO.md target"
620
+ };
621
+ }
622
+ const existing = locateContinue(target.content);
623
+ let carriedForward = false;
624
+ let effectiveBody = opts.body;
625
+ if (opts.authored === "auto" && existing) {
626
+ if (existing.meta?.authored === "model" && existing.meta.session === opts.sessionLine) {
627
+ return {
628
+ action: "preserved",
629
+ path: target.path,
630
+ block: buildContinueBlock(opts),
631
+ preservedMeta: existing.meta
632
+ };
633
+ }
634
+ if (!existing.meta && !isBoilerplateOnly(existing.lines)) {
635
+ const salvaged = extractSectionContent(existing.lines);
636
+ if (salvaged) {
637
+ effectiveBody = [
638
+ "_Carried forward from the previous checkpoint (author unknown \u2014 this",
639
+ "block predates checkpoint authorship markers):_",
640
+ "",
641
+ salvaged
642
+ ].join("\n");
643
+ carriedForward = true;
644
+ }
645
+ }
646
+ }
647
+ const block = buildContinueBlock({ ...opts, body: effectiveBody });
648
+ if (opts.dryRun) {
649
+ return { action: "written", path: target.path, block, carriedForward };
650
+ }
651
+ const newContent = block + stripContinue(target.content).trimStart();
652
+ const tmpPath = `${target.path}.continue.tmp`;
653
+ try {
654
+ writeFileSync2(tmpPath, newContent, "utf8");
655
+ renameSync3(tmpPath, target.path);
656
+ } catch (err) {
657
+ try {
658
+ if (existsSync6(tmpPath)) renameSync3(tmpPath, `${tmpPath}.dead`);
659
+ } catch {
660
+ }
661
+ return {
662
+ action: "failed",
663
+ path: target.path,
664
+ block,
665
+ error: String(err)
666
+ };
667
+ }
668
+ return { action: "written", path: target.path, block, carriedForward };
669
+ }
670
+
671
+ // src/hooks/ts/lib/project-utils/todo.ts
425
672
  function ensureTodoMd(cwd) {
426
673
  const todoPath = findTodoPath(cwd);
427
- if (!existsSync6(todoPath)) {
428
- const parentDir = join5(todoPath, "..");
429
- if (!existsSync6(parentDir)) mkdirSync3(parentDir, { recursive: true });
674
+ if (!existsSync7(todoPath)) {
675
+ const parentDir = join6(todoPath, "..");
676
+ if (!existsSync7(parentDir)) mkdirSync4(parentDir, { recursive: true });
430
677
  const content = `# TODO
431
678
 
432
679
  ## Current Session
@@ -441,43 +688,52 @@ function ensureTodoMd(cwd) {
441
688
 
442
689
  *Last updated: ${(/* @__PURE__ */ new Date()).toISOString()}*
443
690
  `;
444
- writeFileSync2(todoPath, content);
691
+ writeFileSync3(todoPath, content);
445
692
  console.error(`Created TODO.md: ${todoPath}`);
446
693
  }
447
694
  return todoPath;
448
695
  }
449
696
  function updateTodoContinue(cwd, noteFilename, state, tokenDisplay) {
450
- const todoPath = ensureTodoMd(cwd);
451
- let content = readFileSync5(todoPath, "utf-8");
452
- content = content.replace(/## Continue\n[\s\S]*?\n---\n+/, "");
453
- const now = (/* @__PURE__ */ new Date()).toISOString();
454
- 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.`;
455
- const continueSection = `## Continue
456
-
457
- > **Last session:** ${noteFilename.replace(".md", "")}
458
- > **Paused at:** ${now}
459
- >
460
- ${stateLines}
461
-
462
- ---
463
-
464
- `;
465
- content = content.replace(/^\s+/, "");
466
- const titleMatch = content.match(/^(# [^\n]+\n+)/);
467
- if (titleMatch) {
468
- content = titleMatch[1] + continueSection + content.substring(titleMatch[0].length);
469
- } else {
470
- content = continueSection + content;
697
+ ensureTodoMd(cwd);
698
+ const result = applyContinue({
699
+ rootPath: cwd,
700
+ authored: "auto",
701
+ // The hooks identify a session by its note filename; `pai pause` resolves
702
+ // the same string from the registry (and falls back to this filename when
703
+ // the registry has no session row). They must agree, because this string
704
+ // is the key that decides whether an authored checkpoint belongs to the
705
+ // current session and must be preserved.
706
+ sessionLine: noteFilename.replace(/\.md$/, ""),
707
+ cwd,
708
+ body: state?.trim() || void 0
709
+ });
710
+ if (result.action === "preserved") {
711
+ console.error(
712
+ "TODO.md ## Continue left intact \u2014 authored checkpoint for this session"
713
+ );
714
+ return;
471
715
  }
472
- content = content.replace(/(\n---\s*)*(\n\*Last updated:.*\*\s*)+$/g, "");
473
- content = content.trimEnd() + `
716
+ if (result.action === "failed") {
717
+ console.error(`TODO.md ## Continue update failed: ${result.error}`);
718
+ return;
719
+ }
720
+ try {
721
+ const todoPath = result.path;
722
+ const now = (/* @__PURE__ */ new Date()).toISOString();
723
+ let content = readFileSync6(todoPath, "utf-8");
724
+ content = content.replace(/(\n---\s*)*(\n\*Last updated:.*\*\s*)+$/g, "");
725
+ content = content.trimEnd() + `
474
726
 
475
727
  ---
476
728
 
477
729
  *Last updated: ${now}*
478
730
  `;
479
- writeFileSync2(todoPath, content);
480
- console.error("TODO.md ## Continue section updated");
731
+ writeFileSync3(todoPath, content);
732
+ } catch {
733
+ }
734
+ console.error(
735
+ result.carriedForward ? "TODO.md ## Continue section updated (previous content carried forward)" : "TODO.md ## Continue section updated"
736
+ );
481
737
  }
482
738
 
483
739
  // src/hooks/ts/pre-compact/context-compression-hook.ts
@@ -497,7 +753,7 @@ function contentToText(content) {
497
753
  }
498
754
  function getTranscriptStats(transcriptPath) {
499
755
  try {
500
- const content = readFileSync6(transcriptPath, "utf-8");
756
+ const content = readFileSync7(transcriptPath, "utf-8");
501
757
  const lines = content.trim().split("\n");
502
758
  let userMessages = 0;
503
759
  let assistantMessages = 0;
@@ -526,7 +782,7 @@ function parseTranscript(transcriptPath) {
526
782
  workItems: []
527
783
  };
528
784
  try {
529
- const raw = readFileSync6(transcriptPath, "utf-8");
785
+ const raw = readFileSync7(transcriptPath, "utf-8");
530
786
  const lines = raw.trim().split("\n");
531
787
  const seenSummaries = /* @__PURE__ */ new Set();
532
788
  for (const line of lines) {
@@ -662,9 +918,9 @@ function deriveTitle(data) {
662
918
  var CUMULATIVE_STATE_FILE = ".compact-state.json";
663
919
  function loadCumulativeState(notesDir) {
664
920
  try {
665
- const filePath = join6(notesDir, CUMULATIVE_STATE_FILE);
666
- if (!existsSync7(filePath)) return null;
667
- const raw = JSON.parse(readFileSync6(filePath, "utf-8"));
921
+ const filePath = join7(notesDir, CUMULATIVE_STATE_FILE);
922
+ if (!existsSync8(filePath)) return null;
923
+ const raw = JSON.parse(readFileSync7(filePath, "utf-8"));
668
924
  return {
669
925
  userMessages: raw.userMessages || [],
670
926
  summaries: raw.summaries || [],
@@ -696,8 +952,8 @@ function mergeTranscriptData(accumulated, current) {
696
952
  }
697
953
  function saveCumulativeState(notesDir, data, notePath) {
698
954
  try {
699
- const filePath = join6(notesDir, CUMULATIVE_STATE_FILE);
700
- writeFileSync3(filePath, JSON.stringify({
955
+ const filePath = join7(notesDir, CUMULATIVE_STATE_FILE);
956
+ writeFileSync4(filePath, JSON.stringify({
701
957
  ...data,
702
958
  notePath,
703
959
  lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
@@ -840,9 +1096,9 @@ async function main() {
840
1096
  const data = parseTranscript(hookInput.transcript_path);
841
1097
  let notesInfo;
842
1098
  try {
843
- notesInfo = hookInput.cwd ? findNotesDir(hookInput.cwd) : { path: join6(dirname(hookInput.transcript_path), "Notes"), isLocal: false };
1099
+ notesInfo = hookInput.cwd ? findNotesDir(hookInput.cwd) : { path: join7(dirname(hookInput.transcript_path), "Notes"), isLocal: false };
844
1100
  } catch {
845
- notesInfo = { path: join6(dirname(hookInput.transcript_path), "Notes"), isLocal: false };
1101
+ notesInfo = { path: join7(dirname(hookInput.transcript_path), "Notes"), isLocal: false };
846
1102
  }
847
1103
  const accumulated = loadCumulativeState(notesInfo.path);
848
1104
  const merged = mergeTranscriptData(accumulated, data);
@@ -858,10 +1114,10 @@ async function main() {
858
1114
  notePath = createSessionNote(notesInfo.path, "Untitled Session");
859
1115
  } else {
860
1116
  try {
861
- let noteContent = readFileSync6(notePath, "utf-8");
1117
+ let noteContent = readFileSync7(notePath, "utf-8");
862
1118
  if (noteContent.includes("**Status:** Completed")) {
863
1119
  noteContent = noteContent.replace("**Status:** Completed", "**Status:** In Progress");
864
- writeFileSync3(notePath, noteContent);
1120
+ writeFileSync4(notePath, noteContent);
865
1121
  console.error(`Reopened completed note for continued session: ${basename3(notePath)}`);
866
1122
  }
867
1123
  } catch {
@@ -880,12 +1136,12 @@ ${state}` : `Context compression triggered at ~${tokenDisplay} tokens with ${sta
880
1136
  const newPath = renameSessionNote(notePath, title);
881
1137
  if (newPath !== notePath) {
882
1138
  try {
883
- let noteContent = readFileSync6(newPath, "utf-8");
1139
+ let noteContent = readFileSync7(newPath, "utf-8");
884
1140
  noteContent = noteContent.replace(
885
1141
  /^(# Session \d+:)\s*.*$/m,
886
1142
  `$1 ${title}`
887
1143
  );
888
- writeFileSync3(newPath, noteContent);
1144
+ writeFileSync4(newPath, noteContent);
889
1145
  console.error(`Updated note H1 to match rename`);
890
1146
  } catch {
891
1147
  }
@@ -926,8 +1182,8 @@ rename it based on actual work done and add a rich summary.` : "";
926
1182
  "</system-reminder>"
927
1183
  ].join("\n");
928
1184
  try {
929
- const stateFile = join6(tmpdir(), `pai-compact-state-${hookInput.session_id}.txt`);
930
- writeFileSync3(stateFile, injection, "utf-8");
1185
+ const stateFile = join7(tmpdir(), `pai-compact-state-${hookInput.session_id}.txt`);
1186
+ writeFileSync4(stateFile, injection, "utf-8");
931
1187
  console.error(`Session state saved to ${stateFile} (${injection.length} chars)`);
932
1188
  } catch (err) {
933
1189
  console.error(`Failed to save state file: ${err}`);