claude-memory-hub 0.9.4 → 0.9.5

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,33 @@ Format follows [Keep a Changelog](https://keepachangelog.com/).
5
5
 
6
6
  ---
7
7
 
8
+ ## [0.9.5] - 2026-04-03
9
+
10
+ Stable install path — hooks no longer break after reboot or bunx cache cleanup.
11
+
12
+ ### Bug Fixes
13
+
14
+ - **Hooks pointing to temp `bunx` path** — `bunx claude-memory-hub install` registered hooks at `/private/tmp/bunx-*/...` (macOS) or `%TEMP%/bunx-*/...` (Windows). These paths are ephemeral and get deleted on reboot or cache cleanup, causing **all hooks to silently fail** — sessions stop being captured with no error visible to the user
15
+ - **Install now copies `dist/` to `~/.claude-memory-hub/dist/`** — a stable, persistent location under the user's home directory. Both hooks and MCP server reference this path instead of the package install location
16
+ - **Old hook entries auto-replaced** — `install` removes previous claude-memory-hub hook entries before registering new ones, fixing stale paths from prior installs without manual cleanup
17
+ - **`install.sh` updated** — shell-based installer uses the same stable path strategy with full bun binary resolution
18
+
19
+ ### How It Works
20
+
21
+ ```
22
+ bunx claude-memory-hub install
23
+ 1. Downloads package to temp dir (bunx behavior)
24
+ 2. Copies dist/*.js + dist/hooks/*.js → ~/.claude-memory-hub/dist/ ← NEW
25
+ 3. Registers hooks pointing to ~/.claude-memory-hub/dist/hooks/ ← STABLE
26
+ 4. Registers MCP server pointing to ~/.claude-memory-hub/dist/index.js
27
+ ```
28
+
29
+ ### Upgrade Note
30
+
31
+ Run `bunx claude-memory-hub@latest install` to fix broken hooks. No data loss — only hook paths are updated.
32
+
33
+ ---
34
+
8
35
  ## [0.9.4] - 2026-04-02
9
36
 
10
37
  Windows path fix — backslashes no longer eaten by bash.
package/dist/cli.js CHANGED
@@ -1669,7 +1669,7 @@ var init_importer = __esm(() => {
1669
1669
  });
1670
1670
 
1671
1671
  // src/cli/main.ts
1672
- import { existsSync as existsSync5, mkdirSync as mkdirSync3, readFileSync, writeFileSync } from "fs";
1672
+ import { existsSync as existsSync5, mkdirSync as mkdirSync3, readFileSync, writeFileSync, readdirSync } from "fs";
1673
1673
  import { homedir as homedir5 } from "os";
1674
1674
  import { join as join5, resolve, dirname } from "path";
1675
1675
 
@@ -1967,6 +1967,7 @@ import { spawnSync } from "child_process";
1967
1967
  var CLAUDE_DIR = join5(homedir5(), ".claude");
1968
1968
  var SETTINGS_PATH = join5(CLAUDE_DIR, "settings.json");
1969
1969
  var PKG_DIR = resolve(dirname(import.meta.dir));
1970
+ var STABLE_DIR = join5(homedir5(), ".claude-memory-hub");
1970
1971
  function shellPath(p) {
1971
1972
  const normalized = p.replace(/\\/g, "/");
1972
1973
  return normalized.includes(" ") ? `"${normalized}"` : normalized;
@@ -1988,11 +1989,37 @@ function getBunPath() {
1988
1989
  }
1989
1990
  return "bun";
1990
1991
  }
1992
+ function copyDistToStableDir() {
1993
+ const srcDist = join5(PKG_DIR, "dist");
1994
+ const destDist = join5(STABLE_DIR, "dist");
1995
+ if (!existsSync5(srcDist)) {
1996
+ throw new Error(`dist/ not found at ${srcDist}. Run 'bun run build:all' first.`);
1997
+ }
1998
+ const destHooks = join5(destDist, "hooks");
1999
+ mkdirSync3(destHooks, { recursive: true });
2000
+ for (const file of readdirSync(srcDist)) {
2001
+ if (file.endsWith(".js")) {
2002
+ const src = join5(srcDist, file);
2003
+ const dest = join5(destDist, file);
2004
+ writeFileSync(dest, readFileSync(src));
2005
+ }
2006
+ }
2007
+ const srcHooks = join5(srcDist, "hooks");
2008
+ if (existsSync5(srcHooks)) {
2009
+ for (const file of readdirSync(srcHooks)) {
2010
+ if (file.endsWith(".js")) {
2011
+ const src = join5(srcHooks, file);
2012
+ const dest = join5(destHooks, file);
2013
+ writeFileSync(dest, readFileSync(src));
2014
+ }
2015
+ }
2016
+ }
2017
+ }
1991
2018
  function getHookPath(hookName) {
1992
- return shellPath(join5(PKG_DIR, "dist", "hooks", `${hookName}.js`));
2019
+ return shellPath(join5(STABLE_DIR, "dist", "hooks", `${hookName}.js`));
1993
2020
  }
1994
2021
  function getMcpServerPath() {
1995
- return shellPath(join5(PKG_DIR, "dist", "index.js"));
2022
+ return shellPath(join5(STABLE_DIR, "dist", "index.js"));
1996
2023
  }
1997
2024
  function loadSettings() {
1998
2025
  if (!existsSync5(SETTINGS_PATH))
@@ -2012,7 +2039,16 @@ function saveSettings(settings) {
2012
2039
  function install() {
2013
2040
  console.log(`claude-memory-hub \u2014 install
2014
2041
  `);
2015
- console.log("1. Registering MCP server...");
2042
+ console.log("0. Copying dist/ to ~/.claude-memory-hub/dist/...");
2043
+ try {
2044
+ copyDistToStableDir();
2045
+ console.log(" Files copied to stable location.");
2046
+ } catch (e) {
2047
+ console.error(` Failed to copy dist/: ${e}`);
2048
+ console.error(" Hooks will reference package location (may break after bunx cleanup).");
2049
+ }
2050
+ console.log(`
2051
+ 1. Registering MCP server...`);
2016
2052
  const mcpPath = getMcpServerPath();
2017
2053
  const bunBin = getBunPath();
2018
2054
  const result = spawnSync("claude", ["mcp", "add", "claude-memory-hub", "-s", "user", "--", bunBin, "run", mcpPath], {
@@ -2044,14 +2080,12 @@ function install() {
2044
2080
  for (const [event, scriptPath] of hookEntries) {
2045
2081
  const hooks = settings.hooks;
2046
2082
  hooks[event] ??= [];
2047
- const exists = hooks[event].some((e) => JSON.stringify(e).includes("claude-memory-hub"));
2048
- if (!exists) {
2049
- hooks[event].push({
2050
- matcher: "",
2051
- hooks: [{ type: "command", command: `${bunBin} run ${scriptPath}` }]
2052
- });
2053
- registered++;
2054
- }
2083
+ hooks[event] = hooks[event].filter((e) => !JSON.stringify(e).includes("claude-memory-hub"));
2084
+ hooks[event].push({
2085
+ matcher: "",
2086
+ hooks: [{ type: "command", command: `${bunBin} run ${scriptPath}` }]
2087
+ });
2088
+ registered++;
2055
2089
  }
2056
2090
  saveSettings(settings);
2057
2091
  console.log(` ${registered} hook(s) registered. (${5 - registered} already existed)`);
@@ -376,6 +376,11 @@ class SessionStore {
376
376
  this.db.run("UPDATE sessions SET status = 'completed', ended_at = ? WHERE id = ?", [Date.now(), id]);
377
377
  }
378
378
  insertEntity(entity) {
379
+ if (entity.entity_type === "decision" || entity.entity_type === "observation") {
380
+ const existing = this.db.query("SELECT COUNT(*) as c FROM entities WHERE session_id = ? AND entity_type = ? AND entity_value = ?").get(entity.session_id, entity.entity_type, entity.entity_value);
381
+ if (existing && existing.c > 0)
382
+ return -1;
383
+ }
379
384
  const result = this.db.run(`INSERT INTO entities(session_id, project, tool_name, entity_type, entity_value, context, importance, created_at, prompt_number)
380
385
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
381
386
  entity.session_id,
@@ -376,6 +376,11 @@ class SessionStore {
376
376
  this.db.run("UPDATE sessions SET status = 'completed', ended_at = ? WHERE id = ?", [Date.now(), id]);
377
377
  }
378
378
  insertEntity(entity) {
379
+ if (entity.entity_type === "decision" || entity.entity_type === "observation") {
380
+ const existing = this.db.query("SELECT COUNT(*) as c FROM entities WHERE session_id = ? AND entity_type = ? AND entity_value = ?").get(entity.session_id, entity.entity_type, entity.entity_value);
381
+ if (existing && existing.c > 0)
382
+ return -1;
383
+ }
379
384
  const result = this.db.run(`INSERT INTO entities(session_id, project, tool_name, entity_type, entity_value, context, importance, created_at, prompt_number)
380
385
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
381
386
  entity.session_id,
@@ -376,6 +376,11 @@ class SessionStore {
376
376
  this.db.run("UPDATE sessions SET status = 'completed', ended_at = ? WHERE id = ?", [Date.now(), id]);
377
377
  }
378
378
  insertEntity(entity) {
379
+ if (entity.entity_type === "decision" || entity.entity_type === "observation") {
380
+ const existing = this.db.query("SELECT COUNT(*) as c FROM entities WHERE session_id = ? AND entity_type = ? AND entity_value = ?").get(entity.session_id, entity.entity_type, entity.entity_value);
381
+ if (existing && existing.c > 0)
382
+ return -1;
383
+ }
379
384
  const result = this.db.run(`INSERT INTO entities(session_id, project, tool_name, entity_type, entity_value, context, importance, created_at, prompt_number)
380
385
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
381
386
  entity.session_id,
@@ -376,6 +376,11 @@ class SessionStore {
376
376
  this.db.run("UPDATE sessions SET status = 'completed', ended_at = ? WHERE id = ?", [Date.now(), id]);
377
377
  }
378
378
  insertEntity(entity) {
379
+ if (entity.entity_type === "decision" || entity.entity_type === "observation") {
380
+ const existing = this.db.query("SELECT COUNT(*) as c FROM entities WHERE session_id = ? AND entity_type = ? AND entity_value = ?").get(entity.session_id, entity.entity_type, entity.entity_value);
381
+ if (existing && existing.c > 0)
382
+ return -1;
383
+ }
379
384
  const result = this.db.run(`INSERT INTO entities(session_id, project, tool_name, entity_type, entity_value, context, importance, created_at, prompt_number)
380
385
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
381
386
  entity.session_id,
@@ -376,6 +376,11 @@ class SessionStore {
376
376
  this.db.run("UPDATE sessions SET status = 'completed', ended_at = ? WHERE id = ?", [Date.now(), id]);
377
377
  }
378
378
  insertEntity(entity) {
379
+ if (entity.entity_type === "decision" || entity.entity_type === "observation") {
380
+ const existing = this.db.query("SELECT COUNT(*) as c FROM entities WHERE session_id = ? AND entity_type = ? AND entity_value = ?").get(entity.session_id, entity.entity_type, entity.entity_value);
381
+ if (existing && existing.c > 0)
382
+ return -1;
383
+ }
379
384
  const result = this.db.run(`INSERT INTO entities(session_id, project, tool_name, entity_type, entity_value, context, importance, created_at, prompt_number)
380
385
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
381
386
  entity.session_id,
package/dist/index.js CHANGED
@@ -14140,6 +14140,11 @@ class SessionStore {
14140
14140
  this.db.run("UPDATE sessions SET status = 'completed', ended_at = ? WHERE id = ?", [Date.now(), id]);
14141
14141
  }
14142
14142
  insertEntity(entity) {
14143
+ if (entity.entity_type === "decision" || entity.entity_type === "observation") {
14144
+ const existing = this.db.query("SELECT COUNT(*) as c FROM entities WHERE session_id = ? AND entity_type = ? AND entity_value = ?").get(entity.session_id, entity.entity_type, entity.entity_value);
14145
+ if (existing && existing.c > 0)
14146
+ return -1;
14147
+ }
14143
14148
  const result = this.db.run(`INSERT INTO entities(session_id, project, tool_name, entity_type, entity_value, context, importance, created_at, prompt_number)
14144
14149
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
14145
14150
  entity.session_id,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-memory-hub",
3
- "version": "0.9.4",
3
+ "version": "0.9.5",
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",