"""
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