@velvetmonkey/flywheel-memory 2.0.122 → 2.0.123
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 +213 -82
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -10720,7 +10720,7 @@ function registerHealthTools(server2, getIndex, getVaultPath, getConfig2 = () =>
|
|
|
10720
10720
|
import { z as z4 } from "zod";
|
|
10721
10721
|
init_embeddings();
|
|
10722
10722
|
import {
|
|
10723
|
-
searchEntities,
|
|
10723
|
+
searchEntities as searchEntities2,
|
|
10724
10724
|
searchEntitiesPrefix
|
|
10725
10725
|
} from "@velvetmonkey/vault-core";
|
|
10726
10726
|
|
|
@@ -10729,7 +10729,6 @@ import {
|
|
|
10729
10729
|
getEntityByName as getEntityByName2
|
|
10730
10730
|
} from "@velvetmonkey/vault-core";
|
|
10731
10731
|
var TOP_LINKS = 10;
|
|
10732
|
-
var RECALL_TOP_LINKS = 5;
|
|
10733
10732
|
function recencyDecay(modifiedDate) {
|
|
10734
10733
|
if (!modifiedDate) return 0.5;
|
|
10735
10734
|
const daysSince = (Date.now() - modifiedDate.getTime()) / (1e3 * 60 * 60 * 24);
|
|
@@ -10819,6 +10818,63 @@ function rankBacklinks(backlinks, notePath, index, stateDb2, maxLinks = TOP_LINK
|
|
|
10819
10818
|
return out;
|
|
10820
10819
|
}).sort((a, b) => (b.weight ?? 1) - (a.weight ?? 1)).slice(0, maxLinks);
|
|
10821
10820
|
}
|
|
10821
|
+
var COMPACT_OUTLINK_NAMES = 10;
|
|
10822
|
+
function enrichResultCompact(result, index, stateDb2, opts) {
|
|
10823
|
+
const note = index.notes.get(result.path);
|
|
10824
|
+
const normalizedPath = result.path.toLowerCase().replace(/\.md$/, "");
|
|
10825
|
+
const backlinks = index.backlinks.get(normalizedPath) || [];
|
|
10826
|
+
const enriched = {
|
|
10827
|
+
path: result.path,
|
|
10828
|
+
title: result.title
|
|
10829
|
+
};
|
|
10830
|
+
if (result.snippet) {
|
|
10831
|
+
enriched.snippet = result.snippet;
|
|
10832
|
+
} else {
|
|
10833
|
+
const preview = getContentPreview(result.path);
|
|
10834
|
+
if (preview) enriched.snippet = preview;
|
|
10835
|
+
}
|
|
10836
|
+
if (note) {
|
|
10837
|
+
enriched.backlink_count = backlinks.length;
|
|
10838
|
+
enriched.modified = note.modified.toISOString();
|
|
10839
|
+
if (note.tags.length > 0) enriched.tags = note.tags;
|
|
10840
|
+
if (note.outlinks.length > 0) {
|
|
10841
|
+
enriched.outlink_names = getOutlinkNames(note.outlinks, result.path, index, stateDb2, COMPACT_OUTLINK_NAMES);
|
|
10842
|
+
}
|
|
10843
|
+
}
|
|
10844
|
+
if (stateDb2) {
|
|
10845
|
+
try {
|
|
10846
|
+
const entity = getEntityByName2(stateDb2, result.title);
|
|
10847
|
+
if (entity) {
|
|
10848
|
+
enriched.category = entity.category;
|
|
10849
|
+
enriched.hub_score = entity.hubScore;
|
|
10850
|
+
if (!enriched.snippet && entity.description) {
|
|
10851
|
+
enriched.snippet = entity.description;
|
|
10852
|
+
}
|
|
10853
|
+
}
|
|
10854
|
+
} catch {
|
|
10855
|
+
}
|
|
10856
|
+
}
|
|
10857
|
+
if (opts?.via) enriched.via = opts.via;
|
|
10858
|
+
if (opts?.hop) enriched.hop = opts.hop;
|
|
10859
|
+
return enriched;
|
|
10860
|
+
}
|
|
10861
|
+
function getOutlinkNames(outlinks, notePath, index, stateDb2, max) {
|
|
10862
|
+
const weightMap = /* @__PURE__ */ new Map();
|
|
10863
|
+
if (stateDb2) {
|
|
10864
|
+
try {
|
|
10865
|
+
const rows = stateDb2.db.prepare(
|
|
10866
|
+
"SELECT target, weight, weight_updated_at FROM note_links WHERE note_path = ?"
|
|
10867
|
+
).all(notePath);
|
|
10868
|
+
for (const row of rows) {
|
|
10869
|
+
const daysSince = row.weight_updated_at ? (Date.now() - row.weight_updated_at) / (1e3 * 60 * 60 * 24) : 0;
|
|
10870
|
+
const decay = Math.max(0.1, 1 - daysSince / 180);
|
|
10871
|
+
weightMap.set(row.target, row.weight * decay);
|
|
10872
|
+
}
|
|
10873
|
+
} catch {
|
|
10874
|
+
}
|
|
10875
|
+
}
|
|
10876
|
+
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);
|
|
10877
|
+
}
|
|
10822
10878
|
function enrichResult(result, index, stateDb2) {
|
|
10823
10879
|
const note = index.notes.get(result.path);
|
|
10824
10880
|
const normalizedPath = result.path.toLowerCase().replace(/\.md$/, "");
|
|
@@ -10886,7 +10942,7 @@ function enrichResultLight(result, index, stateDb2) {
|
|
|
10886
10942
|
}
|
|
10887
10943
|
return enriched;
|
|
10888
10944
|
}
|
|
10889
|
-
function
|
|
10945
|
+
function enrichEntityCompact(entityName, stateDb2, index) {
|
|
10890
10946
|
const enriched = {};
|
|
10891
10947
|
if (stateDb2) {
|
|
10892
10948
|
try {
|
|
@@ -10908,36 +10964,27 @@ function enrichEntityResult(entityName, stateDb2, index) {
|
|
|
10908
10964
|
const backlinks = index.backlinks.get(normalizedPath) || [];
|
|
10909
10965
|
enriched.backlink_count = backlinks.length;
|
|
10910
10966
|
if (note) {
|
|
10911
|
-
enriched.outlink_count = note.outlinks.length;
|
|
10912
10967
|
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
10968
|
if (note.outlinks.length > 0) {
|
|
10917
|
-
enriched.
|
|
10969
|
+
enriched.outlink_names = getOutlinkNames(note.outlinks, entityPath, index, stateDb2, COMPACT_OUTLINK_NAMES);
|
|
10918
10970
|
}
|
|
10919
10971
|
}
|
|
10920
10972
|
}
|
|
10921
10973
|
}
|
|
10922
10974
|
return enriched;
|
|
10923
10975
|
}
|
|
10924
|
-
function
|
|
10976
|
+
function enrichNoteCompact(notePath, stateDb2, index) {
|
|
10925
10977
|
const enriched = {};
|
|
10926
10978
|
if (!index) return enriched;
|
|
10927
10979
|
const note = index.notes.get(notePath);
|
|
10928
10980
|
if (!note) return enriched;
|
|
10929
10981
|
const normalizedPath = notePath.toLowerCase().replace(/\.md$/, "");
|
|
10930
10982
|
const backlinks = index.backlinks.get(normalizedPath) || [];
|
|
10931
|
-
enriched.frontmatter = note.frontmatter;
|
|
10932
10983
|
if (note.tags.length > 0) enriched.tags = note.tags;
|
|
10933
10984
|
enriched.backlink_count = backlinks.length;
|
|
10934
|
-
enriched.outlink_count = note.outlinks.length;
|
|
10935
10985
|
enriched.modified = note.modified.toISOString();
|
|
10936
|
-
if (backlinks.length > 0) {
|
|
10937
|
-
enriched.top_backlinks = rankBacklinks(backlinks, notePath, index, stateDb2, RECALL_TOP_LINKS);
|
|
10938
|
-
}
|
|
10939
10986
|
if (note.outlinks.length > 0) {
|
|
10940
|
-
enriched.
|
|
10987
|
+
enriched.outlink_names = getOutlinkNames(note.outlinks, notePath, index, stateDb2, COMPACT_OUTLINK_NAMES);
|
|
10941
10988
|
}
|
|
10942
10989
|
if (stateDb2) {
|
|
10943
10990
|
try {
|
|
@@ -10952,8 +10999,132 @@ function enrichNoteResult(notePath, stateDb2, index) {
|
|
|
10952
10999
|
return enriched;
|
|
10953
11000
|
}
|
|
10954
11001
|
|
|
11002
|
+
// src/core/read/multihop.ts
|
|
11003
|
+
import { getEntityByName as getEntityByName3, searchEntities } from "@velvetmonkey/vault-core";
|
|
11004
|
+
var DEFAULT_CONFIG2 = {
|
|
11005
|
+
maxParents: 10,
|
|
11006
|
+
maxHops: 2,
|
|
11007
|
+
maxOutlinksPerHop: 10,
|
|
11008
|
+
maxBackfill: 10
|
|
11009
|
+
};
|
|
11010
|
+
function multiHopBackfill(primaryResults, index, stateDb2, config = {}) {
|
|
11011
|
+
const cfg = { ...DEFAULT_CONFIG2, ...config };
|
|
11012
|
+
const seen = new Set(primaryResults.map((r) => r.path).filter(Boolean));
|
|
11013
|
+
const candidates = [];
|
|
11014
|
+
const hop1Results = [];
|
|
11015
|
+
for (const primary of primaryResults.slice(0, cfg.maxParents)) {
|
|
11016
|
+
const primaryPath = primary.path;
|
|
11017
|
+
if (!primaryPath) continue;
|
|
11018
|
+
const note = index.notes.get(primaryPath);
|
|
11019
|
+
if (!note) continue;
|
|
11020
|
+
for (const outlink of note.outlinks.slice(0, cfg.maxOutlinksPerHop)) {
|
|
11021
|
+
const targetPath = index.entities.get(outlink.target.toLowerCase());
|
|
11022
|
+
if (!targetPath || seen.has(targetPath)) continue;
|
|
11023
|
+
seen.add(targetPath);
|
|
11024
|
+
const targetNote = index.notes.get(targetPath);
|
|
11025
|
+
const title = targetNote?.title ?? outlink.target;
|
|
11026
|
+
hop1Results.push({ path: targetPath, title, via: primaryPath });
|
|
11027
|
+
}
|
|
11028
|
+
}
|
|
11029
|
+
for (const h1 of hop1Results) {
|
|
11030
|
+
const enriched = enrichResultCompact(
|
|
11031
|
+
{ path: h1.path, title: h1.title },
|
|
11032
|
+
index,
|
|
11033
|
+
stateDb2,
|
|
11034
|
+
{ via: h1.via, hop: 1 }
|
|
11035
|
+
);
|
|
11036
|
+
const score = scoreCandidate(h1.path, index, stateDb2);
|
|
11037
|
+
candidates.push({ result: enriched, score });
|
|
11038
|
+
}
|
|
11039
|
+
if (cfg.maxHops >= 2) {
|
|
11040
|
+
for (const h1 of hop1Results) {
|
|
11041
|
+
const note = index.notes.get(h1.path);
|
|
11042
|
+
if (!note) continue;
|
|
11043
|
+
for (const outlink of note.outlinks.slice(0, cfg.maxOutlinksPerHop)) {
|
|
11044
|
+
const targetPath = index.entities.get(outlink.target.toLowerCase());
|
|
11045
|
+
if (!targetPath || seen.has(targetPath)) continue;
|
|
11046
|
+
seen.add(targetPath);
|
|
11047
|
+
const targetNote = index.notes.get(targetPath);
|
|
11048
|
+
const title = targetNote?.title ?? outlink.target;
|
|
11049
|
+
const enriched = enrichResultCompact(
|
|
11050
|
+
{ path: targetPath, title },
|
|
11051
|
+
index,
|
|
11052
|
+
stateDb2,
|
|
11053
|
+
{ via: h1.path, hop: 2 }
|
|
11054
|
+
);
|
|
11055
|
+
const score = scoreCandidate(targetPath, index, stateDb2);
|
|
11056
|
+
candidates.push({ result: enriched, score });
|
|
11057
|
+
}
|
|
11058
|
+
}
|
|
11059
|
+
}
|
|
11060
|
+
candidates.sort((a, b) => b.score - a.score);
|
|
11061
|
+
return candidates.slice(0, cfg.maxBackfill).map((c) => c.result);
|
|
11062
|
+
}
|
|
11063
|
+
function scoreCandidate(path33, index, stateDb2) {
|
|
11064
|
+
const note = index.notes.get(path33);
|
|
11065
|
+
const decay = recencyDecay(note?.modified);
|
|
11066
|
+
let hubScore = 1;
|
|
11067
|
+
if (stateDb2) {
|
|
11068
|
+
try {
|
|
11069
|
+
const title = note?.title ?? path33.replace(/\.md$/, "").split("/").pop() ?? "";
|
|
11070
|
+
const entity = getEntityByName3(stateDb2, title);
|
|
11071
|
+
if (entity) hubScore = entity.hubScore ?? 1;
|
|
11072
|
+
} catch {
|
|
11073
|
+
}
|
|
11074
|
+
}
|
|
11075
|
+
return hubScore * decay;
|
|
11076
|
+
}
|
|
11077
|
+
function extractExpansionTerms(results, originalQuery, index) {
|
|
11078
|
+
const queryLower = originalQuery.toLowerCase();
|
|
11079
|
+
const terms = /* @__PURE__ */ new Set();
|
|
11080
|
+
for (const r of results.slice(0, 5)) {
|
|
11081
|
+
const outlinks = r.outlink_names;
|
|
11082
|
+
if (outlinks) {
|
|
11083
|
+
for (const name of outlinks) {
|
|
11084
|
+
if (!queryLower.includes(name.toLowerCase()) && index.entities.has(name.toLowerCase())) {
|
|
11085
|
+
terms.add(name);
|
|
11086
|
+
}
|
|
11087
|
+
}
|
|
11088
|
+
}
|
|
11089
|
+
const snippet = r.snippet;
|
|
11090
|
+
if (snippet) {
|
|
11091
|
+
const matches = snippet.match(/\[\[([^\]|]+)(?:\|[^\]]+)?\]\]/g);
|
|
11092
|
+
if (matches) {
|
|
11093
|
+
for (const wl of matches) {
|
|
11094
|
+
const name = wl.replace(/\[\[|\]\]/g, "").split("|")[0];
|
|
11095
|
+
if (!queryLower.includes(name.toLowerCase()) && index.entities.has(name.toLowerCase())) {
|
|
11096
|
+
terms.add(name);
|
|
11097
|
+
}
|
|
11098
|
+
}
|
|
11099
|
+
}
|
|
11100
|
+
}
|
|
11101
|
+
}
|
|
11102
|
+
return Array.from(terms).slice(0, 10);
|
|
11103
|
+
}
|
|
11104
|
+
function expandQuery(expansionTerms, primaryResults, index, stateDb2) {
|
|
11105
|
+
if (!stateDb2 || expansionTerms.length === 0) return [];
|
|
11106
|
+
const seen = new Set(primaryResults.map((r) => r.path).filter(Boolean));
|
|
11107
|
+
const results = [];
|
|
11108
|
+
for (const term of expansionTerms) {
|
|
11109
|
+
try {
|
|
11110
|
+
const entities = searchEntities(stateDb2, term, 3);
|
|
11111
|
+
for (const entity of entities) {
|
|
11112
|
+
if (!entity.path || seen.has(entity.path)) continue;
|
|
11113
|
+
seen.add(entity.path);
|
|
11114
|
+
results.push(enrichResultCompact(
|
|
11115
|
+
{ path: entity.path, title: entity.name },
|
|
11116
|
+
index,
|
|
11117
|
+
stateDb2,
|
|
11118
|
+
{ via: "query_expansion" }
|
|
11119
|
+
));
|
|
11120
|
+
}
|
|
11121
|
+
} catch {
|
|
11122
|
+
}
|
|
11123
|
+
}
|
|
11124
|
+
return results;
|
|
11125
|
+
}
|
|
11126
|
+
|
|
10955
11127
|
// src/tools/read/query.ts
|
|
10956
|
-
init_wikilinkFeedback();
|
|
10957
11128
|
function matchesFrontmatter(note, where) {
|
|
10958
11129
|
for (const [key, value] of Object.entries(where)) {
|
|
10959
11130
|
const noteValue = note.frontmatter[key];
|
|
@@ -11134,7 +11305,7 @@ function registerQueryTools(server2, getIndex, getVaultPath, getStateDb2) {
|
|
|
11134
11305
|
const stateDbEntity = getStateDb2();
|
|
11135
11306
|
if (stateDbEntity) {
|
|
11136
11307
|
try {
|
|
11137
|
-
entityResults =
|
|
11308
|
+
entityResults = searchEntities2(stateDbEntity, query, limit);
|
|
11138
11309
|
} catch {
|
|
11139
11310
|
}
|
|
11140
11311
|
}
|
|
@@ -11206,46 +11377,22 @@ function registerQueryTools(server2, getIndex, getVaultPath, getStateDb2) {
|
|
|
11206
11377
|
scored.sort((a, b) => b.rrf_score - a.rrf_score);
|
|
11207
11378
|
const filtered = applyFolderFilter(scored);
|
|
11208
11379
|
const stateDb2 = getStateDb2();
|
|
11209
|
-
const
|
|
11210
|
-
...(
|
|
11380
|
+
const results2 = filtered.slice(0, limit).map((item) => ({
|
|
11381
|
+
...enrichResultCompact({ path: item.path, title: item.title, snippet: item.snippet }, index, stateDb2),
|
|
11211
11382
|
rrf_score: item.rrf_score,
|
|
11212
11383
|
in_fts5: item.in_fts5,
|
|
11213
11384
|
in_semantic: item.in_semantic,
|
|
11214
11385
|
in_entity: item.in_entity
|
|
11215
11386
|
}));
|
|
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
|
-
}
|
|
11387
|
+
const hopResults2 = multiHopBackfill(results2, index, stateDb2, { maxBackfill: limit });
|
|
11388
|
+
const expansionTerms2 = extractExpansionTerms(results2, query, index);
|
|
11389
|
+
const expansionResults2 = expandQuery(expansionTerms2, [...results2, ...hopResults2], index, stateDb2);
|
|
11390
|
+
results2.push(...hopResults2, ...expansionResults2);
|
|
11244
11391
|
return { content: [{ type: "text", text: JSON.stringify({
|
|
11245
11392
|
method: "hybrid",
|
|
11246
11393
|
query,
|
|
11247
11394
|
total_results: filtered.length,
|
|
11248
|
-
results
|
|
11395
|
+
results: results2
|
|
11249
11396
|
}, null, 2) }] };
|
|
11250
11397
|
} catch (err) {
|
|
11251
11398
|
console.error("[Semantic] Hybrid search failed, falling back to FTS5:", err instanceof Error ? err.message : err);
|
|
@@ -11262,49 +11409,33 @@ function registerQueryTools(server2, getIndex, getVaultPath, getStateDb2) {
|
|
|
11262
11409
|
const filtered = applyFolderFilter(mergedItems);
|
|
11263
11410
|
const stateDb2 = getStateDb2();
|
|
11264
11411
|
const sliced = filtered.slice(0, limit);
|
|
11265
|
-
const
|
|
11266
|
-
...(
|
|
11412
|
+
const results2 = sliced.map((item) => ({
|
|
11413
|
+
...enrichResultCompact({ path: item.path, title: item.title, snippet: item.snippet }, index, stateDb2),
|
|
11267
11414
|
..."in_fts5" in item ? { in_fts5: true } : { in_entity: true }
|
|
11268
11415
|
}));
|
|
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
|
-
}
|
|
11416
|
+
const hopResults2 = multiHopBackfill(results2, index, stateDb2, { maxBackfill: limit });
|
|
11417
|
+
const expansionTerms2 = extractExpansionTerms(results2, query, index);
|
|
11418
|
+
const expansionResults2 = expandQuery(expansionTerms2, [...results2, ...hopResults2], index, stateDb2);
|
|
11419
|
+
results2.push(...hopResults2, ...expansionResults2);
|
|
11293
11420
|
return { content: [{ type: "text", text: JSON.stringify({
|
|
11294
11421
|
method: "fts5",
|
|
11295
11422
|
query,
|
|
11296
11423
|
total_results: filtered.length,
|
|
11297
|
-
results
|
|
11424
|
+
results: results2
|
|
11298
11425
|
}, null, 2) }] };
|
|
11299
11426
|
}
|
|
11300
11427
|
const stateDbFts = getStateDb2();
|
|
11301
11428
|
const fts5Filtered = applyFolderFilter(fts5Results);
|
|
11302
|
-
const
|
|
11429
|
+
const results = fts5Filtered.map((r) => ({ ...enrichResultCompact({ path: r.path, title: r.title, snippet: r.snippet }, index, stateDbFts), in_fts5: true }));
|
|
11430
|
+
const hopResults = multiHopBackfill(results, index, stateDbFts, { maxBackfill: limit });
|
|
11431
|
+
const expansionTerms = extractExpansionTerms(results, query, index);
|
|
11432
|
+
const expansionResults = expandQuery(expansionTerms, [...results, ...hopResults], index, stateDbFts);
|
|
11433
|
+
results.push(...hopResults, ...expansionResults);
|
|
11303
11434
|
return { content: [{ type: "text", text: JSON.stringify({
|
|
11304
11435
|
method: "fts5",
|
|
11305
11436
|
query,
|
|
11306
|
-
total_results:
|
|
11307
|
-
results
|
|
11437
|
+
total_results: results.length,
|
|
11438
|
+
results
|
|
11308
11439
|
}, null, 2) }] };
|
|
11309
11440
|
}
|
|
11310
11441
|
return { content: [{ type: "text", text: JSON.stringify({ error: "Provide a query or metadata filters (where, has_tag, folder, etc.)" }, null, 2) }] };
|
|
@@ -12030,7 +12161,7 @@ async function findSections(index, headingPattern, vaultPath2, folder) {
|
|
|
12030
12161
|
}
|
|
12031
12162
|
|
|
12032
12163
|
// src/tools/read/primitives.ts
|
|
12033
|
-
import { getEntityByName as
|
|
12164
|
+
import { getEntityByName as getEntityByName4 } from "@velvetmonkey/vault-core";
|
|
12034
12165
|
function registerPrimitiveTools(server2, getIndex, getVaultPath, getConfig2 = () => ({}), getStateDb2 = () => null) {
|
|
12035
12166
|
server2.registerTool(
|
|
12036
12167
|
"get_note_structure",
|
|
@@ -12073,7 +12204,7 @@ function registerPrimitiveTools(server2, getIndex, getVaultPath, getConfig2 = ()
|
|
|
12073
12204
|
const stateDb2 = getStateDb2();
|
|
12074
12205
|
if (stateDb2 && note) {
|
|
12075
12206
|
try {
|
|
12076
|
-
const entity =
|
|
12207
|
+
const entity = getEntityByName4(stateDb2, note.title);
|
|
12077
12208
|
if (entity) {
|
|
12078
12209
|
enriched.category = entity.category;
|
|
12079
12210
|
enriched.hub_score = entity.hubScore;
|
|
@@ -19325,13 +19456,13 @@ function registerRecallTools(server2, getStateDb2, getVaultPath, getIndex) {
|
|
|
19325
19456
|
description: e.content,
|
|
19326
19457
|
score: Math.round(e.score * 10) / 10,
|
|
19327
19458
|
breakdown: e.breakdown,
|
|
19328
|
-
...
|
|
19459
|
+
...enrichEntityCompact(e.id, stateDb2, index)
|
|
19329
19460
|
})),
|
|
19330
19461
|
notes: notes.map((n) => ({
|
|
19331
19462
|
path: n.id,
|
|
19332
19463
|
snippet: n.content,
|
|
19333
19464
|
score: Math.round(n.score * 10) / 10,
|
|
19334
|
-
...
|
|
19465
|
+
...enrichNoteCompact(n.id, stateDb2, index)
|
|
19335
19466
|
})),
|
|
19336
19467
|
memories: memories.map((m) => ({
|
|
19337
19468
|
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.123",
|
|
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",
|