@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,16 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import type { VaultIndex } from "../../vault-index.js";
3
+ /**
4
+ * Core link-update logic, exported so batch_rename can call it directly.
5
+ */
6
+ export declare function updateWikilinks(vaultPath: string, vault: VaultIndex, oldPath: string, newPath: string, dryRun: boolean): Promise<{
7
+ filesScanned: number;
8
+ filesUpdated: number;
9
+ totalLinks: number;
10
+ results: Array<{
11
+ path: string;
12
+ links_updated: number;
13
+ }>;
14
+ }>;
15
+ export declare function registerUpdateLinks(server: McpServer, vaultPath: string, vault: VaultIndex): void;
16
+ //# sourceMappingURL=update-links.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"update-links.d.ts","sourceRoot":"","sources":["../../../src/tools/links/update-links.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAIpE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAGvD;;GAEG;AACH,wBAAsB,eAAe,CACnC,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,UAAU,EACjB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,OAAO,GACd,OAAO,CAAC;IAAE,YAAY,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAAE,CAAC,CA6DtI;AAED,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,SAAS,EACjB,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,UAAU,GAChB,IAAI,CAoCN"}
@@ -0,0 +1,88 @@
1
+ import { z } from "zod";
2
+ import { readFile, writeFile } from "node:fs/promises";
3
+ import path from "node:path";
4
+ import { escapeRegex, extractStem, getAllMdFiles } from "./link-utils.js";
5
+ /**
6
+ * Core link-update logic, exported so batch_rename can call it directly.
7
+ */
8
+ export async function updateWikilinks(vaultPath, vault, oldPath, newPath, dryRun) {
9
+ const oldRef = oldPath.replace(/\.md$/, "");
10
+ const newRef = newPath.replace(/\.md$/, "");
11
+ const oldStem = extractStem(oldPath);
12
+ const newStem = extractStem(newPath);
13
+ const escapedRef = escapeRegex(oldRef);
14
+ const escapedStem = escapeRegex(oldStem);
15
+ // Build replacement patterns (full path first, then stem)
16
+ const patterns = [
17
+ {
18
+ regex: new RegExp(`(!?\\[\\[)${escapedRef}(#[^\\]|]*)?(?:\\|([^\\]]*))?\\]\\]`, "g"),
19
+ newBase: newRef,
20
+ },
21
+ ];
22
+ if (oldStem !== newStem) {
23
+ patterns.push({
24
+ regex: new RegExp(`(!?\\[\\[)${escapedStem}(#[^\\]|]*)?(?:\\|([^\\]]*))?\\]\\]`, "g"),
25
+ newBase: newStem,
26
+ });
27
+ }
28
+ const allFiles = getAllMdFiles(vault);
29
+ const results = [];
30
+ let totalLinks = 0;
31
+ for (const filePath of allFiles) {
32
+ if (filePath === newPath)
33
+ continue;
34
+ const fullPath = path.join(vaultPath, filePath);
35
+ let content;
36
+ try {
37
+ content = await readFile(fullPath, "utf-8");
38
+ }
39
+ catch {
40
+ continue;
41
+ }
42
+ let newContent = content;
43
+ let matchCount = 0;
44
+ for (const { regex, newBase } of patterns) {
45
+ newContent = newContent.replace(regex, (_match, prefix, heading, alias) => {
46
+ matchCount++;
47
+ const h = heading || "";
48
+ const a = alias !== undefined ? `|${alias}` : "";
49
+ return `${prefix}${newBase}${h}${a}]]`;
50
+ });
51
+ }
52
+ if (matchCount > 0) {
53
+ totalLinks += matchCount;
54
+ results.push({ path: filePath, links_updated: matchCount });
55
+ if (!dryRun) {
56
+ await writeFile(fullPath, newContent, "utf-8");
57
+ }
58
+ }
59
+ }
60
+ return { filesScanned: allFiles.length, filesUpdated: results.length, totalLinks, results };
61
+ }
62
+ export function registerUpdateLinks(server, vaultPath, vault) {
63
+ server.tool("update_links", "Update all wikilinks across vault after moving/renaming a file. Handles all link forms: stem, path, alias, heading, embed. Dry run by default.", {
64
+ old_path: z.string().describe("File path before the move/rename"),
65
+ new_path: z.string().describe("File path after the move/rename"),
66
+ dry_run: z.boolean().default(true).describe("Preview changes without writing (default: true)"),
67
+ }, async ({ old_path, new_path, dry_run }) => {
68
+ await vault.waitReady();
69
+ const result = await updateWikilinks(vaultPath, vault, old_path, new_path, dry_run);
70
+ return {
71
+ content: [
72
+ {
73
+ type: "text",
74
+ text: JSON.stringify({
75
+ old_path,
76
+ new_path,
77
+ files_scanned: result.filesScanned,
78
+ files_updated: result.filesUpdated,
79
+ total_links_updated: result.totalLinks,
80
+ dry_run,
81
+ results: result.results,
82
+ }, null, 2),
83
+ },
84
+ ],
85
+ };
86
+ });
87
+ }
88
+ //# sourceMappingURL=update-links.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"update-links.js","sourceRoot":"","sources":["../../../src/tools/links/update-links.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import type { VaultIndex } from "../../vault-index.js";
3
+ interface ParsedFrontmatter {
4
+ frontmatter: Record<string, any>;
5
+ body: string;
6
+ hasFrontmatter: boolean;
7
+ }
8
+ export declare function parseFrontmatter(content: string): ParsedFrontmatter;
9
+ export declare function serializeFrontmatter(fm: Record<string, any>): string;
10
+ export declare function registerFrontmatter(server: McpServer, vaultPath: string, vault: VaultIndex): void;
11
+ export {};
12
+ //# sourceMappingURL=frontmatter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"frontmatter.d.ts","sourceRoot":"","sources":["../../../src/tools/metadata/frontmatter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAIpE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAEvD,UAAU,iBAAiB;IACzB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,cAAc,EAAE,OAAO,CAAC;CACzB;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,iBAAiB,CA6DnE;AAUD,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM,CAUpE;AAED,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,SAAS,EACjB,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,UAAU,GAChB,IAAI,CA+HN"}
@@ -0,0 +1,190 @@
1
+ import { z } from "zod";
2
+ import { readFile, writeFile } from "node:fs/promises";
3
+ import path from "node:path";
4
+ export function parseFrontmatter(content) {
5
+ // Normalize CRLF → LF for cross-platform compatibility
6
+ const normalized = content.replace(/\r\n/g, "\n");
7
+ const match = normalized.match(/^---\n([\s\S]*?)\n---\n?([\s\S]*)$/);
8
+ if (!match)
9
+ return { frontmatter: {}, body: content, hasFrontmatter: false };
10
+ const fm = {};
11
+ let currentKey = null;
12
+ let listItems = [];
13
+ let inList = false;
14
+ for (const line of match[1].split("\n")) {
15
+ // YAML list item (- value)
16
+ if (inList && /^\s+-\s+(.*)$/.test(line)) {
17
+ const itemMatch = line.match(/^\s+-\s+(.*)$/);
18
+ if (itemMatch) {
19
+ listItems.push(parseYamlValue(itemMatch[1].trim()));
20
+ }
21
+ continue;
22
+ }
23
+ // If we were collecting list items, save them
24
+ if (inList && currentKey) {
25
+ fm[currentKey] = listItems;
26
+ inList = false;
27
+ listItems = [];
28
+ currentKey = null;
29
+ }
30
+ const kv = line.match(/^([\w][\w-]*):\s*(.*)$/);
31
+ if (!kv)
32
+ continue;
33
+ const key = kv[1];
34
+ const rawValue = kv[2].trim();
35
+ // Check if this is the start of a YAML list
36
+ if (rawValue === "") {
37
+ currentKey = key;
38
+ inList = true;
39
+ listItems = [];
40
+ continue;
41
+ }
42
+ // Inline array: [val1, val2]
43
+ if (rawValue.startsWith("[") && rawValue.endsWith("]")) {
44
+ fm[key] = rawValue
45
+ .slice(1, -1)
46
+ .split(",")
47
+ .map((v) => parseYamlValue(v.trim()))
48
+ .filter((v) => v !== "");
49
+ }
50
+ else {
51
+ fm[key] = parseYamlValue(rawValue);
52
+ }
53
+ }
54
+ // Final list collection
55
+ if (inList && currentKey) {
56
+ fm[currentKey] = listItems;
57
+ }
58
+ return { frontmatter: fm, body: match[2], hasFrontmatter: true };
59
+ }
60
+ function parseYamlValue(value) {
61
+ if (/^\d+(\.\d+)?$/.test(value))
62
+ return Number(value);
63
+ if (value === "true")
64
+ return true;
65
+ if (value === "false")
66
+ return false;
67
+ // Strip surrounding quotes
68
+ return value.replace(/^["']|["']$/g, "");
69
+ }
70
+ export function serializeFrontmatter(fm) {
71
+ const lines = [];
72
+ for (const [key, value] of Object.entries(fm)) {
73
+ if (Array.isArray(value)) {
74
+ lines.push(`${key}: [${value.join(", ")}]`);
75
+ }
76
+ else {
77
+ lines.push(`${key}: ${value}`);
78
+ }
79
+ }
80
+ return `---\n${lines.join("\n")}\n---`;
81
+ }
82
+ export function registerFrontmatter(server, vaultPath, vault) {
83
+ server.tool("frontmatter", "Read/write/merge YAML frontmatter as structured data. No string parsing needed. Actions: read, set, merge, delete_keys.", {
84
+ path: z.string().describe("File path or stem"),
85
+ action: z.enum(["read", "set", "merge", "delete_keys"]).describe("Operation to perform"),
86
+ data: z.record(z.string(), z.any()).optional().describe("Data for set/merge actions"),
87
+ keys: z.array(z.string()).optional().describe("Keys to remove for delete_keys action"),
88
+ }, async ({ path: filePath, action, data, keys }) => {
89
+ await vault.waitReady();
90
+ const resolved = vault.resolve(filePath);
91
+ const rel = resolved?.rel ?? filePath;
92
+ const fullPath = path.join(vaultPath, rel);
93
+ let content;
94
+ try {
95
+ content = await readFile(fullPath, "utf-8");
96
+ }
97
+ catch {
98
+ if (action === "read") {
99
+ return {
100
+ content: [{ type: "text", text: `ERROR: File not found: ${rel}` }],
101
+ isError: true,
102
+ };
103
+ }
104
+ // For write actions, start with empty content
105
+ content = "";
106
+ }
107
+ const parsed = parseFrontmatter(content);
108
+ switch (action) {
109
+ case "read": {
110
+ return {
111
+ content: [
112
+ {
113
+ type: "text",
114
+ text: JSON.stringify({
115
+ path: rel,
116
+ frontmatter: parsed.frontmatter,
117
+ has_frontmatter: parsed.hasFrontmatter,
118
+ }, null, 2),
119
+ },
120
+ ],
121
+ };
122
+ }
123
+ case "set": {
124
+ if (!data) {
125
+ return {
126
+ content: [{ type: "text", text: "ERROR: 'data' is required for 'set' action" }],
127
+ isError: true,
128
+ };
129
+ }
130
+ const before = { ...parsed.frontmatter };
131
+ const newContent = serializeFrontmatter(data) + "\n" + parsed.body;
132
+ await writeFile(fullPath, newContent, "utf-8");
133
+ return {
134
+ content: [
135
+ {
136
+ type: "text",
137
+ text: JSON.stringify({ path: rel, action: "set", before, after: data }, null, 2),
138
+ },
139
+ ],
140
+ };
141
+ }
142
+ case "merge": {
143
+ if (!data) {
144
+ return {
145
+ content: [{ type: "text", text: "ERROR: 'data' is required for 'merge' action" }],
146
+ isError: true,
147
+ };
148
+ }
149
+ const before = { ...parsed.frontmatter };
150
+ const merged = { ...parsed.frontmatter, ...data };
151
+ const newContent = serializeFrontmatter(merged) + "\n" + parsed.body;
152
+ await writeFile(fullPath, newContent, "utf-8");
153
+ return {
154
+ content: [
155
+ {
156
+ type: "text",
157
+ text: JSON.stringify({ path: rel, action: "merge", before, after: merged }, null, 2),
158
+ },
159
+ ],
160
+ };
161
+ }
162
+ case "delete_keys": {
163
+ if (!keys || keys.length === 0) {
164
+ return {
165
+ content: [{ type: "text", text: "ERROR: 'keys' is required for 'delete_keys' action" }],
166
+ isError: true,
167
+ };
168
+ }
169
+ const before = { ...parsed.frontmatter };
170
+ const updated = { ...parsed.frontmatter };
171
+ for (const key of keys) {
172
+ delete updated[key];
173
+ }
174
+ const newContent = Object.keys(updated).length > 0
175
+ ? serializeFrontmatter(updated) + "\n" + parsed.body
176
+ : parsed.body;
177
+ await writeFile(fullPath, newContent, "utf-8");
178
+ return {
179
+ content: [
180
+ {
181
+ type: "text",
182
+ text: JSON.stringify({ path: rel, action: "delete_keys", keys_deleted: keys, before, after: updated }, null, 2),
183
+ },
184
+ ],
185
+ };
186
+ }
187
+ }
188
+ });
189
+ }
190
+ //# sourceMappingURL=frontmatter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"frontmatter.js","sourceRoot":"","sources":["../../../src/tools/metadata/frontmatter.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 registerEditRegex(server: McpServer, vaultPath: string, vault: VaultIndex): void;
4
+ //# sourceMappingURL=edit-regex.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"edit-regex.d.ts","sourceRoot":"","sources":["../../../src/tools/notes/edit-regex.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAIpE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAEvD,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,SAAS,EACjB,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,UAAU,GAChB,IAAI,CA+JN"}
@@ -0,0 +1,142 @@
1
+ import { z } from "zod";
2
+ import { readFile, writeFile } from "node:fs/promises";
3
+ import path from "node:path";
4
+ export function registerEditRegex(server, vaultPath, vault) {
5
+ server.tool("edit_regex", "Regex find-and-replace. Single file or grep-sub across a directory. Supports capture groups ($1, $2). Dry run by default.", {
6
+ path: z.string().optional().describe("Single file path (mutually exclusive with search_paths)"),
7
+ search_paths: z
8
+ .object({
9
+ directory: z.string().describe("Directory to search"),
10
+ recursive: z.boolean().default(false).describe("Include subdirectories"),
11
+ pattern_filter: z.string().default("*.md").describe("Glob pattern to filter files (default: *.md)"),
12
+ })
13
+ .optional()
14
+ .describe("Multi-file mode: search across directory"),
15
+ match: z.string().describe("Regex pattern to find"),
16
+ replace: z.string().describe("Replacement string ($1, $2 for capture groups)"),
17
+ flags: z.string().default("g").describe("Regex flags (default: 'g')"),
18
+ max_replacements: z.number().optional().describe("Safety cap: max replacements per file"),
19
+ dry_run: z.boolean().default(true).describe("Preview changes without writing (default: true)"),
20
+ }, async ({ path: filePath, search_paths, match, replace, flags, max_replacements, dry_run }) => {
21
+ await vault.waitReady();
22
+ // Validate regex
23
+ let regex;
24
+ try {
25
+ regex = new RegExp(match, flags);
26
+ }
27
+ catch (err) {
28
+ return {
29
+ content: [{ type: "text", text: `ERROR: Invalid regex: ${err.message}` }],
30
+ isError: true,
31
+ };
32
+ }
33
+ // Build file list
34
+ let filePaths = [];
35
+ if (filePath) {
36
+ const resolved = vault.resolve(filePath);
37
+ filePaths = [resolved?.rel ?? filePath];
38
+ }
39
+ else if (search_paths) {
40
+ const dir = search_paths.directory;
41
+ let files = search_paths.recursive
42
+ ? vault.searchPaths(dir === "." ? "" : dir)
43
+ : vault.listDir(dir);
44
+ // Apply glob filter
45
+ if (search_paths.pattern_filter && search_paths.pattern_filter !== "*") {
46
+ const extMatch = search_paths.pattern_filter.match(/^\*(\.\w+)$/);
47
+ if (extMatch) {
48
+ files = files.filter((f) => f.ext === extMatch[1]);
49
+ }
50
+ }
51
+ filePaths = files.map((f) => f.rel);
52
+ }
53
+ else {
54
+ return {
55
+ content: [{ type: "text", text: "ERROR: Provide either 'path' or 'search_paths'" }],
56
+ isError: true,
57
+ };
58
+ }
59
+ const results = [];
60
+ let totalMatches = 0;
61
+ let totalReplacements = 0;
62
+ // Ensure 'g' flag for matchAll
63
+ const matchAllFlags = flags.includes("g") ? flags : flags + "g";
64
+ const matchAllRegex = new RegExp(match, matchAllFlags);
65
+ for (const fp of filePaths) {
66
+ const fullPath = path.join(vaultPath, fp);
67
+ let content;
68
+ try {
69
+ content = await readFile(fullPath, "utf-8");
70
+ }
71
+ catch {
72
+ continue;
73
+ }
74
+ // Skip binary-looking files
75
+ if (content.includes("\0"))
76
+ continue;
77
+ const matches = [...content.matchAll(matchAllRegex)];
78
+ if (matches.length === 0)
79
+ continue;
80
+ let effectiveMatches = matches;
81
+ if (max_replacements && matches.length > max_replacements) {
82
+ effectiveMatches = matches.slice(0, max_replacements);
83
+ }
84
+ const replacementsCount = effectiveMatches.length;
85
+ totalMatches += matches.length;
86
+ totalReplacements += replacementsCount;
87
+ // Generate preview (first 3 matches)
88
+ const singleRegex = new RegExp(match, flags.replace("g", ""));
89
+ const preview = effectiveMatches.slice(0, 3).map((m) => {
90
+ const start = Math.max(0, m.index - 30);
91
+ const end = Math.min(content.length, m.index + m[0].length + 30);
92
+ return {
93
+ original: m[0],
94
+ replaced: m[0].replace(singleRegex, replace),
95
+ context: content.slice(start, end),
96
+ };
97
+ });
98
+ results.push({
99
+ path: fp,
100
+ matches_found: matches.length,
101
+ replacements_made: replacementsCount,
102
+ preview,
103
+ });
104
+ if (!dry_run) {
105
+ let newContent;
106
+ if (max_replacements) {
107
+ // Limited replacements: replace only up to max
108
+ let count = 0;
109
+ newContent = content.replace(regex, (match, ...args) => {
110
+ if (count >= max_replacements)
111
+ return match;
112
+ count++;
113
+ // Reconstruct replacement with capture groups
114
+ return match.replace(singleRegex, replace);
115
+ });
116
+ }
117
+ else {
118
+ newContent = content.replace(regex, replace);
119
+ }
120
+ if (newContent !== content) {
121
+ await writeFile(fullPath, newContent, "utf-8");
122
+ }
123
+ }
124
+ }
125
+ return {
126
+ content: [
127
+ {
128
+ type: "text",
129
+ text: JSON.stringify({
130
+ files_searched: filePaths.length,
131
+ files_matched: results.length,
132
+ total_matches: totalMatches,
133
+ total_replacements: totalReplacements,
134
+ dry_run,
135
+ results,
136
+ }, null, 2),
137
+ },
138
+ ],
139
+ };
140
+ });
141
+ }
142
+ //# sourceMappingURL=edit-regex.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"edit-regex.js","sourceRoot":"","sources":["../../../src/tools/notes/edit-regex.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Markdown parsing utilities for search indexing.
3
+ * Strips syntax for BM25 scoring, extracts frontmatter and headings.
4
+ */
5
+ export declare function stripMarkdown(content: string): string;
6
+ export declare function extractFrontmatter(content: string): {
7
+ tags: string;
8
+ aliases: string;
9
+ };
10
+ export declare function extractHeadings(content: string): string;
11
+ export declare function extractSnippet(content: string, query: string, maxLength?: number): string;
12
+ //# sourceMappingURL=markdown-parser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"markdown-parser.d.ts","sourceRoot":"","sources":["../../../src/tools/search/markdown-parser.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAerD;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAYrF;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAGvD;AAED,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,GAAE,MAAY,GAAG,MAAM,CA6B9F"}
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Markdown parsing utilities for search indexing.
3
+ * Strips syntax for BM25 scoring, extracts frontmatter and headings.
4
+ */
5
+ export function stripMarkdown(content) {
6
+ return content
7
+ .replace(/^---[\s\S]*?---/m, "") // frontmatter
8
+ .replace(/```[\s\S]*?```/g, "") // code blocks
9
+ .replace(/`[^`]+`/g, "") // inline code
10
+ .replace(/!\[.*?\]\(.*?\)/g, "") // images
11
+ .replace(/\[([^\]]+)\]\(.*?\)/g, "$1") // links → text only
12
+ .replace(/#{1,6}\s/g, "") // heading markers
13
+ .replace(/[*_~]{1,3}/g, "") // bold/italic/strikethrough
14
+ .replace(/>\s/g, "") // blockquotes
15
+ .replace(/[-*+]\s/g, "") // list markers
16
+ .replace(/\d+\.\s/g, "") // numbered lists
17
+ .replace(/\|.*\|/g, "") // tables
18
+ .replace(/\n{3,}/g, "\n\n") // collapse newlines
19
+ .trim();
20
+ }
21
+ export function extractFrontmatter(content) {
22
+ const match = content.match(/^---\n([\s\S]*?)\n---/);
23
+ if (!match)
24
+ return { tags: "", aliases: "" };
25
+ const yaml = match[1];
26
+ const tagsMatch = yaml.match(/tags:\s*\[?(.*?)\]?\s*\n/);
27
+ const tags = tagsMatch ? tagsMatch[1].replace(/[,\[\]"']/g, " ").trim() : "";
28
+ const aliasMatch = yaml.match(/aliases:\s*\[?(.*?)\]?\s*\n/);
29
+ const aliases = aliasMatch ? aliasMatch[1].replace(/[,\[\]"']/g, " ").trim() : "";
30
+ return { tags, aliases };
31
+ }
32
+ export function extractHeadings(content) {
33
+ const headings = content.match(/^#{1,6}\s+(.+)$/gm);
34
+ return headings ? headings.map((h) => h.replace(/^#{1,6}\s+/, "")).join(" ") : "";
35
+ }
36
+ export function extractSnippet(content, query, maxLength = 200) {
37
+ const terms = query.toLowerCase().split(/\s+/);
38
+ const lower = content.toLowerCase();
39
+ let bestPos = 0;
40
+ let bestScore = 0;
41
+ // Sample positions rather than scanning every character
42
+ const step = Math.max(1, Math.floor(lower.length / 500));
43
+ for (let i = 0; i < lower.length; i += step) {
44
+ let score = 0;
45
+ for (const term of terms) {
46
+ const idx = lower.indexOf(term, i);
47
+ if (idx !== -1 && idx < i + maxLength)
48
+ score++;
49
+ }
50
+ if (score > bestScore) {
51
+ bestScore = score;
52
+ bestPos = i;
53
+ }
54
+ }
55
+ const start = Math.max(0, bestPos - 40);
56
+ const end = Math.min(content.length, start + maxLength);
57
+ let snippet = content.slice(start, end).replace(/\n/g, " ").trim();
58
+ if (start > 0)
59
+ snippet = "..." + snippet;
60
+ if (end < content.length)
61
+ snippet = snippet + "...";
62
+ return snippet;
63
+ }
64
+ //# sourceMappingURL=markdown-parser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"markdown-parser.js","sourceRoot":"","sources":["../../../src/tools/search/markdown-parser.ts"],"names":[],"mappings":"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"}