@blacksmithers/vaultforge 1.0.0

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.
Files changed (107) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +464 -0
  3. package/dist/index.d.ts +3 -0
  4. package/dist/index.d.ts.map +1 -0
  5. package/dist/index.js +705 -0
  6. package/dist/index.js.map +1 -0
  7. package/dist/tool-handlers.d.ts +79 -0
  8. package/dist/tool-handlers.d.ts.map +1 -0
  9. package/dist/tool-handlers.js +326 -0
  10. package/dist/tool-handlers.js.map +1 -0
  11. package/dist/tools/canvas/canvas-create.d.ts +4 -0
  12. package/dist/tools/canvas/canvas-create.d.ts.map +1 -0
  13. package/dist/tools/canvas/canvas-create.js +243 -0
  14. package/dist/tools/canvas/canvas-create.js.map +1 -0
  15. package/dist/tools/canvas/canvas-patch.d.ts +4 -0
  16. package/dist/tools/canvas/canvas-patch.d.ts.map +1 -0
  17. package/dist/tools/canvas/canvas-patch.js +225 -0
  18. package/dist/tools/canvas/canvas-patch.js.map +1 -0
  19. package/dist/tools/canvas/canvas-read.d.ts +4 -0
  20. package/dist/tools/canvas/canvas-read.d.ts.map +1 -0
  21. package/dist/tools/canvas/canvas-read.js +97 -0
  22. package/dist/tools/canvas/canvas-read.js.map +1 -0
  23. package/dist/tools/canvas/canvas-relayout.d.ts +4 -0
  24. package/dist/tools/canvas/canvas-relayout.d.ts.map +1 -0
  25. package/dist/tools/canvas/canvas-relayout.js +152 -0
  26. package/dist/tools/canvas/canvas-relayout.js.map +1 -0
  27. package/dist/tools/canvas/canvas-utils.d.ts +40 -0
  28. package/dist/tools/canvas/canvas-utils.d.ts.map +1 -0
  29. package/dist/tools/canvas/canvas-utils.js +141 -0
  30. package/dist/tools/canvas/canvas-utils.js.map +1 -0
  31. package/dist/tools/canvas/layout-engine.d.ts +34 -0
  32. package/dist/tools/canvas/layout-engine.d.ts.map +1 -0
  33. package/dist/tools/canvas/layout-engine.js +75 -0
  34. package/dist/tools/canvas/layout-engine.js.map +1 -0
  35. package/dist/tools/canvas/types.d.ts +71 -0
  36. package/dist/tools/canvas/types.d.ts.map +1 -0
  37. package/dist/tools/canvas/types.js +2 -0
  38. package/dist/tools/canvas/types.js.map +1 -0
  39. package/dist/tools/files/batch-rename.d.ts +4 -0
  40. package/dist/tools/files/batch-rename.d.ts.map +1 -0
  41. package/dist/tools/files/batch-rename.js +140 -0
  42. package/dist/tools/files/batch-rename.js.map +1 -0
  43. package/dist/tools/files/delete-folder.d.ts +17 -0
  44. package/dist/tools/files/delete-folder.d.ts.map +1 -0
  45. package/dist/tools/files/delete-folder.js +100 -0
  46. package/dist/tools/files/delete-folder.js.map +1 -0
  47. package/dist/tools/files/prune-empty-dirs.d.ts +17 -0
  48. package/dist/tools/files/prune-empty-dirs.d.ts.map +1 -0
  49. package/dist/tools/files/prune-empty-dirs.js +106 -0
  50. package/dist/tools/files/prune-empty-dirs.js.map +1 -0
  51. package/dist/tools/intelligence/clustering.d.ts +15 -0
  52. package/dist/tools/intelligence/clustering.d.ts.map +1 -0
  53. package/dist/tools/intelligence/clustering.js +93 -0
  54. package/dist/tools/intelligence/clustering.js.map +1 -0
  55. package/dist/tools/intelligence/tfidf.d.ts +19 -0
  56. package/dist/tools/intelligence/tfidf.d.ts.map +1 -0
  57. package/dist/tools/intelligence/tfidf.js +68 -0
  58. package/dist/tools/intelligence/tfidf.js.map +1 -0
  59. package/dist/tools/intelligence/vault-suggest.d.ts +7 -0
  60. package/dist/tools/intelligence/vault-suggest.d.ts.map +1 -0
  61. package/dist/tools/intelligence/vault-suggest.js +307 -0
  62. package/dist/tools/intelligence/vault-suggest.js.map +1 -0
  63. package/dist/tools/intelligence/vault-themes.d.ts +30 -0
  64. package/dist/tools/intelligence/vault-themes.d.ts.map +1 -0
  65. package/dist/tools/intelligence/vault-themes.js +88 -0
  66. package/dist/tools/intelligence/vault-themes.js.map +1 -0
  67. package/dist/tools/links/backlinks.d.ts +4 -0
  68. package/dist/tools/links/backlinks.d.ts.map +1 -0
  69. package/dist/tools/links/backlinks.js +50 -0
  70. package/dist/tools/links/backlinks.js.map +1 -0
  71. package/dist/tools/links/link-utils.d.ts +23 -0
  72. package/dist/tools/links/link-utils.d.ts.map +1 -0
  73. package/dist/tools/links/link-utils.js +63 -0
  74. package/dist/tools/links/link-utils.js.map +1 -0
  75. package/dist/tools/links/update-links.d.ts +16 -0
  76. package/dist/tools/links/update-links.d.ts.map +1 -0
  77. package/dist/tools/links/update-links.js +88 -0
  78. package/dist/tools/links/update-links.js.map +1 -0
  79. package/dist/tools/metadata/frontmatter.d.ts +12 -0
  80. package/dist/tools/metadata/frontmatter.d.ts.map +1 -0
  81. package/dist/tools/metadata/frontmatter.js +190 -0
  82. package/dist/tools/metadata/frontmatter.js.map +1 -0
  83. package/dist/tools/notes/edit-regex.d.ts +4 -0
  84. package/dist/tools/notes/edit-regex.d.ts.map +1 -0
  85. package/dist/tools/notes/edit-regex.js +142 -0
  86. package/dist/tools/notes/edit-regex.js.map +1 -0
  87. package/dist/tools/search/markdown-parser.d.ts +12 -0
  88. package/dist/tools/search/markdown-parser.d.ts.map +1 -0
  89. package/dist/tools/search/markdown-parser.js +64 -0
  90. package/dist/tools/search/markdown-parser.js.map +1 -0
  91. package/dist/tools/search/orama-engine.d.ts +76 -0
  92. package/dist/tools/search/orama-engine.d.ts.map +1 -0
  93. package/dist/tools/search/orama-engine.js +152 -0
  94. package/dist/tools/search/orama-engine.js.map +1 -0
  95. package/dist/tools/search/search-reindex.d.ts +7 -0
  96. package/dist/tools/search/search-reindex.d.ts.map +1 -0
  97. package/dist/tools/search/search-reindex.js +34 -0
  98. package/dist/tools/search/search-reindex.js.map +1 -0
  99. package/dist/tools/search/smart-search.d.ts +7 -0
  100. package/dist/tools/search/smart-search.d.ts.map +1 -0
  101. package/dist/tools/search/smart-search.js +102 -0
  102. package/dist/tools/search/smart-search.js.map +1 -0
  103. package/dist/vault-index.d.ts +95 -0
  104. package/dist/vault-index.d.ts.map +1 -0
  105. package/dist/vault-index.js +432 -0
  106. package/dist/vault-index.js.map +1 -0
  107. package/package.json +60 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tfidf.js","sourceRoot":"","sources":["../../../src/tools/intelligence/tfidf.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * vault_suggest — Generate vault reorganization suggestions based on theme analysis.
3
+ */
4
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
5
+ import type { VaultIndex } from "../../vault-index.js";
6
+ export declare function registerVaultSuggest(server: McpServer, vaultPath: string, vault: VaultIndex): void;
7
+ //# sourceMappingURL=vault-suggest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vault-suggest.d.ts","sourceRoot":"","sources":["../../../src/tools/intelligence/vault-suggest.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEzE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAoEvD,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,SAAS,EACjB,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,UAAU,GAChB,IAAI,CAiRN"}
@@ -0,0 +1,307 @@
1
+ /**
2
+ * vault_suggest — Generate vault reorganization suggestions based on theme analysis.
3
+ */
4
+ import { z } from "zod";
5
+ import { computeThemeMap } from "./vault-themes.js";
6
+ import { readFile, writeFile, mkdir } from "node:fs/promises";
7
+ import { join, dirname, basename } from "node:path";
8
+ function findPrimaryFolder(files, folders) {
9
+ // The folder with most files is the primary
10
+ const counts = new Map();
11
+ for (const f of files) {
12
+ const folder = f.split("/").slice(0, -1).join("/");
13
+ counts.set(folder, (counts.get(folder) || 0) + 1);
14
+ }
15
+ let best = folders[0] || "";
16
+ let bestCount = 0;
17
+ for (const [folder, count] of counts) {
18
+ if (count > bestCount) {
19
+ bestCount = count;
20
+ best = folder;
21
+ }
22
+ }
23
+ return best;
24
+ }
25
+ async function scanWikilinks(vaultPath, vault) {
26
+ // Count incoming [[wikilinks]] for each file
27
+ const incomingLinks = new Map();
28
+ const allFiles = vault.allFiles().filter((f) => f.ext === ".md");
29
+ for (const file of allFiles) {
30
+ try {
31
+ const content = await readFile(file.abs, "utf-8");
32
+ const links = content.match(/\[\[([^\]|]+)(?:\|[^\]]+)?\]\]/g);
33
+ if (!links)
34
+ continue;
35
+ for (const link of links) {
36
+ const target = link.replace(/\[\[([^\]|]+)(?:\|[^\]]+)?\]\]/, "$1");
37
+ // Resolve target to a path
38
+ const entry = vault.resolve(target);
39
+ if (entry) {
40
+ incomingLinks.set(entry.rel, (incomingLinks.get(entry.rel) || 0) + 1);
41
+ }
42
+ }
43
+ }
44
+ catch {
45
+ // Skip unreadable files
46
+ }
47
+ }
48
+ return incomingLinks;
49
+ }
50
+ export function registerVaultSuggest(server, vaultPath, vault) {
51
+ server.tool("vault_suggest", "Generate vault reorganization suggestions based on theme analysis. " +
52
+ "Strategies: consolidate scattered themes, create MOCs, archive stale files, " +
53
+ "triage orphans. Use mode='suggest' to preview, mode='execute' to apply.", {
54
+ theme_map: z
55
+ .any()
56
+ .optional()
57
+ .describe("Output from vault_themes (or runs vault_themes internally if omitted)"),
58
+ mode: z
59
+ .enum(["suggest", "execute"])
60
+ .describe("suggest = dry run, execute = apply changes"),
61
+ strategies: z
62
+ .array(z.enum(["consolidate", "create_mocs", "archive_stale", "triage_orphans"]))
63
+ .default(["consolidate", "create_mocs", "archive_stale", "triage_orphans"])
64
+ .describe("Which strategies to apply"),
65
+ archive_folder: z.string().default("90-Archive").describe("Archive folder path"),
66
+ moc_folder: z.string().default("60-MOCs").describe("MOC folder path"),
67
+ stale_days: z.number().default(90).describe("Days without modification to consider stale"),
68
+ }, async ({ theme_map, mode, strategies, archive_folder, moc_folder, stale_days }) => {
69
+ await vault.waitReady();
70
+ // Get or compute theme map
71
+ let themes;
72
+ if (theme_map && typeof theme_map === "object" && "themes" in theme_map) {
73
+ themes = theme_map;
74
+ }
75
+ else {
76
+ themes = await computeThemeMap(vaultPath, vault, {
77
+ minClusterSize: 3,
78
+ maxThemes: 15,
79
+ excludeFolders: [],
80
+ depth: "deep",
81
+ });
82
+ }
83
+ const suggestions = [];
84
+ // --- CONSOLIDATE ---
85
+ if (strategies.includes("consolidate")) {
86
+ for (const theme of themes.themes) {
87
+ if (!theme.crossFolder)
88
+ continue;
89
+ const primary = findPrimaryFolder(theme.files, theme.folders);
90
+ const primaryCount = theme.files.filter((f) => f.split("/").slice(0, -1).join("/") === primary).length;
91
+ for (const filePath of theme.files) {
92
+ const fileFolder = filePath.split("/").slice(0, -1).join("/");
93
+ if (fileFolder !== primary) {
94
+ const fileName = basename(filePath);
95
+ suggestions.push({
96
+ type: "consolidate",
97
+ theme_id: theme.id,
98
+ action: `Move ${filePath} → ${primary}/${fileName}`,
99
+ reason: `File's dominant theme is ${theme.label}. ${primaryCount} of ${theme.files.length} theme files are already in ${primary}/.`,
100
+ });
101
+ }
102
+ }
103
+ }
104
+ }
105
+ // --- CREATE MOCs ---
106
+ if (strategies.includes("create_mocs")) {
107
+ for (const theme of themes.themes) {
108
+ if (theme.files.length < 3)
109
+ continue;
110
+ const mocFileName = `MOC-${theme.id.split("-").map((s) => s.charAt(0).toUpperCase() + s.slice(1)).join("-")}.md`;
111
+ const mocPath = `${moc_folder}/${mocFileName}`;
112
+ const today = new Date().toISOString().slice(0, 10);
113
+ const mocContent = [
114
+ `# MOC: ${theme.label}`,
115
+ "",
116
+ `Key terms: ${theme.keyTerms.join(", ")}`,
117
+ "",
118
+ "## Files",
119
+ "",
120
+ ...theme.files.map((f) => `- [[${f.replace(/\.md$/, "")}]]`),
121
+ "",
122
+ "---",
123
+ `*Auto-generated by vaultforge vault_suggest on ${today}*`,
124
+ ].join("\n");
125
+ suggestions.push({
126
+ type: "create_mocs",
127
+ theme_id: theme.id,
128
+ action: `Create ${mocPath} with ${theme.files.length} links`,
129
+ content_preview: mocContent.slice(0, 200) + (mocContent.length > 200 ? "..." : ""),
130
+ });
131
+ }
132
+ }
133
+ // --- ARCHIVE STALE ---
134
+ if (strategies.includes("archive_stale")) {
135
+ const staleCutoff = Date.now() - stale_days * 24 * 60 * 60 * 1000;
136
+ const incomingLinks = await scanWikilinks(vaultPath, vault);
137
+ const staleFiles = [];
138
+ for (const file of vault.allFiles()) {
139
+ if (file.ext !== ".md")
140
+ continue;
141
+ if (file.rel.startsWith(archive_folder + "/"))
142
+ continue;
143
+ if (file.mtime > staleCutoff)
144
+ continue;
145
+ const links = incomingLinks.get(file.rel) || 0;
146
+ if (links > 0)
147
+ continue; // Still referenced — don't archive
148
+ staleFiles.push({
149
+ path: file.rel,
150
+ last_modified: new Date(file.mtime).toISOString().slice(0, 10),
151
+ incoming_links: links,
152
+ });
153
+ }
154
+ if (staleFiles.length > 0) {
155
+ suggestions.push({
156
+ type: "archive_stale",
157
+ action: `Archive ${staleFiles.length} stale files to ${archive_folder}/`,
158
+ reason: `Files not modified in ${stale_days}+ days with no incoming wikilinks`,
159
+ files: staleFiles,
160
+ });
161
+ }
162
+ }
163
+ // --- TRIAGE ORPHANS ---
164
+ if (strategies.includes("triage_orphans")) {
165
+ for (const orphan of themes.orphans) {
166
+ // Find closest theme by checking if any theme has files in the same folder
167
+ const orphanFolder = orphan.path.split("/").slice(0, -1).join("/");
168
+ let closestTheme = null;
169
+ for (const theme of themes.themes) {
170
+ if (theme.folders.includes(orphanFolder)) {
171
+ closestTheme = theme.id;
172
+ break;
173
+ }
174
+ }
175
+ if (closestTheme) {
176
+ suggestions.push({
177
+ type: "triage_orphans",
178
+ file: orphan.path,
179
+ suggestion: `Could be related to theme "${closestTheme}" — already in a theme folder`,
180
+ closest_theme: closestTheme,
181
+ action: `Review ${orphan.path} for theme affinity`,
182
+ });
183
+ }
184
+ else {
185
+ suggestions.push({
186
+ type: "triage_orphans",
187
+ file: orphan.path,
188
+ suggestion: "No clear theme affinity — consider archiving or manual review",
189
+ closest_theme: null,
190
+ action: `Review ${orphan.path} — no theme match found`,
191
+ });
192
+ }
193
+ }
194
+ }
195
+ // --- EXECUTE ---
196
+ if (mode === "execute") {
197
+ const executed = [];
198
+ let errors = 0;
199
+ let skipped = 0;
200
+ for (const s of suggestions) {
201
+ try {
202
+ if (s.type === "consolidate") {
203
+ const match = s.action.match(/^Move (.+) → (.+)$/);
204
+ if (!match) {
205
+ skipped++;
206
+ continue;
207
+ }
208
+ const [, from, to] = match;
209
+ const absFrom = join(vaultPath, from);
210
+ const absTo = join(vaultPath, to);
211
+ await mkdir(dirname(absTo), { recursive: true });
212
+ const content = await readFile(absFrom, "utf-8");
213
+ await writeFile(absTo, content, "utf-8");
214
+ const { unlink } = await import("node:fs/promises");
215
+ await unlink(absFrom);
216
+ executed.push({ action: "moved", from, to });
217
+ }
218
+ else if (s.type === "create_mocs" && s.content_preview) {
219
+ const match = s.action.match(/^Create (.+) with/);
220
+ if (!match) {
221
+ skipped++;
222
+ continue;
223
+ }
224
+ const mocPath = match[1];
225
+ // Reconstruct full MOC content
226
+ const theme = themes.themes.find((t) => t.id === s.theme_id);
227
+ if (!theme) {
228
+ skipped++;
229
+ continue;
230
+ }
231
+ const today = new Date().toISOString().slice(0, 10);
232
+ const mocContent = [
233
+ `# MOC: ${theme.label}`,
234
+ "",
235
+ `Key terms: ${theme.keyTerms.join(", ")}`,
236
+ "",
237
+ "## Files",
238
+ "",
239
+ ...theme.files.map((f) => `- [[${f.replace(/\.md$/, "")}]]`),
240
+ "",
241
+ "---",
242
+ `*Auto-generated by vaultforge vault_suggest on ${today}*`,
243
+ ].join("\n");
244
+ const absPath = join(vaultPath, mocPath);
245
+ await mkdir(dirname(absPath), { recursive: true });
246
+ await writeFile(absPath, mocContent, "utf-8");
247
+ executed.push({ action: "created", path: mocPath });
248
+ }
249
+ else if (s.type === "archive_stale" && s.files) {
250
+ for (const file of s.files) {
251
+ const from = file.path;
252
+ const to = `${archive_folder}/${basename(from)}`;
253
+ const absFrom = join(vaultPath, from);
254
+ const absTo = join(vaultPath, to);
255
+ await mkdir(dirname(absTo), { recursive: true });
256
+ const content = await readFile(absFrom, "utf-8");
257
+ await writeFile(absTo, content, "utf-8");
258
+ const { unlink } = await import("node:fs/promises");
259
+ await unlink(absFrom);
260
+ executed.push({ action: "moved", from, to });
261
+ }
262
+ }
263
+ else {
264
+ skipped++;
265
+ }
266
+ }
267
+ catch {
268
+ errors++;
269
+ }
270
+ }
271
+ return {
272
+ content: [
273
+ {
274
+ type: "text",
275
+ text: JSON.stringify({
276
+ executed: executed.length,
277
+ skipped,
278
+ errors,
279
+ details: executed,
280
+ }, null, 2),
281
+ },
282
+ ],
283
+ };
284
+ }
285
+ // --- SUGGEST MODE ---
286
+ const summary = {};
287
+ for (const s of suggestions) {
288
+ summary[s.type] = (summary[s.type] || 0) + 1;
289
+ }
290
+ const total = suggestions.length;
291
+ return {
292
+ content: [
293
+ {
294
+ type: "text",
295
+ text: JSON.stringify({
296
+ suggestions,
297
+ summary: {
298
+ ...summary,
299
+ total_actions: total,
300
+ },
301
+ }, null, 2),
302
+ },
303
+ ],
304
+ };
305
+ });
306
+ }
307
+ //# sourceMappingURL=vault-suggest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vault-suggest.js","sourceRoot":"","sources":["../../../src/tools/intelligence/vault-suggest.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * vault_themes — Scan vault and extract dominant themes via TF-IDF + clustering.
3
+ */
4
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
5
+ import type { VaultIndex } from "../../vault-index.js";
6
+ import { type ThemeCluster } from "./clustering.js";
7
+ export interface ThemeMap {
8
+ total_files: number;
9
+ indexed_files: number;
10
+ themes: Array<ThemeCluster & {
11
+ file_count: number;
12
+ }>;
13
+ orphans: Array<{
14
+ path: string;
15
+ reason: string;
16
+ }>;
17
+ cross_folder_warnings: Array<{
18
+ theme_id: string;
19
+ folders: string[];
20
+ suggestion: string;
21
+ }>;
22
+ }
23
+ export declare function computeThemeMap(vaultPath: string, vault: VaultIndex, options: {
24
+ minClusterSize: number;
25
+ maxThemes: number;
26
+ excludeFolders: string[];
27
+ depth: "shallow" | "deep";
28
+ }): Promise<ThemeMap>;
29
+ export declare function registerVaultThemes(server: McpServer, vaultPath: string, vault: VaultIndex): void;
30
+ //# sourceMappingURL=vault-themes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vault-themes.d.ts","sourceRoot":"","sources":["../../../src/tools/intelligence/vault-themes.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEzE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAGvD,OAAO,EAAgB,KAAK,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAElE,MAAM,WAAW,QAAQ;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,KAAK,CACX,YAAY,GAAG;QAAE,UAAU,EAAE,MAAM,CAAA;KAAE,CACtC,CAAC;IACF,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACjD,qBAAqB,EAAE,KAAK,CAAC;QAC3B,QAAQ,EAAE,MAAM,CAAC;QACjB,OAAO,EAAE,MAAM,EAAE,CAAC;QAClB,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC,CAAC;CACJ;AAED,wBAAsB,eAAe,CACnC,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,UAAU,EACjB,OAAO,EAAE;IACP,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,KAAK,EAAE,SAAS,GAAG,MAAM,CAAC;CAC3B,GACA,OAAO,CAAC,QAAQ,CAAC,CA0DnB;AAED,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,SAAS,EACjB,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,UAAU,GAChB,IAAI,CAsCN"}
@@ -0,0 +1,88 @@
1
+ /**
2
+ * vault_themes — Scan vault and extract dominant themes via TF-IDF + clustering.
3
+ */
4
+ import { z } from "zod";
5
+ import { ensureIndex, getAllIndexedDocs } from "../search/orama-engine.js";
6
+ import { computeTfIdf } from "./tfidf.js";
7
+ import { clusterFiles } from "./clustering.js";
8
+ export async function computeThemeMap(vaultPath, vault, options) {
9
+ await ensureIndex(vaultPath, vault);
10
+ const allDocs = getAllIndexedDocs(vaultPath, vault);
11
+ const totalFiles = vault.allFiles().length;
12
+ // Filter excluded folders
13
+ const docs = allDocs.filter((doc) => {
14
+ return !options.excludeFolders.some((folder) => doc.path.startsWith(folder + "/") || doc.path.startsWith(folder + "\\"));
15
+ });
16
+ // Prepare content for TF-IDF based on depth
17
+ const tfidfInput = docs.map((doc) => ({
18
+ path: doc.path,
19
+ title: doc.title,
20
+ content: options.depth === "shallow"
21
+ ? `${doc.title} ${doc.headings}`
22
+ : `${doc.title} ${doc.headings} ${doc.content}`,
23
+ }));
24
+ // Compute fingerprints and cluster
25
+ const fingerprints = computeTfIdf(tfidfInput);
26
+ const clusters = clusterFiles(fingerprints, options.minClusterSize);
27
+ // Limit to maxThemes
28
+ const themes = clusters.slice(0, options.maxThemes).map((c) => ({
29
+ ...c,
30
+ file_count: c.files.length,
31
+ }));
32
+ // Find orphans — files not in any cluster
33
+ const clusteredPaths = new Set(themes.flatMap((t) => t.files));
34
+ const orphans = docs
35
+ .filter((doc) => !clusteredPaths.has(doc.path))
36
+ .map((doc) => ({
37
+ path: doc.path,
38
+ reason: "No strong theme affinity",
39
+ }));
40
+ // Cross-folder warnings
41
+ const crossFolderWarnings = themes
42
+ .filter((t) => t.crossFolder)
43
+ .map((t) => ({
44
+ theme_id: t.id,
45
+ folders: t.folders,
46
+ suggestion: "Theme spans multiple folders — consider consolidating",
47
+ }));
48
+ return {
49
+ total_files: totalFiles,
50
+ indexed_files: docs.length,
51
+ themes,
52
+ orphans,
53
+ cross_folder_warnings: crossFolderWarnings,
54
+ };
55
+ }
56
+ export function registerVaultThemes(server, vaultPath, vault) {
57
+ server.tool("vault_themes", "Scan the vault and extract dominant themes using TF-IDF analysis. " +
58
+ "Returns a thematic atlas: clusters of related files, cross-folder warnings, " +
59
+ "and orphan files. Use before vault_suggest for reorganization planning.", {
60
+ min_cluster_size: z.number().default(3).describe("Minimum files per theme (default: 3)"),
61
+ max_themes: z.number().default(15).describe("Maximum themes to return (default: 15)"),
62
+ exclude_folders: z
63
+ .array(z.string())
64
+ .default([])
65
+ .describe('Folders to exclude (e.g. ["90-Archive", "Templates"])'),
66
+ depth: z
67
+ .enum(["shallow", "deep"])
68
+ .default("deep")
69
+ .describe("shallow = titles+headings only (faster), deep = full content (better)"),
70
+ }, async ({ min_cluster_size, max_themes, exclude_folders, depth }) => {
71
+ await vault.waitReady();
72
+ const themeMap = await computeThemeMap(vaultPath, vault, {
73
+ minClusterSize: min_cluster_size,
74
+ maxThemes: max_themes,
75
+ excludeFolders: exclude_folders,
76
+ depth,
77
+ });
78
+ return {
79
+ content: [
80
+ {
81
+ type: "text",
82
+ text: JSON.stringify(themeMap, null, 2),
83
+ },
84
+ ],
85
+ };
86
+ });
87
+ }
88
+ //# sourceMappingURL=vault-themes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vault-themes.js","sourceRoot":"","sources":["../../../src/tools/intelligence/vault-themes.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import type { VaultIndex } from "../../vault-index.js";
3
+ export declare function registerBacklinks(server: McpServer, vaultPath: string, vault: VaultIndex): void;
4
+ //# sourceMappingURL=backlinks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backlinks.d.ts","sourceRoot":"","sources":["../../../src/tools/links/backlinks.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAIpE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAGvD,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,SAAS,EACjB,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,UAAU,GAChB,IAAI,CAyDN"}
@@ -0,0 +1,50 @@
1
+ import { z } from "zod";
2
+ import { readFile } from "node:fs/promises";
3
+ import path from "node:path";
4
+ import { extractStem, getAllMdFiles, findLinksInContent } from "./link-utils.js";
5
+ export function registerBacklinks(server, vaultPath, vault) {
6
+ server.tool("backlinks", "Find all files that link to a given file. Returns source files, link text, line numbers, context, and embed detection. Impact analysis before move/delete.", {
7
+ path: z.string().describe("Target file path or stem"),
8
+ include_embeds: z.boolean().default(true).describe("Include ![[embed]] links (default: true)"),
9
+ }, async ({ path: targetPath, include_embeds }) => {
10
+ await vault.waitReady();
11
+ const resolved = vault.resolve(targetPath);
12
+ const rel = resolved?.rel ?? targetPath;
13
+ const ref = rel.replace(/\.md$/, "");
14
+ const stem = extractStem(rel);
15
+ const allFiles = getAllMdFiles(vault);
16
+ const results = [];
17
+ let totalCount = 0;
18
+ for (const filePath of allFiles) {
19
+ if (filePath === rel)
20
+ continue;
21
+ const fullPath = path.join(vaultPath, filePath);
22
+ let content;
23
+ try {
24
+ content = await readFile(fullPath, "utf-8");
25
+ }
26
+ catch {
27
+ continue;
28
+ }
29
+ const matches = findLinksInContent(content, ref, stem, include_embeds);
30
+ if (matches.length > 0) {
31
+ totalCount += matches.length;
32
+ results.push({ path: filePath, links: matches });
33
+ }
34
+ }
35
+ return {
36
+ content: [
37
+ {
38
+ type: "text",
39
+ text: JSON.stringify({
40
+ target: rel,
41
+ backlink_count: totalCount,
42
+ file_count: results.length,
43
+ results,
44
+ }, null, 2),
45
+ },
46
+ ],
47
+ };
48
+ });
49
+ }
50
+ //# sourceMappingURL=backlinks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backlinks.js","sourceRoot":"","sources":["../../../src/tools/links/backlinks.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,kBAAkB,EAAkB,MAAM,iBAAiB,CAAC;AAEjG,MAAM,UAAU,iBAAiB,CAC/B,MAAiB,EACjB,SAAiB,EACjB,KAAiB;IAEjB,MAAM,CAAC,IAAI,CACT,WAAW,EACX,4JAA4J,EAC5J;QACE,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,0BAA0B,CAAC;QACrD,cAAc,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,0CAA0C,CAAC;KAC/F,EACD,KAAK,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,cAAc,EAAE,EAAE,EAAE;QAC7C,MAAM,KAAK,CAAC,SAAS,EAAE,CAAC;QAExB,MAAM,QAAQ,GAAG,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;QAC3C,MAAM,GAAG,GAAG,QAAQ,EAAE,GAAG,IAAI,UAAU,CAAC;QACxC,MAAM,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;QACrC,MAAM,IAAI,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC;QAE9B,MAAM,QAAQ,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;QACtC,MAAM,OAAO,GAAgD,EAAE,CAAC;QAChE,IAAI,UAAU,GAAG,CAAC,CAAC;QAEnB,KAAK,MAAM,QAAQ,IAAI,QAAQ,EAAE,CAAC;YAChC,IAAI,QAAQ,KAAK,GAAG;gBAAE,SAAS;YAE/B,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;YAChD,IAAI,OAAe,CAAC;YACpB,IAAI,CAAC;gBACH,OAAO,GAAG,MAAM,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;YAC9C,CAAC;YAAC,MAAM,CAAC;gBACP,SAAS;YACX,CAAC;YAED,MAAM,OAAO,GAAG,kBAAkB,CAAC,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,cAAc,CAAC,CAAC;YACvE,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACvB,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC;gBAC7B,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC;YACnD,CAAC;QACH,CAAC;QAED,OAAO;YACL,OAAO,EAAE;gBACP;oBACE,IAAI,EAAE,MAAM;oBACZ,IAAI,EAAE,IAAI,CAAC,SAAS,CAClB;wBACE,MAAM,EAAE,GAAG;wBACX,cAAc,EAAE,UAAU;wBAC1B,UAAU,EAAE,OAAO,CAAC,MAAM;wBAC1B,OAAO;qBACR,EACD,IAAI,EACJ,CAAC,CACF;iBACF;aACF;SACF,CAAC;IACJ,CAAC,CACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,23 @@
1
+ import type { VaultIndex } from "../../vault-index.js";
2
+ export declare function escapeRegex(str: string): string;
3
+ export declare function extractStem(filePath: string): string;
4
+ /**
5
+ * Build regex patterns to match wikilinks referencing a given file.
6
+ * Returns patterns ordered from most specific (full path) to least (stem only).
7
+ */
8
+ export declare function buildWikiLinkPatterns(ref: string, stem: string, includeEmbeds: boolean): RegExp[];
9
+ /**
10
+ * Get all markdown files from the vault index.
11
+ */
12
+ export declare function getAllMdFiles(vault: VaultIndex): string[];
13
+ export interface LinkMatch {
14
+ link: string;
15
+ line: number;
16
+ context: string;
17
+ is_embed: boolean;
18
+ }
19
+ /**
20
+ * Find all wikilink matches in a file's content for a given target.
21
+ */
22
+ export declare function findLinksInContent(content: string, ref: string, stem: string, includeEmbeds: boolean): LinkMatch[];
23
+ //# sourceMappingURL=link-utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"link-utils.d.ts","sourceRoot":"","sources":["../../../src/tools/links/link-utils.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAEvD,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAE/C;AAED,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEpD;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,MAAM,EACZ,aAAa,EAAE,OAAO,GACrB,MAAM,EAAE,CAwBV;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,EAAE,CAKzD;AAED,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,MAAM,EACf,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,MAAM,EACZ,aAAa,EAAE,OAAO,GACrB,SAAS,EAAE,CAyBb"}
@@ -0,0 +1,63 @@
1
+ import path from "node:path";
2
+ export function escapeRegex(str) {
3
+ return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
4
+ }
5
+ export function extractStem(filePath) {
6
+ return path.basename(filePath, path.extname(filePath));
7
+ }
8
+ /**
9
+ * Build regex patterns to match wikilinks referencing a given file.
10
+ * Returns patterns ordered from most specific (full path) to least (stem only).
11
+ */
12
+ export function buildWikiLinkPatterns(ref, stem, includeEmbeds) {
13
+ const prefix = includeEmbeds ? "!?\\[\\[" : "\\[\\[";
14
+ const escapedRef = escapeRegex(ref);
15
+ const escapedStem = escapeRegex(stem);
16
+ const patterns = [
17
+ // Full path reference: [[folder/filename]], [[folder/filename|alias]], [[folder/filename#heading]]
18
+ new RegExp(`(${prefix})${escapedRef}(#[^\\]|]*)?(?:\\|[^\\]]*)?\\]\\]`, "g"),
19
+ ];
20
+ // Stem-only reference (only if different from ref to avoid duplicate matches)
21
+ if (escapedStem !== escapedRef) {
22
+ patterns.push(new RegExp(`(${prefix})${escapedStem}(#[^\\]|]*)?(?:\\|[^\\]]*)?\\]\\]`, "g"));
23
+ }
24
+ return patterns;
25
+ }
26
+ /**
27
+ * Get all markdown files from the vault index.
28
+ */
29
+ export function getAllMdFiles(vault) {
30
+ return vault
31
+ .allFiles()
32
+ .filter((f) => f.ext === ".md")
33
+ .map((f) => f.rel);
34
+ }
35
+ /**
36
+ * Find all wikilink matches in a file's content for a given target.
37
+ */
38
+ export function findLinksInContent(content, ref, stem, includeEmbeds) {
39
+ const patterns = buildWikiLinkPatterns(ref, stem, includeEmbeds);
40
+ const lines = content.split("\n");
41
+ const matches = [];
42
+ const seen = new Set();
43
+ for (const pattern of patterns) {
44
+ for (let i = 0; i < lines.length; i++) {
45
+ let m;
46
+ const lineCopy = new RegExp(pattern.source, pattern.flags);
47
+ while ((m = lineCopy.exec(lines[i])) !== null) {
48
+ const key = `${i}:${m.index}`;
49
+ if (seen.has(key))
50
+ continue;
51
+ seen.add(key);
52
+ matches.push({
53
+ link: m[0],
54
+ line: i + 1,
55
+ context: lines[i].trim(),
56
+ is_embed: m[0].startsWith("!"),
57
+ });
58
+ }
59
+ }
60
+ }
61
+ return matches;
62
+ }
63
+ //# sourceMappingURL=link-utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"link-utils.js","sourceRoot":"","sources":["../../../src/tools/links/link-utils.ts"],"names":[],"mappings":"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"}