Source code for continuity.core.generators.about

"""
About file generation for Continuity projects.

This module handles generating project ABOUT.md files from templates
and changelog entries, following Unix principles of composition.
"""

from pathlib import Path
from typing import Any, Dict, List

from ..constants import ABOUT_FILE
from .base import BaseGenerator


[docs] class AboutGenerator(BaseGenerator): """Generates ABOUT.md files from templates and changelog entries.""" def __init__(self, project_path: Path): """Initialize about generator. Args: project_path: Path to the project root """ super().__init__(project_path) self.content_path = project_path / "content" self.changelog_path = self.content_path / "changelog" def _parse_changelog_entry(self, file_path: Path) -> Dict[str, Any]: """Parse a single changelog markdown file. Args: file_path: Path to changelog file Returns: Dict with metadata and content """ content = file_path.read_text(encoding="utf-8") # Parse frontmatter parts = content.split("---", 2) if len(parts) < 3: # No frontmatter, just content return { "content": content.strip(), "version": "unknown", "date": "unknown", "sort_key": "0000-00-00", } frontmatter = parts[1].strip() body = parts[2].strip() # Parse frontmatter fields metadata = {} for line in frontmatter.splitlines(): if ":" in line: key, value = line.split(":", 1) metadata[key.strip()] = value.strip() return { "content": body, "version": metadata.get("version", "unknown"), "date": metadata.get("date", "unknown"), "schema_version": metadata.get("schema_version", "unknown"), "type": metadata.get("type", "unknown"), "sort_key": metadata.get("date", "0000-00-00"), } def _collect_changelog_entries(self) -> List[Dict[str, Any]]: """Collect and sort changelog entries. Returns: List of changelog entries sorted by date (newest first) """ if not self.changelog_path.exists(): return [] entries = [] for file_path in self.changelog_path.glob("*.md"): if file_path.is_file(): entry = self._parse_changelog_entry(file_path) entries.append(entry) # Sort by date (newest first) entries.sort(key=lambda x: x["sort_key"], reverse=True) return entries def _format_changelog_entries(self, entries: List[Dict[str, Any]]) -> str: """Format changelog entries for inclusion in ABOUT.md. Args: entries: List of parsed changelog entries Returns: Formatted changelog text """ if not entries: return "No changelog entries found." formatted = [] for entry in entries: formatted.append(entry["content"]) return "\n\n".join(formatted)
[docs] def generate(self) -> str: """Generate ABOUT.md content from template and changelog. Returns: Generated ABOUT.md content """ # Get template content with version substitution template = self._read_template("ABOUT.md") # Collect data app_version = self._get_app_version() changelog_entries = self._collect_changelog_entries() formatted_changelog = self._format_changelog_entries(changelog_entries) # Replace template variables content = template.format( app_version=app_version, changelog_entries=formatted_changelog, ) return content
[docs] def write(self) -> Path: """Generate and write ABOUT.md to continuity directory. Returns: Path to written ABOUT.md file """ content = self.generate() about_path = self.continuity_path / ABOUT_FILE # Ensure continuity directory exists self.continuity_path.mkdir(parents=True, exist_ok=True) about_path.write_text(content, encoding="utf-8") return about_path