From 993dc6a8793f3d964084705eba0073ad4c0f8a6d Mon Sep 17 00:00:00 2001 From: John Lancaster <32917998+jsl12@users.noreply.github.com> Date: Sun, 21 Jun 2026 10:06:25 -0500 Subject: [PATCH] file loading --- src/personal_mcp/registry/file.py | 50 ++++++++++++++++++------------- 1 file changed, 30 insertions(+), 20 deletions(-) diff --git a/src/personal_mcp/registry/file.py b/src/personal_mcp/registry/file.py index 8809091..0b763fb 100644 --- a/src/personal_mcp/registry/file.py +++ b/src/personal_mcp/registry/file.py @@ -1,10 +1,33 @@ from collections.abc import Generator +from dataclasses import dataclass from importlib.abc import Traversable +from itertools import starmap from pathlib import PurePosixPath +from typing import Self -import yaml -from .models.skill import SkillFrontmatter +@dataclass(frozen=True, slots=True) +class LoadedMarkdownDocument: + """Represents a loaded markdown document with its content and frontmatter.""" + + relpath: PurePosixPath + """The relative path of the document within the package resources.""" + content: str + """The raw markdown content of the document.""" + frontmatter: str + """The raw YAML frontmatter of the document, if present.""" + + @classmethod + def from_root(cls, root: Traversable) -> set[Self]: + """Recursively load all markdown documents from the root resource.""" + return set(starmap(cls.from_resource, walk_resources(root))) + + @classmethod + def from_resource(cls, relpath: PurePosixPath, resource: Traversable) -> Self: + """Load a markdown document from a package resource.""" + raw = resource.read_text(encoding="utf-8") + frontmatter = get_raw_frontmatter(raw) + return cls(relpath=relpath, content=raw, frontmatter=frontmatter) def walk_resources( @@ -25,27 +48,14 @@ def walk_resources( yield relpath.as_posix(), child -def get_markdown_content(resource: Traversable) -> dict[str, str]: - """Read the content of a markdown resource as text.""" - return {relpath: doc_file.read_text(encoding="utf-8") for relpath, doc_file in walk_resources(resource)} - - -def get_idx(raw): - for i, line in enumerate(raw.splitlines()): - if line.strip().startswith("---"): - yield i - - def get_raw_frontmatter(raw: str) -> str: - delimiter = iter(get_idx(raw)) + delimiter = iter(get_frontmatter_delim_idx(raw, delimiter="---")) start = next(delimiter) + 1 end = next(delimiter) return "\n".join(raw.splitlines()[start:end]) -def gen_valid_frontmatter(content: dict[str, str]) -> Generator[SkillFrontmatter]: - for relpath, raw in content.items(): - if relpath.endswith("SKILL.md"): - fm = get_raw_frontmatter(raw) - validated = SkillFrontmatter.model_validate(yaml.safe_load(fm)) - yield validated +def get_frontmatter_delim_idx(raw: str, *, delimiter: str = "---") -> Generator[int]: + for i, line in enumerate(raw.splitlines()): + if line.strip().startswith(delimiter): + yield i