Files
the-nexus/docs/bezalel/evennia/cmd_palace.py
Bezalel a0ee7858ff
Some checks failed
Deploy Nexus / deploy (push) Has been cancelled
feat(bezalel): MemPalace ecosystem — validation, audit, sync, auto-revert, Evennia integration
2026-04-07 14:47:12 +00:00

247 lines
7.7 KiB
Python

"""
Palace commands — bridge Evennia to the local MemPalace memory system.
"""
import json
import subprocess
from evennia.commands.command import Command
from evennia import create_object, search_object
PALACE_SCRIPT = "/root/wizards/bezalel/evennia/palace_search.py"
def _search_mempalace(query, wing=None, room=None, n=5, fleet=False):
"""Call the helper script and return parsed results."""
cmd = ["/root/wizards/bezalel/hermes/venv/bin/python", PALACE_SCRIPT, query]
cmd.append(wing or "none")
cmd.append(room or "none")
cmd.append(str(n))
if fleet:
cmd.append("--fleet")
try:
result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
data = json.loads(result.stdout)
return data.get("results", [])
except Exception:
return []
def _get_wing(caller):
"""Return the caller's wing, defaulting to their key or 'general'."""
return caller.db.wing if caller.attributes.has("wing") else (caller.key.lower() if caller.key else "general")
class CmdPalaceSearch(Command):
"""
Search your memory palace.
Usage:
palace/search <query>
palace/search <query> [--room <room>]
palace/recall <topic>
palace/file <name> = <content>
palace/status
"""
key = "palace"
aliases = ["pal"]
locks = "cmd:all()"
help_category = "Mind Palace"
def func(self):
if not self.args.strip():
self.caller.msg("Usage: palace/search <query> | palace/recall <topic> | palace/file <name> = <content> | palace/status")
return
parts = self.args.strip().split(" ", 1)
subcmd = parts[0].lower()
rest = parts[1] if len(parts) > 1 else ""
if subcmd == "search":
self._do_search(rest)
elif subcmd == "recall":
self._do_recall(rest)
elif subcmd == "file":
self._do_file(rest)
elif subcmd == "status":
self._do_status()
else:
self._do_search(self.args.strip())
def _do_search(self, query):
if not query:
self.caller.msg("Search for what?")
return
self.caller.msg(f"Searching the palace for: |c{query}|n...")
wing = _get_wing(self.caller)
results = _search_mempalace(query, wing=wing)
if not results:
self.caller.msg("The palace is silent on that matter.")
return
lines = []
for i, r in enumerate(results[:5], 1):
room = r.get("room", "unknown")
source = r.get("source", "unknown")
content = r.get("content", "")[:400]
lines.append(f"\n|g[{i}]|n |c{room}|n — |x{source}|n")
lines.append(f"{content}\n")
self.caller.msg("\n".join(lines))
def _do_recall(self, topic):
if not topic:
self.caller.msg("Recall what topic?")
return
results = _search_mempalace(topic, wing=_get_wing(self.caller), n=1)
if not results:
self.caller.msg("Nothing to recall.")
return
r = results[0]
content = r.get("content", "")
source = r.get("source", "unknown")
from typeclasses.memory_object import MemoryObject
obj = create_object(
MemoryObject,
key=f"memory:{topic}",
location=self.caller.location,
)
obj.db.memory_content = content
obj.db.source_file = source
obj.db.room_name = r.get("room", "general")
self.caller.location.msg_contents(
f"$You() conjure() a memory shard from the palace: |m{obj.key}|n.",
from_obj=self.caller,
)
def _do_file(self, rest):
if "=" not in rest:
self.caller.msg("Usage: palace/file <name> = <content>")
return
name, content = rest.split("=", 1)
name = name.strip()
content = content.strip()
if not name or not content:
self.caller.msg("Both name and content are required.")
return
from typeclasses.memory_object import MemoryObject
obj = create_object(
MemoryObject,
key=f"memory:{name}",
location=self.caller.location,
)
obj.db.memory_content = content
obj.db.source_file = f"filed by {self.caller.key}"
obj.db.room_name = self.caller.location.key if self.caller.location else "general"
self.caller.location.msg_contents(
f"$You() file() a new memory in the palace: |m{obj.key}|n.",
from_obj=self.caller,
)
def _do_status(self):
cmd = [
"/root/wizards/bezalel/hermes/venv/bin/mempalace",
"--palace", "/root/wizards/bezalel/.mempalace/palace",
"status"
]
try:
result = subprocess.run(cmd, capture_output=True, text=True, timeout=15)
self.caller.msg(result.stdout or result.stderr)
except Exception as e:
self.caller.msg(f"Could not reach the palace: {e}")
class CmdRecall(Command):
"""
Recall a memory from the palace.
Usage:
recall <query>
recall <query> --fleet
recall <query> --room <room>
"""
key = "recall"
aliases = ["remember", "mem"]
locks = "cmd:all()"
help_category = "Mind Palace"
def func(self):
if not self.args.strip():
self.caller.msg("Recall what? Usage: recall <query> [--fleet] [--room <room>]")
return
args = self.args.strip()
fleet = "--fleet" in args
room = None
if "--room" in args:
parts = args.split("--room")
args = parts[0].strip()
room = parts[1].strip().split()[0] if len(parts) > 1 else None
if "--fleet" in args:
args = args.replace("--fleet", "").strip()
self.caller.msg(f"Recalling from the {'fleet' if fleet else 'personal'} palace: |c{args}|n...")
wing = None if fleet else _get_wing(self.caller)
results = _search_mempalace(args, wing=wing, room=room, n=5, fleet=fleet)
if not results:
self.caller.msg("The palace is silent on that matter.")
return
lines = []
for i, r in enumerate(results[:5], 1):
room_name = r.get("room", "unknown")
source = r.get("source", "unknown")
content = r.get("content", "")[:400]
wing_label = r.get("wing", "unknown")
wing_tag = f" |y[{wing_label}]|n" if fleet else ""
lines.append(f"\n|g[{i}]|n |c{room_name}|n{wing_tag} — |x{source}|n")
lines.append(f"{content}\n")
self.caller.msg("\n".join(lines))
class CmdEnterRoom(Command):
"""
Enter a room in the mind palace by topic.
Usage:
enter room <topic>
"""
key = "enter room"
aliases = ["enter palace", "go room"]
locks = "cmd:all()"
help_category = "Mind Palace"
def func(self):
if not self.args.strip():
self.caller.msg("Enter which room? Usage: enter room <topic>")
return
topic = self.args.strip().lower().replace(" ", "-")
wing = _get_wing(self.caller)
room_key = f"palace:{wing}:{topic}"
# Search for existing room
rooms = search_object(room_key, typeclass="typeclasses.palace_room.PalaceRoom")
if rooms:
room = rooms[0]
else:
# Create the room dynamically
from typeclasses.palace_room import PalaceRoom
room = create_object(
PalaceRoom,
key=room_key,
)
room.db.memory_topic = topic
room.db.wing = wing
room.update_description()
self.caller.move_to(room, move_type="teleport")
self.caller.msg(f"You step into the |c{topic}|n room of your mind palace.")