claude-memory-hub 0.9.2 → 0.9.4

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/CHANGELOG.md CHANGED
@@ -5,6 +5,38 @@ Format follows [Keep a Changelog](https://keepachangelog.com/).
5
5
 
6
6
  ---
7
7
 
8
+ ## [0.9.4] - 2026-04-02
9
+
10
+ Windows path fix — backslashes no longer eaten by bash.
11
+
12
+ ### Bug Fixes
13
+
14
+ - **Windows backslash paths in hooks** — `C:\Users\Admin\.bun\bin\bun.exe` was passed raw into bash commands, which stripped backslashes → `C:UsersAdmin.bunbinbun.exe`. New `shellPath()` utility converts all paths to forward slashes (`C:/Users/Admin/.bun/bin/bun.exe`) and quotes paths with spaces. Applied to: bun binary path, hook script paths, MCP server path
15
+ - **`where` output parsing on Windows** — `where bun` returns `\r\n` line endings; now splits on `/\r?\n/` instead of `\n`
16
+
17
+ ---
18
+
19
+ ## [0.9.3] - 2026-04-02
20
+
21
+ Summary quality improvements — cleaner data in, garbage data out.
22
+
23
+ ### Summary Quality
24
+
25
+ - **Strip IDE tags from user_prompt** — `<ide_opened_file>`, `<ide_selection>`, `<system-reminder>` tags are now removed before storing `user_prompt` in L2. Summaries and search results no longer contain IDE noise
26
+ - **Skip low-value sessions** — sessions with only `file_read` entities (browsing, no edits) and no errors/decisions/notes/observations are no longer summarized. Prevents generic "Session in project X" entries from polluting L3
27
+ - **`hasModifiedFiles()` method** — new SessionStore method checks for `file_modified` or `file_created` entities efficiently
28
+
29
+ ### New CLI Command
30
+
31
+ - **`prune` command** — removes low-quality summaries from L3: generic text ("Session worked on...", "Session in project..."), IDE tag noise, and empty summaries with no files/decisions/errors. Supports `--dry-run` for safe preview. Also cleans related embeddings
32
+
33
+ ```bash
34
+ bunx claude-memory-hub prune --dry-run # preview
35
+ bunx claude-memory-hub prune # delete
36
+ ```
37
+
38
+ ---
39
+
8
40
  ## [0.9.2] - 2026-04-02
9
41
 
10
42
  Cross-platform hook reliability — Windows/WSL no longer fails with "bun: command not found".
package/README.md CHANGED
@@ -285,6 +285,7 @@ bunx claude-memory-hub reindex # Rebuild TF-IDF + embedding indexes
285
285
  bunx claude-memory-hub export # Export data as JSONL to stdout
286
286
  bunx claude-memory-hub import # Import JSONL from stdin (--dry-run)
287
287
  bunx claude-memory-hub cleanup # Remove old data (--days N, default 90)
288
+ bunx claude-memory-hub prune # Remove low-quality summaries (--dry-run)
288
289
  ```
289
290
 
290
291
  ### Requirements
package/dist/cli.js CHANGED
@@ -110,6 +110,13 @@ var init_logger = __esm(() => {
110
110
  });
111
111
 
112
112
  // src/db/schema.ts
113
+ var exports_schema = {};
114
+ __export(exports_schema, {
115
+ initDatabase: () => initDatabase,
116
+ getDbPath: () => getDbPath,
117
+ getDatabase: () => getDatabase,
118
+ closeDatabase: () => closeDatabase
119
+ });
113
120
  import { Database } from "bun:sqlite";
114
121
  import { existsSync as existsSync2, mkdirSync as mkdirSync2 } from "fs";
115
122
  import { homedir as homedir2 } from "os";
@@ -242,6 +249,12 @@ function getDatabase() {
242
249
  }
243
250
  return _db;
244
251
  }
252
+ function closeDatabase() {
253
+ if (_db) {
254
+ _db.close();
255
+ _db = null;
256
+ }
257
+ }
245
258
  var log, CREATE_TABLES = `
246
259
  -- Migration version tracking
247
260
  CREATE TABLE IF NOT EXISTS schema_versions (
@@ -1954,29 +1967,32 @@ import { spawnSync } from "child_process";
1954
1967
  var CLAUDE_DIR = join5(homedir5(), ".claude");
1955
1968
  var SETTINGS_PATH = join5(CLAUDE_DIR, "settings.json");
1956
1969
  var PKG_DIR = resolve(dirname(import.meta.dir));
1970
+ function shellPath(p) {
1971
+ const normalized = p.replace(/\\/g, "/");
1972
+ return normalized.includes(" ") ? `"${normalized}"` : normalized;
1973
+ }
1957
1974
  function getBunPath() {
1958
1975
  const result = spawnSync(process.platform === "win32" ? "where" : "which", ["bun"], {
1959
1976
  encoding: "utf-8"
1960
1977
  });
1961
- const resolved = result.stdout?.trim().split(`
1962
- `)[0]?.trim();
1978
+ const resolved = result.stdout?.trim().split(/\r?\n/)[0]?.trim();
1963
1979
  if (resolved && existsSync5(resolved))
1964
- return resolved;
1980
+ return shellPath(resolved);
1965
1981
  const candidates = [
1966
1982
  join5(homedir5(), ".bun", "bin", "bun"),
1967
1983
  join5(homedir5(), ".bun", "bin", "bun.exe")
1968
1984
  ];
1969
1985
  for (const c of candidates) {
1970
1986
  if (existsSync5(c))
1971
- return c;
1987
+ return shellPath(c);
1972
1988
  }
1973
1989
  return "bun";
1974
1990
  }
1975
1991
  function getHookPath(hookName) {
1976
- return join5(PKG_DIR, "dist", "hooks", `${hookName}.js`);
1992
+ return shellPath(join5(PKG_DIR, "dist", "hooks", `${hookName}.js`));
1977
1993
  }
1978
1994
  function getMcpServerPath() {
1979
- return join5(PKG_DIR, "dist", "index.js");
1995
+ return shellPath(join5(PKG_DIR, "dist", "index.js"));
1980
1996
  }
1981
1997
  function loadSettings() {
1982
1998
  if (!existsSync5(SETTINGS_PATH))
@@ -2223,6 +2239,42 @@ switch (command) {
2223
2239
  console.log(`Deleted: ${result.sessions_deleted} sessions, ${result.entities_deleted} entities, ${result.embeddings_deleted} embeddings`);
2224
2240
  break;
2225
2241
  }
2242
+ case "prune": {
2243
+ const { getDatabase: getDatabase2 } = (init_schema(), __toCommonJS(exports_schema));
2244
+ const db = getDatabase2();
2245
+ const dryRun = process.argv.includes("--dry-run");
2246
+ console.log(`claude-memory-hub \u2014 prune low-quality summaries${dryRun ? " (dry run)" : ""}
2247
+ `);
2248
+ const garbage = db.query(`SELECT id, session_id, summary FROM long_term_summaries
2249
+ WHERE length(summary) < 50
2250
+ OR summary LIKE '%Session worked on%'
2251
+ OR summary LIKE '%Session in project%'
2252
+ OR summary LIKE '%<ide_%'
2253
+ OR summary LIKE '%<system-reminder>%'
2254
+ OR (files_touched = '[]' AND decisions = '[]' AND errors_fixed = '[]' AND length(summary) < 100)`).all();
2255
+ if (garbage.length === 0) {
2256
+ console.log(" No low-quality summaries found. Database is clean.");
2257
+ break;
2258
+ }
2259
+ console.log(` Found ${garbage.length} low-quality summaries:`);
2260
+ for (const g of garbage.slice(0, 10)) {
2261
+ console.log(` [${g.id}] "${g.summary.slice(0, 80)}${g.summary.length > 80 ? "..." : ""}"`);
2262
+ }
2263
+ if (garbage.length > 10)
2264
+ console.log(` ... and ${garbage.length - 10} more`);
2265
+ if (dryRun) {
2266
+ console.log(`
2267
+ Dry run \u2014 no changes made. Remove --dry-run to delete.`);
2268
+ } else {
2269
+ const ids = garbage.map((g) => g.id);
2270
+ const placeholders = ids.map(() => "?").join(",");
2271
+ db.run(`DELETE FROM long_term_summaries WHERE id IN (${placeholders})`, ids);
2272
+ db.run(`DELETE FROM embeddings WHERE doc_type = 'summary' AND doc_id IN (${placeholders})`, ids.map(String));
2273
+ console.log(`
2274
+ Deleted ${garbage.length} summaries + related embeddings.`);
2275
+ }
2276
+ break;
2277
+ }
2226
2278
  default:
2227
2279
  console.log(`claude-memory-hub \u2014 persistent memory for Claude Code
2228
2280
  `);
@@ -2237,6 +2289,7 @@ switch (command) {
2237
2289
  console.log(" export Export data as JSONL (--since T, --table T)");
2238
2290
  console.log(" import Import JSONL from stdin (--dry-run)");
2239
2291
  console.log(" cleanup Remove old data (--days N, default 90)");
2292
+ console.log(" prune Remove low-quality summaries (--dry-run)");
2240
2293
  console.log(`
2241
2294
  Usage: npx claude-memory-hub <command>`);
2242
2295
  break;
@@ -407,6 +407,11 @@ class SessionStore {
407
407
  WHERE session_id = ? AND entity_type IN ('file_read','file_modified','file_created')
408
408
  ORDER BY importance DESC, created_at DESC`).all(session_id).map((r) => r.entity_value);
409
409
  }
410
+ hasModifiedFiles(session_id) {
411
+ const row = this.db.query(`SELECT COUNT(*) as c FROM entities
412
+ WHERE session_id = ? AND entity_type IN ('file_modified','file_created') LIMIT 1`).get(session_id);
413
+ return (row?.c ?? 0) > 0;
414
+ }
410
415
  insertNote(note) {
411
416
  this.db.run("INSERT INTO session_notes(session_id, content, created_at) VALUES (?, ?, ?)", [note.session_id, note.content, note.created_at]);
412
417
  }
@@ -658,6 +663,9 @@ class SessionSummarizer {
658
663
  const notes = this.sessionStore.getSessionNotes(session_id).map((n) => n.content);
659
664
  if (files.length === 0 && errors.length === 0 && notes.length === 0)
660
665
  return;
666
+ const hasModified = this.sessionStore.hasModifiedFiles(session_id);
667
+ if (!hasModified && errors.length === 0 && decisions.length === 0 && notes.length === 0 && observations.length === 0)
668
+ return;
661
669
  const obsValues = observations.slice(0, 5).map((o) => o.entity_value);
662
670
  let summaryText;
663
671
  let tier = "rule-based";
@@ -1912,6 +1920,9 @@ function safeJson2(text, fallback) {
1912
1920
 
1913
1921
  // src/capture/hook-handler.ts
1914
1922
  import { basename as basename3 } from "path";
1923
+ function stripIdeTags(prompt) {
1924
+ return prompt.replace(/<ide_opened_file>[\s\S]*?<\/ide_opened_file>\s*/g, "").replace(/<ide_selection>[\s\S]*?<\/ide_selection>\s*/g, "").replace(/<system-reminder>[\s\S]*?<\/system-reminder>\s*/g, "").trim();
1925
+ }
1915
1926
  async function handlePostToolUse(hook, project) {
1916
1927
  const store = new SessionStore;
1917
1928
  store.upsertSession({
@@ -1947,14 +1958,15 @@ async function handlePostToolUse(hook, project) {
1947
1958
  async function handleUserPromptSubmit(hook, project) {
1948
1959
  const store = new SessionStore;
1949
1960
  const ltStore = new LongTermStore;
1961
+ const cleanPrompt = stripIdeTags(hook.prompt);
1950
1962
  store.upsertSession({
1951
1963
  id: hook.session_id,
1952
1964
  project,
1953
1965
  started_at: Date.now(),
1954
- user_prompt: hook.prompt.slice(0, 500),
1966
+ user_prompt: cleanPrompt.slice(0, 500) || hook.prompt.slice(0, 500),
1955
1967
  status: "active"
1956
1968
  });
1957
- const promptObs = extractObservationFromPrompt(hook.prompt, hook.session_id, project, 0);
1969
+ const promptObs = extractObservationFromPrompt(cleanPrompt || hook.prompt, hook.session_id, project, 0);
1958
1970
  if (promptObs)
1959
1971
  store.insertEntity({ ...promptObs, project });
1960
1972
  const results = ltStore.search(hook.prompt, 3);
@@ -407,6 +407,11 @@ class SessionStore {
407
407
  WHERE session_id = ? AND entity_type IN ('file_read','file_modified','file_created')
408
408
  ORDER BY importance DESC, created_at DESC`).all(session_id).map((r) => r.entity_value);
409
409
  }
410
+ hasModifiedFiles(session_id) {
411
+ const row = this.db.query(`SELECT COUNT(*) as c FROM entities
412
+ WHERE session_id = ? AND entity_type IN ('file_modified','file_created') LIMIT 1`).get(session_id);
413
+ return (row?.c ?? 0) > 0;
414
+ }
410
415
  insertNote(note) {
411
416
  this.db.run("INSERT INTO session_notes(session_id, content, created_at) VALUES (?, ?, ?)", [note.session_id, note.content, note.created_at]);
412
417
  }
@@ -1619,6 +1624,9 @@ function safeJson2(text, fallback) {
1619
1624
 
1620
1625
  // src/capture/hook-handler.ts
1621
1626
  import { basename as basename3 } from "path";
1627
+ function stripIdeTags(prompt) {
1628
+ return prompt.replace(/<ide_opened_file>[\s\S]*?<\/ide_opened_file>\s*/g, "").replace(/<ide_selection>[\s\S]*?<\/ide_selection>\s*/g, "").replace(/<system-reminder>[\s\S]*?<\/system-reminder>\s*/g, "").trim();
1629
+ }
1622
1630
  async function handlePostToolUse(hook, project) {
1623
1631
  const store = new SessionStore;
1624
1632
  store.upsertSession({
@@ -1654,14 +1662,15 @@ async function handlePostToolUse(hook, project) {
1654
1662
  async function handleUserPromptSubmit(hook, project) {
1655
1663
  const store = new SessionStore;
1656
1664
  const ltStore = new LongTermStore;
1665
+ const cleanPrompt = stripIdeTags(hook.prompt);
1657
1666
  store.upsertSession({
1658
1667
  id: hook.session_id,
1659
1668
  project,
1660
1669
  started_at: Date.now(),
1661
- user_prompt: hook.prompt.slice(0, 500),
1670
+ user_prompt: cleanPrompt.slice(0, 500) || hook.prompt.slice(0, 500),
1662
1671
  status: "active"
1663
1672
  });
1664
- const promptObs = extractObservationFromPrompt(hook.prompt, hook.session_id, project, 0);
1673
+ const promptObs = extractObservationFromPrompt(cleanPrompt || hook.prompt, hook.session_id, project, 0);
1665
1674
  if (promptObs)
1666
1675
  store.insertEntity({ ...promptObs, project });
1667
1676
  const results = ltStore.search(hook.prompt, 3);
@@ -407,6 +407,11 @@ class SessionStore {
407
407
  WHERE session_id = ? AND entity_type IN ('file_read','file_modified','file_created')
408
408
  ORDER BY importance DESC, created_at DESC`).all(session_id).map((r) => r.entity_value);
409
409
  }
410
+ hasModifiedFiles(session_id) {
411
+ const row = this.db.query(`SELECT COUNT(*) as c FROM entities
412
+ WHERE session_id = ? AND entity_type IN ('file_modified','file_created') LIMIT 1`).get(session_id);
413
+ return (row?.c ?? 0) > 0;
414
+ }
410
415
  insertNote(note) {
411
416
  this.db.run("INSERT INTO session_notes(session_id, content, created_at) VALUES (?, ?, ?)", [note.session_id, note.content, note.created_at]);
412
417
  }
@@ -658,6 +663,9 @@ class SessionSummarizer {
658
663
  const notes = this.sessionStore.getSessionNotes(session_id).map((n) => n.content);
659
664
  if (files.length === 0 && errors.length === 0 && notes.length === 0)
660
665
  return;
666
+ const hasModified = this.sessionStore.hasModifiedFiles(session_id);
667
+ if (!hasModified && errors.length === 0 && decisions.length === 0 && notes.length === 0 && observations.length === 0)
668
+ return;
661
669
  const obsValues = observations.slice(0, 5).map((o) => o.entity_value);
662
670
  let summaryText;
663
671
  let tier = "rule-based";
@@ -1912,6 +1920,9 @@ function safeJson2(text, fallback) {
1912
1920
 
1913
1921
  // src/capture/hook-handler.ts
1914
1922
  import { basename as basename3 } from "path";
1923
+ function stripIdeTags(prompt) {
1924
+ return prompt.replace(/<ide_opened_file>[\s\S]*?<\/ide_opened_file>\s*/g, "").replace(/<ide_selection>[\s\S]*?<\/ide_selection>\s*/g, "").replace(/<system-reminder>[\s\S]*?<\/system-reminder>\s*/g, "").trim();
1925
+ }
1915
1926
  async function handlePostToolUse(hook, project) {
1916
1927
  const store = new SessionStore;
1917
1928
  store.upsertSession({
@@ -1947,14 +1958,15 @@ async function handlePostToolUse(hook, project) {
1947
1958
  async function handleUserPromptSubmit(hook, project) {
1948
1959
  const store = new SessionStore;
1949
1960
  const ltStore = new LongTermStore;
1961
+ const cleanPrompt = stripIdeTags(hook.prompt);
1950
1962
  store.upsertSession({
1951
1963
  id: hook.session_id,
1952
1964
  project,
1953
1965
  started_at: Date.now(),
1954
- user_prompt: hook.prompt.slice(0, 500),
1966
+ user_prompt: cleanPrompt.slice(0, 500) || hook.prompt.slice(0, 500),
1955
1967
  status: "active"
1956
1968
  });
1957
- const promptObs = extractObservationFromPrompt(hook.prompt, hook.session_id, project, 0);
1969
+ const promptObs = extractObservationFromPrompt(cleanPrompt || hook.prompt, hook.session_id, project, 0);
1958
1970
  if (promptObs)
1959
1971
  store.insertEntity({ ...promptObs, project });
1960
1972
  const results = ltStore.search(hook.prompt, 3);
@@ -407,6 +407,11 @@ class SessionStore {
407
407
  WHERE session_id = ? AND entity_type IN ('file_read','file_modified','file_created')
408
408
  ORDER BY importance DESC, created_at DESC`).all(session_id).map((r) => r.entity_value);
409
409
  }
410
+ hasModifiedFiles(session_id) {
411
+ const row = this.db.query(`SELECT COUNT(*) as c FROM entities
412
+ WHERE session_id = ? AND entity_type IN ('file_modified','file_created') LIMIT 1`).get(session_id);
413
+ return (row?.c ?? 0) > 0;
414
+ }
410
415
  insertNote(note) {
411
416
  this.db.run("INSERT INTO session_notes(session_id, content, created_at) VALUES (?, ?, ?)", [note.session_id, note.content, note.created_at]);
412
417
  }
@@ -1619,6 +1624,9 @@ function safeJson2(text, fallback) {
1619
1624
 
1620
1625
  // src/capture/hook-handler.ts
1621
1626
  import { basename as basename3 } from "path";
1627
+ function stripIdeTags(prompt) {
1628
+ return prompt.replace(/<ide_opened_file>[\s\S]*?<\/ide_opened_file>\s*/g, "").replace(/<ide_selection>[\s\S]*?<\/ide_selection>\s*/g, "").replace(/<system-reminder>[\s\S]*?<\/system-reminder>\s*/g, "").trim();
1629
+ }
1622
1630
  async function handlePostToolUse(hook, project) {
1623
1631
  const store = new SessionStore;
1624
1632
  store.upsertSession({
@@ -1654,14 +1662,15 @@ async function handlePostToolUse(hook, project) {
1654
1662
  async function handleUserPromptSubmit(hook, project) {
1655
1663
  const store = new SessionStore;
1656
1664
  const ltStore = new LongTermStore;
1665
+ const cleanPrompt = stripIdeTags(hook.prompt);
1657
1666
  store.upsertSession({
1658
1667
  id: hook.session_id,
1659
1668
  project,
1660
1669
  started_at: Date.now(),
1661
- user_prompt: hook.prompt.slice(0, 500),
1670
+ user_prompt: cleanPrompt.slice(0, 500) || hook.prompt.slice(0, 500),
1662
1671
  status: "active"
1663
1672
  });
1664
- const promptObs = extractObservationFromPrompt(hook.prompt, hook.session_id, project, 0);
1673
+ const promptObs = extractObservationFromPrompt(cleanPrompt || hook.prompt, hook.session_id, project, 0);
1665
1674
  if (promptObs)
1666
1675
  store.insertEntity({ ...promptObs, project });
1667
1676
  const results = ltStore.search(hook.prompt, 3);
@@ -1936,6 +1945,9 @@ class SessionSummarizer {
1936
1945
  const notes = this.sessionStore.getSessionNotes(session_id).map((n) => n.content);
1937
1946
  if (files.length === 0 && errors.length === 0 && notes.length === 0)
1938
1947
  return;
1948
+ const hasModified = this.sessionStore.hasModifiedFiles(session_id);
1949
+ if (!hasModified && errors.length === 0 && decisions.length === 0 && notes.length === 0 && observations.length === 0)
1950
+ return;
1939
1951
  const obsValues = observations.slice(0, 5).map((o) => o.entity_value);
1940
1952
  let summaryText;
1941
1953
  let tier = "rule-based";
@@ -407,6 +407,11 @@ class SessionStore {
407
407
  WHERE session_id = ? AND entity_type IN ('file_read','file_modified','file_created')
408
408
  ORDER BY importance DESC, created_at DESC`).all(session_id).map((r) => r.entity_value);
409
409
  }
410
+ hasModifiedFiles(session_id) {
411
+ const row = this.db.query(`SELECT COUNT(*) as c FROM entities
412
+ WHERE session_id = ? AND entity_type IN ('file_modified','file_created') LIMIT 1`).get(session_id);
413
+ return (row?.c ?? 0) > 0;
414
+ }
410
415
  insertNote(note) {
411
416
  this.db.run("INSERT INTO session_notes(session_id, content, created_at) VALUES (?, ?, ?)", [note.session_id, note.content, note.created_at]);
412
417
  }
@@ -1619,6 +1624,9 @@ function safeJson2(text, fallback) {
1619
1624
 
1620
1625
  // src/capture/hook-handler.ts
1621
1626
  import { basename as basename3 } from "path";
1627
+ function stripIdeTags(prompt) {
1628
+ return prompt.replace(/<ide_opened_file>[\s\S]*?<\/ide_opened_file>\s*/g, "").replace(/<ide_selection>[\s\S]*?<\/ide_selection>\s*/g, "").replace(/<system-reminder>[\s\S]*?<\/system-reminder>\s*/g, "").trim();
1629
+ }
1622
1630
  async function handlePostToolUse(hook, project) {
1623
1631
  const store = new SessionStore;
1624
1632
  store.upsertSession({
@@ -1654,14 +1662,15 @@ async function handlePostToolUse(hook, project) {
1654
1662
  async function handleUserPromptSubmit(hook, project) {
1655
1663
  const store = new SessionStore;
1656
1664
  const ltStore = new LongTermStore;
1665
+ const cleanPrompt = stripIdeTags(hook.prompt);
1657
1666
  store.upsertSession({
1658
1667
  id: hook.session_id,
1659
1668
  project,
1660
1669
  started_at: Date.now(),
1661
- user_prompt: hook.prompt.slice(0, 500),
1670
+ user_prompt: cleanPrompt.slice(0, 500) || hook.prompt.slice(0, 500),
1662
1671
  status: "active"
1663
1672
  });
1664
- const promptObs = extractObservationFromPrompt(hook.prompt, hook.session_id, project, 0);
1673
+ const promptObs = extractObservationFromPrompt(cleanPrompt || hook.prompt, hook.session_id, project, 0);
1665
1674
  if (promptObs)
1666
1675
  store.insertEntity({ ...promptObs, project });
1667
1676
  const results = ltStore.search(hook.prompt, 3);
package/dist/index.js CHANGED
@@ -14171,6 +14171,11 @@ class SessionStore {
14171
14171
  WHERE session_id = ? AND entity_type IN ('file_read','file_modified','file_created')
14172
14172
  ORDER BY importance DESC, created_at DESC`).all(session_id).map((r) => r.entity_value);
14173
14173
  }
14174
+ hasModifiedFiles(session_id) {
14175
+ const row = this.db.query(`SELECT COUNT(*) as c FROM entities
14176
+ WHERE session_id = ? AND entity_type IN ('file_modified','file_created') LIMIT 1`).get(session_id);
14177
+ return (row?.c ?? 0) > 0;
14178
+ }
14174
14179
  insertNote(note) {
14175
14180
  this.db.run("INSERT INTO session_notes(session_id, content, created_at) VALUES (?, ?, ?)", [note.session_id, note.content, note.created_at]);
14176
14181
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-memory-hub",
3
- "version": "0.9.2",
3
+ "version": "0.9.4",
4
4
  "description": "Persistent memory system for Claude Code. Zero API key. Zero Python. 5 hooks + MCP server + SQLite FTS5 + semantic search.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",