claude-memory-hub 0.11.0 → 0.11.1

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,44 @@ Format follows [Keep a Changelog](https://keepachangelog.com/).
5
5
 
6
6
  ---
7
7
 
8
+ ## [0.11.1] - 2026-04-03
9
+
10
+ Quality hardening — more tests, bugfixes, and robustness improvements for v0.11.0 features.
11
+
12
+ ### Bugfixes
13
+
14
+ - **Clock skew guard** — recency decay in search ranking now uses `Math.max(0, ageMs)` to prevent negative age values when `created_at` is in the future due to clock skew. Previously could produce unpredictable scoring
15
+ - **Import consistency** — `uninstallCommands()` in `cli/main.ts` now uses top-level `unlinkSync` import instead of inline `require("fs").unlinkSync`. Cleaner code, consistent with rest of the file
16
+
17
+ ### Testing
18
+
19
+ - **Privacy filter tests** — 35 new unit tests covering all 3 privacy layers:
20
+ - Layer 1: `<private>` tag stripping (single, multiple, multiline, case-insensitive, disable toggle)
21
+ - Layer 2: Secret detection (sk-, ghp_, gho_, Bearer, AKIA, passwords, private keys, hex secrets, short value safety, normal code safety)
22
+ - Layer 3: Path filtering (.env, .env.*, .pem, .key, .p12, credentials, secrets/**, private/**, Windows paths, custom paths)
23
+ - Custom patterns (user-defined regex, invalid pattern graceful handling)
24
+ - Combined layers (tags + secrets in same text)
25
+ - **Tokenizer tests** — 12 new unit tests for code-aware tokenizer:
26
+ - camelCase splitting (`getUserName` → get, user, name)
27
+ - PascalCase splitting (`AuthController` → auth, controller)
28
+ - Acronym handling (`HTMLParser` → html, parser)
29
+ - snake_case splitting + compound preservation (`user_auth_service` → user, auth, service, user_auth_service)
30
+ - File path splitting (`/src/hooks/auth.ts` → src, hooks, auth, ts)
31
+ - Stop word filtering (code keywords + English)
32
+ - Mixed code content, empty input, short tokens
33
+ - **Test count: 108 → 155** (+44% coverage increase)
34
+
35
+ ### Files Changed
36
+
37
+ | File | Change |
38
+ |------|--------|
39
+ | `src/search/search-workflow.ts` | Clock skew guard on recency decay |
40
+ | `src/cli/main.ts` | Import `unlinkSync` properly |
41
+ | `tests/unit/privacy-filter.test.ts` | **New** — 35 tests for privacy filtering |
42
+ | `tests/unit/vector-search.test.ts` | +12 tokenizer edge case tests |
43
+
44
+ ---
45
+
8
46
  ## [0.11.0] - 2026-04-03
9
47
 
10
48
  Privacy-first memory, smarter search, and slash commands. Three features the community asked for most.
package/README.md CHANGED
@@ -520,6 +520,7 @@ Migration is idempotent — safe to run multiple times with zero duplicates.
520
520
  | **v0.9.6** | Agent/Skill result capture, higher summary limits, IDE tag stripping, PostCompact cap, broader observation patterns (20+) |
521
521
  | **v0.10.0** | **Full conversation capture** — all user prompts + assistant responses via transcript parsing, `messages` table with FTS5, `memory_conversation` MCP tool, conversation-enriched summaries |
522
522
  | **v0.11.0** | **Privacy + Search + Commands** — 3-layer privacy filtering (`<private>` tags, auto secret detection, path filtering), code-aware tokenizer (camelCase/snake_case/path splitting), recency-aware ranking (7d/30d/90d decay), RRF multi-source fusion, slash commands (`/mem-search`, `/mem-status`, `/mem-save`), improved MCP tool descriptions with AUTO-USE hints |
523
+ | **v0.11.1** | **Quality hardening** — clock skew guard for recency ranking, import consistency fix, 47 new unit tests (privacy filter + tokenizer edge cases), test count 108 → 155 (+44%) |
523
524
 
524
525
  See [CHANGELOG.md](CHANGELOG.md) for full details.
525
526
 
package/dist/cli.js CHANGED
@@ -859,7 +859,7 @@ __export(exports_vector_search, {
859
859
  });
860
860
  function tokenize(text) {
861
861
  const tokens = [];
862
- const camelSplit = text.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2");
862
+ const camelSplit = text.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2").replace(/([A-Z])([A-Z][a-z])/g, "$1 $2");
863
863
  const words = camelSplit.toLowerCase().replace(/[^a-z0-9_./\-]/g, " ").split(/\s+/).filter(Boolean);
864
864
  for (const word of words) {
865
865
  if (word.includes("/") && word.length > 3) {
@@ -1223,7 +1223,7 @@ async function searchIndex(query, opts = {}, db) {
1223
1223
  const now = Date.now();
1224
1224
  const merged = [...deduped.values()].map((r) => {
1225
1225
  let score = r.score;
1226
- const ageMs = now - r.created_at;
1226
+ const ageMs = Math.max(0, now - r.created_at);
1227
1227
  const ageDays = ageMs / (1000 * 60 * 60 * 24);
1228
1228
  if (ageDays < 7)
1229
1229
  score *= 1.5;
@@ -1820,7 +1820,7 @@ var init_importer = __esm(() => {
1820
1820
  });
1821
1821
 
1822
1822
  // src/cli/main.ts
1823
- import { existsSync as existsSync5, mkdirSync as mkdirSync3, readFileSync, writeFileSync, readdirSync } from "fs";
1823
+ import { existsSync as existsSync5, mkdirSync as mkdirSync3, readFileSync, writeFileSync, readdirSync, unlinkSync } from "fs";
1824
1824
  import { homedir as homedir5 } from "os";
1825
1825
  import { join as join5, resolve, dirname } from "path";
1826
1826
 
@@ -2210,7 +2210,7 @@ function uninstallCommands() {
2210
2210
  const p = join5(COMMANDS_DIR, file);
2211
2211
  try {
2212
2212
  if (existsSync5(p))
2213
- __require("fs").unlinkSync(p);
2213
+ unlinkSync(p);
2214
2214
  } catch {}
2215
2215
  }
2216
2216
  }
package/dist/index.js CHANGED
@@ -15539,7 +15539,7 @@ var STOP_WORDS = new Set([
15539
15539
  ]);
15540
15540
  function tokenize(text) {
15541
15541
  const tokens = [];
15542
- const camelSplit = text.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2");
15542
+ const camelSplit = text.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2").replace(/([A-Z])([A-Z][a-z])/g, "$1 $2");
15543
15543
  const words = camelSplit.toLowerCase().replace(/[^a-z0-9_./\-]/g, " ").split(/\s+/).filter(Boolean);
15544
15544
  for (const word of words) {
15545
15545
  if (word.includes("/") && word.length > 3) {
@@ -15685,7 +15685,7 @@ async function searchIndex(query, opts = {}, db) {
15685
15685
  const now = Date.now();
15686
15686
  const merged = [...deduped.values()].map((r) => {
15687
15687
  let score = r.score;
15688
- const ageMs = now - r.created_at;
15688
+ const ageMs = Math.max(0, now - r.created_at);
15689
15689
  const ageDays = ageMs / (1000 * 60 * 60 * 24);
15690
15690
  if (ageDays < 7)
15691
15691
  score *= 1.5;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-memory-hub",
3
- "version": "0.11.0",
3
+ "version": "0.11.1",
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",