Refactoring Layer

Vault Restructuring

Source Files: restructureVault.ts, linkRewriter.ts Complexity: High

The Vault Restructuring & Link Rewriting Engine provides a comprehensive, vault-wide folder cleanup. By scanning the entire vault structure and note metadata, it asks the LLM to propose folder renames, merges, and note relocations, then rewrites all affected markdown references to prevent broken links.

1. Overview

Over time, personal vaults grow organically and become disorganized. Manually moving folders and notes is tedious, and almost always breaks internal references (like wiki-links and relative image links). This feature provides a robust solution:

  • Vault-Wide Proposal: Evaluates all files in the vault and drafts folder renames, merges, and note moves in a single LLM run.
  • Dry Run Summary: Presents a clear summary of proposed changes for review before taking action.
  • Link Preservation: Automatically scans and rewrites all affected links (wiki-links, markdown links, and relative image references) across the entire vault to ensure nothing is broken.
  • Conservative Approach: Proposes structural refinements rather than radical changes, preserves original folders where possible, and never deletes note contents.

2. Under the Hood & Code Walkthrough

2.1 Safeguarding Code Blocks and Fences

To prevent corrupting code snippets or example configurations inside notes, the link rewriter automatically skips code fenced regions (triple backticks) and inline code spans (single backticks):

export function rewriteLinks(content: string, noteAbsPath: string, vaultRoot: string, pathMap: Map<string, string>): string {
    if (pathMap.size === 0) { return content; }
    
    // Split content by code fences to protect code blocks
    const parts = content.split(/(```[\s\S]*?```)/g);
    for (let i = 0; i < parts.length; i++) {
        // Only rewrite links outside of triple-backtick code blocks
        if (!parts[i].startsWith('```')) {
            // Further split by inline code blocks to protect backtick spans
            const subparts = parts[i].split(/(`[^`\n]+`)/g);
            for (let j = 0; j < subparts.length; j++) {
                if (!subparts[j].startsWith('`')) {
                    subparts[j] = applyRegexRewriting(subparts[j], noteAbsPath, vaultRoot, pathMap);
                }
            }
            parts[i] = subparts.join('');
        }
    }
    return parts.join('');
}

2.2 Link Resolving Algorithms

The linkRewriter.ts module uses pure functions (string in, string out), making it highly testable. It handles three types of links:

  1. Wiki-Links ([[target]] or [[target|alias]]):

    If a note's filename has changed, the extension updates the target reference while preserving the alias using the following regex:

    const wikiRegex = /\[\[([^\]|]+)(?:\|([^\]]+))?\]\]/g;
  2. Standard Markdown Links ([text](relative/path.md#anchor)):

    Computes the absolute path of the target, looks up its new path in the old-to-new mapping, and generates a new relative link from the note's new location:

    const mdLinkRegex = /\[([^\]]*)\]\(([^)]+\.md)(#[^)]*)?\)/g;
  3. Image References (![alt](relative/image.png)):

    Since images aren't moved during restructuring, the extension simply updates the relative paths of images inside moved notes to ensure they still resolve.