@jungjaehoon/mama-core 1.0.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/README.md +171 -0
- package/package.json +68 -0
- package/src/config-loader.js +218 -0
- package/src/db-adapter/README.md +110 -0
- package/src/db-adapter/base-adapter.js +91 -0
- package/src/db-adapter/index.js +31 -0
- package/src/db-adapter/sqlite-adapter.js +364 -0
- package/src/db-adapter/statement.js +127 -0
- package/src/db-manager.js +671 -0
- package/src/debug-logger.js +86 -0
- package/src/decision-formatter.js +1276 -0
- package/src/decision-tracker.js +621 -0
- package/src/embedding-cache.js +222 -0
- package/src/embedding-client.js +141 -0
- package/src/embedding-server/index.js +424 -0
- package/src/embedding-server/mobile/auth.js +160 -0
- package/src/embedding-server/mobile/daemon.js +313 -0
- package/src/embedding-server/mobile/output-parser.js +281 -0
- package/src/embedding-server/mobile/session-api.js +279 -0
- package/src/embedding-server/mobile/session-manager.js +377 -0
- package/src/embedding-server/mobile/websocket-handler.js +389 -0
- package/src/embeddings.js +305 -0
- package/src/errors.js +326 -0
- package/src/index.js +41 -0
- package/src/mama-api.js +2614 -0
- package/src/memory-inject.js +174 -0
- package/src/memory-store.js +89 -0
- package/src/notification-manager.js +3 -0
- package/src/ollama-client.js +391 -0
- package/src/outcome-tracker.js +351 -0
- package/src/progress-indicator.js +110 -0
- package/src/query-intent.js +237 -0
- package/src/relevance-scorer.js +286 -0
- package/src/tier-validator.js +269 -0
- package/src/time-formatter.js +98 -0
package/README.md
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
# @jungjaehoon/mama-core
|
|
2
|
+
|
|
3
|
+
Shared core modules for MAMA (Memory-Augmented MCP Assistant).
|
|
4
|
+
|
|
5
|
+
## What is MAMA Core?
|
|
6
|
+
|
|
7
|
+
MAMA Core is a shared package containing the fundamental modules used by all MAMA packages:
|
|
8
|
+
|
|
9
|
+
- **mcp-server**: MCP protocol server
|
|
10
|
+
- **claude-code-plugin**: Claude Code plugin
|
|
11
|
+
- **standalone**: Standalone HTTP server
|
|
12
|
+
|
|
13
|
+
This package provides embedding generation, database management, decision tracking, and other core functionality without the transport layer (MCP/HTTP).
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install @jungjaehoon/mama-core
|
|
19
|
+
# or
|
|
20
|
+
pnpm add @jungjaehoon/mama-core
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Usage
|
|
24
|
+
|
|
25
|
+
### Import Everything
|
|
26
|
+
|
|
27
|
+
```javascript
|
|
28
|
+
const mama = require('@jungjaehoon/mama-core');
|
|
29
|
+
|
|
30
|
+
// Access all exported functions
|
|
31
|
+
const embedding = await mama.generateEmbedding('your text');
|
|
32
|
+
await mama.initDB();
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Import Specific Modules
|
|
36
|
+
|
|
37
|
+
```javascript
|
|
38
|
+
const { generateEmbedding } = require('@jungjaehoon/mama-core/embeddings');
|
|
39
|
+
const { initDB, getDB } = require('@jungjaehoon/mama-core/db-manager');
|
|
40
|
+
const mamaApi = require('@jungjaehoon/mama-core/mama-api');
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Available Modules
|
|
44
|
+
|
|
45
|
+
### Embedding Modules
|
|
46
|
+
|
|
47
|
+
- **embeddings** - Generate embeddings using Transformers.js
|
|
48
|
+
- `generateEmbedding(text)` - Single text embedding
|
|
49
|
+
- `generateBatchEmbeddings(texts)` - Batch embedding generation
|
|
50
|
+
- `cosineSimilarity(a, b)` - Similarity calculation
|
|
51
|
+
|
|
52
|
+
- **embedding-cache** - In-memory embedding cache
|
|
53
|
+
- `embeddingCache.get(key)` - Retrieve cached embedding
|
|
54
|
+
- `embeddingCache.set(key, value)` - Store embedding
|
|
55
|
+
- `embeddingCache.clear()` - Clear cache
|
|
56
|
+
|
|
57
|
+
- **embedding-client** - HTTP client for embedding server
|
|
58
|
+
- `isServerRunning()` - Check server availability
|
|
59
|
+
- `getEmbeddingFromServer(text)` - Get embedding via HTTP
|
|
60
|
+
- `getServerStatus()` - Server health check
|
|
61
|
+
|
|
62
|
+
### Database Modules
|
|
63
|
+
|
|
64
|
+
- **db-manager** - SQLite database initialization
|
|
65
|
+
- `initDB()` - Initialize database with migrations
|
|
66
|
+
- `getDB()` - Get database connection
|
|
67
|
+
- `closeDB()` - Close connection
|
|
68
|
+
|
|
69
|
+
- **db-adapter** - Database adapter interface
|
|
70
|
+
- `createAdapter(type)` - Create SQLite adapter
|
|
71
|
+
- Supports prepared statements and transactions
|
|
72
|
+
|
|
73
|
+
- **memory-store** - Decision storage operations
|
|
74
|
+
- CRUD operations for decisions
|
|
75
|
+
- Vector similarity search
|
|
76
|
+
|
|
77
|
+
### Core API
|
|
78
|
+
|
|
79
|
+
- **mama-api** - High-level API interface
|
|
80
|
+
- `save(decision)` - Save decision
|
|
81
|
+
- `recall(topic)` - Retrieve decision history
|
|
82
|
+
- `suggest(query)` - Semantic search
|
|
83
|
+
- `updateOutcome(id, outcome)` - Update decision outcome
|
|
84
|
+
|
|
85
|
+
- **decision-tracker** - Decision graph management
|
|
86
|
+
- `learnDecision(decision)` - Learn from decision
|
|
87
|
+
- `createEdgesFromReasoning(reasoning)` - Parse decision links
|
|
88
|
+
|
|
89
|
+
- **relevance-scorer** - Semantic similarity scoring
|
|
90
|
+
- `scoreRelevance(query, decisions)` - Score decision relevance
|
|
91
|
+
- Combines vector, graph, and recency signals
|
|
92
|
+
|
|
93
|
+
### Configuration
|
|
94
|
+
|
|
95
|
+
- **config-loader** - Configuration management
|
|
96
|
+
- `loadConfig()` - Load MAMA configuration
|
|
97
|
+
- `getModelName()` - Get embedding model name
|
|
98
|
+
- `getEmbeddingDim()` - Get embedding dimensions
|
|
99
|
+
- `updateConfig(config)` - Update configuration
|
|
100
|
+
|
|
101
|
+
## Environment Variables
|
|
102
|
+
|
|
103
|
+
- `MAMA_DB_PATH` - Database file path (default: `~/.claude/mama-memory.db`)
|
|
104
|
+
- `MAMA_HTTP_PORT` - Embedding server port (default: `3847`)
|
|
105
|
+
|
|
106
|
+
## Dependencies
|
|
107
|
+
|
|
108
|
+
- **@huggingface/transformers** - Local embedding generation
|
|
109
|
+
- **better-sqlite3** - SQLite database
|
|
110
|
+
- **sqlite-vec** - Vector similarity extension
|
|
111
|
+
|
|
112
|
+
## Development
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
# Install dependencies
|
|
116
|
+
pnpm install
|
|
117
|
+
|
|
118
|
+
# Run tests
|
|
119
|
+
pnpm test
|
|
120
|
+
|
|
121
|
+
# Watch mode
|
|
122
|
+
pnpm test:watch
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Test Coverage
|
|
126
|
+
|
|
127
|
+
- 35 unit tests
|
|
128
|
+
- 100% passing
|
|
129
|
+
- Tests cover:
|
|
130
|
+
- Config loader
|
|
131
|
+
- Database initialization
|
|
132
|
+
- Module exports
|
|
133
|
+
|
|
134
|
+
## Architecture
|
|
135
|
+
|
|
136
|
+
MAMA Core uses CommonJS modules and is designed to be shared across multiple packages:
|
|
137
|
+
|
|
138
|
+
```
|
|
139
|
+
packages/mama-core/
|
|
140
|
+
├── src/
|
|
141
|
+
│ ├── index.js # Main exports
|
|
142
|
+
│ ├── embeddings.js # Embedding generation
|
|
143
|
+
│ ├── db-manager.js # Database management
|
|
144
|
+
│ ├── mama-api.js # High-level API
|
|
145
|
+
│ └── db-adapter/ # Database adapter
|
|
146
|
+
├── db/migrations/ # SQLite migrations
|
|
147
|
+
└── tests/ # Unit tests
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## Migration Files
|
|
151
|
+
|
|
152
|
+
Database migrations are included in `db/migrations/`:
|
|
153
|
+
|
|
154
|
+
- 001-initial-decision-graph.sql
|
|
155
|
+
- 002-add-error-patterns.sql
|
|
156
|
+
- 003-add-validation-fields.sql
|
|
157
|
+
- (and more...)
|
|
158
|
+
|
|
159
|
+
## License
|
|
160
|
+
|
|
161
|
+
MIT - see LICENSE file for details
|
|
162
|
+
|
|
163
|
+
## Links
|
|
164
|
+
|
|
165
|
+
- [GitHub Repository](https://github.com/jungjaehoon-lifegamez/MAMA)
|
|
166
|
+
- [Documentation](https://github.com/jungjaehoon-lifegamez/MAMA/tree/main/docs)
|
|
167
|
+
- [Issues](https://github.com/jungjaehoon-lifegamez/MAMA/issues)
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
**Part of the MAMA monorepo** - Memory-Augmented MCP Assistant
|
package/package.json
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@jungjaehoon/mama-core",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "MAMA Core - Shared modules for Memory-Augmented MCP Assistant",
|
|
5
|
+
"main": "src/index.js",
|
|
6
|
+
"exports": {
|
|
7
|
+
".": "./src/index.js",
|
|
8
|
+
"./embeddings": "./src/embeddings.js",
|
|
9
|
+
"./embedding-cache": "./src/embedding-cache.js",
|
|
10
|
+
"./embedding-client": "./src/embedding-client.js",
|
|
11
|
+
"./embedding-server": "./src/embedding-server/index.js",
|
|
12
|
+
"./memory-store": "./src/memory-store.js",
|
|
13
|
+
"./db-manager": "./src/db-manager.js",
|
|
14
|
+
"./db-adapter": "./src/db-adapter/index.js",
|
|
15
|
+
"./mama-api": "./src/mama-api.js",
|
|
16
|
+
"./config-loader": "./src/config-loader.js",
|
|
17
|
+
"./relevance-scorer": "./src/relevance-scorer.js",
|
|
18
|
+
"./decision-tracker": "./src/decision-tracker.js",
|
|
19
|
+
"./debug-logger": "./src/debug-logger.js",
|
|
20
|
+
"./decision-formatter": "./src/decision-formatter.js",
|
|
21
|
+
"./memory-inject": "./src/memory-inject.js",
|
|
22
|
+
"./ollama-client": "./src/ollama-client.js"
|
|
23
|
+
},
|
|
24
|
+
"scripts": {
|
|
25
|
+
"test": "vitest run",
|
|
26
|
+
"test:watch": "vitest watch",
|
|
27
|
+
"clean": "rm -rf node_modules"
|
|
28
|
+
},
|
|
29
|
+
"keywords": [
|
|
30
|
+
"mama",
|
|
31
|
+
"memory-assistant",
|
|
32
|
+
"decision-tracking",
|
|
33
|
+
"semantic-search",
|
|
34
|
+
"embeddings",
|
|
35
|
+
"sqlite",
|
|
36
|
+
"mcp"
|
|
37
|
+
],
|
|
38
|
+
"author": "SpineLift Team",
|
|
39
|
+
"license": "MIT",
|
|
40
|
+
"repository": {
|
|
41
|
+
"type": "git",
|
|
42
|
+
"url": "https://github.com/jungjaehoon-lifegamez/MAMA.git",
|
|
43
|
+
"directory": "packages/mama-core"
|
|
44
|
+
},
|
|
45
|
+
"bugs": {
|
|
46
|
+
"url": "https://github.com/jungjaehoon-lifegamez/MAMA/issues"
|
|
47
|
+
},
|
|
48
|
+
"homepage": "https://github.com/jungjaehoon-lifegamez/MAMA#readme",
|
|
49
|
+
"engines": {
|
|
50
|
+
"node": ">=18.0.0"
|
|
51
|
+
},
|
|
52
|
+
"dependencies": {
|
|
53
|
+
"@anthropic-ai/sdk": "^0.72.1",
|
|
54
|
+
"@huggingface/transformers": "^3.8.1",
|
|
55
|
+
"better-sqlite3": "^11.0.0",
|
|
56
|
+
"sqlite-vec": "^0.1.0",
|
|
57
|
+
"ws": "^8.19.0"
|
|
58
|
+
},
|
|
59
|
+
"devDependencies": {
|
|
60
|
+
"@types/better-sqlite3": "^7.6.0",
|
|
61
|
+
"vitest": "^1.0.0"
|
|
62
|
+
},
|
|
63
|
+
"files": [
|
|
64
|
+
"src/**/*.js",
|
|
65
|
+
"README.md",
|
|
66
|
+
"LICENSE"
|
|
67
|
+
]
|
|
68
|
+
}
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MAMA Configuration Loader
|
|
3
|
+
*
|
|
4
|
+
* Story M1.4: Configurable embedding model selection
|
|
5
|
+
* Priority: P1 (Core Feature)
|
|
6
|
+
*
|
|
7
|
+
* Loads user configuration from ~/.mama/config.json with sensible defaults.
|
|
8
|
+
* Supports:
|
|
9
|
+
* - Model selection (default: multilingual-e5-small)
|
|
10
|
+
* - Embedding dimensions
|
|
11
|
+
* - Cache directory configuration
|
|
12
|
+
*
|
|
13
|
+
* @module config-loader
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
const fs = require('fs');
|
|
17
|
+
const path = require('path');
|
|
18
|
+
const os = require('os');
|
|
19
|
+
const { info, warn, error: logError } = require('./debug-logger');
|
|
20
|
+
|
|
21
|
+
// Default configuration
|
|
22
|
+
const DEFAULT_CONFIG = {
|
|
23
|
+
modelName: 'Xenova/multilingual-e5-small',
|
|
24
|
+
embeddingDim: 384,
|
|
25
|
+
cacheDir: path.join(os.homedir(), '.cache', 'huggingface', 'transformers'),
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
// Config file path
|
|
29
|
+
const CONFIG_DIR = path.join(os.homedir(), '.mama');
|
|
30
|
+
const CONFIG_PATH = path.join(CONFIG_DIR, 'config.json');
|
|
31
|
+
|
|
32
|
+
// Cached configuration
|
|
33
|
+
let cachedConfig = null;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Ensure config directory exists
|
|
37
|
+
* @returns {void}
|
|
38
|
+
*/
|
|
39
|
+
function ensureConfigDir() {
|
|
40
|
+
if (!fs.existsSync(CONFIG_DIR)) {
|
|
41
|
+
fs.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
42
|
+
info(`[config] Created config directory: ${CONFIG_DIR}`);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Create default config file if it doesn't exist
|
|
48
|
+
* @returns {void}
|
|
49
|
+
*/
|
|
50
|
+
function ensureConfigFile() {
|
|
51
|
+
if (!fs.existsSync(CONFIG_PATH)) {
|
|
52
|
+
ensureConfigDir();
|
|
53
|
+
fs.writeFileSync(CONFIG_PATH, JSON.stringify(DEFAULT_CONFIG, null, 2), 'utf8');
|
|
54
|
+
info(`[config] Created default config file: ${CONFIG_PATH}`);
|
|
55
|
+
info(`[config] Model: ${DEFAULT_CONFIG.modelName} (${DEFAULT_CONFIG.embeddingDim}-dim)`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Load MAMA configuration from ~/.mama/config.json
|
|
61
|
+
*
|
|
62
|
+
* Story M1.4 AC #1: Config parser loads ~/.mama/config.json
|
|
63
|
+
*
|
|
64
|
+
* @param {boolean} reload - Force reload from disk (default: false)
|
|
65
|
+
* @returns {Object} Configuration object with modelName, embeddingDim, cacheDir
|
|
66
|
+
*/
|
|
67
|
+
function loadConfig(reload = false) {
|
|
68
|
+
// Return cached config if available and not forcing reload
|
|
69
|
+
if (cachedConfig && !reload) {
|
|
70
|
+
return cachedConfig;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
try {
|
|
74
|
+
// Ensure config file exists
|
|
75
|
+
ensureConfigFile();
|
|
76
|
+
|
|
77
|
+
// Read and parse config file
|
|
78
|
+
const configData = fs.readFileSync(CONFIG_PATH, 'utf8');
|
|
79
|
+
const userConfig = JSON.parse(configData);
|
|
80
|
+
|
|
81
|
+
// Merge with defaults (user config overrides)
|
|
82
|
+
const config = {
|
|
83
|
+
...DEFAULT_CONFIG,
|
|
84
|
+
...userConfig,
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
// Validate configuration
|
|
88
|
+
if (!config.modelName || typeof config.modelName !== 'string') {
|
|
89
|
+
warn('[config] Invalid modelName, using default:', DEFAULT_CONFIG.modelName);
|
|
90
|
+
config.modelName = DEFAULT_CONFIG.modelName;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (!Number.isInteger(config.embeddingDim) || config.embeddingDim <= 0) {
|
|
94
|
+
warn('[config] Invalid embeddingDim, using default:', DEFAULT_CONFIG.embeddingDim);
|
|
95
|
+
config.embeddingDim = DEFAULT_CONFIG.embeddingDim;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (!config.cacheDir || typeof config.cacheDir !== 'string') {
|
|
99
|
+
warn('[config] Invalid cacheDir, using default:', DEFAULT_CONFIG.cacheDir);
|
|
100
|
+
config.cacheDir = DEFAULT_CONFIG.cacheDir;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Cache the loaded config
|
|
104
|
+
cachedConfig = config;
|
|
105
|
+
|
|
106
|
+
// Log loaded configuration
|
|
107
|
+
if (reload) {
|
|
108
|
+
info(`[config] Configuration reloaded from ${CONFIG_PATH}`);
|
|
109
|
+
info(`[config] Model: ${config.modelName} (${config.embeddingDim}-dim)`);
|
|
110
|
+
info(`[config] Cache: ${config.cacheDir}`);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return config;
|
|
114
|
+
} catch (error) {
|
|
115
|
+
logError(`[config] Failed to load config file: ${error.message}`);
|
|
116
|
+
logError('[config] Using default configuration');
|
|
117
|
+
|
|
118
|
+
// Cache defaults on error
|
|
119
|
+
cachedConfig = { ...DEFAULT_CONFIG };
|
|
120
|
+
return cachedConfig;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Get current model name
|
|
126
|
+
* @returns {string} Current model name
|
|
127
|
+
*/
|
|
128
|
+
function getModelName() {
|
|
129
|
+
const config = loadConfig();
|
|
130
|
+
return config.modelName;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Get current embedding dimension
|
|
135
|
+
* @returns {number} Current embedding dimension
|
|
136
|
+
*/
|
|
137
|
+
function getEmbeddingDim() {
|
|
138
|
+
const config = loadConfig();
|
|
139
|
+
return config.embeddingDim;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Get current cache directory
|
|
144
|
+
* @returns {string} Current cache directory
|
|
145
|
+
*/
|
|
146
|
+
function getCacheDir() {
|
|
147
|
+
const config = loadConfig();
|
|
148
|
+
return config.cacheDir;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Update configuration and save to file
|
|
153
|
+
*
|
|
154
|
+
* Story M1.4 AC #3: Changing model via config triggers informative log + resets caches
|
|
155
|
+
*
|
|
156
|
+
* @param {Object} updates - Configuration updates
|
|
157
|
+
* @param {string} updates.modelName - New model name
|
|
158
|
+
* @param {number} updates.embeddingDim - New embedding dimension
|
|
159
|
+
* @param {string} updates.cacheDir - New cache directory
|
|
160
|
+
* @returns {boolean} Success status
|
|
161
|
+
*/
|
|
162
|
+
function updateConfig(updates) {
|
|
163
|
+
try {
|
|
164
|
+
ensureConfigFile();
|
|
165
|
+
|
|
166
|
+
// Load current config
|
|
167
|
+
const currentConfig = loadConfig();
|
|
168
|
+
|
|
169
|
+
// Check if model is changing
|
|
170
|
+
const modelChanged = updates.modelName && updates.modelName !== currentConfig.modelName;
|
|
171
|
+
const dimChanged = updates.embeddingDim && updates.embeddingDim !== currentConfig.embeddingDim;
|
|
172
|
+
|
|
173
|
+
// Merge updates
|
|
174
|
+
const newConfig = {
|
|
175
|
+
...currentConfig,
|
|
176
|
+
...updates,
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
// Save to file
|
|
180
|
+
fs.writeFileSync(CONFIG_PATH, JSON.stringify(newConfig, null, 2), 'utf8');
|
|
181
|
+
|
|
182
|
+
// Update cache
|
|
183
|
+
cachedConfig = newConfig;
|
|
184
|
+
|
|
185
|
+
// Story M1.4 AC #3: Informative log when model changes
|
|
186
|
+
if (modelChanged || dimChanged) {
|
|
187
|
+
info('[config] ⚠️ Embedding model configuration changed');
|
|
188
|
+
info(`[config] Old: ${currentConfig.modelName} (${currentConfig.embeddingDim}-dim)`);
|
|
189
|
+
info(`[config] New: ${newConfig.modelName} (${newConfig.embeddingDim}-dim)`);
|
|
190
|
+
info('[config] ⚡ Model cache will be reset on next embedding generation');
|
|
191
|
+
info('[config] ⚡ Existing embeddings in database remain unchanged');
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
info(`[config] Configuration saved to ${CONFIG_PATH}`);
|
|
195
|
+
return true;
|
|
196
|
+
} catch (error) {
|
|
197
|
+
logError(`[config] Failed to update config: ${error.message}`);
|
|
198
|
+
return false;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Get config file path
|
|
204
|
+
* @returns {string} Config file path
|
|
205
|
+
*/
|
|
206
|
+
function getConfigPath() {
|
|
207
|
+
return CONFIG_PATH;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
module.exports = {
|
|
211
|
+
loadConfig,
|
|
212
|
+
getModelName,
|
|
213
|
+
getEmbeddingDim,
|
|
214
|
+
getCacheDir,
|
|
215
|
+
updateConfig,
|
|
216
|
+
getConfigPath,
|
|
217
|
+
DEFAULT_CONFIG,
|
|
218
|
+
};
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# Database Adapter Layer
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
Abstraction layer for MAMA database to support both SQLite (development/testing) and PostgreSQL (production on Railway).
|
|
6
|
+
|
|
7
|
+
## Architecture
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
mama-api.js
|
|
11
|
+
↓
|
|
12
|
+
memory-store.js (business logic)
|
|
13
|
+
↓
|
|
14
|
+
DatabaseAdapter (interface)
|
|
15
|
+
↓
|
|
16
|
+
├── SQLiteAdapter (better-sqlite3 + sqlite-vss)
|
|
17
|
+
└── PostgreSQLAdapter (pg + pgvector)
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Decision Rationale
|
|
21
|
+
|
|
22
|
+
**Topic**: mama_db_adapter_pattern
|
|
23
|
+
**Decision**: Abstract database layer with driver-specific implementations
|
|
24
|
+
**Reasoning**:
|
|
25
|
+
|
|
26
|
+
1. **Environment Flexibility**: SQLite for local/testing, PostgreSQL for production
|
|
27
|
+
2. **Zero Breaking Changes**: Existing memory-store.js API remains unchanged
|
|
28
|
+
3. **Vector Search Portability**: sqlite-vss → pgvector migration path
|
|
29
|
+
4. **Testing Simplicity**: Fast SQLite tests, production PostgreSQL validation
|
|
30
|
+
|
|
31
|
+
## Adapter Interface
|
|
32
|
+
|
|
33
|
+
All adapters must implement:
|
|
34
|
+
|
|
35
|
+
```javascript
|
|
36
|
+
class DatabaseAdapter {
|
|
37
|
+
// Connection
|
|
38
|
+
connect(config) → db
|
|
39
|
+
disconnect()
|
|
40
|
+
isConnected() → boolean
|
|
41
|
+
|
|
42
|
+
// Prepared Statements
|
|
43
|
+
prepare(sql) → Statement
|
|
44
|
+
exec(sql)
|
|
45
|
+
transaction(fn) → result
|
|
46
|
+
|
|
47
|
+
// Vector Search
|
|
48
|
+
vectorSearch(embedding, limit) → results
|
|
49
|
+
insertEmbedding(rowid, embedding)
|
|
50
|
+
|
|
51
|
+
// Utility
|
|
52
|
+
getLastInsertRowid() → number
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Implementation Files
|
|
57
|
+
|
|
58
|
+
- `index.js` - Factory + environment detection
|
|
59
|
+
- `sqlite-adapter.js` - SQLite implementation (current behavior)
|
|
60
|
+
- `postgresql-adapter.js` - PostgreSQL implementation
|
|
61
|
+
- `statement.js` - Statement wrapper (unified interface)
|
|
62
|
+
|
|
63
|
+
## Environment Variable
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
# Default: SQLite
|
|
67
|
+
MAMA_DB_PATH=~/.mama/memories.db
|
|
68
|
+
|
|
69
|
+
# PostgreSQL (Railway)
|
|
70
|
+
MAMA_DATABASE_URL=postgresql://user:pass@host:5432/mama_db
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
**Detection Logic**:
|
|
74
|
+
|
|
75
|
+
- If `MAMA_DATABASE_URL` set → PostgreSQL
|
|
76
|
+
- Else → SQLite with `MAMA_DB_PATH`
|
|
77
|
+
|
|
78
|
+
## Migration Strategy
|
|
79
|
+
|
|
80
|
+
### Phase 1: Adapter Layer (Current)
|
|
81
|
+
|
|
82
|
+
1. Create adapter interface
|
|
83
|
+
2. Extract SQLite logic to SQLiteAdapter
|
|
84
|
+
3. Update memory-store.js to use adapter
|
|
85
|
+
|
|
86
|
+
### Phase 2: PostgreSQL Support
|
|
87
|
+
|
|
88
|
+
1. Implement PostgreSQLAdapter
|
|
89
|
+
2. Convert migration SQL files
|
|
90
|
+
3. Add pgvector support
|
|
91
|
+
|
|
92
|
+
### Phase 3: Testing
|
|
93
|
+
|
|
94
|
+
1. Run existing tests with SQLite
|
|
95
|
+
2. Add PostgreSQL integration tests
|
|
96
|
+
3. Validate on Railway
|
|
97
|
+
|
|
98
|
+
## Performance Requirements
|
|
99
|
+
|
|
100
|
+
- Prepared statement caching
|
|
101
|
+
- Connection pooling (PostgreSQL only)
|
|
102
|
+
- Transaction batching support
|
|
103
|
+
- Vector search < 100ms (p95)
|
|
104
|
+
|
|
105
|
+
## Backward Compatibility
|
|
106
|
+
|
|
107
|
+
✅ Existing code works without changes
|
|
108
|
+
✅ SQLite remains default for development
|
|
109
|
+
✅ Environment variable controls database type
|
|
110
|
+
✅ No API changes to memory-store.js
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base Database Adapter Interface
|
|
3
|
+
* All adapters must implement these methods
|
|
4
|
+
*/
|
|
5
|
+
class DatabaseAdapter {
|
|
6
|
+
/**
|
|
7
|
+
* Connect to database
|
|
8
|
+
* @returns {Object} Database connection
|
|
9
|
+
*/
|
|
10
|
+
connect() {
|
|
11
|
+
throw new Error('connect() must be implemented by subclass');
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Disconnect from database
|
|
16
|
+
*/
|
|
17
|
+
disconnect() {
|
|
18
|
+
throw new Error('disconnect() must be implemented by subclass');
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Check if connected
|
|
23
|
+
* @returns {boolean} Connection status
|
|
24
|
+
*/
|
|
25
|
+
isConnected() {
|
|
26
|
+
throw new Error('isConnected() must be implemented by subclass');
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Prepare a SQL statement
|
|
31
|
+
* @param {string} _sql - SQL query
|
|
32
|
+
* @returns {Statement} Prepared statement
|
|
33
|
+
*/
|
|
34
|
+
prepare(_sql) {
|
|
35
|
+
throw new Error('prepare() must be implemented by subclass');
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Execute raw SQL
|
|
40
|
+
* @param {string} _sql - SQL to execute
|
|
41
|
+
*/
|
|
42
|
+
exec(_sql) {
|
|
43
|
+
throw new Error('exec() must be implemented by subclass');
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Execute function in transaction
|
|
48
|
+
* @param {Function} _fn - Function to execute
|
|
49
|
+
* @returns {*} Function return value
|
|
50
|
+
*/
|
|
51
|
+
transaction(_fn) {
|
|
52
|
+
throw new Error('transaction() must be implemented by subclass');
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Vector similarity search
|
|
57
|
+
* @param {number[]} _embedding - Query embedding (384-dim)
|
|
58
|
+
* @param {number} _limit - Max results
|
|
59
|
+
* @returns {Array<Object>} Search results with distance
|
|
60
|
+
*/
|
|
61
|
+
vectorSearch(_embedding, _limit) {
|
|
62
|
+
throw new Error('vectorSearch() must be implemented by subclass');
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Insert vector embedding
|
|
67
|
+
* @param {number} _rowid - Decision rowid
|
|
68
|
+
* @param {number[]} _embedding - Embedding vector
|
|
69
|
+
*/
|
|
70
|
+
insertEmbedding(_rowid, _embedding) {
|
|
71
|
+
throw new Error('insertEmbedding() must be implemented by subclass');
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Get last inserted row ID
|
|
76
|
+
* @returns {number} Last rowid
|
|
77
|
+
*/
|
|
78
|
+
getLastInsertRowid() {
|
|
79
|
+
throw new Error('getLastInsertRowid() must be implemented by subclass');
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Run migrations
|
|
84
|
+
* @param {string} _migrationsDir - Path to migrations directory
|
|
85
|
+
*/
|
|
86
|
+
runMigrations(_migrationsDir) {
|
|
87
|
+
throw new Error('runMigrations() must be implemented by subclass');
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
module.exports = { DatabaseAdapter };
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Database Adapter Factory (SQLite-only)
|
|
3
|
+
*
|
|
4
|
+
* MAMA Plugin uses SQLite exclusively for local storage.
|
|
5
|
+
* PostgreSQL support is only available in the legacy mcp-server.
|
|
6
|
+
*
|
|
7
|
+
* @module db-adapter
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const { info } = require('../debug-logger');
|
|
11
|
+
const SQLiteAdapter = require('./sqlite-adapter');
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Create SQLite database adapter
|
|
15
|
+
*
|
|
16
|
+
* @param {Object} config - Database configuration
|
|
17
|
+
* @param {string} [config.dbPath] - SQLite file path (overrides env)
|
|
18
|
+
* @returns {DatabaseAdapter} Configured SQLite adapter instance
|
|
19
|
+
*/
|
|
20
|
+
function createAdapter(config = {}) {
|
|
21
|
+
info('[db-adapter] Using SQLite adapter (plugin mode)');
|
|
22
|
+
const dbPath = config.dbPath || process.env.MAMA_DB_PATH;
|
|
23
|
+
return new SQLiteAdapter({ dbPath });
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const { DatabaseAdapter } = require('./base-adapter');
|
|
27
|
+
|
|
28
|
+
module.exports = {
|
|
29
|
+
createAdapter,
|
|
30
|
+
DatabaseAdapter,
|
|
31
|
+
};
|