e56e9779ec
Gives Pyra an active memory brain: memory_index.json tracks summary + keywords per file (like an inode table), and three built-in tools let the AI look up, read, and overwrite memory mid-session. write_memory accepts summary/keywords; update_index() merges the JSON index without losing existing metadata. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
71 lines
2.2 KiB
Python
71 lines
2.2 KiB
Python
import datetime
|
|
import json
|
|
from pathlib import Path
|
|
|
|
from pyra.memory import _MEMORY_ROOT
|
|
from pyra.utils.paths import safe_chmod
|
|
|
|
_INDEX_FILE = _MEMORY_ROOT / "MEMORY_INDEX.md"
|
|
_JSON_INDEX_FILE = _MEMORY_ROOT / "memory_index.json"
|
|
|
|
_EXCLUDED = {"MEMORY_INDEX.md", "memory_index.json"}
|
|
|
|
|
|
def _load_json_index() -> dict:
|
|
if not _JSON_INDEX_FILE.exists():
|
|
return {}
|
|
try:
|
|
return json.loads(_JSON_INDEX_FILE.read_text())
|
|
except (json.JSONDecodeError, OSError):
|
|
return {}
|
|
|
|
|
|
def update_index() -> None:
|
|
existing = _load_json_index()
|
|
|
|
files = sorted(_MEMORY_ROOT.rglob("*.md"))
|
|
files = [f for f in files if f.name not in _EXCLUDED]
|
|
|
|
new_json: dict = {}
|
|
rows: list[str] = []
|
|
|
|
for f in files:
|
|
rel = f.relative_to(_MEMORY_ROOT)
|
|
rel_key = rel.as_posix()
|
|
category = rel.parts[0] if len(rel.parts) > 1 else "root"
|
|
mtime = datetime.datetime.fromtimestamp(f.stat().st_mtime)
|
|
mtime_str = mtime.strftime("%Y-%m-%d %H:%M")
|
|
|
|
prev = existing.get(rel_key, {})
|
|
new_json[rel_key] = {
|
|
"summary": prev.get("summary", ""),
|
|
"keywords": prev.get("keywords", []),
|
|
"modified": mtime.isoformat(timespec="seconds"),
|
|
}
|
|
rows.append(f"| {rel} | {category} | {mtime_str} |")
|
|
|
|
_JSON_INDEX_FILE.write_text(json.dumps(new_json, indent=2))
|
|
safe_chmod(_JSON_INDEX_FILE, 0o600)
|
|
|
|
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)
|
|
|
|
|
|
def update_json_entry(rel_path: str, summary: str, keywords: list[str]) -> None:
|
|
"""Update the summary and keywords for one entry in the JSON index."""
|
|
index = _load_json_index()
|
|
entry = index.get(rel_path, {})
|
|
entry["summary"] = summary
|
|
entry["keywords"] = keywords
|
|
index[rel_path] = entry
|
|
_JSON_INDEX_FILE.write_text(json.dumps(index, indent=2))
|
|
safe_chmod(_JSON_INDEX_FILE, 0o600)
|