@velvetmonkey/flywheel-memory 2.0.122 → 2.0.124
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/index.js +218 -83
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -6314,7 +6314,11 @@ var indexProgress = { parsed: 0, total: 0 };
|
|
|
6314
6314
|
var indexError = null;
|
|
6315
6315
|
function getIndexState() {
|
|
6316
6316
|
const scope = getActiveScopeOrNull();
|
|
6317
|
-
|
|
6317
|
+
if (scope) {
|
|
6318
|
+
if (scope.indexState === "building" && indexState === "ready") return "ready";
|
|
6319
|
+
return scope.indexState;
|
|
6320
|
+
}
|
|
6321
|
+
return indexState;
|
|
6318
6322
|
}
|
|
6319
6323
|
function getIndexProgress() {
|
|
6320
6324
|
return { ...indexProgress };
|
|
@@ -10720,7 +10724,7 @@ function registerHealthTools(server2, getIndex, getVaultPath, getConfig2 = () =>
|
|
|
10720
10724
|
import { z as z4 } from "zod";
|
|
10721
10725
|
init_embeddings();
|
|
10722
10726
|
import {
|
|
10723
|
-
searchEntities,
|
|
10727
|
+
searchEntities as searchEntities2,
|
|
10724
10728
|
searchEntitiesPrefix
|
|
10725
10729
|
} from "@velvetmonkey/vault-core";
|
|
10726
10730
|
|
|
@@ -10729,7 +10733,6 @@ import {
|
|
|
10729
10733
|
getEntityByName as getEntityByName2
|
|
10730
10734
|
} from "@velvetmonkey/vault-core";
|
|
10731
10735
|
var TOP_LINKS = 10;
|
|
10732
|
-
var RECALL_TOP_LINKS = 5;
|
|
10733
10736
|
function recencyDecay(modifiedDate) {
|
|
10734
10737
|
if (!modifiedDate) return 0.5;
|
|
10735
10738
|
const daysSince = (Date.now() - modifiedDate.getTime()) / (1e3 * 60 * 60 * 24);
|
|
@@ -10819,6 +10822,63 @@ function rankBacklinks(backlinks, notePath, index, stateDb2, maxLinks = TOP_LINK
|
|
|
10819
10822
|
return out;
|
|
10820
10823
|
}).sort((a, b) => (b.weight ?? 1) - (a.weight ?? 1)).slice(0, maxLinks);
|
|
10821
10824
|
}
|
|
10825
|
+
var COMPACT_OUTLINK_NAMES = 10;
|
|
10826
|
+
function enrichResultCompact(result, index, stateDb2, opts) {
|
|
10827
|
+
const note = index.notes.get(result.path);
|
|
10828
|
+
const normalizedPath = result.path.toLowerCase().replace(/\.md$/, "");
|
|
10829
|
+
const backlinks = index.backlinks.get(normalizedPath) || [];
|
|
10830
|
+
const enriched = {
|
|
10831
|
+
path: result.path,
|
|
10832
|
+
title: result.title
|
|
10833
|
+
};
|
|
10834
|
+
if (result.snippet) {
|
|
10835
|
+
enriched.snippet = result.snippet;
|
|
10836
|
+
} else {
|
|
10837
|
+
const preview = getContentPreview(result.path);
|
|
10838
|
+
if (preview) enriched.snippet = preview;
|
|
10839
|
+
}
|
|
10840
|
+
if (note) {
|
|
10841
|
+
enriched.backlink_count = backlinks.length;
|
|
10842
|
+
enriched.modified = note.modified.toISOString();
|
|
10843
|
+
if (note.tags.length > 0) enriched.tags = note.tags;
|
|
10844
|
+
if (note.outlinks.length > 0) {
|
|
10845
|
+
enriched.outlink_names = getOutlinkNames(note.outlinks, result.path, index, stateDb2, COMPACT_OUTLINK_NAMES);
|
|
10846
|
+
}
|
|
10847
|
+
}
|
|
10848
|
+
if (stateDb2) {
|
|
10849
|
+
try {
|
|
10850
|
+
const entity = getEntityByName2(stateDb2, result.title);
|
|
10851
|
+
if (entity) {
|
|
10852
|
+
enriched.category = entity.category;
|
|
10853
|
+
enriched.hub_score = entity.hubScore;
|
|
10854
|
+
if (!enriched.snippet && entity.description) {
|
|
10855
|
+
enriched.snippet = entity.description;
|
|
10856
|
+
}
|
|
10857
|
+
}
|
|
10858
|
+
} catch {
|
|
10859
|
+
}
|
|
10860
|
+
}
|
|
10861
|
+
if (opts?.via) enriched.via = opts.via;
|
|
10862
|
+
if (opts?.hop) enriched.hop = opts.hop;
|
|
10863
|
+
return enriched;
|
|
10864
|
+
}
|
|
10865
|
+
function getOutlinkNames(outlinks, notePath, index, stateDb2, max) {
|
|
10866
|
+
const weightMap = /* @__PURE__ */ new Map();
|
|
10867
|
+
if (stateDb2) {
|
|
10868
|
+
try {
|
|
10869
|
+
const rows = stateDb2.db.prepare(
|
|
10870
|
+
"SELECT target, weight, weight_updated_at FROM note_links WHERE note_path = ?"
|
|
10871
|
+
).all(notePath);
|
|
10872
|
+
for (const row of rows) {
|
|
10873
|
+
const daysSince = row.weight_updated_at ? (Date.now() - row.weight_updated_at) / (1e3 * 60 * 60 * 24) : 0;
|
|
10874
|
+
const decay = Math.max(0.1, 1 - daysSince / 180);
|
|
10875
|
+
weightMap.set(row.target, row.weight * decay);
|
|
10876
|
+
}
|
|
10877
|
+
} catch {
|
|
10878
|
+
}
|
|
10879
|
+
}
|
|
10880
|
+
return outlinks.map((l) => ({ name: l.target, weight: weightMap.get(l.target.toLowerCase()) ?? 1 })).sort((a, b) => b.weight - a.weight).slice(0, max).map((l) => l.name);
|
|
10881
|
+
}
|
|
10822
10882
|
function enrichResult(result, index, stateDb2) {
|
|
10823
10883
|
const note = index.notes.get(result.path);
|
|
10824
10884
|
const normalizedPath = result.path.toLowerCase().replace(/\.md$/, "");
|
|
@@ -10886,7 +10946,7 @@ function enrichResultLight(result, index, stateDb2) {
|
|
|
10886
10946
|
}
|
|
10887
10947
|
return enriched;
|
|
10888
10948
|
}
|
|
10889
|
-
function
|
|
10949
|
+
function enrichEntityCompact(entityName, stateDb2, index) {
|
|
10890
10950
|
const enriched = {};
|
|
10891
10951
|
if (stateDb2) {
|
|
10892
10952
|
try {
|
|
@@ -10908,36 +10968,27 @@ function enrichEntityResult(entityName, stateDb2, index) {
|
|
|
10908
10968
|
const backlinks = index.backlinks.get(normalizedPath) || [];
|
|
10909
10969
|
enriched.backlink_count = backlinks.length;
|
|
10910
10970
|
if (note) {
|
|
10911
|
-
enriched.outlink_count = note.outlinks.length;
|
|
10912
10971
|
if (note.tags.length > 0) enriched.tags = note.tags;
|
|
10913
|
-
if (backlinks.length > 0) {
|
|
10914
|
-
enriched.top_backlinks = rankBacklinks(backlinks, entityPath, index, stateDb2, RECALL_TOP_LINKS);
|
|
10915
|
-
}
|
|
10916
10972
|
if (note.outlinks.length > 0) {
|
|
10917
|
-
enriched.
|
|
10973
|
+
enriched.outlink_names = getOutlinkNames(note.outlinks, entityPath, index, stateDb2, COMPACT_OUTLINK_NAMES);
|
|
10918
10974
|
}
|
|
10919
10975
|
}
|
|
10920
10976
|
}
|
|
10921
10977
|
}
|
|
10922
10978
|
return enriched;
|
|
10923
10979
|
}
|
|
10924
|
-
function
|
|
10980
|
+
function enrichNoteCompact(notePath, stateDb2, index) {
|
|
10925
10981
|
const enriched = {};
|
|
10926
10982
|
if (!index) return enriched;
|
|
10927
10983
|
const note = index.notes.get(notePath);
|
|
10928
10984
|
if (!note) return enriched;
|
|
10929
10985
|
const normalizedPath = notePath.toLowerCase().replace(/\.md$/, "");
|
|
10930
10986
|
const backlinks = index.backlinks.get(normalizedPath) || [];
|
|
10931
|
-
enriched.frontmatter = note.frontmatter;
|
|
10932
10987
|
if (note.tags.length > 0) enriched.tags = note.tags;
|
|
10933
10988
|
enriched.backlink_count = backlinks.length;
|
|
10934
|
-
enriched.outlink_count = note.outlinks.length;
|
|
10935
10989
|
enriched.modified = note.modified.toISOString();
|
|
10936
|
-
if (backlinks.length > 0) {
|
|
10937
|
-
enriched.top_backlinks = rankBacklinks(backlinks, notePath, index, stateDb2, RECALL_TOP_LINKS);
|
|
10938
|
-
}
|
|
10939
10990
|
if (note.outlinks.length > 0) {
|
|
10940
|
-
enriched.
|
|
10991
|
+
enriched.outlink_names = getOutlinkNames(note.outlinks, notePath, index, stateDb2, COMPACT_OUTLINK_NAMES);
|
|
10941
10992
|
}
|
|
10942
10993
|
if (stateDb2) {
|
|
10943
10994
|
try {
|
|
@@ -10952,8 +11003,132 @@ function enrichNoteResult(notePath, stateDb2, index) {
|
|
|
10952
11003
|
return enriched;
|
|
10953
11004
|
}
|
|
10954
11005
|
|
|
11006
|
+
// src/core/read/multihop.ts
|
|
11007
|
+
import { getEntityByName as getEntityByName3, searchEntities } from "@velvetmonkey/vault-core";
|
|
11008
|
+
var DEFAULT_CONFIG2 = {
|
|
11009
|
+
maxParents: 10,
|
|
11010
|
+
maxHops: 2,
|
|
11011
|
+
maxOutlinksPerHop: 10,
|
|
11012
|
+
maxBackfill: 10
|
|
11013
|
+
};
|
|
11014
|
+
function multiHopBackfill(primaryResults, index, stateDb2, config = {}) {
|
|
11015
|
+
const cfg = { ...DEFAULT_CONFIG2, ...config };
|
|
11016
|
+
const seen = new Set(primaryResults.map((r) => r.path).filter(Boolean));
|
|
11017
|
+
const candidates = [];
|
|
11018
|
+
const hop1Results = [];
|
|
11019
|
+
for (const primary of primaryResults.slice(0, cfg.maxParents)) {
|
|
11020
|
+
const primaryPath = primary.path;
|
|
11021
|
+
if (!primaryPath) continue;
|
|
11022
|
+
const note = index.notes.get(primaryPath);
|
|
11023
|
+
if (!note) continue;
|
|
11024
|
+
for (const outlink of note.outlinks.slice(0, cfg.maxOutlinksPerHop)) {
|
|
11025
|
+
const targetPath = index.entities.get(outlink.target.toLowerCase());
|
|
11026
|
+
if (!targetPath || seen.has(targetPath)) continue;
|
|
11027
|
+
seen.add(targetPath);
|
|
11028
|
+
const targetNote = index.notes.get(targetPath);
|
|
11029
|
+
const title = targetNote?.title ?? outlink.target;
|
|
11030
|
+
hop1Results.push({ path: targetPath, title, via: primaryPath });
|
|
11031
|
+
}
|
|
11032
|
+
}
|
|
11033
|
+
for (const h1 of hop1Results) {
|
|
11034
|
+
const enriched = enrichResultCompact(
|
|
11035
|
+
{ path: h1.path, title: h1.title },
|
|
11036
|
+
index,
|
|
11037
|
+
stateDb2,
|
|
11038
|
+
{ via: h1.via, hop: 1 }
|
|
11039
|
+
);
|
|
11040
|
+
const score = scoreCandidate(h1.path, index, stateDb2);
|
|
11041
|
+
candidates.push({ result: enriched, score });
|
|
11042
|
+
}
|
|
11043
|
+
if (cfg.maxHops >= 2) {
|
|
11044
|
+
for (const h1 of hop1Results) {
|
|
11045
|
+
const note = index.notes.get(h1.path);
|
|
11046
|
+
if (!note) continue;
|
|
11047
|
+
for (const outlink of note.outlinks.slice(0, cfg.maxOutlinksPerHop)) {
|
|
11048
|
+
const targetPath = index.entities.get(outlink.target.toLowerCase());
|
|
11049
|
+
if (!targetPath || seen.has(targetPath)) continue;
|
|
11050
|
+
seen.add(targetPath);
|
|
11051
|
+
const targetNote = index.notes.get(targetPath);
|
|
11052
|
+
const title = targetNote?.title ?? outlink.target;
|
|
11053
|
+
const enriched = enrichResultCompact(
|
|
11054
|
+
{ path: targetPath, title },
|
|
11055
|
+
index,
|
|
11056
|
+
stateDb2,
|
|
11057
|
+
{ via: h1.path, hop: 2 }
|
|
11058
|
+
);
|
|
11059
|
+
const score = scoreCandidate(targetPath, index, stateDb2);
|
|
11060
|
+
candidates.push({ result: enriched, score });
|
|
11061
|
+
}
|
|
11062
|
+
}
|
|
11063
|
+
}
|
|
11064
|
+
candidates.sort((a, b) => b.score - a.score);
|
|
11065
|
+
return candidates.slice(0, cfg.maxBackfill).map((c) => c.result);
|
|
11066
|
+
}
|
|
11067
|
+
function scoreCandidate(path33, index, stateDb2) {
|
|
11068
|
+
const note = index.notes.get(path33);
|
|
11069
|
+
const decay = recencyDecay(note?.modified);
|
|
11070
|
+
let hubScore = 1;
|
|
11071
|
+
if (stateDb2) {
|
|
11072
|
+
try {
|
|
11073
|
+
const title = note?.title ?? path33.replace(/\.md$/, "").split("/").pop() ?? "";
|
|
11074
|
+
const entity = getEntityByName3(stateDb2, title);
|
|
11075
|
+
if (entity) hubScore = entity.hubScore ?? 1;
|
|
11076
|
+
} catch {
|
|
11077
|
+
}
|
|
11078
|
+
}
|
|
11079
|
+
return hubScore * decay;
|
|
11080
|
+
}
|
|
11081
|
+
function extractExpansionTerms(results, originalQuery, index) {
|
|
11082
|
+
const queryLower = originalQuery.toLowerCase();
|
|
11083
|
+
const terms = /* @__PURE__ */ new Set();
|
|
11084
|
+
for (const r of results.slice(0, 5)) {
|
|
11085
|
+
const outlinks = r.outlink_names;
|
|
11086
|
+
if (outlinks) {
|
|
11087
|
+
for (const name of outlinks) {
|
|
11088
|
+
if (!queryLower.includes(name.toLowerCase()) && index.entities.has(name.toLowerCase())) {
|
|
11089
|
+
terms.add(name);
|
|
11090
|
+
}
|
|
11091
|
+
}
|
|
11092
|
+
}
|
|
11093
|
+
const snippet = r.snippet;
|
|
11094
|
+
if (snippet) {
|
|
11095
|
+
const matches = snippet.match(/\[\[([^\]|]+)(?:\|[^\]]+)?\]\]/g);
|
|
11096
|
+
if (matches) {
|
|
11097
|
+
for (const wl of matches) {
|
|
11098
|
+
const name = wl.replace(/\[\[|\]\]/g, "").split("|")[0];
|
|
11099
|
+
if (!queryLower.includes(name.toLowerCase()) && index.entities.has(name.toLowerCase())) {
|
|
11100
|
+
terms.add(name);
|
|
11101
|
+
}
|
|
11102
|
+
}
|
|
11103
|
+
}
|
|
11104
|
+
}
|
|
11105
|
+
}
|
|
11106
|
+
return Array.from(terms).slice(0, 10);
|
|
11107
|
+
}
|
|
11108
|
+
function expandQuery(expansionTerms, primaryResults, index, stateDb2) {
|
|
11109
|
+
if (!stateDb2 || expansionTerms.length === 0) return [];
|
|
11110
|
+
const seen = new Set(primaryResults.map((r) => r.path).filter(Boolean));
|
|
11111
|
+
const results = [];
|
|
11112
|
+
for (const term of expansionTerms) {
|
|
11113
|
+
try {
|
|
11114
|
+
const entities = searchEntities(stateDb2, term, 3);
|
|
11115
|
+
for (const entity of entities) {
|
|
11116
|
+
if (!entity.path || seen.has(entity.path)) continue;
|
|
11117
|
+
seen.add(entity.path);
|
|
11118
|
+
results.push(enrichResultCompact(
|
|
11119
|
+
{ path: entity.path, title: entity.name },
|
|
11120
|
+
index,
|
|
11121
|
+
stateDb2,
|
|
11122
|
+
{ via: "query_expansion" }
|
|
11123
|
+
));
|
|
11124
|
+
}
|
|
11125
|
+
} catch {
|
|
11126
|
+
}
|
|
11127
|
+
}
|
|
11128
|
+
return results;
|
|
11129
|
+
}
|
|
11130
|
+
|
|
10955
11131
|
// src/tools/read/query.ts
|
|
10956
|
-
init_wikilinkFeedback();
|
|
10957
11132
|
function matchesFrontmatter(note, where) {
|
|
10958
11133
|
for (const [key, value] of Object.entries(where)) {
|
|
10959
11134
|
const noteValue = note.frontmatter[key];
|
|
@@ -11134,7 +11309,7 @@ function registerQueryTools(server2, getIndex, getVaultPath, getStateDb2) {
|
|
|
11134
11309
|
const stateDbEntity = getStateDb2();
|
|
11135
11310
|
if (stateDbEntity) {
|
|
11136
11311
|
try {
|
|
11137
|
-
entityResults =
|
|
11312
|
+
entityResults = searchEntities2(stateDbEntity, query, limit);
|
|
11138
11313
|
} catch {
|
|
11139
11314
|
}
|
|
11140
11315
|
}
|
|
@@ -11206,46 +11381,22 @@ function registerQueryTools(server2, getIndex, getVaultPath, getStateDb2) {
|
|
|
11206
11381
|
scored.sort((a, b) => b.rrf_score - a.rrf_score);
|
|
11207
11382
|
const filtered = applyFolderFilter(scored);
|
|
11208
11383
|
const stateDb2 = getStateDb2();
|
|
11209
|
-
const
|
|
11210
|
-
...(
|
|
11384
|
+
const results2 = filtered.slice(0, limit).map((item) => ({
|
|
11385
|
+
...enrichResultCompact({ path: item.path, title: item.title, snippet: item.snippet }, index, stateDb2),
|
|
11211
11386
|
rrf_score: item.rrf_score,
|
|
11212
11387
|
in_fts5: item.in_fts5,
|
|
11213
11388
|
in_semantic: item.in_semantic,
|
|
11214
11389
|
in_entity: item.in_entity
|
|
11215
11390
|
}));
|
|
11216
|
-
|
|
11217
|
-
|
|
11218
|
-
|
|
11219
|
-
|
|
11220
|
-
const rPath = r.path;
|
|
11221
|
-
if (!rPath) continue;
|
|
11222
|
-
try {
|
|
11223
|
-
const outlinks = getStoredNoteLinks(stateDb2, rPath);
|
|
11224
|
-
for (const target of outlinks) {
|
|
11225
|
-
const entityRow = stateDb2.db.prepare(
|
|
11226
|
-
"SELECT path FROM entities WHERE name_lower = ?"
|
|
11227
|
-
).get(target);
|
|
11228
|
-
if (entityRow?.path && !existingPaths.has(entityRow.path)) {
|
|
11229
|
-
existingPaths.add(entityRow.path);
|
|
11230
|
-
backfill.push({
|
|
11231
|
-
...enrichResultLight({ path: entityRow.path, title: target }, index, stateDb2),
|
|
11232
|
-
rrf_score: 0,
|
|
11233
|
-
in_fts5: false,
|
|
11234
|
-
in_semantic: false,
|
|
11235
|
-
in_entity: false
|
|
11236
|
-
});
|
|
11237
|
-
}
|
|
11238
|
-
}
|
|
11239
|
-
} catch {
|
|
11240
|
-
}
|
|
11241
|
-
}
|
|
11242
|
-
results.push(...backfill.slice(0, limit - results.length));
|
|
11243
|
-
}
|
|
11391
|
+
const hopResults2 = multiHopBackfill(results2, index, stateDb2, { maxBackfill: limit });
|
|
11392
|
+
const expansionTerms2 = extractExpansionTerms(results2, query, index);
|
|
11393
|
+
const expansionResults2 = expandQuery(expansionTerms2, [...results2, ...hopResults2], index, stateDb2);
|
|
11394
|
+
results2.push(...hopResults2, ...expansionResults2);
|
|
11244
11395
|
return { content: [{ type: "text", text: JSON.stringify({
|
|
11245
11396
|
method: "hybrid",
|
|
11246
11397
|
query,
|
|
11247
11398
|
total_results: filtered.length,
|
|
11248
|
-
results
|
|
11399
|
+
results: results2
|
|
11249
11400
|
}, null, 2) }] };
|
|
11250
11401
|
} catch (err) {
|
|
11251
11402
|
console.error("[Semantic] Hybrid search failed, falling back to FTS5:", err instanceof Error ? err.message : err);
|
|
@@ -11262,49 +11413,33 @@ function registerQueryTools(server2, getIndex, getVaultPath, getStateDb2) {
|
|
|
11262
11413
|
const filtered = applyFolderFilter(mergedItems);
|
|
11263
11414
|
const stateDb2 = getStateDb2();
|
|
11264
11415
|
const sliced = filtered.slice(0, limit);
|
|
11265
|
-
const
|
|
11266
|
-
...(
|
|
11416
|
+
const results2 = sliced.map((item) => ({
|
|
11417
|
+
...enrichResultCompact({ path: item.path, title: item.title, snippet: item.snippet }, index, stateDb2),
|
|
11267
11418
|
..."in_fts5" in item ? { in_fts5: true } : { in_entity: true }
|
|
11268
11419
|
}));
|
|
11269
|
-
|
|
11270
|
-
|
|
11271
|
-
|
|
11272
|
-
|
|
11273
|
-
const rPath = r.path;
|
|
11274
|
-
if (!rPath) continue;
|
|
11275
|
-
try {
|
|
11276
|
-
const outlinks = getStoredNoteLinks(stateDb2, rPath);
|
|
11277
|
-
for (const target of outlinks) {
|
|
11278
|
-
const entityRow = stateDb2.db.prepare(
|
|
11279
|
-
"SELECT path FROM entities WHERE name_lower = ?"
|
|
11280
|
-
).get(target);
|
|
11281
|
-
if (entityRow?.path && !existingPaths.has(entityRow.path)) {
|
|
11282
|
-
existingPaths.add(entityRow.path);
|
|
11283
|
-
backfill.push({
|
|
11284
|
-
...enrichResultLight({ path: entityRow.path, title: target }, index, stateDb2)
|
|
11285
|
-
});
|
|
11286
|
-
}
|
|
11287
|
-
}
|
|
11288
|
-
} catch {
|
|
11289
|
-
}
|
|
11290
|
-
}
|
|
11291
|
-
results.push(...backfill.slice(0, limit - results.length));
|
|
11292
|
-
}
|
|
11420
|
+
const hopResults2 = multiHopBackfill(results2, index, stateDb2, { maxBackfill: limit });
|
|
11421
|
+
const expansionTerms2 = extractExpansionTerms(results2, query, index);
|
|
11422
|
+
const expansionResults2 = expandQuery(expansionTerms2, [...results2, ...hopResults2], index, stateDb2);
|
|
11423
|
+
results2.push(...hopResults2, ...expansionResults2);
|
|
11293
11424
|
return { content: [{ type: "text", text: JSON.stringify({
|
|
11294
11425
|
method: "fts5",
|
|
11295
11426
|
query,
|
|
11296
11427
|
total_results: filtered.length,
|
|
11297
|
-
results
|
|
11428
|
+
results: results2
|
|
11298
11429
|
}, null, 2) }] };
|
|
11299
11430
|
}
|
|
11300
11431
|
const stateDbFts = getStateDb2();
|
|
11301
11432
|
const fts5Filtered = applyFolderFilter(fts5Results);
|
|
11302
|
-
const
|
|
11433
|
+
const results = fts5Filtered.map((r) => ({ ...enrichResultCompact({ path: r.path, title: r.title, snippet: r.snippet }, index, stateDbFts), in_fts5: true }));
|
|
11434
|
+
const hopResults = multiHopBackfill(results, index, stateDbFts, { maxBackfill: limit });
|
|
11435
|
+
const expansionTerms = extractExpansionTerms(results, query, index);
|
|
11436
|
+
const expansionResults = expandQuery(expansionTerms, [...results, ...hopResults], index, stateDbFts);
|
|
11437
|
+
results.push(...hopResults, ...expansionResults);
|
|
11303
11438
|
return { content: [{ type: "text", text: JSON.stringify({
|
|
11304
11439
|
method: "fts5",
|
|
11305
11440
|
query,
|
|
11306
|
-
total_results:
|
|
11307
|
-
results
|
|
11441
|
+
total_results: results.length,
|
|
11442
|
+
results
|
|
11308
11443
|
}, null, 2) }] };
|
|
11309
11444
|
}
|
|
11310
11445
|
return { content: [{ type: "text", text: JSON.stringify({ error: "Provide a query or metadata filters (where, has_tag, folder, etc.)" }, null, 2) }] };
|
|
@@ -12030,7 +12165,7 @@ async function findSections(index, headingPattern, vaultPath2, folder) {
|
|
|
12030
12165
|
}
|
|
12031
12166
|
|
|
12032
12167
|
// src/tools/read/primitives.ts
|
|
12033
|
-
import { getEntityByName as
|
|
12168
|
+
import { getEntityByName as getEntityByName4 } from "@velvetmonkey/vault-core";
|
|
12034
12169
|
function registerPrimitiveTools(server2, getIndex, getVaultPath, getConfig2 = () => ({}), getStateDb2 = () => null) {
|
|
12035
12170
|
server2.registerTool(
|
|
12036
12171
|
"get_note_structure",
|
|
@@ -12073,7 +12208,7 @@ function registerPrimitiveTools(server2, getIndex, getVaultPath, getConfig2 = ()
|
|
|
12073
12208
|
const stateDb2 = getStateDb2();
|
|
12074
12209
|
if (stateDb2 && note) {
|
|
12075
12210
|
try {
|
|
12076
|
-
const entity =
|
|
12211
|
+
const entity = getEntityByName4(stateDb2, note.title);
|
|
12077
12212
|
if (entity) {
|
|
12078
12213
|
enriched.category = entity.category;
|
|
12079
12214
|
enriched.hub_score = entity.hubScore;
|
|
@@ -19325,13 +19460,13 @@ function registerRecallTools(server2, getStateDb2, getVaultPath, getIndex) {
|
|
|
19325
19460
|
description: e.content,
|
|
19326
19461
|
score: Math.round(e.score * 10) / 10,
|
|
19327
19462
|
breakdown: e.breakdown,
|
|
19328
|
-
...
|
|
19463
|
+
...enrichEntityCompact(e.id, stateDb2, index)
|
|
19329
19464
|
})),
|
|
19330
19465
|
notes: notes.map((n) => ({
|
|
19331
19466
|
path: n.id,
|
|
19332
19467
|
snippet: n.content,
|
|
19333
19468
|
score: Math.round(n.score * 10) / 10,
|
|
19334
|
-
...
|
|
19469
|
+
...enrichNoteCompact(n.id, stateDb2, index)
|
|
19335
19470
|
})),
|
|
19336
19471
|
memories: memories.map((m) => ({
|
|
19337
19472
|
key: m.id,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@velvetmonkey/flywheel-memory",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.124",
|
|
4
4
|
"description": "MCP server that gives Claude full read/write access to your Obsidian vault. Select from 69 tools for search, backlinks, graph queries, mutations, agent memory, and hybrid semantic search.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|