@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,140 @@
1
+ import { z } from "zod";
2
+ import { rename, mkdir } from "node:fs/promises";
3
+ import path from "node:path";
4
+ import { updateWikilinks } from "../links/update-links.js";
5
+ export function registerBatchRename(server, vaultPath, vault) {
6
+ server.tool("batch_rename", "Rename/move files. Explicit pairs or regex pattern on filenames. Auto-updates wikilinks. Dry run by default. Atomic fs.rename preserves metadata.", {
7
+ renames: z
8
+ .array(z.object({ from: z.string(), to: z.string() }))
9
+ .optional()
10
+ .describe("Mode 1: explicit rename pairs"),
11
+ pattern: z
12
+ .object({
13
+ directory: z.string().describe("Directory to search"),
14
+ match: z.string().describe("Regex applied to filename only"),
15
+ replace: z.string().describe("Replacement string ($1, $2 for capture groups)"),
16
+ filter_ext: z.string().default(".md").describe("Extension filter (default: .md)"),
17
+ recursive: z.boolean().default(false).describe("Search subdirectories"),
18
+ })
19
+ .optional()
20
+ .describe("Mode 2: regex pattern on filenames"),
21
+ dry_run: z.boolean().default(true).describe("Preview without executing (default: true)"),
22
+ update_links: z.boolean().default(true).describe("Auto-update wikilinks after rename (default: true)"),
23
+ }, async ({ renames, pattern, dry_run, update_links: doUpdateLinks }) => {
24
+ await vault.waitReady();
25
+ // Build rename pairs
26
+ let pairs = [];
27
+ if (renames && renames.length > 0) {
28
+ // Mode 1: explicit pairs
29
+ for (const r of renames) {
30
+ const fromResolved = vault.resolve(r.from);
31
+ const from = fromResolved?.rel ?? r.from;
32
+ pairs.push({ from, to: r.to });
33
+ }
34
+ }
35
+ else if (pattern) {
36
+ // Mode 2: regex on filenames
37
+ let regex;
38
+ try {
39
+ regex = new RegExp(pattern.match);
40
+ }
41
+ catch (err) {
42
+ return {
43
+ content: [{ type: "text", text: `ERROR: Invalid regex: ${err.message}` }],
44
+ isError: true,
45
+ };
46
+ }
47
+ const searchDir = pattern.directory;
48
+ const files = pattern.recursive
49
+ ? vault.searchPaths(searchDir === "." ? "" : searchDir)
50
+ : vault.listDir(searchDir);
51
+ for (const f of files) {
52
+ if (f.ext !== pattern.filter_ext)
53
+ continue;
54
+ const filename = path.basename(f.rel);
55
+ if (!regex.test(filename))
56
+ continue;
57
+ const newFilename = filename.replace(regex, pattern.replace);
58
+ if (newFilename === filename)
59
+ continue;
60
+ pairs.push({
61
+ from: f.rel,
62
+ to: path.join(path.dirname(f.rel), newFilename).replace(/\\/g, "/"),
63
+ });
64
+ }
65
+ }
66
+ else {
67
+ return {
68
+ content: [{ type: "text", text: "ERROR: Provide either 'renames' or 'pattern'" }],
69
+ isError: true,
70
+ };
71
+ }
72
+ if (pairs.length === 0) {
73
+ return {
74
+ content: [{ type: "text", text: JSON.stringify({ renamed: 0, results: [], dry_run, message: "No files matched" }, null, 2) }],
75
+ };
76
+ }
77
+ // Pre-flight: detect conflicts
78
+ const targets = new Set();
79
+ const sources = new Set();
80
+ for (const p of pairs) {
81
+ if (targets.has(p.to)) {
82
+ return {
83
+ content: [{ type: "text", text: `ERROR: Duplicate target: ${p.to}` }],
84
+ isError: true,
85
+ };
86
+ }
87
+ targets.add(p.to);
88
+ sources.add(p.from);
89
+ }
90
+ // Detect circular renames
91
+ for (const p of pairs) {
92
+ if (sources.has(p.to) && targets.has(p.from)) {
93
+ return {
94
+ content: [{ type: "text", text: `ERROR: Circular rename detected: ${p.from} ↔ ${p.to}` }],
95
+ isError: true,
96
+ };
97
+ }
98
+ }
99
+ // Check target conflicts with existing files
100
+ for (const p of pairs) {
101
+ if (!sources.has(p.to) && vault.has(p.to)) {
102
+ return {
103
+ content: [{ type: "text", text: `ERROR: Target already exists: ${p.to}` }],
104
+ isError: true,
105
+ };
106
+ }
107
+ }
108
+ const results = [];
109
+ let totalLinksUpdated = 0;
110
+ for (const p of pairs) {
111
+ let linksAffected = 0;
112
+ if (!dry_run) {
113
+ const fromAbs = path.join(vaultPath, p.from);
114
+ const toAbs = path.join(vaultPath, p.to);
115
+ await mkdir(path.dirname(toAbs), { recursive: true });
116
+ await rename(fromAbs, toAbs);
117
+ }
118
+ if (doUpdateLinks) {
119
+ const linkResult = await updateWikilinks(vaultPath, vault, p.from, p.to, dry_run);
120
+ linksAffected = linkResult.totalLinks;
121
+ totalLinksUpdated += linksAffected;
122
+ }
123
+ results.push({ from: p.from, to: p.to, links_affected: linksAffected });
124
+ }
125
+ return {
126
+ content: [
127
+ {
128
+ type: "text",
129
+ text: JSON.stringify({
130
+ renamed: pairs.length,
131
+ links_updated: totalLinksUpdated,
132
+ dry_run,
133
+ results,
134
+ }, null, 2),
135
+ },
136
+ ],
137
+ };
138
+ });
139
+ }
140
+ //# sourceMappingURL=batch-rename.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"batch-rename.js","sourceRoot":"","sources":["../../../src/tools/files/batch-rename.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import type { VaultIndex } from "../../vault-index.js";
3
+ interface ToolResult {
4
+ content: Array<{
5
+ type: "text";
6
+ text: string;
7
+ }>;
8
+ isError?: boolean;
9
+ }
10
+ export declare function handleDeleteFolder(vault: VaultIndex, vaultPath: string, args: {
11
+ path: string;
12
+ recursive: boolean;
13
+ permanent: boolean;
14
+ }): Promise<ToolResult>;
15
+ export declare function registerDeleteFolder(server: McpServer, vaultPath: string, vault: VaultIndex): void;
16
+ export {};
17
+ //# sourceMappingURL=delete-folder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"delete-folder.d.ts","sourceRoot":"","sources":["../../../src/tools/files/delete-folder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAIpE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AACvD,UAAU,UAAU;IAClB,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC/C,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAcD,wBAAsB,kBAAkB,CACtC,KAAK,EAAE,UAAU,EACjB,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,OAAO,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAAE,GAC7D,OAAO,CAAC,UAAU,CAAC,CAuErB;AAWD,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,SAAS,EACjB,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,UAAU,GAChB,IAAI,CAeN"}
@@ -0,0 +1,100 @@
1
+ import { z } from "zod";
2
+ import { mkdir, readdir, rm, rename as fsRename, stat } from "node:fs/promises";
3
+ import path from "node:path";
4
+ const PROTECTED_DIRS = [".obsidian", ".vaultforge", ".trash", ".git"];
5
+ function isProtected(relPath) {
6
+ const first = relPath.split("/")[0];
7
+ return PROTECTED_DIRS.includes(first);
8
+ }
9
+ function isVaultRoot(relPath) {
10
+ const norm = relPath.replace(/\\/g, "/").replace(/^\/+/, "").replace(/\/+$/, "");
11
+ return norm === "" || norm === ".";
12
+ }
13
+ export async function handleDeleteFolder(vault, vaultPath, args) {
14
+ const relPath = args.path.replace(/\\/g, "/").replace(/^\/+/, "").replace(/\/+$/, "");
15
+ if (isVaultRoot(relPath)) {
16
+ return { content: [{ type: "text", text: "ERROR: Refusing to delete vault root." }], isError: true };
17
+ }
18
+ if (isProtected(relPath)) {
19
+ return { content: [{ type: "text", text: `ERROR: Refusing to delete protected directory: ${relPath}` }], isError: true };
20
+ }
21
+ const absPath = path.join(vaultPath, relPath);
22
+ // Verify it exists and is a directory
23
+ try {
24
+ const st = await stat(absPath);
25
+ if (!st.isDirectory()) {
26
+ return { content: [{ type: "text", text: `ERROR: Not a directory: ${relPath}` }], isError: true };
27
+ }
28
+ }
29
+ catch {
30
+ return { content: [{ type: "text", text: `ERROR: Directory not found: ${relPath}` }], isError: true };
31
+ }
32
+ // Count children on disk
33
+ const childCount = await countDiskChildren(absPath);
34
+ if (!args.recursive && childCount > 0) {
35
+ return {
36
+ content: [{
37
+ type: "text",
38
+ text: `ERROR: Directory '${relPath}' is not empty (${childCount} children). Use recursive: true to delete with contents, or delete contents first.`,
39
+ }],
40
+ isError: true,
41
+ };
42
+ }
43
+ // Count index entries BEFORE deletion (fs.watch may clean up after rm)
44
+ const preRemoved = vault.removeDir(relPath);
45
+ let filesRemoved = preRemoved.filesRemoved;
46
+ let dirsRemoved = preRemoved.dirsRemoved;
47
+ if (dirsRemoved === 0)
48
+ dirsRemoved = 1; // at least the dir itself
49
+ if (!args.permanent) {
50
+ // Move to .trash preserving relative path
51
+ const trashDir = path.join(vaultPath, ".trash");
52
+ const trashDest = path.join(trashDir, relPath);
53
+ await mkdir(path.dirname(trashDest), { recursive: true });
54
+ try {
55
+ await fsRename(absPath, trashDest);
56
+ }
57
+ catch {
58
+ // Cross-device or other issue — copy then delete
59
+ const { cpSync } = await import("node:fs");
60
+ cpSync(absPath, trashDest, { recursive: true });
61
+ await rm(absPath, { recursive: true, force: true });
62
+ }
63
+ }
64
+ else {
65
+ await rm(absPath, { recursive: true, force: true });
66
+ }
67
+ return {
68
+ content: [{
69
+ type: "text",
70
+ text: JSON.stringify({
71
+ path: relPath,
72
+ deleted: true,
73
+ files_removed: filesRemoved,
74
+ dirs_removed: dirsRemoved,
75
+ permanent: args.permanent,
76
+ }, null, 2),
77
+ }],
78
+ };
79
+ }
80
+ async function countDiskChildren(absDir) {
81
+ try {
82
+ const entries = await readdir(absDir);
83
+ return entries.length;
84
+ }
85
+ catch {
86
+ return 0;
87
+ }
88
+ }
89
+ export function registerDeleteFolder(server, vaultPath, vault) {
90
+ server.tool("delete_folder", "Delete a directory from the vault. Refuses non-empty dirs by default — use recursive: true for non-empty. Moves to .trash by default for safety.", {
91
+ path: z.string().describe("Relative path to the directory"),
92
+ recursive: z.boolean().default(false).describe("If true, delete the folder AND all contents. If false (default), refuse non-empty directories."),
93
+ permanent: z.boolean().default(false).describe("If true, skip .trash and delete permanently. If false (default), move to .trash."),
94
+ }, async ({ path: dirPath, recursive, permanent }) => {
95
+ await vault.waitReady();
96
+ const result = await handleDeleteFolder(vault, vaultPath, { path: dirPath, recursive, permanent });
97
+ return { ...result };
98
+ });
99
+ }
100
+ //# sourceMappingURL=delete-folder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"delete-folder.js","sourceRoot":"","sources":["../../../src/tools/files/delete-folder.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import type { VaultIndex } from "../../vault-index.js";
3
+ interface ToolResult {
4
+ content: Array<{
5
+ type: "text";
6
+ text: string;
7
+ }>;
8
+ isError?: boolean;
9
+ }
10
+ export declare function handlePruneEmptyDirs(vault: VaultIndex, vaultPath: string, args: {
11
+ path: string;
12
+ dry_run: boolean;
13
+ exclude: string[];
14
+ }): Promise<ToolResult>;
15
+ export declare function registerPruneEmptyDirs(server: McpServer, vaultPath: string, vault: VaultIndex): void;
16
+ export {};
17
+ //# sourceMappingURL=prune-empty-dirs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prune-empty-dirs.d.ts","sourceRoot":"","sources":["../../../src/tools/files/prune-empty-dirs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAIpE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AACvD,UAAU,UAAU;IAClB,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC/C,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAID,wBAAsB,oBAAoB,CACxC,KAAK,EAAE,UAAU,EACjB,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,GAC1D,OAAO,CAAC,UAAU,CAAC,CAiGrB;AAED,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,SAAS,EACjB,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,UAAU,GAChB,IAAI,CAeN"}
@@ -0,0 +1,106 @@
1
+ import { z } from "zod";
2
+ import { readdir, rm, rmdir } from "node:fs/promises";
3
+ import path from "node:path";
4
+ const DEFAULT_EXCLUDES = [".obsidian", ".vaultforge", ".trash", ".git", "Templates"];
5
+ export async function handlePruneEmptyDirs(vault, vaultPath, args) {
6
+ const relStart = args.path.replace(/\\/g, "/").replace(/^\/+/, "").replace(/\/+$/, "") || ".";
7
+ const absStart = relStart === "." ? vaultPath : path.join(vaultPath, relStart);
8
+ const excludeSet = new Set(args.exclude);
9
+ const emptyDirs = [];
10
+ let scanned = 0;
11
+ // Post-order traversal: returns true if the directory is empty after pruning
12
+ async function walk(absDir, relDir) {
13
+ scanned++;
14
+ let entries;
15
+ try {
16
+ entries = await readdir(absDir, { withFileTypes: true });
17
+ }
18
+ catch {
19
+ return false;
20
+ }
21
+ let hasFiles = false;
22
+ let hasNonEmptySubdirs = false;
23
+ let excludedChildCount = 0;
24
+ for (const entry of entries) {
25
+ // Skip hidden files (but let hidden directories through to the excludeSet check)
26
+ if (entry.name.startsWith(".") && !entry.isDirectory())
27
+ continue;
28
+ if (entry.name.startsWith(".") && entry.isDirectory() && !excludeSet.has(entry.name))
29
+ continue;
30
+ const childRel = relDir === "." ? entry.name : `${relDir}/${entry.name}`;
31
+ const childAbs = path.join(absDir, entry.name);
32
+ if (entry.isDirectory()) {
33
+ // Skip excluded directories — track them separately
34
+ if (excludeSet.has(entry.name)) {
35
+ excludedChildCount++;
36
+ continue;
37
+ }
38
+ const childEmpty = await walk(childAbs, childRel);
39
+ if (!childEmpty) {
40
+ hasNonEmptySubdirs = true;
41
+ }
42
+ }
43
+ else {
44
+ hasFiles = true;
45
+ }
46
+ }
47
+ if (!hasFiles && !hasNonEmptySubdirs && relDir !== ".") {
48
+ if (excludedChildCount > 0) {
49
+ emptyDirs.push({ path: relDir, reason: "Only contains excluded directories" });
50
+ return true;
51
+ }
52
+ const reason = entries.length === 0
53
+ ? "No files or subdirectories"
54
+ : "Only contained empty subdirectories (pruned)";
55
+ emptyDirs.push({ path: relDir, reason });
56
+ return true;
57
+ }
58
+ return false;
59
+ }
60
+ await walk(absStart, relStart);
61
+ // Sort bottom-up (deepest paths first) for deletion order
62
+ emptyDirs.sort((a, b) => b.path.split("/").length - a.path.split("/").length);
63
+ let deleted = 0;
64
+ if (!args.dry_run) {
65
+ for (const dir of emptyDirs) {
66
+ const absDir = path.join(vaultPath, dir.path);
67
+ try {
68
+ if (dir.reason.includes("excluded directories")) {
69
+ await rm(absDir, { recursive: true, force: true });
70
+ }
71
+ else {
72
+ await rmdir(absDir);
73
+ }
74
+ vault.removeDir(dir.path);
75
+ deleted++;
76
+ }
77
+ catch {
78
+ // Directory may have been already removed or not actually empty
79
+ }
80
+ }
81
+ }
82
+ return {
83
+ content: [{
84
+ type: "text",
85
+ text: JSON.stringify({
86
+ scanned,
87
+ empty_found: emptyDirs.length,
88
+ deleted: args.dry_run ? 0 : deleted,
89
+ dry_run: args.dry_run,
90
+ directories: emptyDirs,
91
+ }, null, 2),
92
+ }],
93
+ };
94
+ }
95
+ export function registerPruneEmptyDirs(server, vaultPath, vault) {
96
+ server.tool("prune_empty_dirs", "Find and remove empty directories in the vault. Dry run by default — preview before deleting. Bottom-up pruning handles cascading empty dirs.", {
97
+ path: z.string().default(".").describe("Starting directory to scan (default: vault root)"),
98
+ dry_run: z.boolean().default(true).describe("Preview without deleting (default: true). Set false to execute."),
99
+ exclude: z.array(z.string()).default(DEFAULT_EXCLUDES).describe("Directories to skip (default: .obsidian, .vaultforge, .trash, .git, Templates)"),
100
+ }, async ({ path: dirPath, dry_run, exclude }) => {
101
+ await vault.waitReady();
102
+ const result = await handlePruneEmptyDirs(vault, vaultPath, { path: dirPath, dry_run, exclude });
103
+ return { ...result };
104
+ });
105
+ }
106
+ //# sourceMappingURL=prune-empty-dirs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prune-empty-dirs.js","sourceRoot":"","sources":["../../../src/tools/files/prune-empty-dirs.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Agglomerative clustering using Jaccard similarity on TF-IDF fingerprints.
3
+ */
4
+ import type { FileFingerprint } from "./tfidf.js";
5
+ export interface ThemeCluster {
6
+ id: string;
7
+ label: string;
8
+ keyTerms: string[];
9
+ files: string[];
10
+ folders: string[];
11
+ coherenceScore: number;
12
+ crossFolder: boolean;
13
+ }
14
+ export declare function clusterFiles(fingerprints: FileFingerprint[], minClusterSize?: number, similarityThreshold?: number): ThemeCluster[];
15
+ //# sourceMappingURL=clustering.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clustering.d.ts","sourceRoot":"","sources":["../../../src/tools/intelligence/clustering.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAElD,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,OAAO,CAAC;CACtB;AAgCD,wBAAgB,YAAY,CAC1B,YAAY,EAAE,eAAe,EAAE,EAC/B,cAAc,GAAE,MAAU,EAC1B,mBAAmB,GAAE,MAAa,GACjC,YAAY,EAAE,CAuEhB"}
@@ -0,0 +1,93 @@
1
+ /**
2
+ * Agglomerative clustering using Jaccard similarity on TF-IDF fingerprints.
3
+ */
4
+ function capitalize(s) {
5
+ return s.charAt(0).toUpperCase() + s.slice(1);
6
+ }
7
+ function jaccardSimilarity(a, b) {
8
+ let intersection = 0;
9
+ for (const t of a) {
10
+ if (b.has(t))
11
+ intersection++;
12
+ }
13
+ const union = a.size + b.size - intersection;
14
+ return union === 0 ? 0 : intersection / union;
15
+ }
16
+ function computeAverageJaccard(files) {
17
+ if (files.length < 2)
18
+ return 1.0;
19
+ const termSets = files.map((f) => new Set(f.topTerms.map((t) => t.term)));
20
+ let totalSim = 0;
21
+ let pairs = 0;
22
+ for (let i = 0; i < termSets.length; i++) {
23
+ for (let j = i + 1; j < termSets.length; j++) {
24
+ totalSim += jaccardSimilarity(termSets[i], termSets[j]);
25
+ pairs++;
26
+ }
27
+ }
28
+ return pairs > 0 ? totalSim / pairs : 0;
29
+ }
30
+ export function clusterFiles(fingerprints, minClusterSize = 3, similarityThreshold = 0.15) {
31
+ let clusters = fingerprints.map((fp) => ({
32
+ files: [fp],
33
+ terms: new Set(fp.topTerms.map((t) => t.term)),
34
+ }));
35
+ // Agglomerative merging
36
+ while (true) {
37
+ let bestSim = 0;
38
+ let bestI = -1;
39
+ let bestJ = -1;
40
+ for (let i = 0; i < clusters.length; i++) {
41
+ for (let j = i + 1; j < clusters.length; j++) {
42
+ const sim = jaccardSimilarity(clusters[i].terms, clusters[j].terms);
43
+ if (sim > bestSim) {
44
+ bestSim = sim;
45
+ bestI = i;
46
+ bestJ = j;
47
+ }
48
+ }
49
+ }
50
+ if (bestSim < similarityThreshold || bestI === -1)
51
+ break;
52
+ // Merge bestJ into bestI
53
+ clusters[bestI].files.push(...clusters[bestJ].files);
54
+ for (const t of clusters[bestJ].terms) {
55
+ clusters[bestI].terms.add(t);
56
+ }
57
+ clusters.splice(bestJ, 1);
58
+ }
59
+ // Convert to ThemeCluster
60
+ return clusters
61
+ .filter((c) => c.files.length >= minClusterSize)
62
+ .map((c) => {
63
+ const folders = [...new Set(c.files.map((f) => f.folder))];
64
+ // Label = top terms by combined TF-IDF
65
+ const termScores = new Map();
66
+ for (const file of c.files) {
67
+ for (const { term, tfidf } of file.topTerms) {
68
+ termScores.set(term, (termScores.get(term) || 0) + tfidf);
69
+ }
70
+ }
71
+ const sortedTerms = [...termScores.entries()]
72
+ .sort(([, a], [, b]) => b - a)
73
+ .slice(0, 6);
74
+ const coherence = computeAverageJaccard(c.files);
75
+ return {
76
+ id: sortedTerms
77
+ .slice(0, 2)
78
+ .map(([t]) => t)
79
+ .join("-"),
80
+ label: sortedTerms
81
+ .slice(0, 3)
82
+ .map(([t]) => capitalize(t))
83
+ .join(" + "),
84
+ keyTerms: sortedTerms.map(([t]) => t),
85
+ files: c.files.map((f) => f.path),
86
+ folders,
87
+ coherenceScore: Math.round(coherence * 100) / 100,
88
+ crossFolder: folders.length > 1,
89
+ };
90
+ })
91
+ .sort((a, b) => b.files.length - a.files.length);
92
+ }
93
+ //# sourceMappingURL=clustering.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clustering.js","sourceRoot":"","sources":["../../../src/tools/intelligence/clustering.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * TF-IDF computation and tokenizer for vault theme extraction.
3
+ */
4
+ export declare function tokenize(text: string): string[];
5
+ export interface FileFingerprint {
6
+ path: string;
7
+ title: string;
8
+ topTerms: Array<{
9
+ term: string;
10
+ tfidf: number;
11
+ }>;
12
+ folder: string;
13
+ }
14
+ export declare function computeTfIdf(files: Array<{
15
+ path: string;
16
+ title: string;
17
+ content: string;
18
+ }>): FileFingerprint[];
19
+ //# sourceMappingURL=tfidf.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tfidf.d.ts","sourceRoot":"","sources":["../../../src/tools/intelligence/tfidf.ts"],"names":[],"mappings":"AAAA;;GAEG;AAqBH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAM/C;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACjD,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,YAAY,CAC1B,KAAK,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,GAC7D,eAAe,EAAE,CA4CnB"}
@@ -0,0 +1,68 @@
1
+ /**
2
+ * TF-IDF computation and tokenizer for vault theme extraction.
3
+ */
4
+ const STOP_WORDS = new Set([
5
+ // English
6
+ "the", "a", "an", "is", "are", "was", "were", "be", "been", "being",
7
+ "have", "has", "had", "do", "does", "did", "will", "would", "could",
8
+ "should", "may", "might", "can", "shall", "to", "of", "in", "for",
9
+ "on", "with", "at", "by", "from", "this", "that", "these", "those",
10
+ "it", "its", "not", "but", "and", "or", "if", "then", "so", "as",
11
+ "than", "too", "very", "just", "about", "up", "out", "no", "all",
12
+ "also", "each", "which", "their", "there", "them", "they", "we",
13
+ "you", "your", "our", "my", "me", "he", "she", "his", "her",
14
+ // Portuguese
15
+ "de", "da", "do", "das", "dos", "em", "no", "na", "nos", "nas",
16
+ "um", "uma", "uns", "umas", "para", "por", "com", "sem", "sob",
17
+ "que", "se", "como", "mas", "ou", "e", "não", "mais", "muito",
18
+ "também", "já", "ainda", "só", "ao", "aos", "à", "às", "é", "são",
19
+ "foi", "ser", "ter", "está", "este", "esta", "esse", "essa", "isso",
20
+ "ele", "ela", "eles", "elas", "seu", "sua", "seus", "suas",
21
+ ]);
22
+ export function tokenize(text) {
23
+ return text
24
+ .toLowerCase()
25
+ .replace(/[^a-záàâãéèêíïóôõöúçñ0-9\s-]/gi, " ")
26
+ .split(/\s+/)
27
+ .filter((t) => t.length > 2 && !STOP_WORDS.has(t));
28
+ }
29
+ export function computeTfIdf(files) {
30
+ // Step 1: tokenize all files
31
+ const tokenized = files.map((f) => ({
32
+ ...f,
33
+ tokens: tokenize(f.content),
34
+ }));
35
+ const totalFiles = tokenized.length;
36
+ if (totalFiles === 0)
37
+ return [];
38
+ // Step 2: document frequency
39
+ const df = new Map();
40
+ for (const file of tokenized) {
41
+ const uniqueTerms = new Set(file.tokens);
42
+ for (const term of uniqueTerms) {
43
+ df.set(term, (df.get(term) || 0) + 1);
44
+ }
45
+ }
46
+ // Step 3: TF-IDF per file
47
+ return tokenized.map((file) => {
48
+ const termCounts = new Map();
49
+ for (const token of file.tokens) {
50
+ termCounts.set(token, (termCounts.get(token) || 0) + 1);
51
+ }
52
+ const totalTerms = file.tokens.length || 1;
53
+ const tfidfScores = [];
54
+ for (const [term, count] of termCounts) {
55
+ const tf = count / totalTerms;
56
+ const idf = Math.log(totalFiles / (df.get(term) || 1));
57
+ tfidfScores.push({ term, tfidf: tf * idf });
58
+ }
59
+ tfidfScores.sort((a, b) => b.tfidf - a.tfidf);
60
+ return {
61
+ path: file.path,
62
+ title: file.title,
63
+ topTerms: tfidfScores.slice(0, 15),
64
+ folder: file.path.split("/").slice(0, -1).join("/"),
65
+ };
66
+ });
67
+ }
68
+ //# sourceMappingURL=tfidf.js.map