hmem-mcp 1.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.
@@ -0,0 +1,294 @@
1
+ // Semantic Memory Search — Full-text search over .hmem files and optionally project docs.
2
+ //
3
+ // Lightweight: No external dependencies (no ChromaDB/Faiss).
4
+ // Uses keyword matching with TF-based relevance scoring.
5
+ //
6
+ // Searches:
7
+ // - *.hmem files in PROJECT_DIR and subdirectories (agent memories — SQLite, hierarchical)
8
+ // - Optional: Personality.md, project docs, skill docs (if directories exist)
9
+ import fs from "node:fs";
10
+ import path from "node:path";
11
+ import { HmemStore } from "./hmem-store.js";
12
+ // ---- .hmem search (memories scope) ----
13
+ /**
14
+ * Searches a single .hmem SQLite file for the given keywords.
15
+ * Returns a SearchResult or null if no matches found.
16
+ */
17
+ function searchHmemFile(hmemPath, agentName, keywords, query, projectDir) {
18
+ let store = null;
19
+ try {
20
+ store = new HmemStore(hmemPath);
21
+ const entries = store.read({ search: query });
22
+ if (entries.length === 0)
23
+ return null;
24
+ // Score: number of matching entries weighted by keyword matches in L1
25
+ let score = 0;
26
+ const excerpts = [];
27
+ for (const entry of entries.slice(0, 3)) {
28
+ const text = entry.level_1 || "";
29
+ const entryScore = scoreText(text, keywords);
30
+ score += entryScore + 1; // +1 per match regardless of keyword density
31
+ // Build excerpt: [ID] L1 text + up to 1 child
32
+ let excerpt = `[${entry.id}] ${entry.level_1}`;
33
+ if (entry.children && entry.children.length > 0) {
34
+ const child = entry.children[0];
35
+ const preview = child.content.length > 120
36
+ ? child.content.substring(0, 117) + "..."
37
+ : child.content;
38
+ excerpt += `\n └ ${preview}`;
39
+ }
40
+ excerpts.push(excerpt);
41
+ }
42
+ if (score <= 0)
43
+ return null;
44
+ return {
45
+ file: path.relative(projectDir, hmemPath),
46
+ agent: agentName,
47
+ scope: "memories",
48
+ score: Math.round(score * 100) / 100,
49
+ excerpts,
50
+ };
51
+ }
52
+ catch {
53
+ return null;
54
+ }
55
+ finally {
56
+ store?.close();
57
+ }
58
+ }
59
+ /**
60
+ * Collects all .hmem files for the memories scope.
61
+ * Scans PROJECT_DIR root + common subdirectory patterns (Agents/, Assistenten/, agents/).
62
+ */
63
+ function collectHmemFiles(projectDir) {
64
+ const results = [];
65
+ // Scan common agent directory patterns
66
+ for (const subdir of ["Agents", "Assistenten", "agents"]) {
67
+ const dir = path.join(projectDir, subdir);
68
+ try {
69
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
70
+ for (const d of entries) {
71
+ if (!d.isDirectory())
72
+ continue;
73
+ const hmemPath = path.join(dir, d.name, `${d.name}.hmem`);
74
+ if (fs.existsSync(hmemPath)) {
75
+ results.push({ hmemPath, agentName: d.name });
76
+ }
77
+ }
78
+ }
79
+ catch { /* dir does not exist */ }
80
+ }
81
+ // Check for standalone .hmem files in PROJECT_DIR root
82
+ try {
83
+ const rootEntries = fs.readdirSync(projectDir, { withFileTypes: true });
84
+ for (const entry of rootEntries) {
85
+ if (entry.isFile() && entry.name.endsWith(".hmem")) {
86
+ const name = entry.name.replace(/\.hmem$/, "");
87
+ const hmemPath = path.join(projectDir, entry.name);
88
+ // Avoid duplicates (agent dirs already scanned above)
89
+ if (!results.some(r => r.hmemPath === hmemPath)) {
90
+ results.push({ hmemPath, agentName: name === "memory" ? "default" : name });
91
+ }
92
+ }
93
+ }
94
+ }
95
+ catch { /* */ }
96
+ return results;
97
+ }
98
+ // ---- File-based search (personalities, projects, skills) ----
99
+ /**
100
+ * Collects searchable .md files based on scope.
101
+ */
102
+ function collectMdFiles(dir, results, scope, maxDepth, currentDepth = 0) {
103
+ if (currentDepth > maxDepth)
104
+ return;
105
+ try {
106
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
107
+ for (const entry of entries) {
108
+ const fullPath = path.join(dir, entry.name);
109
+ if (entry.isFile() && entry.name.endsWith(".md")) {
110
+ results.push({ file: fullPath, scope });
111
+ }
112
+ else if (entry.isDirectory() && !entry.name.startsWith(".") && entry.name !== "node_modules") {
113
+ collectMdFiles(fullPath, results, scope, maxDepth, currentDepth + 1);
114
+ }
115
+ }
116
+ }
117
+ catch { /* Access error */ }
118
+ }
119
+ function collectMdFilesByScope(projectDir, scope) {
120
+ const results = [];
121
+ const agentsDir = path.join(projectDir, "Agents");
122
+ const projectsDir = path.join(projectDir, "Projects");
123
+ const skillsDir = path.join(projectDir, "skills");
124
+ if (scope === "personalities" || scope === "all") {
125
+ try {
126
+ const entries = fs.readdirSync(agentsDir, { withFileTypes: true });
127
+ for (const d of entries) {
128
+ if (!d.isDirectory())
129
+ continue;
130
+ const persFile = path.join(agentsDir, d.name, "Personality.md");
131
+ if (fs.existsSync(persFile)) {
132
+ results.push({ file: persFile, scope: "personalities" });
133
+ }
134
+ }
135
+ }
136
+ catch { /* */ }
137
+ }
138
+ if (scope === "projects" || scope === "all") {
139
+ try {
140
+ const projects = fs.readdirSync(projectsDir, { withFileTypes: true });
141
+ for (const p of projects) {
142
+ if (!p.isDirectory())
143
+ continue;
144
+ collectMdFiles(path.join(projectsDir, p.name), results, "projects", 2);
145
+ }
146
+ }
147
+ catch { /* */ }
148
+ }
149
+ if (scope === "skills" || scope === "all") {
150
+ try {
151
+ const skills = fs.readdirSync(skillsDir, { withFileTypes: true });
152
+ for (const s of skills) {
153
+ if (!s.isDirectory())
154
+ continue;
155
+ collectMdFiles(path.join(skillsDir, s.name), results, "skills", 1);
156
+ }
157
+ }
158
+ catch { /* */ }
159
+ }
160
+ return results;
161
+ }
162
+ // ---- Keyword scoring ----
163
+ /**
164
+ * Tokenizes a search query into keywords.
165
+ */
166
+ function tokenize(query) {
167
+ const stopwords = new Set([
168
+ "der", "die", "das", "ein", "eine", "und", "oder", "in", "von", "zu",
169
+ "mit", "auf", "fuer", "ist", "sind", "was", "wie", "wo", "wer",
170
+ "the", "a", "an", "and", "or", "in", "of", "to", "with", "on",
171
+ "for", "is", "are", "what", "how", "where", "who",
172
+ ]);
173
+ return query
174
+ .toLowerCase()
175
+ .replace(/[^a-z0-9äöüß_-]/g, " ")
176
+ .split(/\s+/)
177
+ .filter(w => w.length > 1 && !stopwords.has(w));
178
+ }
179
+ /**
180
+ * Scores a text string based on keyword frequency.
181
+ */
182
+ function scoreText(content, keywords) {
183
+ const lower = content.toLowerCase();
184
+ let score = 0;
185
+ for (const kw of keywords) {
186
+ let idx = 0;
187
+ let count = 0;
188
+ while ((idx = lower.indexOf(kw, idx)) !== -1) {
189
+ count++;
190
+ idx += kw.length;
191
+ }
192
+ if (count > 0) {
193
+ score += count * (1 + kw.length / 5);
194
+ }
195
+ }
196
+ return Math.round(score * 100) / 100;
197
+ }
198
+ /**
199
+ * Extracts text excerpts around keyword matches.
200
+ */
201
+ function extractExcerpts(content, keywords, contextLines) {
202
+ const lines = content.split("\n");
203
+ const matchedLineNums = new Set();
204
+ for (const kw of keywords) {
205
+ for (let i = 0; i < lines.length; i++) {
206
+ if (lines[i].toLowerCase().includes(kw)) {
207
+ matchedLineNums.add(i);
208
+ }
209
+ }
210
+ }
211
+ if (matchedLineNums.size === 0)
212
+ return [];
213
+ const sorted = Array.from(matchedLineNums).sort((a, b) => a - b);
214
+ const excerpts = [];
215
+ let currentGroup = [];
216
+ for (const lineNum of sorted) {
217
+ if (currentGroup.length === 0 || lineNum - currentGroup[currentGroup.length - 1] <= contextLines * 2 + 1) {
218
+ currentGroup.push(lineNum);
219
+ }
220
+ else {
221
+ excerpts.push(buildExcerpt(lines, currentGroup, contextLines));
222
+ currentGroup = [lineNum];
223
+ }
224
+ }
225
+ if (currentGroup.length > 0) {
226
+ excerpts.push(buildExcerpt(lines, currentGroup, contextLines));
227
+ }
228
+ return excerpts.slice(0, 3);
229
+ }
230
+ function buildExcerpt(lines, matchedLines, contextLines) {
231
+ const start = Math.max(0, matchedLines[0] - contextLines);
232
+ const end = Math.min(lines.length - 1, matchedLines[matchedLines.length - 1] + contextLines);
233
+ const excerpt = lines.slice(start, end + 1).join("\n").trim();
234
+ return excerpt.length > 500 ? excerpt.substring(0, 497) + "..." : excerpt;
235
+ }
236
+ // ---- Main export ----
237
+ /**
238
+ * Searches the knowledge base across agent memories (.hmem), personalities,
239
+ * project docs, and skills.
240
+ */
241
+ export function searchMemory(projectDir, query, options = {}) {
242
+ const { scope = "all", maxResults = 10, contextLines = 2 } = options;
243
+ const keywords = tokenize(query);
244
+ if (keywords.length === 0)
245
+ return [];
246
+ const results = [];
247
+ // --- Memories scope: .hmem SQLite ---
248
+ if (scope === "memories" || scope === "all") {
249
+ const hmemFiles = collectHmemFiles(projectDir);
250
+ for (const { hmemPath, agentName } of hmemFiles) {
251
+ const result = searchHmemFile(hmemPath, agentName, keywords, query, projectDir);
252
+ if (result)
253
+ results.push(result);
254
+ }
255
+ }
256
+ // --- File-based scopes: personalities, projects, skills ---
257
+ if (scope !== "memories") {
258
+ const mdScope = scope === "all" ? "all" : scope;
259
+ const mdFiles = collectMdFilesByScope(projectDir, mdScope);
260
+ for (const { file, scope: fileScope } of mdFiles) {
261
+ let content;
262
+ try {
263
+ const stat = fs.statSync(file);
264
+ if (stat.size > 500 * 1024)
265
+ continue;
266
+ content = fs.readFileSync(file, "utf-8");
267
+ }
268
+ catch {
269
+ continue;
270
+ }
271
+ if (content.trim().length < 50)
272
+ continue;
273
+ const score = scoreText(content, keywords);
274
+ if (score <= 0)
275
+ continue;
276
+ const relPath = path.relative(projectDir, file);
277
+ let agent;
278
+ const agentMatch = relPath.match(/^Agents\/([^/]+)\//);
279
+ if (agentMatch)
280
+ agent = agentMatch[1];
281
+ const excerpts = extractExcerpts(content, keywords, contextLines);
282
+ results.push({
283
+ file: relPath,
284
+ agent,
285
+ scope: fileScope,
286
+ score,
287
+ excerpts,
288
+ });
289
+ }
290
+ }
291
+ results.sort((a, b) => b.score - a.score);
292
+ return results.slice(0, maxResults);
293
+ }
294
+ //# sourceMappingURL=memory-search.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory-search.js","sourceRoot":"","sources":["../src/memory-search.ts"],"names":[],"mappings":"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"}
package/dist/types.js ADDED
@@ -0,0 +1,53 @@
1
+ export const DEFAULT_CONFIG = {
2
+ projectDir: "",
3
+ pollMs: 1000,
4
+ ackTimeoutMs: 30_000,
5
+ execWarningMs: 600_000,
6
+ zombieKillMs: 900_000,
7
+ maxSpawnDepth: 3,
8
+ maxSpawnsPerAgent: 5,
9
+ logMaxBytes: 10 * 1024 * 1024,
10
+ logJsonMode: false,
11
+ archiveDays: 7,
12
+ retry: {
13
+ maxRetries: 2,
14
+ retryDelayMs: 10_000,
15
+ backoffMultiplier: 2,
16
+ },
17
+ autoSynthesize: false,
18
+ synthesizeTemplate: "consolidator",
19
+ logLevel: "info",
20
+ enableSurveys: false,
21
+ surveyTemplate: "agent-survey",
22
+ maxConcurrentAgents: 10,
23
+ maxConcurrentOpencode: 2,
24
+ loadThrottleAvg1: 0, // 0 = disabled
25
+ loadKillAvg1: 12, // Kill agents when avg1 > 12
26
+ workerModelBlacklist: [],
27
+ scheduledAgents: [],
28
+ };
29
+ export const MODEL_ALIASES = {
30
+ // Claude (via claude CLI)
31
+ "opus": "claude-opus-4-6",
32
+ "sonnet": "claude-sonnet-4-5-20250929",
33
+ "haiku": "claude-haiku-4-5-20251001",
34
+ // Gemini (via gemini CLI)
35
+ "gemini-pro": "gemini-3-pro-preview",
36
+ "gemini-flash": "gemini-3-flash-preview",
37
+ // OpenRouter (via opencode) — Format: openrouter/provider/model
38
+ "deepseek": "openrouter/deepseek/deepseek-v3.2",
39
+ "qwen-coder": "openrouter/qwen/qwen3-coder",
40
+ "devstral": "openrouter/mistralai/devstral-2512",
41
+ "mistral": "openrouter/mistralai/mistral-medium-3.1",
42
+ "grok-fast": "openrouter/x-ai/grok-4.1-fast",
43
+ "glm": "openrouter/z-ai/glm-5",
44
+ "glm-flash": "openrouter/z-ai/glm-4.7-flash",
45
+ "gpt-mini": "openrouter/openai/gpt-5.1-codex-mini",
46
+ // New frontier models (via opencode/openrouter)
47
+ "minimax": "openrouter/minimax/minimax-m2.5",
48
+ "kimi": "openrouter/moonshotai/kimi-k2.5",
49
+ // Free Tier (via opencode/openrouter)
50
+ "deepseek-free": "openrouter/deepseek/deepseek-r1-0528:free",
51
+ "devstral-free": "openrouter/mistralai/devstral-small-2505:free",
52
+ "qwen-free": "openrouter/qwen/qwen3-235b-a22b:free",
53
+ };
@@ -0,0 +1,13 @@
1
+ {
2
+ "_comment": "Copy this file to your project root as hmem.config.json and adjust to taste.",
3
+
4
+ "maxL1Chars": 500,
5
+ "maxLnChars": 50000,
6
+
7
+ "_comment_limits": "Instead of maxL1Chars/maxLnChars you can set all levels explicitly:",
8
+ "_example_explicit": { "maxCharsPerLevel": [500, 5000, 15000, 30000, 50000] },
9
+
10
+ "maxDepth": 5,
11
+ "recentChildrenCount": 10,
12
+ "defaultReadLimit": 100
13
+ }
package/package.json ADDED
@@ -0,0 +1,68 @@
1
+ {
2
+ "name": "hmem-mcp",
3
+ "version": "1.1.0",
4
+ "description": "Humanlike memory for AI agents — MCP server with 5-level lazy-loaded SQLite memory",
5
+ "author": "Bumblebiber",
6
+ "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/Bumblebiber/hmem.git"
10
+ },
11
+ "homepage": "https://github.com/Bumblebiber/hmem#readme",
12
+ "bugs": {
13
+ "url": "https://github.com/Bumblebiber/hmem/issues"
14
+ },
15
+ "main": "dist/index.js",
16
+ "types": "dist/index.d.ts",
17
+ "bin": {
18
+ "hmem": "dist/cli.js"
19
+ },
20
+ "exports": {
21
+ ".": {
22
+ "import": "./dist/index.js",
23
+ "types": "./dist/index.d.ts"
24
+ }
25
+ },
26
+ "files": [
27
+ "dist",
28
+ "skills",
29
+ "README.md",
30
+ "LICENSE",
31
+ "hmem.config.example.json"
32
+ ],
33
+ "scripts": {
34
+ "build": "tsc",
35
+ "start": "node dist/mcp-server.js",
36
+ "dev": "tsc --watch",
37
+ "prepublishOnly": "npm run build"
38
+ },
39
+ "type": "module",
40
+ "engines": {
41
+ "node": ">=18"
42
+ },
43
+ "keywords": [
44
+ "mcp",
45
+ "model-context-protocol",
46
+ "ai",
47
+ "memory",
48
+ "agents",
49
+ "llm",
50
+ "sqlite",
51
+ "humanlike",
52
+ "hierarchical",
53
+ "long-term-memory",
54
+ "claude",
55
+ "gemini",
56
+ "persistent-memory"
57
+ ],
58
+ "devDependencies": {
59
+ "@types/better-sqlite3": "^7.6.13",
60
+ "@types/node": "^25.2.3",
61
+ "typescript": "^5.9.3"
62
+ },
63
+ "dependencies": {
64
+ "@modelcontextprotocol/sdk": "^1.26.0",
65
+ "better-sqlite3": "^12.6.2",
66
+ "zod": "^4.3.6"
67
+ }
68
+ }
@@ -0,0 +1,66 @@
1
+ ---
2
+ name: hmem-read
3
+ description: Load your long-term memory. Call this skill at session start or after context reset.
4
+ ---
5
+
6
+ # ACTION REQUIRED: Call read_memory() NOW
7
+
8
+ When this skill is invoked, you MUST immediately call the MCP tool `read_memory` with no arguments.
9
+ Do NOT just read this document — execute the tool call.
10
+
11
+ ```
12
+ read_memory()
13
+ ```
14
+
15
+ This returns your Level 1 memory summaries. Show them to the user.
16
+
17
+ If the tool `read_memory` is not available, tell the user:
18
+ "read_memory tool not found. Run `hmem init` to configure the MCP server."
19
+
20
+ ---
21
+
22
+ ## Lazy Loading Protocol (for subsequent reads)
23
+
24
+ After the initial `read_memory()`, use these patterns to drill deeper:
25
+
26
+ ```
27
+ # Filter by category
28
+ read_memory(prefix="E") # only errors
29
+ read_memory(store="company") # shared company knowledge
30
+
31
+ # Expand a root entry → shows L2 children
32
+ read_memory(id="E0042")
33
+
34
+ # Expand an L2 node → shows L3 children
35
+ read_memory(id="E0042.2")
36
+
37
+ # Expand further (rarely needed)
38
+ read_memory(id="E0042.2.1")
39
+ ```
40
+
41
+ **Rule: depth parameter is only useful for listings (max 3), not for ID queries.**
42
+
43
+ ```
44
+ read_memory(depth=2) # all entries with L2 children
45
+ read_memory(prefix="L", depth=2) # all lessons with details
46
+ ```
47
+
48
+ ---
49
+
50
+ ## Search
51
+
52
+ ```
53
+ search_memory(query="Node.js startup crash")
54
+ search_memory(query="auth token", scope="memories")
55
+ ```
56
+
57
+ ---
58
+
59
+ ## Anti-Patterns
60
+
61
+ | Wrong | Right |
62
+ |-------|-------|
63
+ | `read_memory(id="E0042", depth=3)` | `read_memory(id="E0042.2")` — branch by branch |
64
+ | Load everything without purpose | Check L1 first, then expand selectively |
65
+ | Read .hmem file directly | Always use MCP tools — it's a SQLite binary |
66
+ | Just display this skill text | **Call read_memory() immediately** |