MCP Codebase Insight

by tosin2013
Verified
"""ADR (Architecture Decision Record) management module.""" import json from datetime import datetime from enum import Enum from pathlib import Path from typing import Dict, List, Optional from uuid import UUID, uuid4 from pydantic import BaseModel class ADRStatus(str, Enum): """ADR status enumeration.""" PROPOSED = "proposed" ACCEPTED = "accepted" REJECTED = "rejected" SUPERSEDED = "superseded" DEPRECATED = "deprecated" class ADROption(BaseModel): """ADR option model.""" title: str pros: List[str] cons: List[str] description: Optional[str] = None class ADRContext(BaseModel): """ADR context model.""" problem: str constraints: List[str] assumptions: Optional[List[str]] = None background: Optional[str] = None class ADR(BaseModel): """ADR model.""" id: UUID title: str status: ADRStatus context: ADRContext options: List[ADROption] decision: str consequences: Optional[Dict[str, List[str]]] = None metadata: Optional[Dict[str, str]] = None created_at: datetime updated_at: datetime superseded_by: Optional[UUID] = None class ADRManager: """ADR manager for handling architecture decision records.""" def __init__(self, config): """Initialize ADR manager.""" self.config = config self.adr_dir = config.adr_dir self.adr_dir.mkdir(parents=True, exist_ok=True) async def create_adr( self, title: str, context: Dict, options: List[Dict], decision: str, consequences: Optional[Dict[str, List[str]]] = None, metadata: Optional[Dict[str, str]] = None ) -> ADR: """Create a new ADR.""" now = datetime.utcnow() adr = ADR( id=uuid4(), title=title, status=ADRStatus.PROPOSED, context=ADRContext(**context), options=[ADROption(**opt) for opt in options], decision=decision, consequences=consequences, metadata=metadata, created_at=now, updated_at=now ) await self._save_adr(adr) return adr async def get_adr(self, adr_id: UUID) -> Optional[ADR]: """Get ADR by ID.""" adr_path = self.adr_dir / f"{adr_id}.json" if not adr_path.exists(): return None with open(adr_path) as f: data = json.load(f) return ADR(**data) async def update_adr( self, adr_id: UUID, status: Optional[ADRStatus] = None, superseded_by: Optional[UUID] = None, metadata: Optional[Dict[str, str]] = None ) -> Optional[ADR]: """Update ADR status and metadata.""" adr = await self.get_adr(adr_id) if not adr: return None if status: adr.status = status if superseded_by: adr.superseded_by = superseded_by if metadata: adr.metadata = {**(adr.metadata or {}), **metadata} adr.updated_at = datetime.utcnow() await self._save_adr(adr) return adr async def list_adrs( self, status: Optional[ADRStatus] = None ) -> List[ADR]: """List all ADRs, optionally filtered by status.""" adrs = [] for path in self.adr_dir.glob("*.json"): with open(path) as f: data = json.load(f) adr = ADR(**data) if not status or adr.status == status: adrs.append(adr) return sorted(adrs, key=lambda x: x.created_at) async def _save_adr(self, adr: ADR) -> None: """Save ADR to file.""" adr_path = self.adr_dir / f"{adr.id}.json" with open(adr_path, "w") as f: json.dump(adr.model_dump(), f, indent=2, default=str)