persisted-memory 1.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 (56) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +281 -0
  3. package/dist/cli/generate-summary.d.ts +1 -0
  4. package/dist/cli/generate-summary.js +13 -0
  5. package/dist/cli/generate-summary.js.map +1 -0
  6. package/dist/cli/viewer.d.ts +1 -0
  7. package/dist/cli/viewer.js +4 -0
  8. package/dist/cli/viewer.js.map +1 -0
  9. package/dist/embeddings/ollama.d.ts +3 -0
  10. package/dist/embeddings/ollama.js +63 -0
  11. package/dist/embeddings/ollama.js.map +1 -0
  12. package/dist/index.d.ts +2 -0
  13. package/dist/index.js +25 -0
  14. package/dist/index.js.map +1 -0
  15. package/dist/search/hybrid.d.ts +8 -0
  16. package/dist/search/hybrid.js +54 -0
  17. package/dist/search/hybrid.js.map +1 -0
  18. package/dist/server.d.ts +2 -0
  19. package/dist/server.js +399 -0
  20. package/dist/server.js.map +1 -0
  21. package/dist/storage/knowledge-graph.d.ts +32 -0
  22. package/dist/storage/knowledge-graph.js +259 -0
  23. package/dist/storage/knowledge-graph.js.map +1 -0
  24. package/dist/storage/lance-store.d.ts +21 -0
  25. package/dist/storage/lance-store.js +288 -0
  26. package/dist/storage/lance-store.js.map +1 -0
  27. package/dist/storage/markdown-store.d.ts +7 -0
  28. package/dist/storage/markdown-store.js +63 -0
  29. package/dist/storage/markdown-store.js.map +1 -0
  30. package/dist/storage/types.d.ts +19 -0
  31. package/dist/storage/types.js +13 -0
  32. package/dist/storage/types.js.map +1 -0
  33. package/dist/utils/chunking.d.ts +1 -0
  34. package/dist/utils/chunking.js +55 -0
  35. package/dist/utils/chunking.js.map +1 -0
  36. package/dist/utils/privacy.d.ts +13 -0
  37. package/dist/utils/privacy.js +23 -0
  38. package/dist/utils/privacy.js.map +1 -0
  39. package/dist/utils/project.d.ts +3 -0
  40. package/dist/utils/project.js +11 -0
  41. package/dist/utils/project.js.map +1 -0
  42. package/dist/utils/summarize.d.ts +12 -0
  43. package/dist/utils/summarize.js +123 -0
  44. package/dist/utils/summarize.js.map +1 -0
  45. package/dist/viewer/index.html +328 -0
  46. package/dist/viewer/server.d.ts +1 -0
  47. package/dist/viewer/server.js +203 -0
  48. package/dist/viewer/server.js.map +1 -0
  49. package/hooks/on-post-tool.sh +17 -0
  50. package/hooks/on-pre-compact.sh +20 -0
  51. package/hooks/on-session-end.sh +46 -0
  52. package/hooks/on-session-start.sh +14 -0
  53. package/hooks/on-stop.sh +6 -0
  54. package/package.json +60 -0
  55. package/scripts/install.sh +125 -0
  56. package/scripts/uninstall.sh +59 -0
package/package.json ADDED
@@ -0,0 +1,60 @@
1
+ {
2
+ "name": "persisted-memory",
3
+ "version": "1.0.0",
4
+ "description": "Persistent memory system for Claude Code via MCP — dual-write to Markdown + LanceDB with hybrid semantic search",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "bin": {
9
+ "persisted-memory": "dist/index.js"
10
+ },
11
+ "files": [
12
+ "dist",
13
+ "!dist/__tests__",
14
+ "hooks",
15
+ "scripts",
16
+ "README.md",
17
+ "LICENSE"
18
+ ],
19
+ "keywords": [
20
+ "mcp",
21
+ "claude",
22
+ "claude-code",
23
+ "memory",
24
+ "persistent-memory",
25
+ "vector-search",
26
+ "lancedb",
27
+ "model-context-protocol",
28
+ "ai",
29
+ "llm"
30
+ ],
31
+ "repository": {
32
+ "type": "git",
33
+ "url": "https://github.com/saharat/persisted-memory"
34
+ },
35
+ "license": "MIT",
36
+ "author": "saharat",
37
+ "engines": {
38
+ "node": ">=20.0.0"
39
+ },
40
+ "scripts": {
41
+ "build": "tsc && cp src/viewer/index.html dist/viewer/index.html",
42
+ "dev": "tsc --watch",
43
+ "viewer": "node dist/cli/viewer.js",
44
+ "test": "vitest run",
45
+ "test:watch": "vitest",
46
+ "test:coverage": "vitest run --coverage"
47
+ },
48
+ "dependencies": {
49
+ "@lancedb/lancedb": "^0.26.2",
50
+ "@modelcontextprotocol/sdk": "^1.26.0",
51
+ "apache-arrow": "18.1.0",
52
+ "zod": "^4.3.6"
53
+ },
54
+ "devDependencies": {
55
+ "@types/node": "^20",
56
+ "@vitest/coverage-v8": "^3.1.0",
57
+ "typescript": "^5.6",
58
+ "vitest": "^3.1.0"
59
+ }
60
+ }
@@ -0,0 +1,125 @@
1
+ #!/bin/bash
2
+ set -e
3
+
4
+ SCRIPT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
5
+ HOOKS_DIR="$SCRIPT_DIR/hooks"
6
+ SETTINGS_FILE="$HOME/.claude/settings.json"
7
+
8
+ echo "=== Persisted Memory — Install ==="
9
+ echo ""
10
+
11
+ # 1. Build
12
+ echo "Building persisted-memory..."
13
+ cd "$SCRIPT_DIR"
14
+ yarn install --immutable 2>/dev/null || yarn install
15
+ yarn build
16
+ echo " Build complete."
17
+ echo ""
18
+
19
+ # 2. Register MCP server (user-level)
20
+ echo "Registering MCP server (user-level)..."
21
+ claude mcp add --scope user memory -- node "$SCRIPT_DIR/dist/index.js"
22
+ echo " MCP server registered."
23
+ echo ""
24
+
25
+ # 3. Make hook scripts executable
26
+ echo "Making hook scripts executable..."
27
+ chmod +x "$HOOKS_DIR"/*.sh
28
+ echo ""
29
+
30
+ # 4. Merge hooks into ~/.claude/settings.json
31
+ echo "Registering hooks in $SETTINGS_FILE..."
32
+
33
+ # Ensure settings file exists
34
+ mkdir -p "$HOME/.claude"
35
+ if [ ! -f "$SETTINGS_FILE" ]; then
36
+ echo '{}' > "$SETTINGS_FILE"
37
+ fi
38
+
39
+ node -e "
40
+ const fs = require('fs');
41
+ const settingsPath = '$SETTINGS_FILE';
42
+ const hooksDir = '$HOOKS_DIR';
43
+
44
+ let settings;
45
+ try {
46
+ settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
47
+ } catch {
48
+ settings = {};
49
+ }
50
+
51
+ settings.hooks = settings.hooks || {};
52
+
53
+ // Define our hooks with a marker so we can identify them later
54
+ const MARKER = 'persisted-memory';
55
+
56
+ const hookDefs = {
57
+ SessionStart: {
58
+ hooks: [{ type: 'command', command: 'bash ' + hooksDir + '/on-session-start.sh' }]
59
+ },
60
+ PreCompact: {
61
+ hooks: [{ type: 'command', command: 'bash ' + hooksDir + '/on-pre-compact.sh' }]
62
+ },
63
+ PostToolUse: {
64
+ matcher: 'Edit|Write',
65
+ hooks: [{ type: 'command', command: 'bash ' + hooksDir + '/on-post-tool.sh' }]
66
+ },
67
+ Stop: {
68
+ hooks: [{ type: 'command', command: 'bash ' + hooksDir + '/on-stop.sh' }]
69
+ },
70
+ SessionEnd: {
71
+ hooks: [{ type: 'command', command: 'bash ' + hooksDir + '/on-session-end.sh' }]
72
+ }
73
+ };
74
+
75
+ for (const [event, def] of Object.entries(hookDefs)) {
76
+ if (!settings.hooks[event]) {
77
+ settings.hooks[event] = [];
78
+ }
79
+
80
+ // Remove any existing persisted-memory hooks (idempotent)
81
+ settings.hooks[event] = settings.hooks[event].filter(
82
+ entry => !JSON.stringify(entry).includes('persisted_memory')
83
+ );
84
+
85
+ // Add our hook
86
+ settings.hooks[event].push(def);
87
+ }
88
+
89
+ fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2));
90
+ console.log(' Hooks registered successfully.');
91
+ "
92
+
93
+ # 5. Add user-level CLAUDE.md instructions
94
+ CLAUDE_MD="$HOME/.claude/CLAUDE.md"
95
+ MEMORY_BLOCK="## Persistent Memory System
96
+ - Persistent memory is enabled for all projects via MCP server \"memory\"
97
+ - After context compaction, key context is preserved in \`.claude/memory/\`
98
+ - Use \`memory_search\` to recall past decisions, patterns, and context
99
+ - Use \`memory_store\` to explicitly save important decisions (type: \"decision\", importance: \"high\")
100
+ - Use \`memory_recall\` for a specific topic (e.g., \"authentication flow\")
101
+ - Use \`memory_status\` to check memory stats and Ollama availability
102
+ - Memory data lives at \`<project>/.claude/memory/\` — Markdown files are the source of truth"
103
+
104
+ if [ -f "$CLAUDE_MD" ]; then
105
+ if ! grep -q "Persistent Memory System" "$CLAUDE_MD"; then
106
+ echo "" >> "$CLAUDE_MD"
107
+ echo "$MEMORY_BLOCK" >> "$CLAUDE_MD"
108
+ echo " Added memory instructions to $CLAUDE_MD"
109
+ else
110
+ echo " Memory instructions already in $CLAUDE_MD"
111
+ fi
112
+ else
113
+ echo "$MEMORY_BLOCK" > "$CLAUDE_MD"
114
+ echo " Created $CLAUDE_MD with memory instructions"
115
+ fi
116
+
117
+ echo ""
118
+ echo "=== Installation complete! ==="
119
+ echo ""
120
+ echo " MCP server: node $SCRIPT_DIR/dist/index.js"
121
+ echo " Memory data: <project>/.claude/memory/"
122
+ echo ""
123
+ echo " Add to your project .gitignore: .claude/memory/.lance/"
124
+ echo ""
125
+ echo " Restart Claude Code to activate."
@@ -0,0 +1,59 @@
1
+ #!/bin/bash
2
+ set -e
3
+
4
+ SCRIPT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
5
+ SETTINGS_FILE="$HOME/.claude/settings.json"
6
+
7
+ echo "=== Persisted Memory — Uninstall ==="
8
+ echo ""
9
+
10
+ # 1. Remove MCP server
11
+ echo "Removing MCP server registration..."
12
+ claude mcp remove --scope user memory 2>/dev/null || echo " (not registered)"
13
+ echo ""
14
+
15
+ # 2. Remove hooks from settings.json
16
+ echo "Removing hooks from $SETTINGS_FILE..."
17
+ if [ -f "$SETTINGS_FILE" ]; then
18
+ node -e "
19
+ const fs = require('fs');
20
+ const settingsPath = '$SETTINGS_FILE';
21
+
22
+ let settings;
23
+ try {
24
+ settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
25
+ } catch {
26
+ process.exit(0);
27
+ }
28
+
29
+ if (!settings.hooks) process.exit(0);
30
+
31
+ for (const event of Object.keys(settings.hooks)) {
32
+ if (Array.isArray(settings.hooks[event])) {
33
+ settings.hooks[event] = settings.hooks[event].filter(
34
+ entry => !JSON.stringify(entry).includes('persisted_memory')
35
+ );
36
+ // Remove empty arrays
37
+ if (settings.hooks[event].length === 0) {
38
+ delete settings.hooks[event];
39
+ }
40
+ }
41
+ }
42
+
43
+ // Remove hooks key if empty
44
+ if (Object.keys(settings.hooks).length === 0) {
45
+ delete settings.hooks;
46
+ }
47
+
48
+ fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2));
49
+ console.log(' Hooks removed.');
50
+ "
51
+ fi
52
+
53
+ echo ""
54
+ echo "=== Uninstall complete! ==="
55
+ echo ""
56
+ echo " Note: Per-project memory data at <project>/.claude/memory/ is preserved."
57
+ echo " Delete manually if no longer needed."
58
+ echo ""
59
+ echo " Restart Claude Code to deactivate."