from __future__ import annotations import os from dataclasses import dataclass from typing import Iterable @dataclass(frozen=True) class VaultPaths: root: str notes: str attachments: str kb: str class Vault: def __init__(self, root_path: str): if not root_path: raise ValueError("Vault root path must be provided") self.root_path = os.path.abspath(root_path) self.paths = VaultPaths( root=self.root_path, notes=os.path.join(self.root_path, "notes"), attachments=os.path.join(self.root_path, "attachments"), kb=os.path.join(self.root_path, ".kb"), ) def ensure_structure(self) -> None: os.makedirs(self.paths.root, exist_ok=True) os.makedirs(self.paths.notes, exist_ok=True) os.makedirs(self.paths.attachments, exist_ok=True) os.makedirs(self.paths.kb, exist_ok=True) def relpath(self, abs_path: str) -> str: return os.path.relpath(abs_path, self.paths.root) def abspath(self, rel_path: str) -> str: return os.path.join(self.paths.root, rel_path) def iter_markdown_files(self) -> Iterable[str]: """Yield absolute file paths for all .md files under notes/ recursively.""" for dirpath, _, filenames in os.walk(self.paths.notes): for fn in filenames: if fn.lower().endswith(".md"): yield os.path.join(dirpath, fn)