78 lines
2.5 KiB
Python
78 lines
2.5 KiB
Python
from __future__ import annotations
|
|
|
|
import hashlib
|
|
import re
|
|
from pathlib import Path
|
|
|
|
|
|
class MemoryStore:
|
|
def __init__(self, cwd: Path) -> None:
|
|
self.cwd = cwd.resolve()
|
|
self.root = Path.home() / ".config" / "cc-slim" / "memory" / self._sanitize_cwd(self.cwd)
|
|
self.root.mkdir(parents=True, exist_ok=True)
|
|
self.ensure_structure()
|
|
|
|
def ensure_structure(self) -> None:
|
|
path = self.path()
|
|
if not path.exists():
|
|
path.write_text(self._default_template(), encoding="utf-8")
|
|
return
|
|
|
|
existing = path.read_text(encoding="utf-8").strip()
|
|
if self._is_structured(existing):
|
|
return
|
|
|
|
migrated = self._default_template(existing)
|
|
path.write_text(migrated, encoding="utf-8")
|
|
|
|
def append(self, text: str) -> str:
|
|
content = self.read().rstrip()
|
|
entry = self._format_scratch_entry(text)
|
|
updated = f"{content}\n\n{entry}\n"
|
|
self.path().write_text(updated, encoding="utf-8")
|
|
return "Scratch Notes"
|
|
|
|
def read(self) -> str:
|
|
self.ensure_structure()
|
|
return self.path().read_text(encoding="utf-8").strip()
|
|
|
|
def path(self) -> Path:
|
|
return self.root / "MEMORY.md"
|
|
|
|
def _default_template(self, scratch_notes: str = "") -> str:
|
|
scratch = self._format_scratch_entry(scratch_notes) if scratch_notes.strip() else ""
|
|
template = (
|
|
"# Memory\n\n"
|
|
"## User Memory\n\n"
|
|
"## Project Memory\n\n"
|
|
"## Constraints\n\n"
|
|
"## Consolidated Facts\n\n"
|
|
"## Scratch Notes\n"
|
|
)
|
|
return f"{template}\n{scratch}\n" if scratch else f"{template}\n"
|
|
|
|
def _format_scratch_entry(self, text: str) -> str:
|
|
lines = [line.rstrip() for line in text.strip().splitlines() if line.strip()]
|
|
if not lines:
|
|
return ""
|
|
if len(lines) == 1:
|
|
return f"- {lines[0]}"
|
|
tail = "\n".join(f" {line}" for line in lines[1:])
|
|
return f"- {lines[0]}\n{tail}"
|
|
|
|
def _is_structured(self, text: str) -> bool:
|
|
required_sections = [
|
|
"# Memory",
|
|
"## User Memory",
|
|
"## Project Memory",
|
|
"## Constraints",
|
|
"## Consolidated Facts",
|
|
"## Scratch Notes",
|
|
]
|
|
return all(section in text for section in required_sections)
|
|
|
|
def _sanitize_cwd(self, cwd: Path) -> str:
|
|
text = re.sub(r"[^A-Za-z0-9._-]+", "_", str(cwd))
|
|
digest = hashlib.sha1(str(cwd).encode("utf-8")).hexdigest()[:8]
|
|
return f"{text[:48]}-{digest}"
|