feat: Phase 8 - Write Operations & Concurrency

Implement safe write operations with concurrency control, partial updates,
conflict resolution, and file rename/move with automatic link updates.

New Tools:
- update_frontmatter: Partial frontmatter updates with concurrency control
- update_sections: Line-based section edits to reduce race conditions
- rename_file: File rename/move with automatic wikilink updates

Enhanced Tools:
- create_note: Added onConflict strategies (error, overwrite, rename)
- delete_note: Added soft delete, dryRun, and concurrency control

Key Features:
- ETag-based optimistic locking via ifMatch parameter
- Version tracking on all write operations
- Conflict resolution strategies
- Link integrity maintenance during file operations
- Safe operations with preview and recovery options

Files Created:
- src/utils/version-utils.ts

Files Modified:
- src/tools/note-tools.ts
- src/utils/frontmatter-utils.ts
- src/tools/index.ts
- src/types/mcp-types.ts
- ROADMAP.md
- CHANGELOG.md

Fixes:
- Fixed rename_file backlinks API issue (not available in Obsidian API)
- Fixed update_frontmatter null-object error when patch is undefined
This commit is contained in:
2025-10-17 00:38:45 -04:00
parent 4e399e00f8
commit 99e2ade3ca
7 changed files with 1115 additions and 64 deletions

View File

@@ -207,3 +207,80 @@ export interface ExcalidrawMetadata {
/** Full compressed drawing data (when includeCompressed=true) */
compressedData?: string;
}
// Phase 8: Write Operations & Concurrency Types
/**
* Conflict resolution strategy for create_note
*/
export type ConflictStrategy = 'error' | 'overwrite' | 'rename';
/**
* Section edit operation for update_sections
*/
export interface SectionEdit {
/** Starting line number (1-indexed) */
startLine: number;
/** Ending line number (1-indexed, inclusive) */
endLine: number;
/** New content to replace the section */
content: string;
}
/**
* Result from update_frontmatter operation
*/
export interface UpdateFrontmatterResult {
success: boolean;
path: string;
versionId: string;
modified: number;
updatedFields: string[];
removedFields: string[];
}
/**
* Result from update_sections operation
*/
export interface UpdateSectionsResult {
success: boolean;
path: string;
versionId: string;
modified: number;
sectionsUpdated: number;
}
/**
* Result from create_note operation
*/
export interface CreateNoteResult {
success: boolean;
path: string;
versionId: string;
created: number;
renamed?: boolean;
originalPath?: string;
}
/**
* Result from rename_file operation
*/
export interface RenameFileResult {
success: boolean;
oldPath: string;
newPath: string;
linksUpdated: number;
affectedFiles: string[];
versionId: string;
}
/**
* Result from delete_note operation
*/
export interface DeleteNoteResult {
deleted: boolean;
path: string;
destination?: string;
dryRun: boolean;
soft: boolean;
}