"""Obsidian review draft writer.""" from __future__ import annotations import re from datetime import datetime, timezone from pathlib import Path from .config import get_config from .schemas import MemoryRecord def _slugify(value: str, fallback: str) -> str: slug = re.sub(r"[^a-zA-Z0-9\u4e00-\u9fff_-]+", "-", value.lower()).strip("-") slug = re.sub(r"-+", "-", slug)[:80].strip("-") return slug or fallback def write_review_draft(memory: MemoryRecord, reason: str, conflict_ids: list[str] | None = None) -> Path: config = get_config() review_dir = getattr(config.obsidian, "review_dir", "Reviews/Queue") vault_path = Path(config.obsidian.vault_path) target_dir = vault_path / review_dir target_dir.mkdir(parents=True, exist_ok=True) title = memory.summary or memory.content[:80] or memory.id filename = f"{_slugify(title, memory.id)}-{memory.id}.md" path = target_dir / filename conflict_ids = conflict_ids or [] content = "\n".join( [ "---", "type: memory_review", f"memory_id: {memory.id}", f"user_id: {memory.user_id}", f"agent_id: {memory.agent_id or ''}", f"workspace_id: {memory.workspace_id or ''}", f"namespace: {memory.namespace}", f"visibility: {memory.visibility.value}", f"importance: {memory.importance}", f"confidence: {memory.confidence}", f"reason: {reason}", f"created_at: {datetime.now(timezone.utc).isoformat()}", "tags:", " - memory/review", " - source/evermemos", "---", "", f"# Memory Review - {title}", "", "## Candidate", "", memory.content, "", "## Summary", "", memory.summary or "", "", "## Proposed Action", "", "- [ ] Accept", "- [ ] Edit", "- [ ] Reject", "- [ ] Merge", "- [ ] Archive", "", "## Conflict IDs", "", "\n".join(f"- {memory_id}" for memory_id in conflict_ids) if conflict_ids else "- none", "", "## Notes", "", ] ) path.write_text(content, encoding="utf-8") return path