""" 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 palace/search [--room ] palace/recall palace/file = 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 | palace/recall | palace/file = | 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 = ") 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 recall --fleet recall --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 [--fleet] [--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 """ 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 ") 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.")