feat(memory): sandboxed markdown memory system
- memory/index.py: auto-regenerate MEMORY_INDEX.md on every write - memory/reader.py: list_memories(), read_memory(), load_context_for_session() all go through assert_safe_path() + relative_to check - memory/writer.py: write_memory(), append_memory() — relative names only, no absolute paths or traversal, calls update_index() after every write Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,30 @@
|
||||
import datetime
|
||||
from pathlib import Path
|
||||
|
||||
from pyra.utils.paths import pyra_home, safe_chmod
|
||||
|
||||
_MEMORY_ROOT = pyra_home() / "memory"
|
||||
_INDEX_FILE = _MEMORY_ROOT / "MEMORY_INDEX.md"
|
||||
|
||||
|
||||
def update_index() -> None:
|
||||
files = sorted(_MEMORY_ROOT.rglob("*.md"))
|
||||
files = [f for f in files if f.name != "MEMORY_INDEX.md"]
|
||||
|
||||
rows: list[str] = []
|
||||
for f in files:
|
||||
rel = f.relative_to(_MEMORY_ROOT)
|
||||
category = rel.parts[0] if len(rel.parts) > 1 else "root"
|
||||
mtime = datetime.datetime.fromtimestamp(f.stat().st_mtime).strftime("%Y-%m-%d %H:%M")
|
||||
rows.append(f"| {rel} | {category} | {mtime} |")
|
||||
|
||||
table = "\n".join(rows) if rows else "| _(no memory files)_ | — | — |"
|
||||
content = (
|
||||
"# Memory Index\n\n"
|
||||
"| File | Category | Last Modified |\n"
|
||||
"|------|----------|---------------|\n"
|
||||
f"{table}\n\n"
|
||||
"_Auto-maintained by Pyra. Do not edit manually._\n"
|
||||
)
|
||||
_INDEX_FILE.write_text(content)
|
||||
safe_chmod(_INDEX_FILE, 0o600)
|
||||
Reference in New Issue
Block a user