@tekmidian/pai 0.15.0 → 0.17.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 (251) hide show
  1. package/README.md +11 -3
  2. package/dist/{auto-route-CLYcToZ5.mjs → auto-route-CdvjEWs5.mjs} +4 -4
  3. package/dist/{auto-route-CLYcToZ5.mjs.map → auto-route-CdvjEWs5.mjs.map} +1 -1
  4. package/dist/auto-route-DkIRampF.mjs +86 -0
  5. package/dist/auto-route-DkIRampF.mjs.map +1 -0
  6. package/dist/{kg-extraction-C8DEUHTS.mjs → checkpoint-block-Bl0HD3U2.mjs} +442 -13
  7. package/dist/checkpoint-block-Bl0HD3U2.mjs.map +1 -0
  8. package/dist/checkpoint-block-C05sK_HK.mjs +1056 -0
  9. package/dist/checkpoint-block-C05sK_HK.mjs.map +1 -0
  10. package/dist/checkpoint-block-CUCG10Uh.mjs +1022 -0
  11. package/dist/checkpoint-block-CUCG10Uh.mjs.map +1 -0
  12. package/dist/checkpoint-block-D5IyLLTr.mjs +1062 -0
  13. package/dist/checkpoint-block-D5IyLLTr.mjs.map +1 -0
  14. package/dist/cli/index.mjs +14 -12
  15. package/dist/cli/index.mjs.map +1 -1
  16. package/dist/cli/program.d.mts.map +1 -1
  17. package/dist/cli/program.mjs +31 -16
  18. package/dist/cli/program.mjs.map +1 -1
  19. package/dist/{clusters-BYPw7vfW.mjs → clusters-CKWDwcMX.mjs} +2 -2
  20. package/dist/{clusters-BYPw7vfW.mjs.map → clusters-CKWDwcMX.mjs.map} +1 -1
  21. package/dist/{config-C8m-tPhP.mjs → config-DGOfoGfm.mjs} +1 -1
  22. package/dist/{config-C8m-tPhP.mjs.map → config-DGOfoGfm.mjs.map} +1 -1
  23. package/dist/daemon/index.mjs +18 -16
  24. package/dist/daemon/index.mjs.map +1 -1
  25. package/dist/{daemon-COSvHTNu.mjs → daemon-1rKbN-fd.mjs} +31 -30
  26. package/dist/{daemon-COSvHTNu.mjs.map → daemon-1rKbN-fd.mjs.map} +1 -1
  27. package/dist/daemon-9AL6Pwh-.mjs +1581 -0
  28. package/dist/daemon-9AL6Pwh-.mjs.map +1 -0
  29. package/dist/daemon-Bbk-_rV6.mjs +1581 -0
  30. package/dist/daemon-Bbk-_rV6.mjs.map +1 -0
  31. package/dist/daemon-BvcaMk4g.mjs +1581 -0
  32. package/dist/daemon-BvcaMk4g.mjs.map +1 -0
  33. package/dist/daemon-CTDEfwcq.mjs +1576 -0
  34. package/dist/daemon-CTDEfwcq.mjs.map +1 -0
  35. package/dist/daemon-ClbfaF60.mjs +1576 -0
  36. package/dist/daemon-ClbfaF60.mjs.map +1 -0
  37. package/dist/daemon-DDQNDgYF.mjs +1576 -0
  38. package/dist/daemon-DDQNDgYF.mjs.map +1 -0
  39. package/dist/daemon-DU8yDWlQ.mjs +1576 -0
  40. package/dist/daemon-DU8yDWlQ.mjs.map +1 -0
  41. package/dist/daemon-NJr4iSW3.mjs +1576 -0
  42. package/dist/daemon-NJr4iSW3.mjs.map +1 -0
  43. package/dist/daemon-mcp/index.mjs +2 -2
  44. package/dist/db-BtuN768f.mjs +206 -0
  45. package/dist/db-BtuN768f.mjs.map +1 -0
  46. package/dist/{db-CmYbAVCD.mjs → db-CYmBWcjh.mjs} +1 -1
  47. package/dist/{db-CmYbAVCD.mjs.map → db-CYmBWcjh.mjs.map} +1 -1
  48. package/dist/{detect-KjycLtXM.mjs → detect-Bf2z-oKB.mjs} +1 -1
  49. package/dist/{detect-KjycLtXM.mjs.map → detect-Bf2z-oKB.mjs.map} +1 -1
  50. package/dist/{detector-ZiOhHszd.mjs → detector-Bwzm3E_x.mjs} +2 -2
  51. package/dist/{detector-ZiOhHszd.mjs.map → detector-Bwzm3E_x.mjs.map} +1 -1
  52. package/dist/detector-rq9JGTmS.mjs +74 -0
  53. package/dist/detector-rq9JGTmS.mjs.map +1 -0
  54. package/dist/embeddings-Bn86ssxR.mjs +119 -0
  55. package/dist/embeddings-Bn86ssxR.mjs.map +1 -0
  56. package/dist/{embeddings-BJPOcbik.mjs → embeddings-DGRAPAYb.mjs} +1 -1
  57. package/dist/{embeddings-BJPOcbik.mjs.map → embeddings-DGRAPAYb.mjs.map} +1 -1
  58. package/dist/{factory-Q88X1bAN.mjs → factory-0-57Ac-7.mjs} +8 -5
  59. package/dist/{factory-Q88X1bAN.mjs.map → factory-0-57Ac-7.mjs.map} +1 -1
  60. package/dist/factory-HcQsQqh0.mjs +72 -0
  61. package/dist/factory-HcQsQqh0.mjs.map +1 -0
  62. package/dist/factory-PDXQdkLO.mjs +72 -0
  63. package/dist/factory-PDXQdkLO.mjs.map +1 -0
  64. package/dist/{helpers-IjZkXBhj.mjs → helpers-crDEr6S2.mjs} +1 -1
  65. package/dist/{helpers-IjZkXBhj.mjs.map → helpers-crDEr6S2.mjs.map} +1 -1
  66. package/dist/hooks/capture-all-events.mjs.map +1 -1
  67. package/dist/hooks/capture-session-summary.mjs.map +1 -1
  68. package/dist/hooks/capture-tool-output.mjs.map +1 -1
  69. package/dist/hooks/cleanup-session-files.mjs.map +1 -1
  70. package/dist/hooks/context-compression-hook.mjs +336 -48
  71. package/dist/hooks/context-compression-hook.mjs.map +4 -4
  72. package/dist/hooks/initialize-session.mjs.map +1 -1
  73. package/dist/hooks/inject-observations.mjs.map +1 -1
  74. package/dist/hooks/load-core-context.mjs.map +1 -1
  75. package/dist/hooks/load-project-context.mjs +188 -6
  76. package/dist/hooks/load-project-context.mjs.map +4 -4
  77. package/dist/hooks/observe.mjs.map +1 -1
  78. package/dist/hooks/post-compact-inject.mjs.map +1 -1
  79. package/dist/hooks/security-validator.mjs.map +1 -1
  80. package/dist/hooks/session-commands.mjs.map +1 -1
  81. package/dist/hooks/stop-hook.mjs +331 -43
  82. package/dist/hooks/stop-hook.mjs.map +4 -4
  83. package/dist/hooks/subagent-stop-hook.mjs.map +1 -1
  84. package/dist/hooks/sync-todo-to-md.mjs.map +1 -1
  85. package/dist/hooks/update-tab-on-action.mjs.map +1 -1
  86. package/dist/hooks/update-tab-titles.mjs.map +1 -1
  87. package/dist/hooks/whisper-rules.mjs.map +1 -1
  88. package/dist/index.d.mts +0 -9
  89. package/dist/index.d.mts.map +1 -1
  90. package/dist/index.mjs +10 -9
  91. package/dist/indexer-AEcT8wHf.mjs +1 -0
  92. package/dist/{indexer-backend-Cm5RS7y0.mjs → indexer-backend-BQZXoarl.mjs} +3 -3
  93. package/dist/{indexer-backend-Cm5RS7y0.mjs.map → indexer-backend-BQZXoarl.mjs.map} +1 -1
  94. package/dist/indexer-backend-BmaS2VcD.mjs +299 -0
  95. package/dist/indexer-backend-BmaS2VcD.mjs.map +1 -0
  96. package/dist/indexer-backend-COrJ5rjS.mjs +299 -0
  97. package/dist/indexer-backend-COrJ5rjS.mjs.map +1 -0
  98. package/dist/indexer-backend-NwD7TBGh.mjs +301 -0
  99. package/dist/indexer-backend-NwD7TBGh.mjs.map +1 -0
  100. package/dist/{ipc-client-CoyUHPod.mjs → ipc-client-vO2GV325.mjs} +1 -1
  101. package/dist/{ipc-client-CoyUHPod.mjs.map → ipc-client-vO2GV325.mjs.map} +1 -1
  102. package/dist/{kg-entity-LXblD7LZ.mjs → kg-entity-DbOMPdF9.mjs} +1 -1
  103. package/dist/{kg-entity-LXblD7LZ.mjs.map → kg-entity-DbOMPdF9.mjs.map} +1 -1
  104. package/dist/{latent-ideas-RyC6eyqI.mjs → latent-ideas-BwM3WdBp.mjs} +4 -4
  105. package/dist/{latent-ideas-RyC6eyqI.mjs.map → latent-ideas-BwM3WdBp.mjs.map} +1 -1
  106. package/dist/latent-ideas-C6IFFT0-.mjs +191 -0
  107. package/dist/latent-ideas-C6IFFT0-.mjs.map +1 -0
  108. package/dist/{main-resolver-uFxNiDy7.mjs → main-resolver-DimnS2qP.mjs} +22 -22
  109. package/dist/{main-resolver-uFxNiDy7.mjs.map → main-resolver-DimnS2qP.mjs.map} +1 -1
  110. package/dist/{migrate-B02wDDgS.mjs → migrate-fLD6rAdO.mjs} +2 -2
  111. package/dist/{migrate-B02wDDgS.mjs.map → migrate-fLD6rAdO.mjs.map} +1 -1
  112. package/dist/{neighborhood-CklGIB8r.mjs → neighborhood-CzRNG2Oy.mjs} +2 -2
  113. package/dist/{neighborhood-CklGIB8r.mjs.map → neighborhood-CzRNG2Oy.mjs.map} +1 -1
  114. package/dist/neighborhood-DZY0wkpE.mjs +135 -0
  115. package/dist/neighborhood-DZY0wkpE.mjs.map +1 -0
  116. package/dist/{note-context-qvZlVXVO.mjs → note-context-CrfMbr4R.mjs} +1 -1
  117. package/dist/{note-context-qvZlVXVO.mjs.map → note-context-CrfMbr4R.mjs.map} +1 -1
  118. package/dist/{pai-marker-HVBwwBW-.mjs → pai-marker-B20KqhA8.mjs} +44 -2
  119. package/dist/pai-marker-B20KqhA8.mjs.map +1 -0
  120. package/dist/pick-B0q5cy79.mjs +11785 -0
  121. package/dist/pick-B0q5cy79.mjs.map +1 -0
  122. package/dist/{pick-r95zvydr.mjs → pick-B3zEYjin.mjs} +2288 -497
  123. package/dist/pick-B3zEYjin.mjs.map +1 -0
  124. package/dist/pick-BHE9_iA0.mjs +11947 -0
  125. package/dist/pick-BHE9_iA0.mjs.map +1 -0
  126. package/dist/pick-BaTjoNqM.mjs +11544 -0
  127. package/dist/pick-BaTjoNqM.mjs.map +1 -0
  128. package/dist/pick-C6JbZfN4.mjs +11893 -0
  129. package/dist/pick-C6JbZfN4.mjs.map +1 -0
  130. package/dist/pick-CK-bJyjp.mjs +11883 -0
  131. package/dist/pick-CK-bJyjp.mjs.map +1 -0
  132. package/dist/pick-CLBkGHyI.mjs +11826 -0
  133. package/dist/pick-CLBkGHyI.mjs.map +1 -0
  134. package/dist/pick-CTOif21h.mjs +11544 -0
  135. package/dist/pick-CTOif21h.mjs.map +1 -0
  136. package/dist/pick-Ck2cdQgD.mjs +11893 -0
  137. package/dist/pick-Ck2cdQgD.mjs.map +1 -0
  138. package/dist/pick-Cr4nJzHA.mjs +11914 -0
  139. package/dist/pick-Cr4nJzHA.mjs.map +1 -0
  140. package/dist/pick-D4dFfKAu.mjs +11893 -0
  141. package/dist/pick-D4dFfKAu.mjs.map +1 -0
  142. package/dist/pick-D7-lYiGX.mjs +11543 -0
  143. package/dist/pick-D7-lYiGX.mjs.map +1 -0
  144. package/dist/pick-DMjCY0np.mjs +11875 -0
  145. package/dist/pick-DMjCY0np.mjs.map +1 -0
  146. package/dist/pick-DbX4OrCP.mjs +11543 -0
  147. package/dist/pick-DbX4OrCP.mjs.map +1 -0
  148. package/dist/pick-iCiPjlnC.mjs +11893 -0
  149. package/dist/pick-iCiPjlnC.mjs.map +1 -0
  150. package/dist/pick-sF9GnQUS.mjs +11893 -0
  151. package/dist/pick-sF9GnQUS.mjs.map +1 -0
  152. package/dist/pick-yHKUrnfG.mjs +11793 -0
  153. package/dist/pick-yHKUrnfG.mjs.map +1 -0
  154. package/dist/postgres-BJxiqtqg.mjs +891 -0
  155. package/dist/postgres-BJxiqtqg.mjs.map +1 -0
  156. package/dist/{postgres-B451Hnkm.mjs → postgres-zjKqPl4X.mjs} +2 -2
  157. package/dist/{postgres-B451Hnkm.mjs.map → postgres-zjKqPl4X.mjs.map} +1 -1
  158. package/dist/{query-feedback-fapbqRgh.mjs → query-feedback-BoY8_Dbb.mjs} +1 -1
  159. package/dist/{query-feedback-fapbqRgh.mjs.map → query-feedback-BoY8_Dbb.mjs.map} +1 -1
  160. package/dist/{reranker-C08R99zn.mjs → reranker-CMNZcfVx.mjs} +1 -1
  161. package/dist/{reranker-C08R99zn.mjs.map → reranker-CMNZcfVx.mjs.map} +1 -1
  162. package/dist/{search-HcdKtMla.mjs → search-CpTv1I24.mjs} +3 -3
  163. package/dist/{search-HcdKtMla.mjs.map → search-CpTv1I24.mjs.map} +1 -1
  164. package/dist/search-i2nlQ-JM.mjs +298 -0
  165. package/dist/search-i2nlQ-JM.mjs.map +1 -0
  166. package/dist/skills/End/SKILL.md +78 -0
  167. package/dist/skills/Pause/SKILL.md +84 -0
  168. package/dist/sqlite-CZ0LFili.mjs +271 -0
  169. package/dist/sqlite-CZ0LFili.mjs.map +1 -0
  170. package/dist/{sqlite-DQpY1Esi.mjs → sqlite-ChSCYwiC.mjs} +3 -3
  171. package/dist/{sqlite-DQpY1Esi.mjs.map → sqlite-ChSCYwiC.mjs.map} +1 -1
  172. package/dist/sqlite-Nw8ZW5Rj.mjs +271 -0
  173. package/dist/sqlite-Nw8ZW5Rj.mjs.map +1 -0
  174. package/dist/{state-BIlxNRUn.mjs → state-BXIdbxDs.mjs} +1 -1
  175. package/dist/{state-BIlxNRUn.mjs.map → state-BXIdbxDs.mjs.map} +1 -1
  176. package/dist/{stop-words-BwplsQ3z.mjs → stop-words-BaMEGVeY.mjs} +1 -1
  177. package/dist/{stop-words-BwplsQ3z.mjs.map → stop-words-BaMEGVeY.mjs.map} +1 -1
  178. package/dist/{sync-uPR4g438.mjs → sync--BoxBBok.mjs} +6 -205
  179. package/dist/sync--BoxBBok.mjs.map +1 -0
  180. package/dist/sync-CmBKOL3K.mjs +310 -0
  181. package/dist/sync-CmBKOL3K.mjs.map +1 -0
  182. package/dist/{themes-Cg8oG9Ra.mjs → themes-BBOlGXAg.mjs} +3 -3
  183. package/dist/{themes-Cg8oG9Ra.mjs.map → themes-BBOlGXAg.mjs.map} +1 -1
  184. package/dist/themes-nvRM8v84.mjs +148 -0
  185. package/dist/themes-nvRM8v84.mjs.map +1 -0
  186. package/dist/tools-B6BmaAvz.mjs +1939 -0
  187. package/dist/tools-B6BmaAvz.mjs.map +1 -0
  188. package/dist/tools-CAIY8TAb.mjs +1939 -0
  189. package/dist/tools-CAIY8TAb.mjs.map +1 -0
  190. package/dist/{tools-Op3C2Nm6.mjs → tools-CznjzfYz.mjs} +26 -26
  191. package/dist/{tools-Op3C2Nm6.mjs.map → tools-CznjzfYz.mjs.map} +1 -1
  192. package/dist/{trace-CLK-NPkb.mjs → trace-B8oz1ok0.mjs} +1 -1
  193. package/dist/{trace-CLK-NPkb.mjs.map → trace-B8oz1ok0.mjs.map} +1 -1
  194. package/dist/{utils-CqqgB0dH.mjs → utils-BAxjW3j8.mjs} +1 -1
  195. package/dist/{utils-CqqgB0dH.mjs.map → utils-BAxjW3j8.mjs.map} +1 -1
  196. package/dist/{vault-indexer-Dn_BccVK.mjs → vault-indexer-DrttVeEk.mjs} +2 -2
  197. package/dist/{vault-indexer-Dn_BccVK.mjs.map → vault-indexer-DrttVeEk.mjs.map} +1 -1
  198. package/dist/{work-queue-worker-BwouhVkb.mjs → work-queue-worker-BPXLkEik.mjs} +47 -30
  199. package/dist/work-queue-worker-BPXLkEik.mjs.map +1 -0
  200. package/dist/work-queue-worker-BoHIH70K.mjs +1856 -0
  201. package/dist/work-queue-worker-BoHIH70K.mjs.map +1 -0
  202. package/dist/work-queue-worker-CZGcQoLw.mjs +1856 -0
  203. package/dist/work-queue-worker-CZGcQoLw.mjs.map +1 -0
  204. package/dist/work-queue-worker-D05Ze9e9.mjs +1856 -0
  205. package/dist/work-queue-worker-D05Ze9e9.mjs.map +1 -0
  206. package/dist/work-queue-worker-DdXFINfH.mjs +1856 -0
  207. package/dist/work-queue-worker-DdXFINfH.mjs.map +1 -0
  208. package/dist/{zettelkasten-NwBD1NsT.mjs → zettelkasten-CTlt1dGv.mjs} +4 -4
  209. package/dist/{zettelkasten-NwBD1NsT.mjs.map → zettelkasten-CTlt1dGv.mjs.map} +1 -1
  210. package/dist/zettelkasten-D0N_A_OE.mjs +1063 -0
  211. package/dist/zettelkasten-D0N_A_OE.mjs.map +1 -0
  212. package/docs/commands/README.md +18 -0
  213. package/docs/commands/backup.md +1 -1
  214. package/docs/commands/clear-names.md +1 -1
  215. package/docs/commands/daemon.md +1 -1
  216. package/docs/commands/db.md +1 -1
  217. package/docs/commands/end.md +4 -1
  218. package/docs/commands/help.md +1 -1
  219. package/docs/commands/kg.md +1 -1
  220. package/docs/commands/mcp.md +1 -1
  221. package/docs/commands/memory.md +1 -1
  222. package/docs/commands/notify.md +1 -1
  223. package/docs/commands/observation.md +1 -1
  224. package/docs/commands/obsidian.md +1 -1
  225. package/docs/commands/pause.md +5 -2
  226. package/docs/commands/project.md +1 -1
  227. package/docs/commands/projects.md +1 -1
  228. package/docs/commands/registry.md +18 -1
  229. package/docs/commands/restore.md +1 -1
  230. package/docs/commands/session.md +295 -0
  231. package/docs/commands/sessions.md +1 -1
  232. package/docs/commands/setup.md +1 -1
  233. package/docs/commands/shell-init.md +1 -1
  234. package/docs/commands/skill.md +1 -1
  235. package/docs/commands/task.md +1 -1
  236. package/docs/commands/topic.md +1 -1
  237. package/docs/commands/update.md +1 -1
  238. package/docs/commands/zettel.md +1 -1
  239. package/package.json +1 -1
  240. package/plugins/context-preservation/hooks/hooks.json +12 -1
  241. package/scripts/build-hooks.mjs +33 -3
  242. package/src/hooks/session-autosave.sh +69 -0
  243. package/src/hooks/session-stop.sh +32 -1
  244. package/src/hooks/ts/lib/project-utils/todo.test.ts +132 -0
  245. package/src/hooks/ts/lib/project-utils/todo.ts +60 -34
  246. package/src/hooks/ts/session-start/load-project-context.ts +44 -0
  247. package/dist/kg-extraction-C8DEUHTS.mjs.map +0 -1
  248. package/dist/pai-marker-HVBwwBW-.mjs.map +0 -1
  249. package/dist/pick-r95zvydr.mjs.map +0 -1
  250. package/dist/sync-uPR4g438.mjs.map +0 -1
  251. package/dist/work-queue-worker-BwouhVkb.mjs.map +0 -1
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["../../src/hooks/ts/pre-tool-use/security-validator.ts"],
3
+ "sources": ["../../../src/hooks/ts/pre-tool-use/security-validator.ts"],
4
4
  "sourcesContent": ["#!/usr/bin/env node\n\n/**\n * security-validator.ts - PreToolUse Security Validation Hook\n *\n * Fast pattern-based security validation for Bash commands.\n * Blocks commands matching known attack patterns before execution.\n *\n * Design Principles:\n * - Fast path: Most commands allowed with minimal processing\n * - Pre-compiled regex patterns at module load\n * - Only log/block on high-confidence attack detection\n * - Fail open on errors (don't break legitimate work)\n *\n * CUSTOMIZATION REQUIRED:\n * This template includes basic examples. Add your own security patterns\n * based on your threat model and environment.\n */\n\nimport { appendFileSync, mkdirSync, existsSync } from 'fs';\nimport { dirname } from 'path';\n\n// ============================================================================\n// ATTACK PATTERNS - CUSTOMIZE THESE FOR YOUR ENVIRONMENT\n// ============================================================================\n\n// Example: Reverse Shell Patterns (BLOCK - rarely legitimate)\nconst REVERSE_SHELL_PATTERNS: RegExp[] = [\n /\\/dev\\/(tcp|udp)\\/[0-9]/, // Bash TCP/UDP device\n /bash\\s+-i\\s+>&?\\s*\\/dev\\//, // Interactive bash redirect\n // Add your own reverse shell patterns here\n];\n\n// Example: Instruction Override (BLOCK - prompt injection)\nconst INSTRUCTION_OVERRIDE_PATTERNS: RegExp[] = [\n /ignore\\s+(all\\s+)?previous\\s+instructions?/i,\n /disregard\\s+(all\\s+)?(prior|previous)\\s+(instructions?|rules?)/i,\n // Add your own prompt injection patterns here\n];\n\n// Example: Catastrophic Deletion Patterns (BLOCK - filesystem destruction)\nconst CATASTROPHIC_DELETION_PATTERNS: RegExp[] = [\n // Trailing tilde bypass\n /\\s+~\\/?(\\s*$|\\s+)/, // Space then ~/ at end\n /\\brm\\s+(-[rfivd]+\\s+)*\\S+\\s+~\\/?/, // rm something ~/\n\n // Relative path recursive deletion\n /\\brm\\s+(-[rfivd]+\\s+)*\\.\\/\\s*$/, // rm -rf ./\n /\\brm\\s+(-[rfivd]+\\s+)*\\.\\.\\/\\s*$/, // rm -rf ../\n\n // Add your own dangerous deletion patterns here\n];\n\n// Example: Dangerous File Operations (BLOCK - data destruction)\nconst DANGEROUS_FILE_OPS_PATTERNS: RegExp[] = [\n /\\bchmod\\s+(-R\\s+)?0{3,}/, // chmod 000\n // Add your own dangerous file operation patterns here\n];\n\n// OPTIONAL: Operations that require confirmation instead of blocking\nconst DANGEROUS_GIT_PATTERNS: RegExp[] = [\n /\\bgit\\s+push\\s+.*(-f\\b|--force)/i, // git push --force\n /\\bgit\\s+reset\\s+--hard/i, // git reset --hard\n // Add your own git safety patterns here\n];\n\n// Combined patterns for fast iteration\nconst ALL_BLOCK_PATTERNS: { category: string; patterns: RegExp[] }[] = [\n { category: 'reverse_shell', patterns: REVERSE_SHELL_PATTERNS },\n { category: 'instruction_override', patterns: INSTRUCTION_OVERRIDE_PATTERNS },\n { category: 'catastrophic_deletion', patterns: CATASTROPHIC_DELETION_PATTERNS },\n { category: 'dangerous_file_ops', patterns: DANGEROUS_FILE_OPS_PATTERNS },\n];\n\nconst CONFIRM_PATTERNS: { category: string; patterns: RegExp[] }[] = [\n { category: 'dangerous_git', patterns: DANGEROUS_GIT_PATTERNS },\n];\n\n// ============================================================================\n// TYPES\n// ============================================================================\n\ninterface HookInput {\n session_id: string;\n tool_name: string;\n tool_input: Record<string, unknown> | string;\n}\n\ninterface HookOutput {\n permissionDecision: 'allow' | 'deny';\n additionalContext?: string;\n feedback?: string;\n}\n\n// ============================================================================\n// DETECTION LOGIC\n// ============================================================================\n\ninterface DetectionResult {\n blocked: boolean;\n requiresConfirmation?: boolean;\n category?: string;\n pattern?: string;\n}\n\nfunction detectAttack(content: string): DetectionResult {\n // First check for hard blocks\n for (const { category, patterns } of ALL_BLOCK_PATTERNS) {\n for (const pattern of patterns) {\n if (pattern.test(content)) {\n return { blocked: true, category, pattern: pattern.source };\n }\n }\n }\n\n // Then check for confirmation-required patterns\n for (const { category, patterns } of CONFIRM_PATTERNS) {\n for (const pattern of patterns) {\n if (pattern.test(content)) {\n return { blocked: false, requiresConfirmation: true, category, pattern: pattern.source };\n }\n }\n }\n\n return { blocked: false };\n}\n\n// ============================================================================\n// ASYNC LOGGING (fire-and-forget on block only)\n// ============================================================================\n\nfunction logSecurityEvent(event: Record<string, unknown>): void {\n const logPath = `${process.env.PAI_DIR || '~/.claude'}/history/security/security-events.jsonl`;\n const entry = JSON.stringify({ timestamp: new Date().toISOString(), ...event }) + '\\n';\n\n try {\n const dir = dirname(logPath);\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n appendFileSync(logPath, entry);\n } catch {\n // Silently fail - logging should never break the hook\n }\n}\n\n// ============================================================================\n// MAIN HOOK LOGIC\n// ============================================================================\n\nasync function main(): Promise<void> {\n let input: HookInput;\n\n try {\n // Read stdin\n const chunks: Buffer[] = [];\n const timeoutPromise = new Promise<Buffer[]>((_, reject) =>\n setTimeout(() => reject(new Error('timeout')), 100)\n );\n const readPromise = (async () => {\n for await (const chunk of process.stdin) {\n chunks.push(chunk);\n }\n return chunks;\n })();\n\n let text = '';\n try {\n const result = await Promise.race([readPromise, timeoutPromise]);\n text = Buffer.concat(result).toString('utf-8');\n } catch {\n console.log(JSON.stringify({ permissionDecision: 'allow' }));\n return;\n }\n\n if (!text.trim()) {\n console.log(JSON.stringify({ permissionDecision: 'allow' }));\n return;\n }\n\n input = JSON.parse(text);\n } catch {\n // Parse error or timeout - fail open\n console.log(JSON.stringify({ permissionDecision: 'allow' }));\n return;\n }\n\n // Only validate Bash commands\n if (input.tool_name !== 'Bash') {\n console.log(JSON.stringify({ permissionDecision: 'allow' }));\n return;\n }\n\n // Extract command string\n const command = typeof input.tool_input === 'string'\n ? input.tool_input\n : (input.tool_input?.command as string) || '';\n\n if (!command) {\n console.log(JSON.stringify({ permissionDecision: 'allow' }));\n return;\n }\n\n // Check all patterns\n const result = detectAttack(command);\n\n if (result.blocked) {\n // Log and block\n logSecurityEvent({\n type: 'attack_blocked',\n category: result.category,\n pattern: result.pattern,\n command: command.slice(0, 200), // Truncate for log\n session_id: input.session_id,\n });\n\n const output: HookOutput = {\n permissionDecision: 'deny',\n additionalContext: `SECURITY: Blocked ${result.category} pattern`,\n feedback: `This command matched a security pattern (${result.category}). If this is legitimate, please rephrase the command.`,\n };\n\n console.log(JSON.stringify(output));\n process.exit(2); // Exit 2 = blocking error\n }\n\n if (result.requiresConfirmation) {\n // Log warning and require confirmation\n logSecurityEvent({\n type: 'confirmation_required',\n category: result.category,\n pattern: result.pattern,\n command: command.slice(0, 200),\n session_id: input.session_id,\n });\n\n const output: HookOutput = {\n permissionDecision: 'deny',\n additionalContext: `DANGEROUS: ${result.category} operation requires confirmation`,\n feedback: `This is a dangerous operation (${command.slice(0, 50)}...). This can cause data loss. If you're sure, explicitly confirm this command.`,\n };\n\n console.log(JSON.stringify(output));\n process.exit(2); // Exit 2 = requires user confirmation\n }\n\n // Allow - no logging, immediate exit\n console.log(JSON.stringify({ permissionDecision: 'allow' }));\n}\n\n// ============================================================================\n// RUN\n// ============================================================================\n\nmain().catch(() => {\n // On any error, fail open\n console.log(JSON.stringify({ permissionDecision: 'allow' }));\n});\n"],
5
5
  "mappings": "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",
6
6
  "names": ["result"]
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["../../src/hooks/ts/session-start/session-commands.ts"],
3
+ "sources": ["../../../src/hooks/ts/session-start/session-commands.ts"],
4
4
  "sourcesContent": ["#!/usr/bin/env node\n\n/**\n * session-commands.ts\n *\n * Tiny SessionStart hook that injects critical session commands inline.\n * Stays under 2KB to avoid persisted-output truncation.\n *\n * This runs BEFORE load-core-context so these commands are always available\n * even when the full CORE skill gets truncated.\n */\n\nconst commands = `<system-reminder>\nSESSION COMMANDS (always available):\n\n- \"go\" / \"continue\" / \"weiter\" \u2192 Read Notes/TODO.md, find ## Continue section, resume work\n- \"pause session\" \u2192 Save state to TODO.md, update session note, stop (no compact)\n- \"end session\" \u2192 Finalize note, commit if needed, start fresh next time\n- \"cpp\" \u2192 Release workflow: version bump \u2192 build \u2192 npm publish \u2192 git add ALL \u2192 ONE commit \u2192 git push\n\nWhen user says \"go\" with no other context, ALWAYS check TODO.md first.\n\nIMPORTANT: Full PAI context may be truncated due to size. For complete instructions (response format, workflows, principles), READ: ~/.claude/Skills/CORE/SKILL.md\n</system-reminder>`;\n\nconsole.log(commands);\n"],
5
5
  "mappings": ";;;AAYA,IAAM,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAajB,QAAQ,IAAI,QAAQ;",
6
6
  "names": []
@@ -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,292 @@ ${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
+ /^-{3,}$/
461
+ ];
462
+ var BLANK_LINE = /^>?\s*$/;
463
+ function isBoilerplateOnly(lines) {
464
+ return lines.every((line) => {
465
+ const t = line.trim();
466
+ return BLANK_LINE.test(t) || 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 trimBlankEdges(collapseBlankRuns(kept)).join("\n");
477
+ }
478
+ function trimBlankEdges(lines) {
479
+ let start = 0;
480
+ let end = lines.length;
481
+ while (start < end && BLANK_LINE.test(lines[start].trim())) start += 1;
482
+ while (end > start && BLANK_LINE.test(lines[end - 1].trim())) end -= 1;
483
+ return lines.slice(start, end);
484
+ }
485
+ function collapseBlankRuns(lines) {
486
+ const out = [];
487
+ let lastWasBlank = false;
488
+ for (const line of lines) {
489
+ const isBlank = BLANK_LINE.test(line.trim());
490
+ if (isBlank && lastWasBlank) continue;
491
+ out.push(line);
492
+ lastWasBlank = isBlank;
493
+ }
494
+ return out;
495
+ }
496
+ function locateContinue(content) {
497
+ const lines = content.split("\n");
498
+ const startIdx = lines.findIndex((l) => l.trim() === CONTINUE_HEADING);
499
+ if (startIdx === -1) return null;
500
+ let meta = null;
501
+ let markerIdx = -1;
502
+ for (let i = startIdx + 1; i < Math.min(startIdx + 6, lines.length); i++) {
503
+ const parsed = parseMarker(lines[i]);
504
+ if (parsed) {
505
+ meta = parsed;
506
+ markerIdx = i;
507
+ break;
508
+ }
509
+ if (lines[i].trim() !== "") break;
510
+ }
511
+ let endIdx = lines.length;
512
+ if (markerIdx !== -1) {
513
+ const closeIdx = lines.findIndex(
514
+ (l, i) => i > markerIdx && l.trim() === MARKER_CLOSE
515
+ );
516
+ if (closeIdx !== -1) {
517
+ endIdx = closeIdx + 1;
518
+ } else {
519
+ endIdx = heuristicEnd(lines, startIdx);
520
+ }
521
+ } else {
522
+ endIdx = heuristicEnd(lines, startIdx);
523
+ }
524
+ let trailingEnd = endIdx;
525
+ while (trailingEnd < lines.length && lines[trailingEnd].trim() === "") {
526
+ trailingEnd += 1;
527
+ }
528
+ if (trailingEnd < lines.length && lines[trailingEnd].trim() === "---") {
529
+ trailingEnd += 1;
530
+ } else {
531
+ trailingEnd = endIdx;
532
+ }
533
+ return {
534
+ startIdx,
535
+ endIdx: trailingEnd,
536
+ meta,
537
+ lines: lines.slice(startIdx, trailingEnd)
538
+ };
539
+ }
540
+ function heuristicEnd(lines, startIdx) {
541
+ for (let i = startIdx + 1; i < lines.length; i++) {
542
+ const trimmed = lines[i].trim();
543
+ if (trimmed === "---" || /^##(?!#)/.test(trimmed) && trimmed !== CONTINUE_HEADING) {
544
+ return i;
545
+ }
546
+ }
547
+ return lines.length;
548
+ }
549
+ function stripContinue(content) {
550
+ const found = locateContinue(content);
551
+ if (!found) return content;
552
+ const lines = content.split("\n");
553
+ const before = lines.slice(0, found.startIdx);
554
+ const after = lines.slice(found.endIdx);
555
+ while (after.length > 0 && after[0].trim() === "") after.shift();
556
+ return [...before, ...after].join("\n");
557
+ }
558
+ function escapeAttr(value) {
559
+ return value.replace(/"/g, "'");
560
+ }
561
+ function buildContinueBlock(opts) {
562
+ const ts = opts.timestamp ?? (/* @__PURE__ */ new Date()).toISOString();
563
+ const attrs = [
564
+ `authored="${opts.authored}"`,
565
+ `session="${escapeAttr(opts.sessionLine)}"`,
566
+ opts.sessionId ? `session-id="${escapeAttr(opts.sessionId)}"` : null,
567
+ `ts="${ts}"`
568
+ ].filter(Boolean).join(" ");
569
+ const header = [
570
+ `> **Last session:** ${opts.sessionLine}`,
571
+ `> **Paused at:** ${ts}`,
572
+ ">",
573
+ `> Working directory: ${opts.cwd}`
574
+ ];
575
+ if (opts.sessionId) {
576
+ header.push(">", `> Resume with: \`claude --resume ${opts.sessionId}\``);
577
+ }
578
+ const body = (opts.body ?? "").trim();
579
+ const parts = [
580
+ CONTINUE_HEADING,
581
+ "",
582
+ `${MARKER_OPEN} ${attrs} -->`,
583
+ "",
584
+ ...header
585
+ ];
586
+ if (body) {
587
+ parts.push("", body);
588
+ } else {
589
+ parts.push(
590
+ ">",
591
+ "> _No checkpoint body was recorded \u2014 see the latest session note._"
592
+ );
593
+ }
594
+ parts.push("", MARKER_CLOSE, "", "---", "");
595
+ return parts.join("\n");
596
+ }
597
+ function isSameSession(meta, opts) {
598
+ if (meta.sessionId && opts.sessionId) {
599
+ return meta.sessionId === opts.sessionId;
600
+ }
601
+ return meta.session === opts.sessionLine;
602
+ }
603
+ function applyContinue(opts) {
604
+ const target = resolveTodoTarget(opts.rootPath, { create: !opts.dryRun });
605
+ if (!target) {
606
+ return {
607
+ action: "failed",
608
+ path: null,
609
+ block: buildContinueBlock(opts),
610
+ error: "Could not resolve or create a TODO.md target"
611
+ };
612
+ }
613
+ const existing = locateContinue(target.content);
614
+ let carriedForward = false;
615
+ let effectiveBody = opts.body;
616
+ if (opts.authored === "auto" && existing) {
617
+ if (existing.meta?.authored === "model" && isSameSession(existing.meta, opts)) {
618
+ return {
619
+ action: "preserved",
620
+ path: target.path,
621
+ block: buildContinueBlock(opts),
622
+ preservedMeta: existing.meta
623
+ };
624
+ }
625
+ if (existing.meta && !(opts.body ?? "").trim() && !isBoilerplateOnly(existing.lines)) {
626
+ return {
627
+ action: "preserved",
628
+ path: target.path,
629
+ block: buildContinueBlock(opts),
630
+ preservedMeta: existing.meta ?? void 0
631
+ };
632
+ }
633
+ if (!existing.meta && !isBoilerplateOnly(existing.lines)) {
634
+ const salvaged = extractSectionContent(existing.lines);
635
+ if (salvaged) {
636
+ effectiveBody = [
637
+ "_Carried forward from the previous checkpoint (author unknown \u2014 this",
638
+ "block predates checkpoint authorship markers):_",
639
+ "",
640
+ salvaged
641
+ ].join("\n");
642
+ carriedForward = true;
643
+ }
644
+ }
645
+ }
646
+ const block = buildContinueBlock({ ...opts, body: effectiveBody });
647
+ if (opts.dryRun) {
648
+ return { action: "written", path: target.path, block, carriedForward };
649
+ }
650
+ const newContent = block + stripContinue(target.content).trimStart();
651
+ const tmpPath = `${target.path}.continue.tmp`;
652
+ try {
653
+ writeFileSync2(tmpPath, newContent, "utf8");
654
+ renameSync3(tmpPath, target.path);
655
+ } catch (err) {
656
+ try {
657
+ if (existsSync5(tmpPath)) renameSync3(tmpPath, `${tmpPath}.dead`);
658
+ } catch {
659
+ }
660
+ return {
661
+ action: "failed",
662
+ path: target.path,
663
+ block,
664
+ error: String(err)
665
+ };
666
+ }
667
+ return { action: "written", path: target.path, block, carriedForward };
668
+ }
669
+
670
+ // src/hooks/ts/lib/project-utils/todo.ts
392
671
  function ensureTodoMd(cwd) {
393
672
  const todoPath = findTodoPath(cwd);
394
- if (!existsSync5(todoPath)) {
395
- const parentDir = join5(todoPath, "..");
396
- if (!existsSync5(parentDir)) mkdirSync3(parentDir, { recursive: true });
673
+ if (!existsSync6(todoPath)) {
674
+ const parentDir = join6(todoPath, "..");
675
+ if (!existsSync6(parentDir)) mkdirSync4(parentDir, { recursive: true });
397
676
  const content = `# TODO
398
677
 
399
678
  ## Current Session
@@ -408,43 +687,52 @@ function ensureTodoMd(cwd) {
408
687
 
409
688
  *Last updated: ${(/* @__PURE__ */ new Date()).toISOString()}*
410
689
  `;
411
- writeFileSync2(todoPath, content);
690
+ writeFileSync3(todoPath, content);
412
691
  console.error(`Created TODO.md: ${todoPath}`);
413
692
  }
414
693
  return todoPath;
415
694
  }
416
695
  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;
696
+ ensureTodoMd(cwd);
697
+ const result = applyContinue({
698
+ rootPath: cwd,
699
+ authored: "auto",
700
+ // The hooks identify a session by its note filename; `pai pause` resolves
701
+ // the same string from the registry (and falls back to this filename when
702
+ // the registry has no session row). They must agree, because this string
703
+ // is the key that decides whether an authored checkpoint belongs to the
704
+ // current session and must be preserved.
705
+ sessionLine: noteFilename.replace(/\.md$/, ""),
706
+ cwd,
707
+ body: state?.trim() || void 0
708
+ });
709
+ if (result.action === "preserved") {
710
+ console.error(
711
+ "TODO.md ## Continue left intact \u2014 authored checkpoint for this session"
712
+ );
713
+ return;
438
714
  }
439
- content = content.replace(/(\n---\s*)*(\n\*Last updated:.*\*\s*)+$/g, "");
440
- content = content.trimEnd() + `
715
+ if (result.action === "failed") {
716
+ console.error(`TODO.md ## Continue update failed: ${result.error}`);
717
+ return;
718
+ }
719
+ try {
720
+ const todoPath = result.path;
721
+ const now = (/* @__PURE__ */ new Date()).toISOString();
722
+ let content = readFileSync5(todoPath, "utf-8");
723
+ content = content.replace(/(\n---\s*)*(\n\*Last updated:.*\*\s*)+$/g, "");
724
+ content = content.trimEnd() + `
441
725
 
442
726
  ---
443
727
 
444
728
  *Last updated: ${now}*
445
729
  `;
446
- writeFileSync2(todoPath, content);
447
- console.error("TODO.md ## Continue section updated");
730
+ writeFileSync3(todoPath, content);
731
+ } catch {
732
+ }
733
+ console.error(
734
+ result.carriedForward ? "TODO.md ## Continue section updated (previous content carried forward)" : "TODO.md ## Continue section updated"
735
+ );
448
736
  }
449
737
 
450
738
  // src/hooks/ts/stop/stop-hook.ts
@@ -458,7 +746,7 @@ var AUTO_SAVE_INTERVAL = (() => {
458
746
  }
459
747
  return 15;
460
748
  })();
461
- var SESSION_STATE_DIR = join6(
749
+ var SESSION_STATE_DIR = join7(
462
750
  process.env.HOME ?? "/tmp",
463
751
  ".config",
464
752
  "pai",
@@ -466,9 +754,9 @@ var SESSION_STATE_DIR = join6(
466
754
  );
467
755
  function readSessionState(sessionId) {
468
756
  try {
469
- const stateFile = join6(SESSION_STATE_DIR, `${sessionId}.json`);
470
- if (!existsSync6(stateFile)) return { humanMessageCount: 0 };
471
- const raw = readFileSync5(stateFile, "utf-8");
757
+ const stateFile = join7(SESSION_STATE_DIR, `${sessionId}.json`);
758
+ if (!existsSync7(stateFile)) return { humanMessageCount: 0 };
759
+ const raw = readFileSync6(stateFile, "utf-8");
472
760
  const parsed = JSON.parse(raw);
473
761
  return {
474
762
  humanMessageCount: typeof parsed.humanMessageCount === "number" ? parsed.humanMessageCount : 0
@@ -479,17 +767,17 @@ function readSessionState(sessionId) {
479
767
  }
480
768
  function writeSessionState(sessionId, state) {
481
769
  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");
770
+ mkdirSync5(SESSION_STATE_DIR, { recursive: true });
771
+ const stateFile = join7(SESSION_STATE_DIR, `${sessionId}.json`);
772
+ writeFileSync4(stateFile, JSON.stringify(state, null, 2), "utf-8");
485
773
  } catch (e) {
486
774
  console.error(`STOP-HOOK: Could not write session state: ${e}`);
487
775
  }
488
776
  }
489
777
  function deleteSessionState(sessionId) {
490
778
  try {
491
- const stateFile = join6(SESSION_STATE_DIR, `${sessionId}.json`);
492
- if (existsSync6(stateFile)) {
779
+ const stateFile = join7(SESSION_STATE_DIR, `${sessionId}.json`);
780
+ if (existsSync7(stateFile)) {
493
781
  unlinkSync(stateFile);
494
782
  }
495
783
  } catch {
@@ -996,7 +1284,7 @@ STOP-HOOK TRIGGERED AT ${timestamp}`);
996
1284
  }
997
1285
  let transcript;
998
1286
  try {
999
- transcript = readFileSync5(transcriptPath, "utf-8");
1287
+ transcript = readFileSync6(transcriptPath, "utf-8");
1000
1288
  debug(`Transcript loaded: ${transcript.split("\n").length} lines`);
1001
1289
  } catch (e) {
1002
1290
  console.error(`Error reading transcript: ${e}`);