Synthesis Layer

AI-Powered Note Merger

Source Files: noteMerger.ts, ai.ts Complexity: Low

The AI-Powered Note Merger allows you to merge multiple selected notes into a single comprehensive draft. It automatically strips headers and metadata, removes redundant details, organizes contents logically, and suggests structured sub-headings.

1. Overview

When writing notes over time, they often become fragmented. You might have three different notes from separate meetings discussing the same project plan. Merging them manually takes time. This feature solves this problem:

  • Strips Metadata: Automatically extracts and discards YAML frontmatter from target notes.
  • Groups Content: Combines note texts into a single context, prefixing each note's content with its source filename.
  • Applies LLM Synthesis: Employs the LLM to structure, rewrite, and consolidate the notes into a unified document.
  • Generates a Draft: Writes the merged document to the _drafts/ directory to await auto-classification.

2. Under the Hood & Code Walkthrough

2.1 Metadata Stripping & Word Counting

To prevent the LLM from getting confused by metadata, the extension strips YAML blocks and counts the remaining words to ensure it stays within token limits:

export function countWords(text: string): number {
    const trimmed = text.trim();
    if (trimmed.length === 0) { return 0; }
    return trimmed.split(/\s+/).length;
}

export function stripFrontmatter(content: string): string {
    // Matches YAML block from start of file: ---\n<metadata>\n---\n
    return content.replace(/^---\n(?:.*\n)*?---\n/, '');
}

2.2 Notes Merging Synthesis

The extension reads each note, removes its frontmatter, prefixes the text with its source name, checks total words (showing a warning if >8000), and sends the combined prompt to the LLM:

export async function mergeNotes(notePaths: string[], workspaceRoot: string): Promise<string> {
    const contents: string[] = [];
    let totalWords = 0;

    for (const notePath of notePaths) {
        const raw = await fsp.readFile(notePath, 'utf8');
        const cleaned = stripFrontmatter(raw);
        contents.push(`## Source: ${path.basename(notePath)}\n\n${cleaned}`);
        totalWords += countWords(cleaned);
    }

    if (totalWords > 8000) {
        const proceed = await vscode.window.showWarningMessage(
            `Selected notes contain ~${totalWords} words. Continue?`,
            'Continue', 'Cancel'
        );
        if (proceed !== 'Continue') { throw new Error('Merge cancelled.'); }
    }

    const combined = contents.join('\n\n---\n\n');
    const prompt = `Merge these notes into a single comprehensive document. Preserve all key information, remove redundancy, organize logically with clear headings. Output markdown only.

Notes to merge:

${combined}`;

    const merged = await chatCompletionWithRetry(prompt);
    
    // Write synthesized file to _drafts/merged_*.md ...
}