hmem-mcp 7.4.0 → 8.0.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 (315) hide show
  1. package/README.md +266 -54
  2. package/dist/cli-checkpoint-agent.d.ts +27 -0
  3. package/dist/cli-checkpoint-agent.js +320 -0
  4. package/dist/cli-checkpoint-agent.js.map +1 -0
  5. package/dist/cli-checkpoint.js +165 -78
  6. package/dist/cli-checkpoint.js.map +1 -1
  7. package/dist/cli-context-inject.d.ts +7 -7
  8. package/dist/cli-context-inject.js +54 -41
  9. package/dist/cli-context-inject.js.map +1 -1
  10. package/dist/cli-doctor.d.ts +7 -0
  11. package/dist/cli-doctor.js +156 -0
  12. package/dist/cli-doctor.js.map +1 -0
  13. package/dist/cli-hook-startup.d.ts +1 -0
  14. package/dist/cli-hook-startup.js +97 -10
  15. package/dist/cli-hook-startup.js.map +1 -1
  16. package/dist/cli-init.d.ts +0 -4
  17. package/dist/cli-init.js +48 -6
  18. package/dist/cli-init.js.map +1 -1
  19. package/dist/cli-log-exchange.js +10 -2
  20. package/dist/cli-log-exchange.js.map +1 -1
  21. package/dist/cli-mcp-add-curate.d.ts +7 -0
  22. package/dist/cli-mcp-add-curate.js +40 -0
  23. package/dist/cli-mcp-add-curate.js.map +1 -0
  24. package/dist/cli-migrate.d.ts +57 -0
  25. package/dist/cli-migrate.js +394 -0
  26. package/dist/cli-migrate.js.map +1 -0
  27. package/dist/cli-session-summary.d.ts +1 -1
  28. package/dist/cli-session-summary.js +9 -54
  29. package/dist/cli-session-summary.js.map +1 -1
  30. package/dist/cli-statusline.js +15 -11
  31. package/dist/cli-statusline.js.map +1 -1
  32. package/dist/cli-sync-pull.d.ts +3 -0
  33. package/dist/cli-sync-pull.js +79 -0
  34. package/dist/cli-sync-pull.js.map +1 -0
  35. package/dist/cli-sync-push.d.ts +4 -0
  36. package/dist/cli-sync-push.js +85 -0
  37. package/dist/cli-sync-push.js.map +1 -0
  38. package/dist/cli-sync-setup.d.ts +18 -0
  39. package/dist/cli-sync-setup.js +308 -0
  40. package/dist/cli-sync-setup.js.map +1 -0
  41. package/dist/cli-sync-status.d.ts +1 -0
  42. package/dist/cli-sync-status.js +20 -0
  43. package/dist/cli-sync-status.js.map +1 -0
  44. package/dist/cli-v2-checkpoint.d.ts +38 -0
  45. package/dist/cli-v2-checkpoint.js +134 -0
  46. package/dist/cli-v2-checkpoint.js.map +1 -0
  47. package/dist/cli.js +154 -34
  48. package/dist/cli.js.map +1 -1
  49. package/dist/config/config.d.ts +29 -0
  50. package/dist/config/config.js +123 -0
  51. package/dist/config/config.js.map +1 -0
  52. package/dist/diagnostics.js +1 -6
  53. package/dist/diagnostics.js.map +1 -1
  54. package/dist/extensions/pi-hmem.js +141 -35
  55. package/dist/extensions/pi-hmem.js.map +1 -1
  56. package/dist/hmem-config.d.ts +30 -5
  57. package/dist/hmem-config.js +50 -8
  58. package/dist/hmem-config.js.map +1 -1
  59. package/dist/hmem-store.d.ts +60 -2
  60. package/dist/hmem-store.js +205 -26
  61. package/dist/hmem-store.js.map +1 -1
  62. package/dist/hooks/binding-resolver.d.ts +13 -0
  63. package/dist/hooks/binding-resolver.js +42 -0
  64. package/dist/hooks/binding-resolver.js.map +1 -0
  65. package/dist/hooks/brief.d.ts +2 -0
  66. package/dist/hooks/brief.js +26 -0
  67. package/dist/hooks/brief.js.map +1 -0
  68. package/dist/hooks/dispatch.d.ts +5 -0
  69. package/dist/hooks/dispatch.js +119 -0
  70. package/dist/hooks/dispatch.js.map +1 -0
  71. package/dist/hooks/git-probe.d.ts +4 -0
  72. package/dist/hooks/git-probe.js +46 -0
  73. package/dist/hooks/git-probe.js.map +1 -0
  74. package/dist/hooks/harness/claude-code.d.ts +10 -0
  75. package/dist/hooks/harness/claude-code.js +133 -0
  76. package/dist/hooks/harness/claude-code.js.map +1 -0
  77. package/dist/hooks/harness/hermes.d.ts +4 -0
  78. package/dist/hooks/harness/hermes.js +20 -0
  79. package/dist/hooks/harness/hermes.js.map +1 -0
  80. package/dist/hooks/harness/pi.d.ts +4 -0
  81. package/dist/hooks/harness/pi.js +21 -0
  82. package/dist/hooks/harness/pi.js.map +1 -0
  83. package/dist/hooks/prompt-submit.d.ts +16 -0
  84. package/dist/hooks/prompt-submit.js +11 -0
  85. package/dist/hooks/prompt-submit.js.map +1 -0
  86. package/dist/hooks/session-start.d.ts +3 -0
  87. package/dist/hooks/session-start.js +31 -0
  88. package/dist/hooks/session-start.js.map +1 -0
  89. package/dist/hooks/stable-context.d.ts +11 -0
  90. package/dist/hooks/stable-context.js +117 -0
  91. package/dist/hooks/stable-context.js.map +1 -0
  92. package/dist/hooks/stop-log.d.ts +4 -0
  93. package/dist/hooks/stop-log.js +12 -0
  94. package/dist/hooks/stop-log.js.map +1 -0
  95. package/dist/hooks/summarizer-pick.d.ts +8 -0
  96. package/dist/hooks/summarizer-pick.js +18 -0
  97. package/dist/hooks/summarizer-pick.js.map +1 -0
  98. package/dist/hooks/transcript.d.ts +6 -0
  99. package/dist/hooks/transcript.js +72 -0
  100. package/dist/hooks/transcript.js.map +1 -0
  101. package/dist/hooks/types.d.ts +56 -0
  102. package/dist/hooks/types.js +2 -0
  103. package/dist/hooks/types.js.map +1 -0
  104. package/dist/mcp-curate-server.js +50 -4
  105. package/dist/mcp-curate-server.js.map +1 -1
  106. package/dist/mcp-server.js +121 -58
  107. package/dist/mcp-server.js.map +1 -1
  108. package/dist/mcp-shared.js +119 -29
  109. package/dist/mcp-shared.js.map +1 -1
  110. package/dist/memory-search.d.ts +5 -2
  111. package/dist/memory-search.js +5 -2
  112. package/dist/memory-search.js.map +1 -1
  113. package/dist/migrate/o-decompose.d.ts +14 -0
  114. package/dist/migrate/o-decompose.js +112 -0
  115. package/dist/migrate/o-decompose.js.map +1 -0
  116. package/dist/migrate/tree.d.ts +16 -0
  117. package/dist/migrate/tree.js +102 -0
  118. package/dist/migrate/tree.js.map +1 -0
  119. package/dist/migrate/v1-reader.d.ts +38 -0
  120. package/dist/migrate/v1-reader.js +34 -0
  121. package/dist/migrate/v1-reader.js.map +1 -0
  122. package/dist/migrate/v1-to-v2.d.ts +25 -0
  123. package/dist/migrate/v1-to-v2.js +127 -0
  124. package/dist/migrate/v1-to-v2.js.map +1 -0
  125. package/dist/session-state.d.ts +3 -0
  126. package/dist/session-state.js +7 -5
  127. package/dist/session-state.js.map +1 -1
  128. package/dist/store/db.d.ts +25 -0
  129. package/dist/store/db.js +159 -0
  130. package/dist/store/db.js.map +1 -0
  131. package/dist/store/entries.d.ts +48 -0
  132. package/dist/store/entries.js +145 -0
  133. package/dist/store/entries.js.map +1 -0
  134. package/dist/store/events-fts.d.ts +18 -0
  135. package/dist/store/events-fts.js +18 -0
  136. package/dist/store/events-fts.js.map +1 -0
  137. package/dist/store/events.d.ts +30 -0
  138. package/dist/store/events.js +48 -0
  139. package/dist/store/events.js.map +1 -0
  140. package/dist/store/fts.d.ts +24 -0
  141. package/dist/store/fts.js +35 -0
  142. package/dist/store/fts.js.map +1 -0
  143. package/dist/store/links.d.ts +24 -0
  144. package/dist/store/links.js +29 -0
  145. package/dist/store/links.js.map +1 -0
  146. package/dist/store/schema-engine.d.ts +20 -0
  147. package/dist/store/schema-engine.js +41 -0
  148. package/dist/store/schema-engine.js.map +1 -0
  149. package/dist/store/schema-v2.sql +149 -0
  150. package/dist/store/sessions.d.ts +38 -0
  151. package/dist/store/sessions.js +98 -0
  152. package/dist/store/sessions.js.map +1 -0
  153. package/dist/store/types.d.ts +74 -0
  154. package/dist/store/types.js +3 -0
  155. package/dist/store/types.js.map +1 -0
  156. package/dist/store/ulid.d.ts +1 -0
  157. package/dist/store/ulid.js +10 -0
  158. package/dist/store/ulid.js.map +1 -0
  159. package/dist/summarize/anthropic.d.ts +8 -0
  160. package/dist/summarize/anthropic.js +38 -0
  161. package/dist/summarize/anthropic.js.map +1 -0
  162. package/dist/summarize/loop.d.ts +40 -0
  163. package/dist/summarize/loop.js +64 -0
  164. package/dist/summarize/loop.js.map +1 -0
  165. package/dist/sync/api.d.ts +49 -0
  166. package/dist/sync/api.js +99 -0
  167. package/dist/sync/api.js.map +1 -0
  168. package/dist/sync/config.d.ts +16 -0
  169. package/dist/sync/config.js +27 -0
  170. package/dist/sync/config.js.map +1 -0
  171. package/dist/sync/conflict.d.ts +24 -0
  172. package/dist/sync/conflict.js +125 -0
  173. package/dist/sync/conflict.js.map +1 -0
  174. package/dist/sync/crypto.d.ts +53 -0
  175. package/dist/sync/crypto.js +140 -0
  176. package/dist/sync/crypto.js.map +1 -0
  177. package/dist/sync/passphrase.d.ts +1 -0
  178. package/dist/sync/passphrase.js +17 -0
  179. package/dist/sync/passphrase.js.map +1 -0
  180. package/dist/sync/v2/dispatch.d.ts +6 -0
  181. package/dist/sync/v2/dispatch.js +24 -0
  182. package/dist/sync/v2/dispatch.js.map +1 -0
  183. package/dist/sync/v2/envelope.d.ts +13 -0
  184. package/dist/sync/v2/envelope.js +36 -0
  185. package/dist/sync/v2/envelope.js.map +1 -0
  186. package/dist/sync/v2/export.d.ts +10 -0
  187. package/dist/sync/v2/export.js +32 -0
  188. package/dist/sync/v2/export.js.map +1 -0
  189. package/dist/sync/v2/import.d.ts +10 -0
  190. package/dist/sync/v2/import.js +130 -0
  191. package/dist/sync/v2/import.js.map +1 -0
  192. package/dist/sync/v2/queue.d.ts +18 -0
  193. package/dist/sync/v2/queue.js +59 -0
  194. package/dist/sync/v2/queue.js.map +1 -0
  195. package/dist/sync/v2/reconcile.d.ts +3 -0
  196. package/dist/sync/v2/reconcile.js +18 -0
  197. package/dist/sync/v2/reconcile.js.map +1 -0
  198. package/dist/sync/v2/staging.d.ts +27 -0
  199. package/dist/sync/v2/staging.js +46 -0
  200. package/dist/sync/v2/staging.js.map +1 -0
  201. package/dist/sync/v2/sync.d.ts +18 -0
  202. package/dist/sync/v2/sync.js +63 -0
  203. package/dist/sync/v2/sync.js.map +1 -0
  204. package/dist/sync/v2/transport.d.ts +43 -0
  205. package/dist/sync/v2/transport.js +20 -0
  206. package/dist/sync/v2/transport.js.map +1 -0
  207. package/dist/sync-bridge.d.ts +2 -0
  208. package/dist/sync-bridge.js +134 -0
  209. package/dist/sync-bridge.js.map +1 -0
  210. package/dist/tools/analyze-v1.d.ts +1 -0
  211. package/dist/tools/analyze-v1.js +142 -0
  212. package/dist/tools/analyze-v1.js.map +1 -0
  213. package/dist/util/iso.d.ts +8 -0
  214. package/dist/util/iso.js +15 -0
  215. package/dist/util/iso.js.map +1 -0
  216. package/dist/utils.d.ts +7 -0
  217. package/dist/utils.js +14 -0
  218. package/dist/utils.js.map +1 -0
  219. package/dist/v2/addressing.d.ts +9 -0
  220. package/dist/v2/addressing.js +55 -0
  221. package/dist/v2/addressing.js.map +1 -0
  222. package/dist/v2/checkpoint-agent.d.ts +75 -0
  223. package/dist/v2/checkpoint-agent.js +231 -0
  224. package/dist/v2/checkpoint-agent.js.map +1 -0
  225. package/dist/v2/checkpoint-select.d.ts +60 -0
  226. package/dist/v2/checkpoint-select.js +106 -0
  227. package/dist/v2/checkpoint-select.js.map +1 -0
  228. package/dist/v2/checkpoint.d.ts +62 -0
  229. package/dist/v2/checkpoint.js +103 -0
  230. package/dist/v2/checkpoint.js.map +1 -0
  231. package/dist/v2/curation.d.ts +23 -0
  232. package/dist/v2/curation.js +118 -0
  233. package/dist/v2/curation.js.map +1 -0
  234. package/dist/v2/projects.d.ts +29 -0
  235. package/dist/v2/projects.js +60 -0
  236. package/dist/v2/projects.js.map +1 -0
  237. package/dist/v2/read-render.d.ts +31 -0
  238. package/dist/v2/read-render.js +150 -0
  239. package/dist/v2/read-render.js.map +1 -0
  240. package/dist/v2/related.d.ts +8 -0
  241. package/dist/v2/related.js +35 -0
  242. package/dist/v2/related.js.map +1 -0
  243. package/dist/v2/search.d.ts +6 -0
  244. package/dist/v2/search.js +24 -0
  245. package/dist/v2/search.js.map +1 -0
  246. package/dist/v2/session-id.d.ts +7 -0
  247. package/dist/v2/session-id.js +14 -0
  248. package/dist/v2/session-id.js.map +1 -0
  249. package/dist/v2/sessions-tools.d.ts +11 -0
  250. package/dist/v2/sessions-tools.js +30 -0
  251. package/dist/v2/sessions-tools.js.map +1 -0
  252. package/dist/v2/store-facade.d.ts +23 -0
  253. package/dist/v2/store-facade.js +33 -0
  254. package/dist/v2/store-facade.js.map +1 -0
  255. package/dist/v2/summarize-adapter.d.ts +11 -0
  256. package/dist/v2/summarize-adapter.js +19 -0
  257. package/dist/v2/summarize-adapter.js.map +1 -0
  258. package/dist/v2/writes.d.ts +30 -0
  259. package/dist/v2/writes.js +135 -0
  260. package/dist/v2/writes.js.map +1 -0
  261. package/hmem.config.example.json +92 -10
  262. package/opencode-plugin/hmem.js +5 -2
  263. package/package.json +7 -3
  264. package/plugins/hermes-hmem/README.md +56 -0
  265. package/plugins/hermes-hmem/__init__.py +187 -0
  266. package/plugins/hermes-hmem/plugin.yaml +8 -0
  267. package/server.json +2 -2
  268. package/skills/hmem-activate/SKILL.md +1 -1
  269. package/skills/hmem-config/SKILL.md +70 -5
  270. package/skills/hmem-curate/SKILL.md +13 -6
  271. package/skills/hmem-dispatch/SKILL.md +1 -1
  272. package/skills/hmem-migrate-o/SKILL.md +3 -8
  273. package/skills/hmem-new-error/SKILL.md +1 -9
  274. package/skills/hmem-new-project/SKILL.md +1 -9
  275. package/skills/hmem-new-rule/SKILL.md +93 -0
  276. package/skills/hmem-read/SKILL.md +16 -8
  277. package/skills/hmem-release/SKILL.md +108 -9
  278. package/skills/hmem-search/SKILL.md +48 -25
  279. package/skills/hmem-session-start/SKILL.md +147 -46
  280. package/skills/hmem-setup/SKILL.md +3 -3
  281. package/skills/hmem-sync-setup/SKILL.md +1 -5
  282. package/skills/hmem-update/SKILL.md +345 -19
  283. package/skills/hmem-wipe/SKILL.md +41 -7
  284. package/skills/hmem-write/SKILL.md +1 -12
  285. package/skills/hmem-activate-workspace/evals/evals.json +0 -29
  286. package/skills/hmem-activate-workspace/iteration-1/benchmark.json +0 -370
  287. package/skills/hmem-activate-workspace/iteration-1/benchmark.md +0 -13
  288. package/skills/hmem-activate-workspace/iteration-1/eval-1-simple-switch/eval_metadata.json +0 -23
  289. package/skills/hmem-activate-workspace/iteration-1/eval-1-simple-switch/with_skill/grading.json +0 -55
  290. package/skills/hmem-activate-workspace/iteration-1/eval-1-simple-switch/with_skill/outputs/response.md +0 -121
  291. package/skills/hmem-activate-workspace/iteration-1/eval-1-simple-switch/with_skill/run-1/grading.json +0 -55
  292. package/skills/hmem-activate-workspace/iteration-1/eval-1-simple-switch/without_skill/grading.json +0 -44
  293. package/skills/hmem-activate-workspace/iteration-1/eval-1-simple-switch/without_skill/outputs/response.md +0 -78
  294. package/skills/hmem-activate-workspace/iteration-1/eval-1-simple-switch/without_skill/run-1/grading.json +0 -44
  295. package/skills/hmem-activate-workspace/iteration-1/eval-2-misrouting-fix/eval_metadata.json +0 -23
  296. package/skills/hmem-activate-workspace/iteration-1/eval-2-misrouting-fix/with_skill/grading.json +0 -49
  297. package/skills/hmem-activate-workspace/iteration-1/eval-2-misrouting-fix/with_skill/outputs/response.md +0 -98
  298. package/skills/hmem-activate-workspace/iteration-1/eval-2-misrouting-fix/with_skill/run-1/grading.json +0 -49
  299. package/skills/hmem-activate-workspace/iteration-1/eval-2-misrouting-fix/without_skill/grading.json +0 -44
  300. package/skills/hmem-activate-workspace/iteration-1/eval-2-misrouting-fix/without_skill/outputs/response.md +0 -57
  301. package/skills/hmem-activate-workspace/iteration-1/eval-2-misrouting-fix/without_skill/run-1/grading.json +0 -44
  302. package/skills/hmem-activate-workspace/iteration-1/eval-3-temporary-load/eval_metadata.json +0 -23
  303. package/skills/hmem-activate-workspace/iteration-1/eval-3-temporary-load/with_skill/grading.json +0 -48
  304. package/skills/hmem-activate-workspace/iteration-1/eval-3-temporary-load/with_skill/outputs/response.md +0 -69
  305. package/skills/hmem-activate-workspace/iteration-1/eval-3-temporary-load/with_skill/run-1/grading.json +0 -48
  306. package/skills/hmem-activate-workspace/iteration-1/eval-3-temporary-load/without_skill/grading.json +0 -44
  307. package/skills/hmem-activate-workspace/iteration-1/eval-3-temporary-load/without_skill/outputs/response.md +0 -59
  308. package/skills/hmem-activate-workspace/iteration-1/eval-3-temporary-load/without_skill/run-1/grading.json +0 -44
  309. package/skills/hmem-activate-workspace/iteration-1/eval-4-wrong-project/eval_metadata.json +0 -23
  310. package/skills/hmem-activate-workspace/iteration-1/eval-4-wrong-project/with_skill/grading.json +0 -55
  311. package/skills/hmem-activate-workspace/iteration-1/eval-4-wrong-project/with_skill/outputs/response.md +0 -182
  312. package/skills/hmem-activate-workspace/iteration-1/eval-4-wrong-project/with_skill/run-1/grading.json +0 -55
  313. package/skills/hmem-activate-workspace/iteration-1/eval-4-wrong-project/without_skill/grading.json +0 -44
  314. package/skills/hmem-activate-workspace/iteration-1/eval-4-wrong-project/without_skill/outputs/response.md +0 -43
  315. package/skills/hmem-activate-workspace/iteration-1/eval-4-wrong-project/without_skill/run-1/grading.json +0 -44
@@ -0,0 +1,394 @@
1
+ /**
2
+ * Script: cli-migrate.ts
3
+ * Purpose: User-facing `hmem migrate` — safely upgrade a v1 (.hmem) store to v2.
4
+ * Author: DEVELOPER
5
+ * Created: 2026-05-27
6
+ *
7
+ * Safety contract (see docs/.../2026-05-26-v2-hmem-migrate-command-plan.md §2):
8
+ * Until the single atomic rename in the swap step, LIVE is byte-for-byte the
9
+ * original. A timestamped backup (live + -wal/-shm sidecars, copied verbatim)
10
+ * is the safety net for the rename window and is ALWAYS kept.
11
+ *
12
+ * The migrator itself (./migrate/v1-to-v2.js) opens --in READ-ONLY and writes only
13
+ * --out, so it never mutates the live file. This module orchestrates classify →
14
+ * quiesce → confirm → backup → migrate-to-temp → validate → atomic-swap → verify.
15
+ */
16
+ import { existsSync, copyFileSync, renameSync, readFileSync, rmSync, } from 'node:fs';
17
+ import { resolve, dirname, join } from 'node:path';
18
+ import { createInterface } from 'node:readline';
19
+ import Database from 'better-sqlite3';
20
+ import { migrate } from './migrate/v1-to-v2.js';
21
+ import { resolveHmemPath } from './hmem-store.js';
22
+ // ---------------------------------------------------------------------------
23
+ // Read-only classification (NEVER openDb on a v1 file — that stamps it v2)
24
+ // ---------------------------------------------------------------------------
25
+ /**
26
+ * Classify a store path WITHOUT mutating it. Opens read-only and inspects
27
+ * sqlite_master + schema_version, mirroring openDb()'s precheck.
28
+ */
29
+ export function classify(path) {
30
+ if (!existsSync(path))
31
+ return 'missing';
32
+ let db;
33
+ try {
34
+ db = new Database(path, { readonly: true });
35
+ const tables = db.prepare("SELECT name FROM sqlite_master WHERE type='table'").all().map(t => t.name);
36
+ const hasEntries = tables.includes('entries');
37
+ const hasMemories = tables.includes('memories');
38
+ if (hasEntries) {
39
+ const row = db.prepare("SELECT value FROM schema_version WHERE key='schema_major'").get();
40
+ return row?.value === '2' ? 'v2' : 'ambiguous';
41
+ }
42
+ if (hasMemories)
43
+ return 'v1';
44
+ return 'ambiguous';
45
+ }
46
+ catch {
47
+ return 'ambiguous';
48
+ }
49
+ finally {
50
+ try {
51
+ db?.close();
52
+ }
53
+ catch { /* ignore */ }
54
+ }
55
+ }
56
+ function sourceCounts(path) {
57
+ const db = new Database(path, { readonly: true });
58
+ try {
59
+ const m = db.prepare('SELECT COUNT(*) c FROM memories').get().c;
60
+ const n = db.prepare('SELECT COUNT(*) c FROM memory_nodes').get().c;
61
+ return { memories: m, memoryNodes: n };
62
+ }
63
+ finally {
64
+ db.close();
65
+ }
66
+ }
67
+ // ---------------------------------------------------------------------------
68
+ // Default validator (§2 step 6)
69
+ // ---------------------------------------------------------------------------
70
+ const defaultValidator = (tmpPath, source) => {
71
+ const db = new Database(tmpPath, { readonly: true });
72
+ try {
73
+ const integrity = db.pragma('integrity_check', { simple: true });
74
+ if (integrity !== 'ok')
75
+ throw new Error(`integrity_check failed: ${integrity}`);
76
+ const tables = db.prepare("SELECT name FROM sqlite_master WHERE type='table'").all().map(t => t.name);
77
+ if (!tables.includes('entries'))
78
+ throw new Error('migrated store is missing the `entries` table');
79
+ if (tables.includes('memories'))
80
+ throw new Error('migrated store still has the v1 `memories` table');
81
+ const entries = db.prepare('SELECT COUNT(*) c FROM entries').get().c;
82
+ if (entries <= 0)
83
+ throw new Error('migrated store has 0 entries');
84
+ const major = db.prepare("SELECT value v FROM schema_version WHERE key='schema_major'").get()?.v;
85
+ if (major !== '2')
86
+ throw new Error(`migrated store schema_major=${major ?? 'null'} (expected 2)`);
87
+ const mig = db.prepare("SELECT value FROM schema_version WHERE key='v1_migration'").get();
88
+ if (!mig)
89
+ throw new Error('migrated store missing the v1_migration marker key');
90
+ // Count sanity: entries should not exceed the v1 root count (memories rows).
91
+ // The migrator's exact invariant (non-O/non-F roots) is asserted in
92
+ // test/migrate/acceptance.test.ts; here we guard against gross loss/explosion.
93
+ if (entries > source.memories) {
94
+ throw new Error(`entries (${entries}) exceed source roots (${source.memories}) — unexpected`);
95
+ }
96
+ }
97
+ finally {
98
+ db.close();
99
+ }
100
+ };
101
+ // ---------------------------------------------------------------------------
102
+ // Real serve-lockfile probe (Layer 1, §3)
103
+ // ---------------------------------------------------------------------------
104
+ const SERVE_LOCK_NAME = '.hmem.serve.lock';
105
+ function pidAlive(pid) {
106
+ if (!Number.isInteger(pid) || pid <= 0)
107
+ return false;
108
+ try {
109
+ process.kill(pid, 0); // signal 0 = existence check, doesn't actually signal
110
+ return true;
111
+ }
112
+ catch (e) {
113
+ // EPERM means the process exists but we can't signal it → still alive.
114
+ return e?.code === 'EPERM';
115
+ }
116
+ }
117
+ /** Default lock probe: read <dir>/.hmem.serve.lock and report PID liveness. */
118
+ export const defaultLockProbe = (dir) => {
119
+ const lockPath = join(dir, SERVE_LOCK_NAME);
120
+ if (!existsSync(lockPath))
121
+ return null;
122
+ try {
123
+ const data = JSON.parse(readFileSync(lockPath, 'utf8'));
124
+ const pid = typeof data.pid === 'number' ? data.pid : -1;
125
+ return { pid, alive: pidAlive(pid) };
126
+ }
127
+ catch {
128
+ // Unparseable lockfile → treat as stale (don't block on garbage).
129
+ return { pid: -1, alive: false };
130
+ }
131
+ };
132
+ // ---------------------------------------------------------------------------
133
+ // Core (testable): migratePlan
134
+ // ---------------------------------------------------------------------------
135
+ function utcStamp() {
136
+ return new Date().toISOString().replace(/[-:]/g, '').replace(/\.\d+Z$/, 'Z');
137
+ }
138
+ export async function migratePlan(opts) {
139
+ const log = opts.log ?? ((m) => console.log(m));
140
+ const validate = opts.validate ?? defaultValidator;
141
+ const probeLock = opts.probeLock ?? defaultLockProbe;
142
+ const LIVE = resolve(opts.in);
143
+ const dir = dirname(LIVE);
144
+ // --- Step 1: resolve + classify (read-only) ---
145
+ const klass = classify(LIVE);
146
+ if (klass === 'missing') {
147
+ return { action: 'noop-missing', exitCode: 1,
148
+ message: `No hmem store at ${LIVE}. Nothing to migrate.` };
149
+ }
150
+ if (klass === 'v2') {
151
+ return { action: 'noop-already-v2', exitCode: 0,
152
+ message: `Already v2 (${LIVE}). Nothing to do.` };
153
+ }
154
+ if (klass === 'ambiguous') {
155
+ return { action: 'refused-ambiguous', exitCode: 1,
156
+ message: `Cannot classify ${LIVE} as a v1 or v2 hmem store (neither memories nor v2 entries table). Refusing to migrate.` };
157
+ }
158
+ // klass === 'v1' below.
159
+ // --- --out side-by-side (advanced) mode: skip quiesce/backup/swap ---
160
+ // We still run the confirm/dry-run gates? Per §4, --out just runs migrate({in,out}).
161
+ // Dry-run is independent of --out: it always uses :memory:. Handle dry-run first.
162
+ // --- Step: dry-run (touches nothing) ---
163
+ if (opts.dryRun) {
164
+ try {
165
+ // same-path guard fires unconditionally in migrate(); pass a sentinel out so it
166
+ // doesn't trip. dryRun routes the migrator to :memory:; `out` is otherwise unused.
167
+ const sentinel = LIVE + '.dry-run-placeholder';
168
+ const res = migrate({ in: LIVE, out: sentinel, dryRun: true });
169
+ return { action: 'dry-run', exitCode: 0, report: res.report,
170
+ message: 'Dry run complete — no files were changed.' };
171
+ }
172
+ catch (e) {
173
+ return { action: 'failed', exitCode: 1, message: errMsg(e) };
174
+ }
175
+ }
176
+ // --- Step 2: quiesce preflight (Layer 1 — serve lockfile) ---
177
+ const lock = probeLock(dir, LIVE);
178
+ if (lock && lock.alive) {
179
+ return { action: 'refused-locked', exitCode: 2,
180
+ message: `An hmem MCP server (PID ${lock.pid}) is using this store. ` +
181
+ `Close Claude Code (or stop the server) and re-run \`hmem migrate\`.` };
182
+ }
183
+ if (lock && !lock.alive) {
184
+ log(`[hmem migrate] Ignoring a stale serve lockfile (PID ${lock.pid} is not running).`);
185
+ }
186
+ // --- --out side-by-side mode: migrate({in,out}), no backup/swap ---
187
+ if (opts.out) {
188
+ const OUT = resolve(opts.out);
189
+ // Layer 2 confirm (unless --yes) — still warn the user to quiesce.
190
+ if (!opts.yes) {
191
+ const ok = await runConfirm(opts.confirm, LIVE, OUT, /*sideBySide*/ true);
192
+ if (!ok)
193
+ return { action: 'aborted', exitCode: 0, message: 'Aborted by user.' };
194
+ }
195
+ try {
196
+ const res = migrate({ in: LIVE, out: OUT, dryRun: false });
197
+ if (res.skipped) {
198
+ return { action: 'noop-already-v2', exitCode: 0,
199
+ message: `${OUT} is already a migrated v2 store. Nothing to do.` };
200
+ }
201
+ // validate the side-by-side output too
202
+ validate(OUT, sourceCounts(LIVE));
203
+ return { action: 'side-by-side', exitCode: 0, report: res.report,
204
+ message: `Wrote v2 store to ${OUT}. The original (${LIVE}) is unchanged.\n` +
205
+ `Point HMEM_PATH at ${OUT} (or re-run \`hmem migrate\` in-place once ready).` };
206
+ }
207
+ catch (e) {
208
+ return { action: 'failed', exitCode: 1, message: errMsg(e) };
209
+ }
210
+ }
211
+ // --- In-place flow (§2 steps 2–9) ---
212
+ // --- Step 3: confirm (unless --yes) ---
213
+ if (!opts.yes) {
214
+ const ok = await runConfirm(opts.confirm, LIVE, undefined, /*sideBySide*/ false);
215
+ if (!ok)
216
+ return { action: 'aborted', exitCode: 0, message: 'Aborted by user.' };
217
+ }
218
+ // --- Step 4: backup (copy LIVE + sidecars verbatim) ---
219
+ const backupPath = `${LIVE}.v1-backup-${utcStamp()}`;
220
+ try {
221
+ copyFileSync(LIVE, backupPath);
222
+ for (const ext of ['-wal', '-shm']) {
223
+ if (existsSync(LIVE + ext))
224
+ copyFileSync(LIVE + ext, backupPath + ext);
225
+ }
226
+ }
227
+ catch (e) {
228
+ // backup failed → abort, LIVE untouched. Clean any partial backup.
229
+ cleanup([backupPath, backupPath + '-wal', backupPath + '-shm']);
230
+ return { action: 'failed', exitCode: 1,
231
+ message: `Backup failed (${errMsg(e)}). LIVE is unchanged; nothing was migrated.` };
232
+ }
233
+ // --- Step 5: migrate to TMP ---
234
+ const TMP = `${LIVE}.v2-migrating-${process.pid}-${Date.now()}.tmp`;
235
+ let report;
236
+ try {
237
+ const res = migrate({ in: LIVE, out: TMP, dryRun: false });
238
+ if (res.skipped) {
239
+ // Unexpected: classify said v1 but migrator found the marker. Treat as failure
240
+ // (ROLLBACK A) — LIVE is untouched (migrate opens it read-only).
241
+ cleanup([TMP, TMP + '-wal', TMP + '-shm']);
242
+ return { action: 'failed', exitCode: 1, backupPath,
243
+ message: 'Migrator unexpectedly reported the source as already-migrated. LIVE is unchanged; backup kept.' };
244
+ }
245
+ report = res.report;
246
+ }
247
+ catch (e) {
248
+ cleanup([TMP, TMP + '-wal', TMP + '-shm']);
249
+ return { action: 'failed', exitCode: 1, backupPath,
250
+ message: `Migration failed (${errMsg(e)}). LIVE is unchanged (source was read-only); backup kept at ${backupPath}.` };
251
+ }
252
+ // --- Step 6: validate TMP (read-only) ---
253
+ try {
254
+ validate(TMP, sourceCounts(LIVE));
255
+ }
256
+ catch (e) {
257
+ cleanup([TMP, TMP + '-wal', TMP + '-shm']);
258
+ return { action: 'failed', exitCode: 1, backupPath,
259
+ message: `Validation of the migrated store failed (${errMsg(e)}). ` +
260
+ `LIVE is unchanged; backup kept at ${backupPath}.` };
261
+ }
262
+ // --- Step 7: atomic swap (renameSync) + delete stale v1 sidecars ---
263
+ try {
264
+ renameSync(TMP, LIVE);
265
+ }
266
+ catch (e) {
267
+ // ROLLBACK B (Windows EBUSY etc.): TMP intact, LIVE intact.
268
+ return { action: 'failed', exitCode: 2, backupPath,
269
+ message: `Could not replace the live store (${errMsg(e)}). ` +
270
+ `Close all tools using ${LIVE} and re-run. LIVE is unchanged; ` +
271
+ `the migrated file is at ${TMP}; backup kept at ${backupPath}.` };
272
+ }
273
+ // Delete the now-stale -wal/-shm left over from the OLD v1 file (the new v2 file
274
+ // creates its own on next open). Best-effort.
275
+ cleanup([LIVE + '-wal', LIVE + '-shm']);
276
+ // --- Step 8: post-swap verify ---
277
+ // The v1 source is gone after the swap, so the count-sanity check has nothing to
278
+ // compare against; pass a relaxed source so only the structural/integrity checks
279
+ // run (these are what catch a corrupt swap). The exact count invariant was already
280
+ // enforced against the real source in step 6.
281
+ try {
282
+ validate(LIVE, RELAXED_SOURCE_COUNTS);
283
+ }
284
+ catch (e) {
285
+ // Should be impossible after step 6. Emergency restore from backup.
286
+ try {
287
+ copyFileSync(backupPath, LIVE);
288
+ cleanup([LIVE + '-wal', LIVE + '-shm']);
289
+ if (existsSync(backupPath + '-wal'))
290
+ copyFileSync(backupPath + '-wal', LIVE + '-wal');
291
+ if (existsSync(backupPath + '-shm'))
292
+ copyFileSync(backupPath + '-shm', LIVE + '-shm');
293
+ }
294
+ catch { /* restore best-effort; backup still on disk for the user */ }
295
+ return { action: 'failed', exitCode: 1, backupPath,
296
+ message: `Post-swap verification failed (${errMsg(e)}). Restored from backup. ` +
297
+ `Your backup is at ${backupPath}.` };
298
+ }
299
+ // The read-only verify above opens the WAL-mode db and leaves empty -wal/-shm
300
+ // sidecars (a read-only connection can't delete them on close). They're freshly
301
+ // created and contain no committed frames; remove them so a clean migrate leaves
302
+ // exactly one file. The next real open recreates them as needed.
303
+ cleanup([LIVE + '-wal', LIVE + '-shm']);
304
+ // --- Step 9: report + keep backup ---
305
+ return {
306
+ action: 'migrated', exitCode: 0, backupPath, report,
307
+ message: buildSuccessReport(LIVE, backupPath, report),
308
+ };
309
+ }
310
+ /** Source counts that disable the count-sanity check (entries can never exceed this). */
311
+ const RELAXED_SOURCE_COUNTS = {
312
+ memories: Number.MAX_SAFE_INTEGER, memoryNodes: Number.MAX_SAFE_INTEGER,
313
+ };
314
+ // ---------------------------------------------------------------------------
315
+ // Helpers
316
+ // ---------------------------------------------------------------------------
317
+ function errMsg(e) {
318
+ return e instanceof Error ? e.message : String(e);
319
+ }
320
+ function cleanup(paths) {
321
+ for (const p of paths) {
322
+ try {
323
+ if (existsSync(p))
324
+ rmSync(p);
325
+ }
326
+ catch { /* ignore */ }
327
+ }
328
+ }
329
+ async function runConfirm(confirm, live, out, _sideBySide) {
330
+ if (confirm)
331
+ return confirm();
332
+ return promptYesNo(live, out);
333
+ }
334
+ function buildSuccessReport(live, backupPath, report) {
335
+ const lines = [
336
+ `✓ Migrated ${live} to the v2 schema.`,
337
+ ` Backup (v1) kept at: ${backupPath}`,
338
+ ];
339
+ if (report) {
340
+ const total = Object.values(report.entriesByPrefix).reduce((a, b) => a + b, 0);
341
+ lines.push(` Entries: ${total} | sessions: ${report.sessionsReconstructed} | events: ${report.eventsWritten}`);
342
+ lines.push(` Links resolved: ${report.linksResolved} | dangling: ${report.linksDangling.length}`);
343
+ }
344
+ lines.push('');
345
+ lines.push('Your store is now v2. To sync it:');
346
+ lines.push(' hmem sync push # the first push uploads the full v2 store; later pushes are incremental.');
347
+ lines.push('If you use multiple devices, run `hmem migrate` on each — until then, other devices cannot sync.');
348
+ return lines.join('\n');
349
+ }
350
+ /** Default interactive double-confirm prompt (Layer 2, §3). Reads y/N from stdin. */
351
+ async function promptYesNo(live, out) {
352
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
353
+ const ask = (q) => new Promise(res => rl.question(q, ans => res(ans)));
354
+ try {
355
+ console.log('');
356
+ console.log('Before migrating, fully QUIT Claude Code (and any other tool using this store).');
357
+ console.log('Migration replaces the store file; a writer left open can lose data.');
358
+ console.log(` Source (v1): ${live}`);
359
+ if (out)
360
+ console.log(` Output (v2): ${out} (the original stays unchanged)`);
361
+ else
362
+ console.log(' The live file will be replaced in place (a timestamped v1 backup is kept).');
363
+ const a = (await ask(`Have you closed all tools using ${live}? [y/N] `)).trim().toLowerCase();
364
+ return a === 'y' || a === 'yes';
365
+ }
366
+ finally {
367
+ rl.close();
368
+ }
369
+ }
370
+ // ---------------------------------------------------------------------------
371
+ // CLI entry: runMigrate
372
+ // ---------------------------------------------------------------------------
373
+ export async function runMigrate(argv) {
374
+ const getFlag = (k) => {
375
+ const i = argv.indexOf(k);
376
+ return i >= 0 ? argv[i + 1] : undefined;
377
+ };
378
+ const inPath = getFlag('--in') ?? resolveHmemPath();
379
+ const outPath = getFlag('--out');
380
+ const dryRun = argv.includes('--dry-run');
381
+ const yes = argv.includes('--yes') || argv.includes('-y');
382
+ const keepBackup = argv.includes('--keep-backup'); // default-true no-op confirming flag
383
+ const outcome = await migratePlan({
384
+ in: inPath, out: outPath, dryRun, yes, keepBackup,
385
+ });
386
+ if (outcome.exitCode === 0) {
387
+ console.log(outcome.message);
388
+ }
389
+ else {
390
+ console.error(outcome.message);
391
+ }
392
+ process.exit(outcome.exitCode);
393
+ }
394
+ //# sourceMappingURL=cli-migrate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli-migrate.js","sourceRoot":"","sources":["../src/cli-migrate.ts"],"names":[],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * cli-session-summary.ts
3
3
  *
4
- * Spawns Haiku to write a session summary (L2 body) for a completed session.
4
+ * Spawns a checkpoint agent to write a session summary (L2 body) for a completed session.
5
5
  * Called async from SessionStart hook when previous session lacks a summary.
6
6
  *
7
7
  * Usage: hmem summarize-session O0048.3
@@ -1,46 +1,17 @@
1
1
  /**
2
2
  * cli-session-summary.ts
3
3
  *
4
- * Spawns Haiku to write a session summary (L2 body) for a completed session.
4
+ * Spawns a checkpoint agent to write a session summary (L2 body) for a completed session.
5
5
  * Called async from SessionStart hook when previous session lacks a summary.
6
6
  *
7
7
  * Usage: hmem summarize-session O0048.3
8
8
  */
9
9
  import fs from "node:fs";
10
- import os from "node:os";
11
10
  import path from "node:path";
12
- import { execFileSync, execSync } from "node:child_process";
13
11
  import { HmemStore } from "./hmem-store.js";
14
12
  import { loadHmemConfig } from "./hmem-config.js";
15
13
  import { resolveEnvDefaults } from "./cli-env.js";
16
- function buildMcpConfig(projectDir, hmemPath) {
17
- let hmemServerPath;
18
- try {
19
- hmemServerPath = execSync("which hmem", { encoding: "utf8" }).trim();
20
- const realPath = fs.realpathSync(hmemServerPath);
21
- hmemServerPath = path.join(path.dirname(realPath), "mcp-server.js");
22
- if (!fs.existsSync(hmemServerPath)) {
23
- hmemServerPath = path.join(path.dirname(path.dirname(realPath)), "dist", "mcp-server.js");
24
- }
25
- }
26
- catch {
27
- hmemServerPath = path.join(process.env.HOME || "/home", ".nvm/versions/node", process.version, "lib/node_modules/hmem-mcp/dist/mcp-server.js");
28
- }
29
- const config = {
30
- mcpServers: {
31
- hmem: {
32
- command: process.execPath,
33
- args: [hmemServerPath],
34
- env: { HMEM_PROJECT_DIR: projectDir, HMEM_PATH: hmemPath, HMEM_NO_SESSION: "1" },
35
- },
36
- },
37
- };
38
- const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "hmem-session-summary-"));
39
- fs.chmodSync(tmpDir, 0o700);
40
- const tmpPath = path.join(tmpDir, "mcp-config.json");
41
- fs.writeFileSync(tmpPath, JSON.stringify(config), "utf8");
42
- return tmpPath;
43
- }
14
+ import { runCheckpointAgent } from "./cli-checkpoint-agent.js";
44
15
  export async function summarizeSession(sessionId) {
45
16
  if (!sessionId) {
46
17
  console.error("[hmem summarize-session] No session ID provided");
@@ -55,20 +26,16 @@ export async function summarizeSession(sessionId) {
55
26
  process.exit(0);
56
27
  const config = loadHmemConfig(path.dirname(hmemPath));
57
28
  const store = new HmemStore(hmemPath, config);
58
- let mcpConfigPath = "";
59
29
  try {
60
- // Get all L3 batch nodes for this session
61
30
  const batches = store.getChildNodes(sessionId);
62
31
  if (batches.length === 0)
63
32
  return;
64
33
  const batchSummaries = batches
65
- .filter(b => b.depth === 3 && b.content !== b.title) // only batches with actual summaries
34
+ .filter(b => b.depth === 3 && b.content !== b.title)
66
35
  .map(b => `${b.id}: ${b.content}`)
67
36
  .join("\n\n");
68
37
  if (!batchSummaries)
69
- return; // no summaries to work with
70
- store.close();
71
- mcpConfigPath = buildMcpConfig(projectDir, hmemPath);
38
+ return;
72
39
  const prompt = `Summarize session ${sessionId}.
73
40
 
74
41
  == Batch Summaries ==
@@ -80,29 +47,17 @@ What was achieved? What's still open?
80
47
  Match the language of the batch summaries.
81
48
 
82
49
  update_memory(id="${sessionId}", content="Session summary text here")`;
83
- const allowedTools = "mcp__hmem__update_memory mcp__hmem__read_memory";
84
- execFileSync("claude", [
85
- "-p", "--model", "haiku",
86
- "--mcp-config", mcpConfigPath,
87
- "--allowedTools", allowedTools,
88
- "--dangerously-skip-permissions",
89
- ], { input: prompt, encoding: "utf8", timeout: 60_000 });
90
- console.log(`[hmem] Session summary written for ${sessionId}`);
50
+ await runCheckpointAgent(prompt, store, config, hmemPath);
51
+ console.log(`[hmem] Session summary written for ${sessionId} (${config.checkpointProvider}/${config.checkpointModel})`);
91
52
  }
92
53
  catch (e) {
93
54
  console.error(`[hmem summarize-session] ${e}`);
94
55
  }
95
56
  finally {
96
- if (mcpConfigPath) {
97
- try {
98
- fs.unlinkSync(mcpConfigPath);
99
- }
100
- catch { }
101
- try {
102
- fs.rmdirSync(path.dirname(mcpConfigPath));
103
- }
104
- catch { }
57
+ try {
58
+ store.close();
105
59
  }
60
+ catch { }
106
61
  }
107
62
  }
108
63
  //# sourceMappingURL=cli-session-summary.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"cli-session-summary.js","sourceRoot":"","sources":["../src/cli-session-summary.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cli-session-summary.js","sourceRoot":"","sources":["../src/cli-session-summary.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAE/D,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,SAAiB;IACtD,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,iDAAiD,CAAC,CAAC;QACjE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,kBAAkB,EAAE,CAAC;IACrB,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,gBAAiB,CAAC;IACjD,IAAI,CAAC,UAAU;QAAE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAEjC,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,SAAU,CAAC;IACxC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC;QAAE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAE9C,MAAM,MAAM,GAAG,cAAc,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC;IACtD,MAAM,KAAK,GAAG,IAAI,SAAS,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IAE9C,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,KAAK,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC;QAC/C,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO;QAEjC,MAAM,cAAc,GAAG,OAAO;aAC3B,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,IAAI,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,KAAK,CAAC;aACnD,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,OAAO,EAAE,CAAC;aACjC,IAAI,CAAC,MAAM,CAAC,CAAC;QAEhB,IAAI,CAAC,cAAc;YAAE,OAAO;QAE5B,MAAM,MAAM,GAAG,qBAAqB,SAAS;;;EAG/C,cAAc;;;iEAGiD,SAAS;;;;oBAItD,SAAS,yCAAyC,CAAC;QAEnE,MAAM,kBAAkB,CAAC,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC;QAC1D,OAAO,CAAC,GAAG,CAAC,sCAAsC,SAAS,KAAK,MAAM,CAAC,kBAAkB,IAAI,MAAM,CAAC,eAAe,GAAG,CAAC,CAAC;IAE1H,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,OAAO,CAAC,KAAK,CAAC,4BAA4B,CAAC,EAAE,CAAC,CAAC;IACjD,CAAC;YAAS,CAAC;QACT,IAAI,CAAC;YAAC,KAAK,CAAC,KAAK,EAAE,CAAC;QAAC,CAAC;QAAC,MAAM,CAAC,CAAA,CAAC;IACjC,CAAC;AACH,CAAC"}
@@ -69,7 +69,7 @@ function buildContextBar(input) {
69
69
  return `${color}[${bar}]${C.reset} ${C.white}${tokLabel}${C.reset}`;
70
70
  }
71
71
  async function getHmemStatus(sessionId) {
72
- const empty = { project: "", device: "", exchanges: 0, interval: 0 };
72
+ const empty = { project: "", device: "", exchanges: 0, interval: 0, oSession: "" };
73
73
  const CACHE_FILE = cacheFile(sessionId);
74
74
  // Check cache
75
75
  try {
@@ -101,10 +101,13 @@ async function getHmemStatus(sessionId) {
101
101
  // Active project — per-session marker lookup
102
102
  const { readSessionMarker } = await import("./session-state.js");
103
103
  const marker = sessionId ? readSessionMarker(sessionId) : null;
104
+ // O-sub-node ID for this session (written by Stop-Hook after first exchange).
105
+ // Suppress when the session is explicitly deactivated (post-/clear).
106
+ const oSession = (marker && !marker.deactivated && marker.oSessionId) ? marker.oSessionId : "";
104
107
  let projRow;
105
108
  if (marker?.projectId) {
106
109
  // Session marker has an explicit project → use it
107
- projRow = db.prepare("SELECT id, title FROM memories WHERE id = ? AND prefix='P' AND obsolete!=1 LIMIT 1").get(marker.projectId);
110
+ projRow = db.prepare("SELECT id, title, level_1 FROM memories WHERE id = ? AND prefix='P' AND obsolete!=1 LIMIT 1").get(marker.projectId);
108
111
  }
109
112
  else if (marker?.deactivated) {
110
113
  // Explicitly deactivated after /clear → show "no project", skip all fallbacks
@@ -119,25 +122,25 @@ async function getHmemStatus(sessionId) {
119
122
  // handle the bash-intermediary case transparently.
120
123
  const activeFromFile = readActiveProjectForCurrentProcess();
121
124
  if (activeFromFile) {
122
- projRow = db.prepare("SELECT id, title FROM memories WHERE id = ? AND prefix='P' AND obsolete!=1 LIMIT 1").get(activeFromFile);
125
+ projRow = db.prepare("SELECT id, title, level_1 FROM memories WHERE id = ? AND prefix='P' AND obsolete!=1 LIMIT 1").get(activeFromFile);
123
126
  }
124
127
  if (!projRow) {
125
128
  // Fallback 2: shared DB active flag (legacy — unreliable in multi-session setups)
126
- projRow = db.prepare("SELECT id, title FROM memories WHERE prefix='P' AND active=1 AND obsolete!=1 LIMIT 1").get();
129
+ projRow = db.prepare("SELECT id, title, level_1 FROM memories WHERE prefix='P' AND active=1 AND obsolete!=1 LIMIT 1").get();
127
130
  }
128
131
  }
129
132
  let project = "";
130
133
  if (projRow) {
131
- const name = (projRow.title ?? projRow.id).split("|")[0].trim();
134
+ const name = (projRow.title ?? projRow.level_1 ?? projRow.id).split("|")[0].trim();
132
135
  project = `${projRow.id} ${name}`;
133
136
  }
134
137
  // Active device — global per-machine file
135
138
  let device = "";
136
139
  const deviceId = getActiveDevice();
137
140
  if (deviceId) {
138
- const devRow = db.prepare("SELECT id, title FROM memories WHERE id = ? AND prefix='I' AND obsolete!=1 LIMIT 1").get(deviceId);
141
+ const devRow = db.prepare("SELECT id, title, level_1 FROM memories WHERE id = ? AND prefix='I' AND obsolete!=1 LIMIT 1").get(deviceId);
139
142
  if (devRow) {
140
- device = (devRow.title ?? deviceId).split("|")[0].trim();
143
+ device = (devRow.title ?? devRow.level_1 ?? deviceId).split("|")[0].trim();
141
144
  }
142
145
  else {
143
146
  device = deviceId;
@@ -159,14 +162,14 @@ async function getHmemStatus(sessionId) {
159
162
  const batchExchanges = db.prepare("SELECT COUNT(*) as n FROM memory_nodes WHERE parent_id = ? AND depth = 4").get(latestBatch.id)?.n ?? 0;
160
163
  const interval = hmemConfig.checkpointInterval;
161
164
  exchanges = batchExchanges;
162
- status = { project, device, exchanges, interval };
165
+ status = { project, device, exchanges, interval, oSession };
163
166
  }
164
167
  else {
165
- status = { project, device, exchanges: 0, interval: hmemConfig.checkpointInterval };
168
+ status = { project, device, exchanges: 0, interval: hmemConfig.checkpointInterval, oSession };
166
169
  }
167
170
  }
168
171
  else {
169
- status = { project, device, exchanges: 0, interval: hmemConfig.checkpointInterval };
172
+ status = { project, device, exchanges: 0, interval: hmemConfig.checkpointInterval, oSession };
170
173
  }
171
174
  }
172
175
  finally {
@@ -207,7 +210,8 @@ export async function statusline() {
207
210
  parts.push(`${C.gray}identify device${C.reset}`);
208
211
  }
209
212
  if (status.project) {
210
- parts.push(`${C.cyan}${status.project}${C.reset}`);
213
+ const oTag = status.oSession ? ` ${C.gray}→${C.reset} ${C.cyan}${status.oSession}${C.reset}` : "";
214
+ parts.push(`${C.cyan}${status.project}${C.reset}${oTag}`);
211
215
  }
212
216
  else {
213
217
  parts.push(`${C.gray}no project${C.reset}`);