@velvetmonkey/flywheel-memory 2.3.1 → 2.3.3
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 +103 -37
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -11847,10 +11847,11 @@ function resolveToolConfig(envValue) {
|
|
|
11847
11847
|
};
|
|
11848
11848
|
}
|
|
11849
11849
|
var TOOL_CATEGORY = {
|
|
11850
|
-
// search
|
|
11850
|
+
// search
|
|
11851
11851
|
search: "search",
|
|
11852
11852
|
init_semantic: "search",
|
|
11853
11853
|
find_similar: "search",
|
|
11854
|
+
discover_tools: "search",
|
|
11854
11855
|
// read (3 tools) -- note reading
|
|
11855
11856
|
get_note_structure: "read",
|
|
11856
11857
|
get_section_content: "read",
|
|
@@ -11938,10 +11939,11 @@ var TOOL_CATEGORY = {
|
|
|
11938
11939
|
tool_selection_feedback: "diagnostics"
|
|
11939
11940
|
};
|
|
11940
11941
|
var TOOL_TIER = {
|
|
11941
|
-
// Tier 1 — always visible (= agent preset,
|
|
11942
|
+
// Tier 1 — always visible (= agent preset, see TIER_1_TOOL_COUNT)
|
|
11942
11943
|
search: 1,
|
|
11943
11944
|
init_semantic: 1,
|
|
11944
11945
|
find_similar: 1,
|
|
11946
|
+
discover_tools: 1,
|
|
11945
11947
|
get_note_structure: 1,
|
|
11946
11948
|
get_section_content: 1,
|
|
11947
11949
|
find_sections: 1,
|
|
@@ -11957,7 +11959,7 @@ var TOOL_TIER = {
|
|
|
11957
11959
|
vault_add_task: 1,
|
|
11958
11960
|
memory: 1,
|
|
11959
11961
|
brief: 1,
|
|
11960
|
-
// Tier 2 — context-triggered categories + core diagnostics (
|
|
11962
|
+
// Tier 2 — context-triggered categories + core diagnostics (see TIER_2_TOOL_COUNT)
|
|
11961
11963
|
graph_analysis: 2,
|
|
11962
11964
|
semantic_analysis: 2,
|
|
11963
11965
|
get_backlinks: 2,
|
|
@@ -11991,7 +11993,7 @@ var TOOL_TIER = {
|
|
|
11991
11993
|
flywheel_config: 2,
|
|
11992
11994
|
server_log: 2,
|
|
11993
11995
|
flywheel_doctor: 2,
|
|
11994
|
-
// Tier 3 — explicit or advanced operations (
|
|
11996
|
+
// Tier 3 — explicit or advanced operations (see TIER_3_TOOL_COUNT)
|
|
11995
11997
|
vault_schema: 3,
|
|
11996
11998
|
schema_conventions: 3,
|
|
11997
11999
|
schema_validate: 3,
|
|
@@ -12031,6 +12033,10 @@ function assertToolTierCoverage() {
|
|
|
12031
12033
|
}
|
|
12032
12034
|
}
|
|
12033
12035
|
assertToolTierCoverage();
|
|
12036
|
+
var TOTAL_TOOL_COUNT = Object.keys(TOOL_CATEGORY).length;
|
|
12037
|
+
var TIER_1_TOOL_COUNT = Object.values(TOOL_TIER).filter((t) => t === 1).length;
|
|
12038
|
+
var TIER_2_TOOL_COUNT = Object.values(TOOL_TIER).filter((t) => t === 2).length;
|
|
12039
|
+
var TIER_3_TOOL_COUNT = Object.values(TOOL_TIER).filter((t) => t === 3).length;
|
|
12034
12040
|
function generateInstructions(categories, registry, activeTierCategories) {
|
|
12035
12041
|
const parts = [];
|
|
12036
12042
|
const tieringActive = activeTierCategories !== void 0;
|
|
@@ -12140,8 +12146,6 @@ Use "get_connection_strength" to measure link strength between two entities.
|
|
|
12140
12146
|
Use "get_link_path" to trace the shortest path between any two entities or notes.
|
|
12141
12147
|
Use "get_strong_connections" to find the strongest or most-connected relationships for an entity.`);
|
|
12142
12148
|
} else if (tieringActive && categories.has("graph")) {
|
|
12143
|
-
parts.push(`
|
|
12144
|
-
**More tools available:** Ask about graph connections, backlinks, hubs, clusters, or paths to unlock graph analysis tools.`);
|
|
12145
12149
|
}
|
|
12146
12150
|
if (isCategoryVisible("note-ops")) {
|
|
12147
12151
|
parts.push(`
|
|
@@ -12167,8 +12171,6 @@ Use "schema_conventions" to infer frontmatter conventions from folder usage patt
|
|
|
12167
12171
|
Use "schema_validate" to validate frontmatter against explicit rules or find notes missing expected fields by folder.
|
|
12168
12172
|
Use "note_intelligence" for per-note analysis (completeness, quality, suggestions).`);
|
|
12169
12173
|
} else if (tieringActive && categories.has("schema")) {
|
|
12170
|
-
parts.push(`
|
|
12171
|
-
**Advanced tools:** Ask to unlock schema tools for conventions, validation, migrations, and bulk metadata analysis.`);
|
|
12172
12174
|
}
|
|
12173
12175
|
if (isCategoryVisible("wikilinks")) {
|
|
12174
12176
|
parts.push(`
|
|
@@ -12184,8 +12186,6 @@ Link quality and discovery \u2014 not for finding content (use search for that).
|
|
|
12184
12186
|
- "Was that link correct?" \u2192 wikilink_feedback (accept/reject, improves future suggestions)
|
|
12185
12187
|
- "What aliases am I missing?" \u2192 suggest_entity_aliases (acronyms, short forms, alternate names)`);
|
|
12186
12188
|
} else if (tieringActive && categories.has("wikilinks")) {
|
|
12187
|
-
parts.push(`
|
|
12188
|
-
**More tools available:** Ask about wikilinks, suggestions, stubs, or unlinked mentions to unlock wikilink tools.`);
|
|
12189
12189
|
}
|
|
12190
12190
|
if (isCategoryVisible("corrections")) {
|
|
12191
12191
|
parts.push(`
|
|
@@ -12198,8 +12198,6 @@ When the user says something is wrong \u2014 a bad link, wrong entity, wrong cat
|
|
|
12198
12198
|
"vault_resolve_correction" marks a correction as applied or dismissed.
|
|
12199
12199
|
Use "absorb_as_alias" when two names should resolve to the same entity without merging note bodies.`);
|
|
12200
12200
|
} else if (tieringActive && categories.has("corrections")) {
|
|
12201
|
-
parts.push(`
|
|
12202
|
-
**More tools available:** Ask about errors, wrong links, or fixes to unlock correction tools.`);
|
|
12203
12201
|
}
|
|
12204
12202
|
if (isCategoryVisible("temporal")) {
|
|
12205
12203
|
parts.push(`
|
|
@@ -12215,8 +12213,6 @@ Temporal tools analyze *patterns and changes* over time \u2014 use them for "wha
|
|
|
12215
12213
|
|
|
12216
12214
|
temporal_summary composes the other three \u2014 use it for weekly/monthly reviews.`);
|
|
12217
12215
|
} else if (tieringActive && categories.has("temporal")) {
|
|
12218
|
-
parts.push(`
|
|
12219
|
-
**More tools available:** Ask about time, history, evolution, or stale notes to unlock temporal tools.`);
|
|
12220
12216
|
}
|
|
12221
12217
|
if (isCategoryVisible("diagnostics")) {
|
|
12222
12218
|
parts.push(`
|
|
@@ -12231,9 +12227,10 @@ temporal_summary composes the other three \u2014 use it for weekly/monthly revie
|
|
|
12231
12227
|
|
|
12232
12228
|
Use "flywheel_config" to inspect runtime configuration and set "tool_tier_override" to "auto", "full", or "minimal" for this vault.`);
|
|
12233
12229
|
} else if (tieringActive && categories.has("diagnostics")) {
|
|
12230
|
+
}
|
|
12231
|
+
if (tieringActive) {
|
|
12234
12232
|
parts.push(`
|
|
12235
|
-
**More tools available:**
|
|
12236
|
-
**Advanced tools:** Ask to unlock note operations or deep diagnostics for note mutations, benchmarks, history, graph exports, and learning reports.`);
|
|
12233
|
+
**More tools available:** Call \`discover_tools({ query: "your need" })\` to find and activate specialized tools for graph analysis, wikilinks, diagnostics, schema, temporal analysis, note operations, and more. Returns tool names, descriptions, and input schemas.`);
|
|
12237
12234
|
}
|
|
12238
12235
|
return parts.join("\n");
|
|
12239
12236
|
}
|
|
@@ -12243,7 +12240,7 @@ import * as path38 from "path";
|
|
|
12243
12240
|
import { dirname as dirname5, join as join19 } from "path";
|
|
12244
12241
|
import { statSync as statSync6, readFileSync as readFileSync5 } from "fs";
|
|
12245
12242
|
import { fileURLToPath } from "url";
|
|
12246
|
-
import { z as
|
|
12243
|
+
import { z as z41 } from "zod";
|
|
12247
12244
|
import { CallToolRequestSchema, ErrorCode, McpError } from "@modelcontextprotocol/sdk/types.js";
|
|
12248
12245
|
import { getSessionId } from "@velvetmonkey/vault-core";
|
|
12249
12246
|
|
|
@@ -26550,6 +26547,61 @@ function registerCalibrationExportTools(server2, getIndex, getStateDb4, getConfi
|
|
|
26550
26547
|
);
|
|
26551
26548
|
}
|
|
26552
26549
|
|
|
26550
|
+
// src/tools/read/discovery.ts
|
|
26551
|
+
import { z as z40 } from "zod";
|
|
26552
|
+
import { normalizeObjectSchema } from "@modelcontextprotocol/sdk/server/zod-compat.js";
|
|
26553
|
+
import { toJsonSchemaCompat } from "@modelcontextprotocol/sdk/server/zod-json-schema-compat.js";
|
|
26554
|
+
function toJsonSchema(inputSchema) {
|
|
26555
|
+
const obj = normalizeObjectSchema(inputSchema);
|
|
26556
|
+
if (!obj) return { type: "object" };
|
|
26557
|
+
return toJsonSchemaCompat(obj, { strictUnions: true, pipeStrategy: "input" });
|
|
26558
|
+
}
|
|
26559
|
+
function registerDiscoveryTools(server2, controller) {
|
|
26560
|
+
server2.tool(
|
|
26561
|
+
"discover_tools",
|
|
26562
|
+
'Find and activate specialized tools. Call with what you need \u2014 e.g. "vault health", "graph connections", "schema migration". Returns matching tool names, descriptions, and input schemas. Does not execute discovered tools \u2014 call them separately after discovery.',
|
|
26563
|
+
{
|
|
26564
|
+
query: z40.string().describe(
|
|
26565
|
+
'Natural language description of what you need \u2014 e.g. "vault health", "backlinks and graph", "schema migration"'
|
|
26566
|
+
)
|
|
26567
|
+
},
|
|
26568
|
+
async ({ query }) => {
|
|
26569
|
+
const signals = unionSignalsByCategory(getPatternSignals(query));
|
|
26570
|
+
const matchedCategories = [];
|
|
26571
|
+
const newlyActivatedCategories = [];
|
|
26572
|
+
for (const { category, tier } of signals) {
|
|
26573
|
+
const wasActive = controller.activeCategories.has(category);
|
|
26574
|
+
matchedCategories.push(category);
|
|
26575
|
+
controller.activateCategory(category, tier);
|
|
26576
|
+
if (!wasActive && controller.activeCategories.has(category)) {
|
|
26577
|
+
newlyActivatedCategories.push(category);
|
|
26578
|
+
}
|
|
26579
|
+
}
|
|
26580
|
+
const tools = [];
|
|
26581
|
+
for (const [name, handle] of controller.getRegisteredTools()) {
|
|
26582
|
+
const cat = TOOL_CATEGORY[name];
|
|
26583
|
+
if (handle.enabled && cat && matchedCategories.includes(cat)) {
|
|
26584
|
+
tools.push({
|
|
26585
|
+
name,
|
|
26586
|
+
description: handle.description ?? "",
|
|
26587
|
+
category: cat,
|
|
26588
|
+
inputSchema: toJsonSchema(handle.inputSchema)
|
|
26589
|
+
});
|
|
26590
|
+
}
|
|
26591
|
+
}
|
|
26592
|
+
const result = {
|
|
26593
|
+
matched_categories: matchedCategories,
|
|
26594
|
+
newly_activated_categories: newlyActivatedCategories,
|
|
26595
|
+
tools,
|
|
26596
|
+
hint: tools.length === 0 ? `No tools matched "${query}". Available categories: ${ALL_CATEGORIES.join(", ")}` : `${tools.length} tools available across ${matchedCategories.join(", ")}`
|
|
26597
|
+
};
|
|
26598
|
+
return {
|
|
26599
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
26600
|
+
};
|
|
26601
|
+
}
|
|
26602
|
+
);
|
|
26603
|
+
}
|
|
26604
|
+
|
|
26553
26605
|
// src/resources/vault.ts
|
|
26554
26606
|
function registerVaultResources(server2, getIndex) {
|
|
26555
26607
|
server2.registerResource(
|
|
@@ -26698,6 +26750,14 @@ function getPatternSignals(raw) {
|
|
|
26698
26750
|
if (!raw) return [];
|
|
26699
26751
|
return ACTIVATION_PATTERNS.filter(({ patterns }) => patterns.some((pattern) => pattern.test(raw))).map(({ category, tier }) => ({ category, tier }));
|
|
26700
26752
|
}
|
|
26753
|
+
function unionSignalsByCategory(signals) {
|
|
26754
|
+
const best = /* @__PURE__ */ new Map();
|
|
26755
|
+
for (const { category, tier } of signals) {
|
|
26756
|
+
const existing = best.get(category);
|
|
26757
|
+
if (!existing || tier > existing) best.set(category, tier);
|
|
26758
|
+
}
|
|
26759
|
+
return Array.from(best.entries()).map(([category, tier]) => ({ category, tier }));
|
|
26760
|
+
}
|
|
26701
26761
|
async function getActivationSignals(toolName, params, searchMethod, toolTierMode2 = "off") {
|
|
26702
26762
|
if (toolName !== "search" && toolName !== "brief") return [];
|
|
26703
26763
|
if (!params || typeof params !== "object") return [];
|
|
@@ -26715,14 +26775,7 @@ async function getActivationSignals(toolName, params, searchMethod, toolTierMode
|
|
|
26715
26775
|
if (routingMode === "semantic" && searchMethod !== "hybrid") {
|
|
26716
26776
|
return getPatternSignals(raw);
|
|
26717
26777
|
}
|
|
26718
|
-
|
|
26719
|
-
for (const { category, tier } of [...patternSignals, ...semanticSignals]) {
|
|
26720
|
-
const existing = categoryBest.get(category);
|
|
26721
|
-
if (!existing || tier > existing) {
|
|
26722
|
-
categoryBest.set(category, tier);
|
|
26723
|
-
}
|
|
26724
|
-
}
|
|
26725
|
-
return Array.from(categoryBest.entries()).map(([category, tier]) => ({ category, tier }));
|
|
26778
|
+
return unionSignalsByCategory([...patternSignals, ...semanticSignals]);
|
|
26726
26779
|
}
|
|
26727
26780
|
function extractSearchMethod(result) {
|
|
26728
26781
|
if (!result || typeof result !== "object") return void 0;
|
|
@@ -26759,12 +26812,12 @@ function applyToolGating(targetServer, categories, getDb4, registry, getVaultPat
|
|
|
26759
26812
|
return true;
|
|
26760
26813
|
}
|
|
26761
26814
|
function enableCategory(category, tier) {
|
|
26762
|
-
if (!categories.has(category)) return;
|
|
26815
|
+
if (!categories.has(category)) return [];
|
|
26763
26816
|
const previousTier = activatedCategoryTiers.get(category) ?? 0;
|
|
26764
26817
|
if (tier > previousTier) {
|
|
26765
26818
|
activatedCategoryTiers.set(category, tier);
|
|
26766
26819
|
}
|
|
26767
|
-
refreshToolVisibility();
|
|
26820
|
+
return refreshToolVisibility();
|
|
26768
26821
|
}
|
|
26769
26822
|
function shouldEnableTool(toolName) {
|
|
26770
26823
|
const tier = TOOL_TIER[toolName];
|
|
@@ -26779,26 +26832,29 @@ function applyToolGating(targetServer, categories, getDb4, registry, getVaultPat
|
|
|
26779
26832
|
return activatedTier >= tier;
|
|
26780
26833
|
}
|
|
26781
26834
|
function refreshToolVisibility() {
|
|
26782
|
-
|
|
26835
|
+
const newlyEnabled = [];
|
|
26783
26836
|
for (const [name, handle] of toolHandles) {
|
|
26784
26837
|
const enabled = shouldEnableTool(name);
|
|
26785
26838
|
if (enabled !== handle.enabled) {
|
|
26786
26839
|
handle.enabled = enabled;
|
|
26787
|
-
|
|
26840
|
+
if (enabled) newlyEnabled.push(name);
|
|
26788
26841
|
}
|
|
26789
26842
|
}
|
|
26790
|
-
if (
|
|
26843
|
+
if (newlyEnabled.length > 0) {
|
|
26791
26844
|
targetServer.sendToolListChanged();
|
|
26792
26845
|
}
|
|
26793
26846
|
if (controllerRef) {
|
|
26794
26847
|
onTierStateChange?.(controllerRef);
|
|
26795
26848
|
}
|
|
26849
|
+
return newlyEnabled;
|
|
26796
26850
|
}
|
|
26797
26851
|
async function maybeActivateFromContext(toolName, params, searchMethod) {
|
|
26798
|
-
if (tierMode !== "tiered" || tierOverride === "full") return;
|
|
26852
|
+
if (tierMode !== "tiered" || tierOverride === "full") return [];
|
|
26853
|
+
const newlyEnabled = [];
|
|
26799
26854
|
for (const { category, tier } of await getActivationSignals(toolName, params, searchMethod, tierMode)) {
|
|
26800
|
-
enableCategory(category, tier);
|
|
26855
|
+
newlyEnabled.push(...enableCategory(category, tier));
|
|
26801
26856
|
}
|
|
26857
|
+
return newlyEnabled;
|
|
26802
26858
|
}
|
|
26803
26859
|
function ensureToolEnabledForDirectCall(toolName) {
|
|
26804
26860
|
if (tierMode !== "tiered") return;
|
|
@@ -26844,7 +26900,14 @@ function applyToolGating(targetServer, categories, getDb4, registry, getVaultPat
|
|
|
26844
26900
|
try {
|
|
26845
26901
|
result = await handler(...args);
|
|
26846
26902
|
const searchMethod = extractSearchMethod(result);
|
|
26847
|
-
await maybeActivateFromContext(toolName, params, searchMethod);
|
|
26903
|
+
const newlyActivated = await maybeActivateFromContext(toolName, params, searchMethod);
|
|
26904
|
+
if (newlyActivated.length > 0 && result?.content && Array.isArray(result.content)) {
|
|
26905
|
+
result.content.push({
|
|
26906
|
+
type: "text",
|
|
26907
|
+
text: `
|
|
26908
|
+
[Progressive disclosure: ${newlyActivated.length} new tools activated: ${newlyActivated.join(", ")}. Call tools/list to refresh.]`
|
|
26909
|
+
});
|
|
26910
|
+
}
|
|
26848
26911
|
return result;
|
|
26849
26912
|
} catch (err) {
|
|
26850
26913
|
success = false;
|
|
@@ -27010,7 +27073,7 @@ function applyToolGating(targetServer, categories, getDb4, registry, getVaultPat
|
|
|
27010
27073
|
const schemaIdx = handlerIdx - 1;
|
|
27011
27074
|
const schema = args[schemaIdx];
|
|
27012
27075
|
if (schema && typeof schema === "object" && !Array.isArray(schema)) {
|
|
27013
|
-
schema.vault =
|
|
27076
|
+
schema.vault = z41.string().optional().describe(
|
|
27014
27077
|
`Vault name for multi-vault mode. Available: ${registry.getVaultNames().join(", ")}. Default: ${registry.primaryName}`
|
|
27015
27078
|
);
|
|
27016
27079
|
}
|
|
@@ -27127,7 +27190,7 @@ function applyToolGating(targetServer, categories, getDb4, registry, getVaultPat
|
|
|
27127
27190
|
controllerRef = controller;
|
|
27128
27191
|
return controller;
|
|
27129
27192
|
}
|
|
27130
|
-
function registerAllTools(targetServer, ctx) {
|
|
27193
|
+
function registerAllTools(targetServer, ctx, controller) {
|
|
27131
27194
|
const { getVaultPath: gvp, getVaultIndex: gvi, getStateDb: gsd, getFlywheelConfig: gcf } = ctx;
|
|
27132
27195
|
registerHealthTools(targetServer, gvi, gvp, gcf, gsd, ctx.getWatcherStatus, () => trPkg.version, ctx.getPipelineActivity);
|
|
27133
27196
|
registerSystemTools(
|
|
@@ -27220,6 +27283,9 @@ function registerAllTools(targetServer, ctx) {
|
|
|
27220
27283
|
registerCalibrationExportTools(targetServer, gvi, gsd, gcf);
|
|
27221
27284
|
registerMemoryTools(targetServer, gsd);
|
|
27222
27285
|
registerBriefTools(targetServer, gsd);
|
|
27286
|
+
if (controller) {
|
|
27287
|
+
registerDiscoveryTools(targetServer, controller);
|
|
27288
|
+
}
|
|
27223
27289
|
registerVaultResources(targetServer, () => gvi() ?? null);
|
|
27224
27290
|
}
|
|
27225
27291
|
|
|
@@ -27305,7 +27371,7 @@ function createConfiguredServer() {
|
|
|
27305
27371
|
toolTierMode,
|
|
27306
27372
|
handleTierStateChange
|
|
27307
27373
|
);
|
|
27308
|
-
registerAllTools(s, ctx);
|
|
27374
|
+
registerAllTools(s, ctx, toolTierController);
|
|
27309
27375
|
toolTierController.setOverride(runtimeToolTierOverride);
|
|
27310
27376
|
for (const [category, tier] of runtimeActiveCategoryTiers) {
|
|
27311
27377
|
toolTierController.activateCategory(category, tier);
|
|
@@ -27358,7 +27424,7 @@ var _gatingResult = applyToolGating(
|
|
|
27358
27424
|
toolTierMode,
|
|
27359
27425
|
handleTierStateChange
|
|
27360
27426
|
);
|
|
27361
|
-
registerAllTools(server, _registryCtx);
|
|
27427
|
+
registerAllTools(server, _registryCtx, _gatingResult);
|
|
27362
27428
|
_gatingResult.setOverride(runtimeToolTierOverride);
|
|
27363
27429
|
_gatingResult.finalizeRegistration();
|
|
27364
27430
|
primaryToolTierController = _gatingResult;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@velvetmonkey/flywheel-memory",
|
|
3
|
-
"version": "2.3.
|
|
3
|
+
"version": "2.3.3",
|
|
4
4
|
"description": "MCP tools that search, write, and auto-link your Obsidian vault — and learn from your edits.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
"dependencies": {
|
|
56
56
|
"@huggingface/transformers": "^3.8.1",
|
|
57
57
|
"@modelcontextprotocol/sdk": "^1.25.1",
|
|
58
|
-
"@velvetmonkey/vault-core": "^2.3.
|
|
58
|
+
"@velvetmonkey/vault-core": "^2.3.3",
|
|
59
59
|
"better-sqlite3": "^12.0.0",
|
|
60
60
|
"chokidar": "^4.0.0",
|
|
61
61
|
"gray-matter": "^4.0.3",
|