@betterdb/memory 0.1.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/LICENSE +21 -0
- package/README.md +70 -0
- package/package.json +60 -0
- package/scripts/aging-worker.ts +24 -0
- package/scripts/check-providers.ts +103 -0
- package/scripts/install-hooks.sh +103 -0
- package/scripts/migrate-embeddings.ts +69 -0
- package/scripts/setup-index.ts +14 -0
- package/scripts/validate-pack.sh +67 -0
- package/src/client/model.ts +281 -0
- package/src/client/providers/_prompt.ts +35 -0
- package/src/client/providers/anthropic.ts +70 -0
- package/src/client/providers/groq.ts +102 -0
- package/src/client/providers/ollama.ts +53 -0
- package/src/client/providers/openai.ts +125 -0
- package/src/client/providers/together.ts +94 -0
- package/src/client/providers/voyage.ts +46 -0
- package/src/client/valkey.ts +448 -0
- package/src/config.ts +67 -0
- package/src/hooks/_utils.ts +53 -0
- package/src/hooks/post-tool.ts +46 -0
- package/src/hooks/pre-tool.ts +59 -0
- package/src/hooks/session-end.ts +194 -0
- package/src/hooks/session-start.ts +43 -0
- package/src/index.ts +435 -0
- package/src/mcp/server.ts +201 -0
- package/src/memory/aging.ts +321 -0
- package/src/memory/capture.ts +122 -0
- package/src/memory/retrieval.ts +114 -0
- package/src/memory/schema.ts +111 -0
- package/tsconfig.json +21 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 BetterDB Inc.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# BetterDB Memory for Claude Code
|
|
2
|
+
|
|
3
|
+
Persistent, semantic memory for Claude Code sessions — powered by Valkey.
|
|
4
|
+
|
|
5
|
+
Every time you start a new Claude Code session, context is lost. BetterDB Memory
|
|
6
|
+
automatically captures what you did, embeds it as vectors in Valkey, and retrieves
|
|
7
|
+
relevant history at the start of each new session.
|
|
8
|
+
|
|
9
|
+
## Quick Start
|
|
10
|
+
|
|
11
|
+
### Prerequisites
|
|
12
|
+
- [Bun](https://bun.sh) runtime — **required** (the CLI and all hooks run on Bun, not Node)
|
|
13
|
+
- [Claude Code](https://docs.anthropic.com/en/docs/claude-code) installed
|
|
14
|
+
- [Valkey](https://valkey.io) 8.0+ with the Search module
|
|
15
|
+
|
|
16
|
+
### Install
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
# Bun must be installed first — npx delegates to it
|
|
20
|
+
npx @betterdb/memory install
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
This will:
|
|
24
|
+
1. Compile native hook binaries to `~/.betterdb/bin/`
|
|
25
|
+
2. Register 4 lifecycle hooks with Claude Code
|
|
26
|
+
3. Register the MCP server for mid-conversation tools
|
|
27
|
+
4. Create the Valkey search index
|
|
28
|
+
|
|
29
|
+
### How It Works
|
|
30
|
+
|
|
31
|
+
| Hook | What it does |
|
|
32
|
+
|------|-------------|
|
|
33
|
+
| **SessionStart** | Retrieves relevant memories via vector search, injects as context |
|
|
34
|
+
| **PostToolUse** | Records every tool call to a temp JSONL file |
|
|
35
|
+
| **Stop** | Summarizes the session, embeds it, stores in Valkey |
|
|
36
|
+
| **PreToolUse** | Surfaces file-specific history when accessing known files |
|
|
37
|
+
|
|
38
|
+
### MCP Tools
|
|
39
|
+
|
|
40
|
+
Claude can use these mid-conversation:
|
|
41
|
+
- `search_context` — Semantic search over past sessions
|
|
42
|
+
- `store_insight` — Save a decision, pattern, or warning
|
|
43
|
+
- `list_open_threads` — Show unresolved items
|
|
44
|
+
- `forget` — Delete a specific memory
|
|
45
|
+
|
|
46
|
+
### CLI Commands
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
npx @betterdb/memory install # Set up hooks + MCP server
|
|
50
|
+
npx @betterdb/memory status # Check health
|
|
51
|
+
npx @betterdb/memory uninstall # Remove everything
|
|
52
|
+
npx @betterdb/memory maintain # Run aging/compression manually
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Configuration
|
|
56
|
+
|
|
57
|
+
Via environment variables or `~/.betterdb/memory.json`:
|
|
58
|
+
|
|
59
|
+
| Variable | Default | Description |
|
|
60
|
+
|----------|---------|-------------|
|
|
61
|
+
| `BETTERDB_VALKEY_URL` | `redis://localhost:6379` | Valkey connection URL |
|
|
62
|
+
| `BETTERDB_EMBED_MODEL` | auto-detect | Embedding provider |
|
|
63
|
+
| `BETTERDB_SUMMARIZE_MODEL` | auto-detect | Summarization provider |
|
|
64
|
+
| `BETTERDB_EMBED_DIM` | `1024` | Embedding dimensions |
|
|
65
|
+
| `BETTERDB_MAX_CONTEXT_MEMORIES` | `5` | Memories injected per session |
|
|
66
|
+
| `BETTERDB_CONTEXT_FILE` | `.betterdb_context.md` | Context injection file |
|
|
67
|
+
|
|
68
|
+
## License
|
|
69
|
+
|
|
70
|
+
MIT
|
package/package.json
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@betterdb/memory",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "BetterDB Memory for Claude Code — Valkey-powered persistent memory across sessions",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "BetterDB Inc. <hello@betterdb.com>",
|
|
7
|
+
"homepage": "https://github.com/BetterDB-inc/memory",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "https://github.com/BetterDB-inc/memory.git"
|
|
11
|
+
},
|
|
12
|
+
"keywords": [
|
|
13
|
+
"claude-code",
|
|
14
|
+
"memory",
|
|
15
|
+
"valkey",
|
|
16
|
+
"mcp",
|
|
17
|
+
"ai-memory",
|
|
18
|
+
"vector-search",
|
|
19
|
+
"betterdb"
|
|
20
|
+
],
|
|
21
|
+
"type": "module",
|
|
22
|
+
"bin": {
|
|
23
|
+
"betterdb-memory": "./src/index.ts"
|
|
24
|
+
},
|
|
25
|
+
"files": [
|
|
26
|
+
"src/",
|
|
27
|
+
"scripts/",
|
|
28
|
+
"README.md",
|
|
29
|
+
"LICENSE",
|
|
30
|
+
"package.json",
|
|
31
|
+
"tsconfig.json"
|
|
32
|
+
],
|
|
33
|
+
"engines": {
|
|
34
|
+
"node": ">=20.0.0"
|
|
35
|
+
},
|
|
36
|
+
"scripts": {
|
|
37
|
+
"dev": "bun run src/index.ts",
|
|
38
|
+
"test": "bun test",
|
|
39
|
+
"test:unit": "bun test tests/unit",
|
|
40
|
+
"test:integration": "bun test tests/integration",
|
|
41
|
+
"validate": "bash scripts/validate-pack.sh",
|
|
42
|
+
"prepublishOnly": "bash scripts/validate-pack.sh",
|
|
43
|
+
"setup-index": "bun run scripts/setup-index.ts",
|
|
44
|
+
"migrate-embeddings": "bun run scripts/migrate-embeddings.ts",
|
|
45
|
+
"check-providers": "bun run scripts/check-providers.ts",
|
|
46
|
+
"typecheck": "tsc --noEmit"
|
|
47
|
+
},
|
|
48
|
+
"dependencies": {
|
|
49
|
+
"iovalkey": "^0.2.1",
|
|
50
|
+
"ollama": "^0.5.14",
|
|
51
|
+
"@modelcontextprotocol/sdk": "^1.12.1",
|
|
52
|
+
"zod": "^3.24.4",
|
|
53
|
+
"zod-to-json-schema": "^3.24.5",
|
|
54
|
+
"@anthropic-ai/sdk": "latest"
|
|
55
|
+
},
|
|
56
|
+
"devDependencies": {
|
|
57
|
+
"@types/bun": "latest",
|
|
58
|
+
"typescript": "^5.8.3"
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
/**
|
|
3
|
+
* Standalone aging pipeline worker.
|
|
4
|
+
* Runs decay, compression, and distillation on all stored memories.
|
|
5
|
+
*
|
|
6
|
+
* Can be run via cron, docker compose, or manually:
|
|
7
|
+
* bun run scripts/aging-worker.ts
|
|
8
|
+
*/
|
|
9
|
+
import { getValkeyClient } from "../src/client/valkey.js";
|
|
10
|
+
import { createModelClient } from "../src/client/model.js";
|
|
11
|
+
import { AgingPipeline } from "../src/memory/aging.js";
|
|
12
|
+
|
|
13
|
+
try {
|
|
14
|
+
const valkeyClient = await getValkeyClient();
|
|
15
|
+
const modelClient = await createModelClient();
|
|
16
|
+
|
|
17
|
+
const pipeline = new AgingPipeline(valkeyClient, modelClient);
|
|
18
|
+
await pipeline.runFullPipeline();
|
|
19
|
+
|
|
20
|
+
await valkeyClient.quit();
|
|
21
|
+
} catch (err) {
|
|
22
|
+
console.error("[betterdb] Aging worker failed:", err);
|
|
23
|
+
process.exit(1);
|
|
24
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
/**
|
|
3
|
+
* Checks which providers are available and prints a summary.
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* bun run check-providers
|
|
7
|
+
* bun run check-providers -- --test (makes live API calls)
|
|
8
|
+
*/
|
|
9
|
+
import { Ollama } from "ollama";
|
|
10
|
+
import { config } from "../src/config.js";
|
|
11
|
+
import { createModelClient } from "../src/client/model.js";
|
|
12
|
+
import { PRESET_CLEAN, PRESET_ATTRIBUTION, PRESET_LIGHTWEIGHT } from "../src/client/model.js";
|
|
13
|
+
|
|
14
|
+
const testMode = process.argv.includes("--test");
|
|
15
|
+
|
|
16
|
+
console.log("BetterDB Provider Check");
|
|
17
|
+
console.log("─────────────────────────────");
|
|
18
|
+
|
|
19
|
+
// --- Ollama ---
|
|
20
|
+
let ollamaModels: Set<string> = new Set();
|
|
21
|
+
try {
|
|
22
|
+
const ollama = new Ollama({ host: config.ollama.url });
|
|
23
|
+
const list = await ollama.list();
|
|
24
|
+
ollamaModels = new Set(list.models.map((m) => m.name.split(":")[0]!));
|
|
25
|
+
|
|
26
|
+
const presets = [PRESET_CLEAN, PRESET_ATTRIBUTION, PRESET_LIGHTWEIGHT];
|
|
27
|
+
const modelChecks: string[] = [];
|
|
28
|
+
for (const preset of presets) {
|
|
29
|
+
const embedBase = preset.embedModel.split(":")[0]!;
|
|
30
|
+
const summarizeBase = preset.summarizeModel.split(":")[0]!;
|
|
31
|
+
if (ollamaModels.has(embedBase)) modelChecks.push(`${preset.embedModel} ✓`);
|
|
32
|
+
if (ollamaModels.has(summarizeBase)) modelChecks.push(`${preset.summarizeModel} ✓`);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const modelStr = modelChecks.length > 0 ? ` (${modelChecks.join(", ")})` : "";
|
|
36
|
+
console.log(` Ollama ✓ running${modelStr}`);
|
|
37
|
+
} catch {
|
|
38
|
+
console.log(" Ollama ✗ not running");
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// --- API Key Providers ---
|
|
42
|
+
const providers = [
|
|
43
|
+
{ name: "OpenAI", key: config.providers.openaiKey },
|
|
44
|
+
{ name: "Anthropic", key: config.providers.anthropicKey },
|
|
45
|
+
{ name: "Voyage", key: config.providers.voyageKey },
|
|
46
|
+
{ name: "Groq", key: config.providers.groqKey },
|
|
47
|
+
{ name: "Together", key: config.providers.togetherKey },
|
|
48
|
+
] as const;
|
|
49
|
+
|
|
50
|
+
for (const p of providers) {
|
|
51
|
+
const status = p.key ? "✓ key set" : "✗ no key";
|
|
52
|
+
console.log(` ${p.name.padEnd(14)}${status}`);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// --- Resolved Configuration ---
|
|
56
|
+
console.log("");
|
|
57
|
+
try {
|
|
58
|
+
const client = await createModelClient();
|
|
59
|
+
console.log("Resolved configuration:");
|
|
60
|
+
console.log(` Embed: ${client.preset.embedModel} (dim=${client.embedDim})`);
|
|
61
|
+
console.log(` Summarize: ${client.preset.summarizeModel}`);
|
|
62
|
+
} catch (err) {
|
|
63
|
+
console.log("Resolution failed:");
|
|
64
|
+
console.log(` ${err instanceof Error ? err.message : String(err)}`);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// --- Live Test ---
|
|
68
|
+
if (testMode) {
|
|
69
|
+
console.log("");
|
|
70
|
+
console.log("Live API tests:");
|
|
71
|
+
console.log("─────────────────────────────");
|
|
72
|
+
|
|
73
|
+
try {
|
|
74
|
+
const client = await createModelClient();
|
|
75
|
+
|
|
76
|
+
// Embed test
|
|
77
|
+
const embedStart = performance.now();
|
|
78
|
+
try {
|
|
79
|
+
const embedding = await client.embed("BetterDB provider test");
|
|
80
|
+
const embedMs = (performance.now() - embedStart).toFixed(0);
|
|
81
|
+
console.log(` Embed: ✓ ${embedding.length} dims, ${embedMs}ms`);
|
|
82
|
+
} catch (err) {
|
|
83
|
+
const embedMs = (performance.now() - embedStart).toFixed(0);
|
|
84
|
+
console.log(` Embed: ✗ ${err instanceof Error ? err.message : String(err)} (${embedMs}ms)`);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Summarize test
|
|
88
|
+
const sumStart = performance.now();
|
|
89
|
+
try {
|
|
90
|
+
const summary = await client.summarize("User asked to add a login button. Added onClick handler to LoginForm component.");
|
|
91
|
+
const sumMs = (performance.now() - sumStart).toFixed(0);
|
|
92
|
+
console.log(` Summarize: ✓ "${summary.oneLineSummary}" (${sumMs}ms)`);
|
|
93
|
+
} catch (err) {
|
|
94
|
+
const sumMs = (performance.now() - sumStart).toFixed(0);
|
|
95
|
+
console.log(` Summarize: ✗ ${err instanceof Error ? err.message : String(err)} (${sumMs}ms)`);
|
|
96
|
+
}
|
|
97
|
+
} catch (err) {
|
|
98
|
+
console.log(` ✗ Cannot test — provider resolution failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
99
|
+
}
|
|
100
|
+
} else {
|
|
101
|
+
console.log("");
|
|
102
|
+
console.log("Run 'bun run check-providers --test' to make a live API call to each available provider.");
|
|
103
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
# BetterDB Memory for Claude Code — Hook & MCP Installer
|
|
5
|
+
# Compiles hook binaries and registers them with Claude Code
|
|
6
|
+
|
|
7
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
8
|
+
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
|
|
9
|
+
|
|
10
|
+
# Check prerequisites
|
|
11
|
+
if ! command -v claude &>/dev/null; then
|
|
12
|
+
echo "ERROR: 'claude' CLI not found on PATH."
|
|
13
|
+
echo "Install Claude Code first: https://docs.anthropic.com/en/docs/claude-code"
|
|
14
|
+
exit 1
|
|
15
|
+
fi
|
|
16
|
+
|
|
17
|
+
if ! command -v bun &>/dev/null; then
|
|
18
|
+
echo "ERROR: 'bun' not found on PATH."
|
|
19
|
+
echo "Install Bun: https://bun.sh"
|
|
20
|
+
exit 1
|
|
21
|
+
fi
|
|
22
|
+
|
|
23
|
+
# Detect platform
|
|
24
|
+
PLATFORM="$(uname -s)-$(uname -m)"
|
|
25
|
+
echo "Platform: $PLATFORM"
|
|
26
|
+
echo "Note: Hook binaries are platform-specific. Rebuild if deploying elsewhere."
|
|
27
|
+
echo ""
|
|
28
|
+
|
|
29
|
+
# Build hooks
|
|
30
|
+
echo "Building hook binaries..."
|
|
31
|
+
cd "$PROJECT_DIR"
|
|
32
|
+
bun run build:hooks
|
|
33
|
+
echo "Hook binaries compiled to dist/hooks/"
|
|
34
|
+
echo ""
|
|
35
|
+
|
|
36
|
+
# Write hooks to global settings
|
|
37
|
+
GLOBAL_SETTINGS="$HOME/.claude/settings.json"
|
|
38
|
+
mkdir -p "$HOME/.claude"
|
|
39
|
+
|
|
40
|
+
# Create file with empty object if it doesn't exist
|
|
41
|
+
if [ ! -f "$GLOBAL_SETTINGS" ]; then
|
|
42
|
+
echo '{}' > "$GLOBAL_SETTINGS"
|
|
43
|
+
fi
|
|
44
|
+
|
|
45
|
+
# Merge hooks into existing settings using Bun (preserves other fields, overwrites hooks block)
|
|
46
|
+
# Each hook command sources the .env file first so compiled binaries get the right env vars
|
|
47
|
+
# (bun build --compile binaries don't auto-load .env like `bun run` does)
|
|
48
|
+
DIST_DIR="$PROJECT_DIR/dist/hooks"
|
|
49
|
+
ENV_FILE="$PROJECT_DIR/.env"
|
|
50
|
+
|
|
51
|
+
bun -e "
|
|
52
|
+
const fs = require('fs');
|
|
53
|
+
const settingsPath = '$GLOBAL_SETTINGS';
|
|
54
|
+
const envFile = '$ENV_FILE';
|
|
55
|
+
const distDir = '$DIST_DIR';
|
|
56
|
+
const existing = JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
|
|
57
|
+
|
|
58
|
+
// Wrap each binary so it loads .env at runtime
|
|
59
|
+
const wrap = (bin) =>
|
|
60
|
+
'bash -c ' + JSON.stringify('set -a; [ -f ' + envFile + ' ] && . ' + envFile + '; set +a; ' + distDir + '/' + bin);
|
|
61
|
+
|
|
62
|
+
const hooks = {
|
|
63
|
+
SessionStart: [{ hooks: [{ type: 'command', command: wrap('session-start') }] }],
|
|
64
|
+
PreToolUse: [{ matcher: '', hooks: [{ type: 'command', command: wrap('pre-tool') }] }],
|
|
65
|
+
PostToolUse: [{ matcher: '', hooks: [{ type: 'command', command: wrap('post-tool') }] }],
|
|
66
|
+
Stop: [{ hooks: [{ type: 'command', command: wrap('session-end') }] }],
|
|
67
|
+
};
|
|
68
|
+
fs.writeFileSync(settingsPath, JSON.stringify({ ...existing, hooks }, null, 2));
|
|
69
|
+
console.log('Hook configuration written to: ' + settingsPath);
|
|
70
|
+
"
|
|
71
|
+
|
|
72
|
+
# Register MCP server
|
|
73
|
+
echo ""
|
|
74
|
+
echo "Registering MCP server..."
|
|
75
|
+
claude mcp add-json betterdb-memory "{\"type\":\"stdio\",\"command\":\"bun\",\"args\":[\"run\",\"$PROJECT_DIR/src/mcp/server.ts\"]}" 2>/dev/null || true
|
|
76
|
+
echo "MCP server registered: betterdb-memory"
|
|
77
|
+
|
|
78
|
+
# Verify hooks
|
|
79
|
+
echo ""
|
|
80
|
+
echo "Verifying global settings..."
|
|
81
|
+
bun -e "
|
|
82
|
+
const fs = require('fs');
|
|
83
|
+
const settings = JSON.parse(fs.readFileSync('$HOME/.claude/settings.json', 'utf8'));
|
|
84
|
+
const hookCount = Object.keys(settings.hooks || {}).length;
|
|
85
|
+
console.log('Hooks registered: ' + hookCount + ' lifecycle events');
|
|
86
|
+
"
|
|
87
|
+
|
|
88
|
+
# Summary
|
|
89
|
+
echo ""
|
|
90
|
+
echo "=== Installation Complete ==="
|
|
91
|
+
echo ""
|
|
92
|
+
echo "Hooks written to: ~/.claude/settings.json"
|
|
93
|
+
echo " SessionStart → $DIST_DIR/session-start"
|
|
94
|
+
echo " Stop → $DIST_DIR/session-end"
|
|
95
|
+
echo " PreToolUse → $DIST_DIR/pre-tool"
|
|
96
|
+
echo " PostToolUse → $DIST_DIR/post-tool"
|
|
97
|
+
echo ""
|
|
98
|
+
echo "MCP server: betterdb-memory (stdio)"
|
|
99
|
+
echo ""
|
|
100
|
+
echo "Next steps:"
|
|
101
|
+
echo " 1. Start infrastructure: docker compose up -d"
|
|
102
|
+
echo " 2. Create search index: bun run setup-index"
|
|
103
|
+
echo " 3. Start a new Claude Code session — memories will be captured automatically"
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
/**
|
|
3
|
+
* Re-embeds all stored memories using the currently configured model.
|
|
4
|
+
* Required after changing BETTERDB_EMBED_MODEL or BETTERDB_EMBED_DIM.
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* bun run migrate-embeddings
|
|
8
|
+
* bun run migrate-embeddings -- --dry-run
|
|
9
|
+
*/
|
|
10
|
+
import { getValkeyClient } from "../src/client/valkey.js";
|
|
11
|
+
import { createModelClient } from "../src/client/model.js";
|
|
12
|
+
import { config } from "../src/config.js";
|
|
13
|
+
|
|
14
|
+
const dryRun = process.argv.includes("--dry-run");
|
|
15
|
+
|
|
16
|
+
const valkeyClient = await getValkeyClient();
|
|
17
|
+
const modelClient = await createModelClient();
|
|
18
|
+
|
|
19
|
+
const memoryIds = await valkeyClient.listMemoryIds();
|
|
20
|
+
console.log(`Found ${memoryIds.length} memories to migrate.`);
|
|
21
|
+
console.log(`Target model: ${modelClient.preset.embedModel} (dim=${modelClient.embedDim})`);
|
|
22
|
+
|
|
23
|
+
if (dryRun) {
|
|
24
|
+
console.log("Dry run — no changes will be made.");
|
|
25
|
+
await valkeyClient.quit();
|
|
26
|
+
process.exit(0);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Step 1: Drop existing index
|
|
30
|
+
console.log("Dropping existing index...");
|
|
31
|
+
await valkeyClient.dropIndex();
|
|
32
|
+
|
|
33
|
+
// Step 2: Update embed dimension metadata
|
|
34
|
+
const redis = await getValkeyClient();
|
|
35
|
+
|
|
36
|
+
// Step 3: Re-embed each memory
|
|
37
|
+
let processed = 0;
|
|
38
|
+
let failed = 0;
|
|
39
|
+
|
|
40
|
+
for (const id of memoryIds) {
|
|
41
|
+
const memory = await valkeyClient.getMemory(id);
|
|
42
|
+
if (!memory) {
|
|
43
|
+
failed++;
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
try {
|
|
48
|
+
const newEmbedding = await modelClient.embed(memory.summary.oneLineSummary);
|
|
49
|
+
await valkeyClient.storeMemory(memory, newEmbedding);
|
|
50
|
+
processed++;
|
|
51
|
+
if (processed % 10 === 0) {
|
|
52
|
+
console.log(`Processed ${processed}/${memoryIds.length}...`);
|
|
53
|
+
}
|
|
54
|
+
} catch (err) {
|
|
55
|
+
console.error(`Failed to re-embed memory ${id}:`, err);
|
|
56
|
+
failed++;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Step 4: Recreate index with new dimension
|
|
61
|
+
console.log("Recreating index...");
|
|
62
|
+
await valkeyClient.ensureIndex(modelClient.embedDim);
|
|
63
|
+
|
|
64
|
+
console.log(`\nMigration complete:`);
|
|
65
|
+
console.log(` Processed: ${processed}`);
|
|
66
|
+
console.log(` Failed: ${failed}`);
|
|
67
|
+
console.log(` Index: ${config.valkey.indexName} (dim=${modelClient.embedDim})`);
|
|
68
|
+
|
|
69
|
+
await valkeyClient.quit();
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
import { getValkeyClient } from "../src/client/valkey.js";
|
|
3
|
+
import { createModelClient } from "../src/client/model.js";
|
|
4
|
+
import { config } from "../src/config.js";
|
|
5
|
+
|
|
6
|
+
const client = await getValkeyClient();
|
|
7
|
+
const modelClient = await createModelClient();
|
|
8
|
+
|
|
9
|
+
await client.ensureIndex(modelClient.embedDim, modelClient.preset.embedModel);
|
|
10
|
+
console.log("Index ready:", config.valkey.indexName);
|
|
11
|
+
console.log("Embedding dimension:", modelClient.embedDim);
|
|
12
|
+
console.log("Preset:", modelClient.preset.embedModel, "/", modelClient.preset.summarizeModel);
|
|
13
|
+
|
|
14
|
+
await client.quit();
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
echo "=== Validating @betterdb/memory package ==="
|
|
5
|
+
FAIL=0
|
|
6
|
+
|
|
7
|
+
# Package name
|
|
8
|
+
NAME=$(node -e "console.log(require('./package.json').name)")
|
|
9
|
+
[ "$NAME" = "@betterdb/memory" ] && echo "✅ Name: $NAME" || { echo "❌ Name: $NAME (expected @betterdb/memory)"; FAIL=1; }
|
|
10
|
+
|
|
11
|
+
# files field
|
|
12
|
+
node -e "const p=require('./package.json'); if(!p.files) process.exit(1)" 2>/dev/null \
|
|
13
|
+
&& echo "✅ Has files field" || { echo "❌ Missing files field"; FAIL=1; }
|
|
14
|
+
|
|
15
|
+
# src/ in files
|
|
16
|
+
node -e "const p=require('./package.json'); if(!p.files.includes('src/')) process.exit(1)" 2>/dev/null \
|
|
17
|
+
&& echo "✅ src/ in files" || { echo "❌ src/ not in files"; FAIL=1; }
|
|
18
|
+
|
|
19
|
+
# dist/ NOT in files
|
|
20
|
+
node -e "const p=require('./package.json'); if(p.files && p.files.includes('dist/')) process.exit(1)" 2>/dev/null \
|
|
21
|
+
|| { echo "❌ dist/ in files — remove it"; FAIL=1; }
|
|
22
|
+
echo "✅ dist/ not in files"
|
|
23
|
+
|
|
24
|
+
# bin entry
|
|
25
|
+
node -e "const p=require('./package.json'); if(!p.bin?.['betterdb-memory']) process.exit(1)" 2>/dev/null \
|
|
26
|
+
&& echo "✅ bin: betterdb-memory" || { echo "❌ Missing bin entry"; FAIL=1; }
|
|
27
|
+
|
|
28
|
+
# bin target exists
|
|
29
|
+
BIN=$(node -e "console.log(require('./package.json').bin['betterdb-memory'])")
|
|
30
|
+
[ -f "$BIN" ] && echo "✅ bin target exists: $BIN" || { echo "❌ bin target missing: $BIN"; FAIL=1; }
|
|
31
|
+
|
|
32
|
+
# No monorepo artifacts
|
|
33
|
+
[ ! -f pnpm-workspace.yaml ] && echo "✅ No pnpm-workspace.yaml" || { echo "❌ pnpm-workspace.yaml exists"; FAIL=1; }
|
|
34
|
+
[ ! -f turbo.json ] && echo "✅ No turbo.json" || { echo "❌ turbo.json exists"; FAIL=1; }
|
|
35
|
+
[ ! -d packages ] && echo "✅ No packages/" || { echo "❌ packages/ directory exists"; FAIL=1; }
|
|
36
|
+
|
|
37
|
+
# No Docker artifacts to ship
|
|
38
|
+
PACK=$(npm pack --dry-run 2>&1)
|
|
39
|
+
echo "$PACK" | grep -q "Dockerfile" && { echo "❌ Dockerfile in package"; FAIL=1; } || echo "✅ No Dockerfile in package"
|
|
40
|
+
|
|
41
|
+
# Leaks
|
|
42
|
+
for leak in "node_modules" ".env" ".git/"; do
|
|
43
|
+
echo "$PACK" | grep -q "$leak" && { echo "❌ $leak leaking"; FAIL=1; }
|
|
44
|
+
done
|
|
45
|
+
echo "✅ No leaks"
|
|
46
|
+
|
|
47
|
+
# Standalone install
|
|
48
|
+
echo ""
|
|
49
|
+
echo "--- Standalone install ---"
|
|
50
|
+
TMPDIR=$(mktemp -d)
|
|
51
|
+
TARBALL=$(npm pack 2>/dev/null)
|
|
52
|
+
(
|
|
53
|
+
cd "$TMPDIR"
|
|
54
|
+
npm init -y > /dev/null 2>&1
|
|
55
|
+
if npm install "$OLDPWD/$TARBALL" > /dev/null 2>&1; then
|
|
56
|
+
echo "✅ Installs standalone"
|
|
57
|
+
[ -d "node_modules/@betterdb/memory/src" ] && echo "✅ src/ present" || { echo "❌ src/ missing"; FAIL=1; }
|
|
58
|
+
[ -f node_modules/.bin/betterdb-memory ] && echo "✅ bin linked" || echo "⚠️ bin not linked"
|
|
59
|
+
else
|
|
60
|
+
echo "❌ Install failed"; FAIL=1
|
|
61
|
+
fi
|
|
62
|
+
)
|
|
63
|
+
rm -rf "$TMPDIR"
|
|
64
|
+
rm -f "$TARBALL"
|
|
65
|
+
|
|
66
|
+
echo ""
|
|
67
|
+
[ $FAIL -eq 0 ] && echo "=== ✅ All checks passed ===" || { echo "=== ❌ FAILED ==="; exit 1; }
|