obsidian_regex_replace
Perform regex or literal find-and-replace across the Obsidian vault with dry-run previews to verify changes before applying.
Instructions
Regex or literal find-and-replace across the vault with dry-run previews by default.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| vault | No | Optional configured vault name. Defaults to the server default vault. | |
| search | Yes | ||
| replace | Yes | ||
| regex | No | ||
| caseSensitive | No | ||
| wholeWord | No | ||
| flexibleWhitespace | No | ||
| pathPrefix | No | ||
| dryRun | No | ||
| limit | No |
Implementation Reference
- src/tools.ts:548-564 (registration)Tool registration for obsidian_regex_replace in registerObsidianTools(). Defines Zod schema for inputs and delegates to regexReplaceAcrossVault().
tool( "obsidian_regex_replace", "Regex or literal find-and-replace across the vault with dry-run previews by default.", { vault: vaultArg, search: z.string(), replace: z.string(), regex: z.boolean().optional().default(true), caseSensitive: z.boolean().optional().default(false), wholeWord: z.boolean().optional().default(false), flexibleWhitespace: z.boolean().optional().default(false), pathPrefix: z.string().optional(), dryRun: z.boolean().optional().default(true), limit: z.number().int().min(1).max(5000).optional().default(1000), }, async (args) => regexReplaceAcrossVault(vaults, args.vault, args), ); - src/ops.ts:13-53 (handler)Core handler: iterates vault markdown files, applies regex/literal pattern using buildSearchPattern, counts replacements, optionally writes changes or returns preview diff.
export async function regexReplaceAcrossVault( vaults: VaultManager, vault: string | undefined, options: { search: string; replace: string; regex?: boolean; caseSensitive?: boolean; wholeWord?: boolean; flexibleWhitespace?: boolean; pathPrefix?: string; dryRun?: boolean; limit?: number; }, ): Promise<{ changed: RegexReplaceResult[]; totalReplacements: number; dryRun: boolean }> { const files = await vaults.markdownFiles(vault); const pattern = buildSearchPattern(options); const flags = `g${options.caseSensitive ? "" : "i"}`; const re = new RegExp(pattern, flags); const changed: RegexReplaceResult[] = []; let totalReplacements = 0; for (const file of files) { if (options.pathPrefix && !file.toLowerCase().startsWith(options.pathPrefix.toLowerCase())) continue; const read = await vaults.readText(file, vault); let count = 0; const next = read.text.replace(re, (...parts) => { count += 1; return options.replace; }); if (count === 0) continue; totalReplacements += count; changed.push({ path: read.path, replacements: count, preview: options.dryRun ? previewDiff(read.text, next) : undefined, }); if (!options.dryRun) await vaults.writeText(read.path, next, vault, { overwrite: true }); if (changed.length >= (options.limit ?? 1000)) break; } return { changed, totalReplacements, dryRun: options.dryRun ?? true }; } - src/ops.ts:7-11 (schema)Type definition for the result of each file processed by regexReplaceAcrossVault.
export type RegexReplaceResult = { path: string; replacements: number; preview?: string; }; - src/ops.ts:188-193 (helper)Helper to build the RegExp pattern string from options (regex literal, flexible whitespace, whole word).
function buildSearchPattern(options: { search: string; regex?: boolean; wholeWord?: boolean; flexibleWhitespace?: boolean }): string { let pattern = options.regex ? options.search : escapeRegExp(options.search); if (!options.regex && options.flexibleWhitespace) pattern = pattern.replace(/\\\s+/g, "\\s+").replace(/\s+/g, "\\s+"); if (options.wholeWord) pattern = `\\b(?:${pattern})\\b`; return pattern; } - src/ops.ts:195-207 (helper)Helper to produce a unified-diff-style preview of changes (up to 20 lines).
function previewDiff(before: string, after: string): string { const beforeLines = before.split("\n"); const afterLines = after.split("\n"); const out: string[] = []; const max = Math.max(beforeLines.length, afterLines.length); for (let i = 0; i < max && out.length < 20; i += 1) { if (beforeLines[i] !== afterLines[i]) { if (beforeLines[i] !== undefined) out.push(`-${beforeLines[i]}`); if (afterLines[i] !== undefined) out.push(`+${afterLines[i]}`); } } return out.join("\n"); }