@velvetmonkey/flywheel-crank 0.2.1 → 0.3.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/index.js +175 -37
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -16,6 +16,22 @@ import matter from "gray-matter";
|
|
|
16
16
|
var HEADING_REGEX = /^(#{1,6})\s+(.+)$/;
|
|
17
17
|
|
|
18
18
|
// src/core/writer.ts
|
|
19
|
+
var EMPTY_PLACEHOLDER_PATTERNS = [
|
|
20
|
+
/^\d+\.\s*$/,
|
|
21
|
+
// "1. " or "2. " (numbered list placeholder)
|
|
22
|
+
/^-\s*$/,
|
|
23
|
+
// "- " (bullet placeholder)
|
|
24
|
+
/^-\s*\[\s*\]\s*$/,
|
|
25
|
+
// "- [ ] " (empty task placeholder)
|
|
26
|
+
/^-\s*\[x\]\s*$/i,
|
|
27
|
+
// "- [x] " (completed task placeholder)
|
|
28
|
+
/^\*\s*$/
|
|
29
|
+
// "* " (asterisk bullet placeholder)
|
|
30
|
+
];
|
|
31
|
+
function isEmptyPlaceholder(line) {
|
|
32
|
+
const trimmed = line.trim();
|
|
33
|
+
return EMPTY_PLACEHOLDER_PATTERNS.some((p) => p.test(trimmed));
|
|
34
|
+
}
|
|
19
35
|
function extractHeadings(content) {
|
|
20
36
|
const lines = content.split("\n");
|
|
21
37
|
const headings = [];
|
|
@@ -93,13 +109,29 @@ function insertInSection(content, section, newContent, position) {
|
|
|
93
109
|
if (position === "prepend") {
|
|
94
110
|
lines.splice(section.contentStartLine, 0, formattedContent);
|
|
95
111
|
} else {
|
|
96
|
-
let
|
|
97
|
-
|
|
98
|
-
|
|
112
|
+
let lastContentLineIdx = -1;
|
|
113
|
+
for (let i = section.endLine; i >= section.contentStartLine; i--) {
|
|
114
|
+
if (lines[i].trim() !== "") {
|
|
115
|
+
lastContentLineIdx = i;
|
|
116
|
+
break;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
if (lastContentLineIdx >= section.contentStartLine && isEmptyPlaceholder(lines[lastContentLineIdx])) {
|
|
120
|
+
lines[lastContentLineIdx] = formattedContent;
|
|
99
121
|
} else {
|
|
100
|
-
insertLine
|
|
122
|
+
let insertLine;
|
|
123
|
+
if (lastContentLineIdx >= section.contentStartLine) {
|
|
124
|
+
for (let i = section.endLine; i > lastContentLineIdx; i--) {
|
|
125
|
+
if (lines[i].trim() === "") {
|
|
126
|
+
lines.splice(i, 1);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
insertLine = lastContentLineIdx + 1;
|
|
130
|
+
} else {
|
|
131
|
+
insertLine = section.contentStartLine;
|
|
132
|
+
}
|
|
133
|
+
lines.splice(insertLine, 0, formattedContent);
|
|
101
134
|
}
|
|
102
|
-
lines.splice(insertLine, 0, formattedContent);
|
|
103
135
|
}
|
|
104
136
|
return lines.join("\n");
|
|
105
137
|
}
|
|
@@ -305,9 +337,104 @@ async function undoLastCommit(vaultPath2) {
|
|
|
305
337
|
}
|
|
306
338
|
}
|
|
307
339
|
|
|
340
|
+
// src/core/wikilinks.ts
|
|
341
|
+
import {
|
|
342
|
+
scanVaultEntities,
|
|
343
|
+
getAllEntities,
|
|
344
|
+
applyWikilinks,
|
|
345
|
+
loadEntityCache,
|
|
346
|
+
saveEntityCache
|
|
347
|
+
} from "@velvetmonkey/vault-core";
|
|
348
|
+
import path3 from "path";
|
|
349
|
+
var entityIndex = null;
|
|
350
|
+
var indexReady = false;
|
|
351
|
+
var indexError = null;
|
|
352
|
+
var DEFAULT_EXCLUDE_FOLDERS = [
|
|
353
|
+
"daily-notes",
|
|
354
|
+
"daily",
|
|
355
|
+
"weekly",
|
|
356
|
+
"monthly",
|
|
357
|
+
"quarterly",
|
|
358
|
+
"periodic",
|
|
359
|
+
"journal",
|
|
360
|
+
"inbox",
|
|
361
|
+
"templates"
|
|
362
|
+
];
|
|
363
|
+
async function initializeEntityIndex(vaultPath2) {
|
|
364
|
+
const cacheFile = path3.join(vaultPath2, ".claude", "wikilink-entities.json");
|
|
365
|
+
try {
|
|
366
|
+
const cached = await loadEntityCache(cacheFile);
|
|
367
|
+
if (cached) {
|
|
368
|
+
entityIndex = cached;
|
|
369
|
+
indexReady = true;
|
|
370
|
+
console.error(`[Crank] Loaded ${cached._metadata.total_entities} entities from cache`);
|
|
371
|
+
const cacheAge = Date.now() - new Date(cached._metadata.generated_at).getTime();
|
|
372
|
+
if (cacheAge > 60 * 60 * 1e3) {
|
|
373
|
+
rebuildIndexInBackground(vaultPath2, cacheFile);
|
|
374
|
+
}
|
|
375
|
+
return;
|
|
376
|
+
}
|
|
377
|
+
await rebuildIndex(vaultPath2, cacheFile);
|
|
378
|
+
} catch (error) {
|
|
379
|
+
indexError = error instanceof Error ? error : new Error(String(error));
|
|
380
|
+
console.error(`[Crank] Failed to initialize entity index: ${indexError.message}`);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
async function rebuildIndex(vaultPath2, cacheFile) {
|
|
384
|
+
console.error(`[Crank] Scanning vault for entities...`);
|
|
385
|
+
const startTime = Date.now();
|
|
386
|
+
entityIndex = await scanVaultEntities(vaultPath2, {
|
|
387
|
+
excludeFolders: DEFAULT_EXCLUDE_FOLDERS
|
|
388
|
+
});
|
|
389
|
+
indexReady = true;
|
|
390
|
+
const duration = Date.now() - startTime;
|
|
391
|
+
console.error(`[Crank] Entity index built: ${entityIndex._metadata.total_entities} entities in ${duration}ms`);
|
|
392
|
+
try {
|
|
393
|
+
await saveEntityCache(cacheFile, entityIndex);
|
|
394
|
+
console.error(`[Crank] Entity cache saved`);
|
|
395
|
+
} catch (e) {
|
|
396
|
+
console.error(`[Crank] Failed to save entity cache: ${e}`);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
function rebuildIndexInBackground(vaultPath2, cacheFile) {
|
|
400
|
+
rebuildIndex(vaultPath2, cacheFile).catch((error) => {
|
|
401
|
+
console.error(`[Crank] Background index rebuild failed: ${error}`);
|
|
402
|
+
});
|
|
403
|
+
}
|
|
404
|
+
function isEntityIndexReady() {
|
|
405
|
+
return indexReady && entityIndex !== null;
|
|
406
|
+
}
|
|
407
|
+
function processWikilinks(content) {
|
|
408
|
+
if (!isEntityIndexReady() || !entityIndex) {
|
|
409
|
+
return {
|
|
410
|
+
content,
|
|
411
|
+
linksAdded: 0,
|
|
412
|
+
linkedEntities: []
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
const entities = getAllEntities(entityIndex);
|
|
416
|
+
return applyWikilinks(content, entities, {
|
|
417
|
+
firstOccurrenceOnly: true,
|
|
418
|
+
caseInsensitive: true
|
|
419
|
+
});
|
|
420
|
+
}
|
|
421
|
+
function maybeApplyWikilinks(content, skipWikilinks) {
|
|
422
|
+
if (skipWikilinks) {
|
|
423
|
+
return { content };
|
|
424
|
+
}
|
|
425
|
+
const result = processWikilinks(content);
|
|
426
|
+
if (result.linksAdded > 0) {
|
|
427
|
+
return {
|
|
428
|
+
content: result.content,
|
|
429
|
+
wikilinkInfo: `Applied ${result.linksAdded} wikilink(s): ${result.linkedEntities.join(", ")}`
|
|
430
|
+
};
|
|
431
|
+
}
|
|
432
|
+
return { content: result.content };
|
|
433
|
+
}
|
|
434
|
+
|
|
308
435
|
// src/tools/mutations.ts
|
|
309
436
|
import fs2 from "fs/promises";
|
|
310
|
-
import
|
|
437
|
+
import path4 from "path";
|
|
311
438
|
function registerMutationTools(server2, vaultPath2) {
|
|
312
439
|
server2.tool(
|
|
313
440
|
"vault_add_to_section",
|
|
@@ -318,11 +445,12 @@ function registerMutationTools(server2, vaultPath2) {
|
|
|
318
445
|
content: z.string().describe("Content to add to the section"),
|
|
319
446
|
position: z.enum(["append", "prepend"]).default("append").describe("Where to insert content"),
|
|
320
447
|
format: z.enum(["plain", "bullet", "task", "numbered", "timestamp-bullet"]).default("plain").describe("How to format the content"),
|
|
321
|
-
commit: z.boolean().default(false).describe("If true, commit this change to git (creates undo point)")
|
|
448
|
+
commit: z.boolean().default(false).describe("If true, commit this change to git (creates undo point)"),
|
|
449
|
+
skipWikilinks: z.boolean().default(false).describe("If true, skip auto-wikilink application (wikilinks are applied by default)")
|
|
322
450
|
},
|
|
323
|
-
async ({ path: notePath, section, content, position, format, commit }) => {
|
|
451
|
+
async ({ path: notePath, section, content, position, format, commit, skipWikilinks }) => {
|
|
324
452
|
try {
|
|
325
|
-
const fullPath =
|
|
453
|
+
const fullPath = path4.join(vaultPath2, notePath);
|
|
326
454
|
try {
|
|
327
455
|
await fs2.access(fullPath);
|
|
328
456
|
} catch {
|
|
@@ -343,7 +471,8 @@ function registerMutationTools(server2, vaultPath2) {
|
|
|
343
471
|
};
|
|
344
472
|
return { content: [{ type: "text", text: JSON.stringify(result2, null, 2) }] };
|
|
345
473
|
}
|
|
346
|
-
const
|
|
474
|
+
const { content: wikilinkedContent, wikilinkInfo } = maybeApplyWikilinks(content, skipWikilinks);
|
|
475
|
+
const formattedContent = formatContent(wikilinkedContent, format);
|
|
347
476
|
const updatedContent = insertInSection(
|
|
348
477
|
fileContent,
|
|
349
478
|
sectionBoundary,
|
|
@@ -361,7 +490,8 @@ function registerMutationTools(server2, vaultPath2) {
|
|
|
361
490
|
gitError = gitResult.error;
|
|
362
491
|
}
|
|
363
492
|
}
|
|
364
|
-
const preview = formattedContent
|
|
493
|
+
const preview = formattedContent + (wikilinkInfo ? `
|
|
494
|
+
(${wikilinkInfo})` : "");
|
|
365
495
|
const result = {
|
|
366
496
|
success: true,
|
|
367
497
|
message: `Added content to section "${sectionBoundary.name}" in ${notePath}`,
|
|
@@ -394,7 +524,7 @@ function registerMutationTools(server2, vaultPath2) {
|
|
|
394
524
|
},
|
|
395
525
|
async ({ path: notePath, section, pattern, mode, useRegex, commit }) => {
|
|
396
526
|
try {
|
|
397
|
-
const fullPath =
|
|
527
|
+
const fullPath = path4.join(vaultPath2, notePath);
|
|
398
528
|
try {
|
|
399
529
|
await fs2.access(fullPath);
|
|
400
530
|
} catch {
|
|
@@ -470,11 +600,12 @@ function registerMutationTools(server2, vaultPath2) {
|
|
|
470
600
|
replacement: z.string().describe("Text to replace with"),
|
|
471
601
|
mode: z.enum(["first", "last", "all"]).default("first").describe("Which matches to replace"),
|
|
472
602
|
useRegex: z.boolean().default(false).describe("Treat search as regex"),
|
|
473
|
-
commit: z.boolean().default(false).describe("If true, commit this change to git (creates undo point)")
|
|
603
|
+
commit: z.boolean().default(false).describe("If true, commit this change to git (creates undo point)"),
|
|
604
|
+
skipWikilinks: z.boolean().default(false).describe("If true, skip auto-wikilink application on replacement text")
|
|
474
605
|
},
|
|
475
|
-
async ({ path: notePath, section, search, replacement, mode, useRegex, commit }) => {
|
|
606
|
+
async ({ path: notePath, section, search, replacement, mode, useRegex, commit, skipWikilinks }) => {
|
|
476
607
|
try {
|
|
477
|
-
const fullPath =
|
|
608
|
+
const fullPath = path4.join(vaultPath2, notePath);
|
|
478
609
|
try {
|
|
479
610
|
await fs2.access(fullPath);
|
|
480
611
|
} catch {
|
|
@@ -495,11 +626,12 @@ function registerMutationTools(server2, vaultPath2) {
|
|
|
495
626
|
};
|
|
496
627
|
return { content: [{ type: "text", text: JSON.stringify(result2, null, 2) }] };
|
|
497
628
|
}
|
|
629
|
+
const { content: wikilinkedReplacement, wikilinkInfo } = maybeApplyWikilinks(replacement, skipWikilinks);
|
|
498
630
|
const replaceResult = replaceInSection(
|
|
499
631
|
fileContent,
|
|
500
632
|
sectionBoundary,
|
|
501
633
|
search,
|
|
502
|
-
|
|
634
|
+
wikilinkedReplacement,
|
|
503
635
|
mode,
|
|
504
636
|
useRegex
|
|
505
637
|
);
|
|
@@ -551,7 +683,7 @@ function registerMutationTools(server2, vaultPath2) {
|
|
|
551
683
|
// src/tools/tasks.ts
|
|
552
684
|
import { z as z2 } from "zod";
|
|
553
685
|
import fs3 from "fs/promises";
|
|
554
|
-
import
|
|
686
|
+
import path5 from "path";
|
|
555
687
|
var TASK_REGEX = /^(\s*)-\s*\[([ xX])\]\s*(.*)$/;
|
|
556
688
|
function findTasks(content, section) {
|
|
557
689
|
const lines = content.split("\n");
|
|
@@ -607,7 +739,7 @@ function registerTaskTools(server2, vaultPath2) {
|
|
|
607
739
|
},
|
|
608
740
|
async ({ path: notePath, task, section, commit }) => {
|
|
609
741
|
try {
|
|
610
|
-
const fullPath =
|
|
742
|
+
const fullPath = path5.join(vaultPath2, notePath);
|
|
611
743
|
try {
|
|
612
744
|
await fs3.access(fullPath);
|
|
613
745
|
} catch {
|
|
@@ -695,11 +827,12 @@ function registerTaskTools(server2, vaultPath2) {
|
|
|
695
827
|
task: z2.string().describe("Task text (without checkbox)"),
|
|
696
828
|
position: z2.enum(["append", "prepend"]).default("append").describe("Where to add the task"),
|
|
697
829
|
completed: z2.boolean().default(false).describe("Whether the task should start as completed"),
|
|
698
|
-
commit: z2.boolean().default(false).describe("If true, commit this change to git (creates undo point)")
|
|
830
|
+
commit: z2.boolean().default(false).describe("If true, commit this change to git (creates undo point)"),
|
|
831
|
+
skipWikilinks: z2.boolean().default(false).describe("If true, skip auto-wikilink application (wikilinks are applied by default)")
|
|
699
832
|
},
|
|
700
|
-
async ({ path: notePath, section, task, position, completed, commit }) => {
|
|
833
|
+
async ({ path: notePath, section, task, position, completed, commit, skipWikilinks }) => {
|
|
701
834
|
try {
|
|
702
|
-
const fullPath =
|
|
835
|
+
const fullPath = path5.join(vaultPath2, notePath);
|
|
703
836
|
try {
|
|
704
837
|
await fs3.access(fullPath);
|
|
705
838
|
} catch {
|
|
@@ -720,8 +853,9 @@ function registerTaskTools(server2, vaultPath2) {
|
|
|
720
853
|
};
|
|
721
854
|
return { content: [{ type: "text", text: JSON.stringify(result2, null, 2) }] };
|
|
722
855
|
}
|
|
856
|
+
const { content: wikilinkedTask, wikilinkInfo } = maybeApplyWikilinks(task.trim(), skipWikilinks);
|
|
723
857
|
const checkbox = completed ? "[x]" : "[ ]";
|
|
724
|
-
const taskLine = `- ${checkbox} ${
|
|
858
|
+
const taskLine = `- ${checkbox} ${wikilinkedTask}`;
|
|
725
859
|
const updatedContent = insertInSection(
|
|
726
860
|
fileContent,
|
|
727
861
|
sectionBoundary,
|
|
@@ -743,7 +877,8 @@ function registerTaskTools(server2, vaultPath2) {
|
|
|
743
877
|
success: true,
|
|
744
878
|
message: `Added task to section "${sectionBoundary.name}" in ${notePath}`,
|
|
745
879
|
path: notePath,
|
|
746
|
-
preview: taskLine
|
|
880
|
+
preview: taskLine + (wikilinkInfo ? `
|
|
881
|
+
(${wikilinkInfo})` : ""),
|
|
747
882
|
gitCommit,
|
|
748
883
|
gitError
|
|
749
884
|
};
|
|
@@ -764,7 +899,7 @@ function registerTaskTools(server2, vaultPath2) {
|
|
|
764
899
|
// src/tools/frontmatter.ts
|
|
765
900
|
import { z as z3 } from "zod";
|
|
766
901
|
import fs4 from "fs/promises";
|
|
767
|
-
import
|
|
902
|
+
import path6 from "path";
|
|
768
903
|
function registerFrontmatterTools(server2, vaultPath2) {
|
|
769
904
|
server2.tool(
|
|
770
905
|
"vault_update_frontmatter",
|
|
@@ -776,7 +911,7 @@ function registerFrontmatterTools(server2, vaultPath2) {
|
|
|
776
911
|
},
|
|
777
912
|
async ({ path: notePath, frontmatter: updates, commit }) => {
|
|
778
913
|
try {
|
|
779
|
-
const fullPath =
|
|
914
|
+
const fullPath = path6.join(vaultPath2, notePath);
|
|
780
915
|
try {
|
|
781
916
|
await fs4.access(fullPath);
|
|
782
917
|
} catch {
|
|
@@ -832,7 +967,7 @@ function registerFrontmatterTools(server2, vaultPath2) {
|
|
|
832
967
|
},
|
|
833
968
|
async ({ path: notePath, key, value, commit }) => {
|
|
834
969
|
try {
|
|
835
|
-
const fullPath =
|
|
970
|
+
const fullPath = path6.join(vaultPath2, notePath);
|
|
836
971
|
try {
|
|
837
972
|
await fs4.access(fullPath);
|
|
838
973
|
} catch {
|
|
@@ -889,7 +1024,7 @@ function registerFrontmatterTools(server2, vaultPath2) {
|
|
|
889
1024
|
// src/tools/notes.ts
|
|
890
1025
|
import { z as z4 } from "zod";
|
|
891
1026
|
import fs5 from "fs/promises";
|
|
892
|
-
import
|
|
1027
|
+
import path7 from "path";
|
|
893
1028
|
function registerNoteTools(server2, vaultPath2) {
|
|
894
1029
|
server2.tool(
|
|
895
1030
|
"vault_create_note",
|
|
@@ -911,7 +1046,7 @@ function registerNoteTools(server2, vaultPath2) {
|
|
|
911
1046
|
};
|
|
912
1047
|
return { content: [{ type: "text", text: JSON.stringify(result2, null, 2) }] };
|
|
913
1048
|
}
|
|
914
|
-
const fullPath =
|
|
1049
|
+
const fullPath = path7.join(vaultPath2, notePath);
|
|
915
1050
|
try {
|
|
916
1051
|
await fs5.access(fullPath);
|
|
917
1052
|
if (!overwrite) {
|
|
@@ -924,7 +1059,7 @@ function registerNoteTools(server2, vaultPath2) {
|
|
|
924
1059
|
}
|
|
925
1060
|
} catch {
|
|
926
1061
|
}
|
|
927
|
-
const dir =
|
|
1062
|
+
const dir = path7.dirname(fullPath);
|
|
928
1063
|
await fs5.mkdir(dir, { recursive: true });
|
|
929
1064
|
await writeVaultFile(vaultPath2, notePath, content, frontmatter);
|
|
930
1065
|
let gitCommit;
|
|
@@ -983,7 +1118,7 @@ Content length: ${content.length} chars`,
|
|
|
983
1118
|
};
|
|
984
1119
|
return { content: [{ type: "text", text: JSON.stringify(result2, null, 2) }] };
|
|
985
1120
|
}
|
|
986
|
-
const fullPath =
|
|
1121
|
+
const fullPath = path7.join(vaultPath2, notePath);
|
|
987
1122
|
try {
|
|
988
1123
|
await fs5.access(fullPath);
|
|
989
1124
|
} catch {
|
|
@@ -1029,7 +1164,7 @@ Content length: ${content.length} chars`,
|
|
|
1029
1164
|
// src/tools/system.ts
|
|
1030
1165
|
import { z as z5 } from "zod";
|
|
1031
1166
|
import fs6 from "fs/promises";
|
|
1032
|
-
import
|
|
1167
|
+
import path8 from "path";
|
|
1033
1168
|
function registerSystemTools(server2, vaultPath2) {
|
|
1034
1169
|
server2.tool(
|
|
1035
1170
|
"vault_list_sections",
|
|
@@ -1049,7 +1184,7 @@ function registerSystemTools(server2, vaultPath2) {
|
|
|
1049
1184
|
};
|
|
1050
1185
|
return { content: [{ type: "text", text: JSON.stringify(result2, null, 2) }] };
|
|
1051
1186
|
}
|
|
1052
|
-
const fullPath =
|
|
1187
|
+
const fullPath = path8.join(vaultPath2, notePath);
|
|
1053
1188
|
try {
|
|
1054
1189
|
await fs6.access(fullPath);
|
|
1055
1190
|
} catch {
|
|
@@ -1157,18 +1292,18 @@ Message: ${undoResult.undoneCommit.message}` : void 0
|
|
|
1157
1292
|
|
|
1158
1293
|
// src/core/vaultRoot.ts
|
|
1159
1294
|
import * as fs7 from "fs";
|
|
1160
|
-
import * as
|
|
1295
|
+
import * as path9 from "path";
|
|
1161
1296
|
var VAULT_MARKERS = [".obsidian", ".claude"];
|
|
1162
1297
|
function findVaultRoot(startPath) {
|
|
1163
|
-
let current =
|
|
1298
|
+
let current = path9.resolve(startPath || process.cwd());
|
|
1164
1299
|
while (true) {
|
|
1165
1300
|
for (const marker of VAULT_MARKERS) {
|
|
1166
|
-
const markerPath =
|
|
1301
|
+
const markerPath = path9.join(current, marker);
|
|
1167
1302
|
if (fs7.existsSync(markerPath) && fs7.statSync(markerPath).isDirectory()) {
|
|
1168
1303
|
return current;
|
|
1169
1304
|
}
|
|
1170
1305
|
}
|
|
1171
|
-
const parent =
|
|
1306
|
+
const parent = path9.dirname(current);
|
|
1172
1307
|
if (parent === current) {
|
|
1173
1308
|
return startPath || process.cwd();
|
|
1174
1309
|
}
|
|
@@ -1179,11 +1314,14 @@ function findVaultRoot(startPath) {
|
|
|
1179
1314
|
// src/index.ts
|
|
1180
1315
|
var server = new McpServer({
|
|
1181
1316
|
name: "flywheel-crank",
|
|
1182
|
-
version: "0.
|
|
1317
|
+
version: "0.3.0"
|
|
1183
1318
|
});
|
|
1184
1319
|
var vaultPath = process.env.PROJECT_PATH || findVaultRoot();
|
|
1185
1320
|
console.error(`[Crank] Starting Flywheel Crank MCP server`);
|
|
1186
1321
|
console.error(`[Crank] Vault path: ${vaultPath}`);
|
|
1322
|
+
initializeEntityIndex(vaultPath).catch((err) => {
|
|
1323
|
+
console.error(`[Crank] Entity index initialization failed: ${err}`);
|
|
1324
|
+
});
|
|
1187
1325
|
registerMutationTools(server, vaultPath);
|
|
1188
1326
|
registerTaskTools(server, vaultPath);
|
|
1189
1327
|
registerFrontmatterTools(server, vaultPath);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@velvetmonkey/flywheel-crank",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "Deterministic vault mutations for Obsidian via MCP",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -36,6 +36,7 @@
|
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
38
|
"@modelcontextprotocol/sdk": "^1.25.1",
|
|
39
|
+
"@velvetmonkey/vault-core": "^0.1.0",
|
|
39
40
|
"gray-matter": "^4.0.3",
|
|
40
41
|
"zod": "^3.22.4",
|
|
41
42
|
"simple-git": "^3.22.0"
|