hmem-mcp 6.6.0 → 6.6.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/dist/hmem-store.d.ts +6 -0
- package/dist/hmem-store.js +43 -2
- package/dist/hmem-store.js.map +1 -1
- package/dist/mcp-server.js +10 -0
- package/dist/mcp-server.js.map +1 -1
- package/package.json +1 -1
- package/skills/hmem-read/SKILL.md +4 -1
- package/skills/hmem-search/SKILL.md +58 -0
package/dist/hmem-store.d.ts
CHANGED
|
@@ -101,6 +101,12 @@ export interface MemoryEntry {
|
|
|
101
101
|
}[];
|
|
102
102
|
/** True if the entry is pinned (super-favorite). Pinned entries show full L2 content in bulk reads. */
|
|
103
103
|
pinned?: boolean;
|
|
104
|
+
/** FTS search: sub-nodes of this entry that matched the query. Empty/absent for root-only or tag-only matches. */
|
|
105
|
+
matchedNodes?: {
|
|
106
|
+
id: string;
|
|
107
|
+
title: string;
|
|
108
|
+
preview: string;
|
|
109
|
+
}[];
|
|
104
110
|
}
|
|
105
111
|
export interface MemoryNode {
|
|
106
112
|
id: string;
|
package/dist/hmem-store.js
CHANGED
|
@@ -636,7 +636,19 @@ export class HmemStore {
|
|
|
636
636
|
return [];
|
|
637
637
|
// FTS5 phrase match — all words must appear in the text
|
|
638
638
|
const ftsMatch = `"${searchTerm}"`;
|
|
639
|
-
const
|
|
639
|
+
const ftsRows = this.db.prepare("SELECT rm.root_id, rm.node_id FROM hmem_fts_rowid_map rm " +
|
|
640
|
+
"JOIN hmem_fts fts ON fts.rowid = rm.fts_rowid " +
|
|
641
|
+
"WHERE hmem_fts MATCH ?").all(ftsMatch);
|
|
642
|
+
const ftsRootIds = new Set();
|
|
643
|
+
const matchedNodesByRoot = new Map();
|
|
644
|
+
for (const r of ftsRows) {
|
|
645
|
+
ftsRootIds.add(r.root_id);
|
|
646
|
+
if (r.node_id) {
|
|
647
|
+
const arr = matchedNodesByRoot.get(r.root_id) ?? [];
|
|
648
|
+
arr.push(r.node_id);
|
|
649
|
+
matchedNodesByRoot.set(r.root_id, arr);
|
|
650
|
+
}
|
|
651
|
+
}
|
|
640
652
|
// Also search tags (e.g. search="#hmem" matches tag "#hmem")
|
|
641
653
|
const tagPattern = `%${opts.search}%`;
|
|
642
654
|
const tagRows = this.db.prepare("SELECT entry_id FROM memory_tags WHERE tag LIKE ?").all(tagPattern);
|
|
@@ -654,9 +666,38 @@ export class HmemStore {
|
|
|
654
666
|
if (limit !== undefined)
|
|
655
667
|
ftsParams.push(limit);
|
|
656
668
|
const rows = this.db.prepare(`SELECT * FROM memories ${where} ORDER BY created_at DESC${limitClause}`).all(...ftsParams);
|
|
669
|
+
// Batch-fetch matched sub-nodes across all roots (skip irrelevant)
|
|
670
|
+
const allMatchedNodeIds = [...new Set([...matchedNodesByRoot.values()].flat())];
|
|
671
|
+
const nodeInfo = new Map();
|
|
672
|
+
if (allMatchedNodeIds.length > 0) {
|
|
673
|
+
const nodePlaceholders = allMatchedNodeIds.map(() => "?").join(", ");
|
|
674
|
+
const nodeRows = this.db.prepare(`SELECT id, root_id, title, content FROM memory_nodes ` +
|
|
675
|
+
`WHERE id IN (${nodePlaceholders}) AND (irrelevant IS NULL OR irrelevant = 0)`).all(...allMatchedNodeIds);
|
|
676
|
+
for (const n of nodeRows) {
|
|
677
|
+
nodeInfo.set(n.id, { root_id: n.root_id, title: n.title ?? "", content: n.content ?? "" });
|
|
678
|
+
}
|
|
679
|
+
}
|
|
657
680
|
for (const row of rows)
|
|
658
681
|
this.bumpAccess(row.id);
|
|
659
|
-
return rows.map(r =>
|
|
682
|
+
return rows.map(r => {
|
|
683
|
+
const entry = this.rowToEntry(r);
|
|
684
|
+
const matchedIds = matchedNodesByRoot.get(r.id);
|
|
685
|
+
if (matchedIds && matchedIds.length > 0) {
|
|
686
|
+
const matched = matchedIds
|
|
687
|
+
.map(nid => {
|
|
688
|
+
const info = nodeInfo.get(nid);
|
|
689
|
+
if (!info)
|
|
690
|
+
return null; // filtered (irrelevant) or missing
|
|
691
|
+
const text = (info.title || info.content).replace(/\s+/g, " ").trim();
|
|
692
|
+
const preview = text.length > 80 ? text.substring(0, 80) + "…" : text;
|
|
693
|
+
return { id: nid, title: info.title || info.content.substring(0, 50), preview };
|
|
694
|
+
})
|
|
695
|
+
.filter((x) => x !== null);
|
|
696
|
+
if (matched.length > 0)
|
|
697
|
+
entry.matchedNodes = matched;
|
|
698
|
+
}
|
|
699
|
+
return entry;
|
|
700
|
+
});
|
|
660
701
|
}
|
|
661
702
|
// Build filtered bulk query (exclude headers: seq > 0)
|
|
662
703
|
const conditions = ["seq > 0"];
|