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.
- package/README.md +266 -54
- package/dist/cli-checkpoint-agent.d.ts +27 -0
- package/dist/cli-checkpoint-agent.js +320 -0
- package/dist/cli-checkpoint-agent.js.map +1 -0
- package/dist/cli-checkpoint.js +165 -78
- package/dist/cli-checkpoint.js.map +1 -1
- package/dist/cli-context-inject.d.ts +7 -7
- package/dist/cli-context-inject.js +54 -41
- package/dist/cli-context-inject.js.map +1 -1
- package/dist/cli-doctor.d.ts +7 -0
- package/dist/cli-doctor.js +156 -0
- package/dist/cli-doctor.js.map +1 -0
- package/dist/cli-hook-startup.d.ts +1 -0
- package/dist/cli-hook-startup.js +97 -10
- package/dist/cli-hook-startup.js.map +1 -1
- package/dist/cli-init.d.ts +0 -4
- package/dist/cli-init.js +48 -6
- package/dist/cli-init.js.map +1 -1
- package/dist/cli-log-exchange.js +10 -2
- package/dist/cli-log-exchange.js.map +1 -1
- package/dist/cli-mcp-add-curate.d.ts +7 -0
- package/dist/cli-mcp-add-curate.js +40 -0
- package/dist/cli-mcp-add-curate.js.map +1 -0
- package/dist/cli-migrate.d.ts +57 -0
- package/dist/cli-migrate.js +394 -0
- package/dist/cli-migrate.js.map +1 -0
- package/dist/cli-session-summary.d.ts +1 -1
- package/dist/cli-session-summary.js +9 -54
- package/dist/cli-session-summary.js.map +1 -1
- package/dist/cli-statusline.js +15 -11
- package/dist/cli-statusline.js.map +1 -1
- package/dist/cli-sync-pull.d.ts +3 -0
- package/dist/cli-sync-pull.js +79 -0
- package/dist/cli-sync-pull.js.map +1 -0
- package/dist/cli-sync-push.d.ts +4 -0
- package/dist/cli-sync-push.js +85 -0
- package/dist/cli-sync-push.js.map +1 -0
- package/dist/cli-sync-setup.d.ts +18 -0
- package/dist/cli-sync-setup.js +308 -0
- package/dist/cli-sync-setup.js.map +1 -0
- package/dist/cli-sync-status.d.ts +1 -0
- package/dist/cli-sync-status.js +20 -0
- package/dist/cli-sync-status.js.map +1 -0
- package/dist/cli-v2-checkpoint.d.ts +38 -0
- package/dist/cli-v2-checkpoint.js +134 -0
- package/dist/cli-v2-checkpoint.js.map +1 -0
- package/dist/cli.js +154 -34
- package/dist/cli.js.map +1 -1
- package/dist/config/config.d.ts +29 -0
- package/dist/config/config.js +123 -0
- package/dist/config/config.js.map +1 -0
- package/dist/diagnostics.js +1 -6
- package/dist/diagnostics.js.map +1 -1
- package/dist/extensions/pi-hmem.js +141 -35
- package/dist/extensions/pi-hmem.js.map +1 -1
- package/dist/hmem-config.d.ts +30 -5
- package/dist/hmem-config.js +50 -8
- package/dist/hmem-config.js.map +1 -1
- package/dist/hmem-store.d.ts +60 -2
- package/dist/hmem-store.js +205 -26
- package/dist/hmem-store.js.map +1 -1
- package/dist/hooks/binding-resolver.d.ts +13 -0
- package/dist/hooks/binding-resolver.js +42 -0
- package/dist/hooks/binding-resolver.js.map +1 -0
- package/dist/hooks/brief.d.ts +2 -0
- package/dist/hooks/brief.js +26 -0
- package/dist/hooks/brief.js.map +1 -0
- package/dist/hooks/dispatch.d.ts +5 -0
- package/dist/hooks/dispatch.js +119 -0
- package/dist/hooks/dispatch.js.map +1 -0
- package/dist/hooks/git-probe.d.ts +4 -0
- package/dist/hooks/git-probe.js +46 -0
- package/dist/hooks/git-probe.js.map +1 -0
- package/dist/hooks/harness/claude-code.d.ts +10 -0
- package/dist/hooks/harness/claude-code.js +133 -0
- package/dist/hooks/harness/claude-code.js.map +1 -0
- package/dist/hooks/harness/hermes.d.ts +4 -0
- package/dist/hooks/harness/hermes.js +20 -0
- package/dist/hooks/harness/hermes.js.map +1 -0
- package/dist/hooks/harness/pi.d.ts +4 -0
- package/dist/hooks/harness/pi.js +21 -0
- package/dist/hooks/harness/pi.js.map +1 -0
- package/dist/hooks/prompt-submit.d.ts +16 -0
- package/dist/hooks/prompt-submit.js +11 -0
- package/dist/hooks/prompt-submit.js.map +1 -0
- package/dist/hooks/session-start.d.ts +3 -0
- package/dist/hooks/session-start.js +31 -0
- package/dist/hooks/session-start.js.map +1 -0
- package/dist/hooks/stable-context.d.ts +11 -0
- package/dist/hooks/stable-context.js +117 -0
- package/dist/hooks/stable-context.js.map +1 -0
- package/dist/hooks/stop-log.d.ts +4 -0
- package/dist/hooks/stop-log.js +12 -0
- package/dist/hooks/stop-log.js.map +1 -0
- package/dist/hooks/summarizer-pick.d.ts +8 -0
- package/dist/hooks/summarizer-pick.js +18 -0
- package/dist/hooks/summarizer-pick.js.map +1 -0
- package/dist/hooks/transcript.d.ts +6 -0
- package/dist/hooks/transcript.js +72 -0
- package/dist/hooks/transcript.js.map +1 -0
- package/dist/hooks/types.d.ts +56 -0
- package/dist/hooks/types.js +2 -0
- package/dist/hooks/types.js.map +1 -0
- package/dist/mcp-curate-server.js +50 -4
- package/dist/mcp-curate-server.js.map +1 -1
- package/dist/mcp-server.js +121 -58
- package/dist/mcp-server.js.map +1 -1
- package/dist/mcp-shared.js +119 -29
- package/dist/mcp-shared.js.map +1 -1
- package/dist/memory-search.d.ts +5 -2
- package/dist/memory-search.js +5 -2
- package/dist/memory-search.js.map +1 -1
- package/dist/migrate/o-decompose.d.ts +14 -0
- package/dist/migrate/o-decompose.js +112 -0
- package/dist/migrate/o-decompose.js.map +1 -0
- package/dist/migrate/tree.d.ts +16 -0
- package/dist/migrate/tree.js +102 -0
- package/dist/migrate/tree.js.map +1 -0
- package/dist/migrate/v1-reader.d.ts +38 -0
- package/dist/migrate/v1-reader.js +34 -0
- package/dist/migrate/v1-reader.js.map +1 -0
- package/dist/migrate/v1-to-v2.d.ts +25 -0
- package/dist/migrate/v1-to-v2.js +127 -0
- package/dist/migrate/v1-to-v2.js.map +1 -0
- package/dist/session-state.d.ts +3 -0
- package/dist/session-state.js +7 -5
- package/dist/session-state.js.map +1 -1
- package/dist/store/db.d.ts +25 -0
- package/dist/store/db.js +159 -0
- package/dist/store/db.js.map +1 -0
- package/dist/store/entries.d.ts +48 -0
- package/dist/store/entries.js +145 -0
- package/dist/store/entries.js.map +1 -0
- package/dist/store/events-fts.d.ts +18 -0
- package/dist/store/events-fts.js +18 -0
- package/dist/store/events-fts.js.map +1 -0
- package/dist/store/events.d.ts +30 -0
- package/dist/store/events.js +48 -0
- package/dist/store/events.js.map +1 -0
- package/dist/store/fts.d.ts +24 -0
- package/dist/store/fts.js +35 -0
- package/dist/store/fts.js.map +1 -0
- package/dist/store/links.d.ts +24 -0
- package/dist/store/links.js +29 -0
- package/dist/store/links.js.map +1 -0
- package/dist/store/schema-engine.d.ts +20 -0
- package/dist/store/schema-engine.js +41 -0
- package/dist/store/schema-engine.js.map +1 -0
- package/dist/store/schema-v2.sql +149 -0
- package/dist/store/sessions.d.ts +38 -0
- package/dist/store/sessions.js +98 -0
- package/dist/store/sessions.js.map +1 -0
- package/dist/store/types.d.ts +74 -0
- package/dist/store/types.js +3 -0
- package/dist/store/types.js.map +1 -0
- package/dist/store/ulid.d.ts +1 -0
- package/dist/store/ulid.js +10 -0
- package/dist/store/ulid.js.map +1 -0
- package/dist/summarize/anthropic.d.ts +8 -0
- package/dist/summarize/anthropic.js +38 -0
- package/dist/summarize/anthropic.js.map +1 -0
- package/dist/summarize/loop.d.ts +40 -0
- package/dist/summarize/loop.js +64 -0
- package/dist/summarize/loop.js.map +1 -0
- package/dist/sync/api.d.ts +49 -0
- package/dist/sync/api.js +99 -0
- package/dist/sync/api.js.map +1 -0
- package/dist/sync/config.d.ts +16 -0
- package/dist/sync/config.js +27 -0
- package/dist/sync/config.js.map +1 -0
- package/dist/sync/conflict.d.ts +24 -0
- package/dist/sync/conflict.js +125 -0
- package/dist/sync/conflict.js.map +1 -0
- package/dist/sync/crypto.d.ts +53 -0
- package/dist/sync/crypto.js +140 -0
- package/dist/sync/crypto.js.map +1 -0
- package/dist/sync/passphrase.d.ts +1 -0
- package/dist/sync/passphrase.js +17 -0
- package/dist/sync/passphrase.js.map +1 -0
- package/dist/sync/v2/dispatch.d.ts +6 -0
- package/dist/sync/v2/dispatch.js +24 -0
- package/dist/sync/v2/dispatch.js.map +1 -0
- package/dist/sync/v2/envelope.d.ts +13 -0
- package/dist/sync/v2/envelope.js +36 -0
- package/dist/sync/v2/envelope.js.map +1 -0
- package/dist/sync/v2/export.d.ts +10 -0
- package/dist/sync/v2/export.js +32 -0
- package/dist/sync/v2/export.js.map +1 -0
- package/dist/sync/v2/import.d.ts +10 -0
- package/dist/sync/v2/import.js +130 -0
- package/dist/sync/v2/import.js.map +1 -0
- package/dist/sync/v2/queue.d.ts +18 -0
- package/dist/sync/v2/queue.js +59 -0
- package/dist/sync/v2/queue.js.map +1 -0
- package/dist/sync/v2/reconcile.d.ts +3 -0
- package/dist/sync/v2/reconcile.js +18 -0
- package/dist/sync/v2/reconcile.js.map +1 -0
- package/dist/sync/v2/staging.d.ts +27 -0
- package/dist/sync/v2/staging.js +46 -0
- package/dist/sync/v2/staging.js.map +1 -0
- package/dist/sync/v2/sync.d.ts +18 -0
- package/dist/sync/v2/sync.js +63 -0
- package/dist/sync/v2/sync.js.map +1 -0
- package/dist/sync/v2/transport.d.ts +43 -0
- package/dist/sync/v2/transport.js +20 -0
- package/dist/sync/v2/transport.js.map +1 -0
- package/dist/sync-bridge.d.ts +2 -0
- package/dist/sync-bridge.js +134 -0
- package/dist/sync-bridge.js.map +1 -0
- package/dist/tools/analyze-v1.d.ts +1 -0
- package/dist/tools/analyze-v1.js +142 -0
- package/dist/tools/analyze-v1.js.map +1 -0
- package/dist/util/iso.d.ts +8 -0
- package/dist/util/iso.js +15 -0
- package/dist/util/iso.js.map +1 -0
- package/dist/utils.d.ts +7 -0
- package/dist/utils.js +14 -0
- package/dist/utils.js.map +1 -0
- package/dist/v2/addressing.d.ts +9 -0
- package/dist/v2/addressing.js +55 -0
- package/dist/v2/addressing.js.map +1 -0
- package/dist/v2/checkpoint-agent.d.ts +75 -0
- package/dist/v2/checkpoint-agent.js +231 -0
- package/dist/v2/checkpoint-agent.js.map +1 -0
- package/dist/v2/checkpoint-select.d.ts +60 -0
- package/dist/v2/checkpoint-select.js +106 -0
- package/dist/v2/checkpoint-select.js.map +1 -0
- package/dist/v2/checkpoint.d.ts +62 -0
- package/dist/v2/checkpoint.js +103 -0
- package/dist/v2/checkpoint.js.map +1 -0
- package/dist/v2/curation.d.ts +23 -0
- package/dist/v2/curation.js +118 -0
- package/dist/v2/curation.js.map +1 -0
- package/dist/v2/projects.d.ts +29 -0
- package/dist/v2/projects.js +60 -0
- package/dist/v2/projects.js.map +1 -0
- package/dist/v2/read-render.d.ts +31 -0
- package/dist/v2/read-render.js +150 -0
- package/dist/v2/read-render.js.map +1 -0
- package/dist/v2/related.d.ts +8 -0
- package/dist/v2/related.js +35 -0
- package/dist/v2/related.js.map +1 -0
- package/dist/v2/search.d.ts +6 -0
- package/dist/v2/search.js +24 -0
- package/dist/v2/search.js.map +1 -0
- package/dist/v2/session-id.d.ts +7 -0
- package/dist/v2/session-id.js +14 -0
- package/dist/v2/session-id.js.map +1 -0
- package/dist/v2/sessions-tools.d.ts +11 -0
- package/dist/v2/sessions-tools.js +30 -0
- package/dist/v2/sessions-tools.js.map +1 -0
- package/dist/v2/store-facade.d.ts +23 -0
- package/dist/v2/store-facade.js +33 -0
- package/dist/v2/store-facade.js.map +1 -0
- package/dist/v2/summarize-adapter.d.ts +11 -0
- package/dist/v2/summarize-adapter.js +19 -0
- package/dist/v2/summarize-adapter.js.map +1 -0
- package/dist/v2/writes.d.ts +30 -0
- package/dist/v2/writes.js +135 -0
- package/dist/v2/writes.js.map +1 -0
- package/hmem.config.example.json +92 -10
- package/opencode-plugin/hmem.js +5 -2
- package/package.json +7 -3
- package/plugins/hermes-hmem/README.md +56 -0
- package/plugins/hermes-hmem/__init__.py +187 -0
- package/plugins/hermes-hmem/plugin.yaml +8 -0
- package/server.json +2 -2
- package/skills/hmem-activate/SKILL.md +1 -1
- package/skills/hmem-config/SKILL.md +70 -5
- package/skills/hmem-curate/SKILL.md +13 -6
- package/skills/hmem-dispatch/SKILL.md +1 -1
- package/skills/hmem-migrate-o/SKILL.md +3 -8
- package/skills/hmem-new-error/SKILL.md +1 -9
- package/skills/hmem-new-project/SKILL.md +1 -9
- package/skills/hmem-new-rule/SKILL.md +93 -0
- package/skills/hmem-read/SKILL.md +16 -8
- package/skills/hmem-release/SKILL.md +108 -9
- package/skills/hmem-search/SKILL.md +48 -25
- package/skills/hmem-session-start/SKILL.md +147 -46
- package/skills/hmem-setup/SKILL.md +3 -3
- package/skills/hmem-sync-setup/SKILL.md +1 -5
- package/skills/hmem-update/SKILL.md +345 -19
- package/skills/hmem-wipe/SKILL.md +41 -7
- package/skills/hmem-write/SKILL.md +1 -12
- package/skills/hmem-activate-workspace/evals/evals.json +0 -29
- package/skills/hmem-activate-workspace/iteration-1/benchmark.json +0 -370
- package/skills/hmem-activate-workspace/iteration-1/benchmark.md +0 -13
- package/skills/hmem-activate-workspace/iteration-1/eval-1-simple-switch/eval_metadata.json +0 -23
- package/skills/hmem-activate-workspace/iteration-1/eval-1-simple-switch/with_skill/grading.json +0 -55
- package/skills/hmem-activate-workspace/iteration-1/eval-1-simple-switch/with_skill/outputs/response.md +0 -121
- package/skills/hmem-activate-workspace/iteration-1/eval-1-simple-switch/with_skill/run-1/grading.json +0 -55
- package/skills/hmem-activate-workspace/iteration-1/eval-1-simple-switch/without_skill/grading.json +0 -44
- package/skills/hmem-activate-workspace/iteration-1/eval-1-simple-switch/without_skill/outputs/response.md +0 -78
- package/skills/hmem-activate-workspace/iteration-1/eval-1-simple-switch/without_skill/run-1/grading.json +0 -44
- package/skills/hmem-activate-workspace/iteration-1/eval-2-misrouting-fix/eval_metadata.json +0 -23
- package/skills/hmem-activate-workspace/iteration-1/eval-2-misrouting-fix/with_skill/grading.json +0 -49
- package/skills/hmem-activate-workspace/iteration-1/eval-2-misrouting-fix/with_skill/outputs/response.md +0 -98
- package/skills/hmem-activate-workspace/iteration-1/eval-2-misrouting-fix/with_skill/run-1/grading.json +0 -49
- package/skills/hmem-activate-workspace/iteration-1/eval-2-misrouting-fix/without_skill/grading.json +0 -44
- package/skills/hmem-activate-workspace/iteration-1/eval-2-misrouting-fix/without_skill/outputs/response.md +0 -57
- package/skills/hmem-activate-workspace/iteration-1/eval-2-misrouting-fix/without_skill/run-1/grading.json +0 -44
- package/skills/hmem-activate-workspace/iteration-1/eval-3-temporary-load/eval_metadata.json +0 -23
- package/skills/hmem-activate-workspace/iteration-1/eval-3-temporary-load/with_skill/grading.json +0 -48
- package/skills/hmem-activate-workspace/iteration-1/eval-3-temporary-load/with_skill/outputs/response.md +0 -69
- package/skills/hmem-activate-workspace/iteration-1/eval-3-temporary-load/with_skill/run-1/grading.json +0 -48
- package/skills/hmem-activate-workspace/iteration-1/eval-3-temporary-load/without_skill/grading.json +0 -44
- package/skills/hmem-activate-workspace/iteration-1/eval-3-temporary-load/without_skill/outputs/response.md +0 -59
- package/skills/hmem-activate-workspace/iteration-1/eval-3-temporary-load/without_skill/run-1/grading.json +0 -44
- package/skills/hmem-activate-workspace/iteration-1/eval-4-wrong-project/eval_metadata.json +0 -23
- package/skills/hmem-activate-workspace/iteration-1/eval-4-wrong-project/with_skill/grading.json +0 -55
- package/skills/hmem-activate-workspace/iteration-1/eval-4-wrong-project/with_skill/outputs/response.md +0 -182
- package/skills/hmem-activate-workspace/iteration-1/eval-4-wrong-project/with_skill/run-1/grading.json +0 -55
- package/skills/hmem-activate-workspace/iteration-1/eval-4-wrong-project/without_skill/grading.json +0 -44
- package/skills/hmem-activate-workspace/iteration-1/eval-4-wrong-project/without_skill/outputs/response.md +0 -43
- 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
|
|
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
|
|
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
|
-
|
|
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)
|
|
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;
|
|
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
|
-
|
|
84
|
-
|
|
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
|
-
|
|
97
|
-
|
|
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":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,
|
|
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"}
|
package/dist/cli-statusline.js
CHANGED
|
@@ -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
|
-
|
|
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}`);
|