name: mempalace description: Operate the local mempalace memory system — save decisions as drawers, add typed facts to the knowledge graph, write diary entries, seed new projects, re-mine existing ones, and capture finished PRs. Use when the user says "remember this", "save to mempalace", "what do we know about X", "bootstrap mempalace for this project", "re-mine the palace", "capture this PR", "wrap up this feature", or asks mempalace to recall past decisions about savia, cloudrig, or shakr.
Mempalace Operations
Mempalace is a local-first AI memory system installed at %USERPROFILE%/pipx/venvs/mempalace/ and exposed as mempalace_* MCP tools. It stores verbatim content in ChromaDB plus a SQLite knowledge graph at ~/.mempalace/palace/.
Current wings
savia_edge,savia_mobile,savia_web— Savia polyrepo (LatAm personal finance: React Native + Next.js + Supabase edge)cloudrig_cr_api,cloudrig_cr_field,cloudrig_cr_pm,cloudrig_cr_view_models— CloudRig polyrepo (construction PM platform: Node.js backend + Next.js web + React Native mobile + shared API client)shakr— haptic playback engine monorepo (Python/PySide6 desktop + Expo mobile + Supabase backend)
Operations
1. Save a decision as a drawer
Triggered by: "remember this", "save this decision", "file this", or when the user describes a non-obvious constraint, tradeoff, or rule that isn't in the code.
- Infer the wing from context, or ask.
- Pick a room. Prefer existing rooms visible in
mempalace_list_rooms; fall back togeneralor a domain-appropriate name. - Call
mempalace_check_duplicatefirst with a ~90% threshold to avoid filing the same decision twice. - Call
mempalace_add_drawerwith verbatim content structured as:- What: the decision, rule, or observation
- Why: the reasoning (constraint, incident, tradeoff, stakeholder ask)
- When: an absolute ISO date (convert "today" → YYYY-MM-DD before filing)
- Revisit trigger: what change would make you reconsider
- Confirm the returned
drawer_idto the user.
2. Add a typed fact to the knowledge graph
Triggered by: the user states a fact about a person, project, or entity that has temporal validity, or during initial project seeding.
- Call
mempalace_kg_queryon the subject first to check for duplicates. - Call
mempalace_kg_addwithsubject,predicate,object, andvalid_fromin ISO date format. - If the new fact replaces an older one (e.g. stack migration, role change), call
mempalace_kg_invalidateon the old triple first — don't silently shadow it. - Prefer stable, typed predicates:
is_a,uses,targets,requires,deployed_on,child_of,partner_of,worked_on,decided_against,open_decision. Avoid free-form phrases.
3. Write a diary entry
Triggered by: end of a substantive session, or explicit "write a diary entry".
- Summarize in three beats: what was done, what was decided, what's still open.
- Keep it under ~200 tokens. Use AAAK-ish style with entity codes, pipes as separators, and date prefixes. Example:
SESSION:2026-04-08|palace.bootstrap|7.wings.seeded|kg.populated.from.CLAUDE.md|next:weekly.remine. - Call
mempalace_diary_writewithagent_name="claude-code"(or the current agent's actual name) and the composed entry.
4. Seed a new project into the palace
Triggered by: "add this repo to mempalace", "bootstrap mempalace for this project".
- Determine structure: monorepo (single wing, many rooms) vs polyrepo (one wing per sub-repo — check each sub-dir for its own
.git/). - Run init with auto-accept + newline for room prompt, via Bash:
cd ~/Documents/Code export PYTHONIOENCODING=utf-8 printf '\n' | mempalace init <path> --yes - Overwrite
<path>/entities.jsonwith a clean single-project entry:{ "people": [], "projects": ["<Clean Project Name>"] } - Run
mempalace mine <path>with a generous timeout (900000ms+) in the background — mines on large repos take several minutes. Stdout is fully buffered when not a TTY; monitor progress vials -la ~/.mempalace/palace/chroma.sqlite3if needed. - After the mine completes, read the project's CLAUDE.md / README / AGENTS.md and call
mempalace_kg_addto seed the KG with identity/stack/rules/open decisions — same pattern as initial bootstrap. - Offer to append a mempalace section to the project's CLAUDE.md referencing the new wing name.
5. Re-mine for freshness
Triggered by: "refresh the palace", "re-mine savia", "update mempalace", or when the user notes the palace is stale.
- Run
mempalace mine <wing_path>for each wing sequentially — never in parallel (ChromaDB write conflicts). - Mempalace deduplicates by content hash; unchanged files are skipped automatically. Report "Files processed / Files skipped / Drawers filed" per wing.
- If a specific wing is named, only re-mine that one.
6. Answer from the palace
Triggered by: the user asks about existing code, past decisions, or project history.
- For factual "what/who/when" questions about an entity →
mempalace_kg_queryfirst. - For implementation/code questions →
mempalace_searchwithwing(androomwhen obvious) filter. Unfiltered search is diluted across 70k+ drawers. - For cross-project pattern discovery →
mempalace_find_tunnelsor unfilteredmempalace_search. - For "what rooms exist in X wing" →
mempalace_list_roomswithwingparam. - Always cite drawer IDs or sources in your answer so the user can verify.
7. Capture a finished PR
Triggered by: "capture this PR", "wrap up this feature in mempalace", "save the decision behind this PR", or when the user is about to merge / has just opened a PR and wants the institutional knowledge preserved.
Core principle: save what's in the author's head that isn't anywhere else yet. Not the diff (git has it), not the PR description (GitHub has it), not the file list (mechanical noise).
Identify the target wing(s). Look at which directories the PR touched. Map to wing names (e.g.
cloudrig/cr-pm/*→cloudrig_cr_pm). A single PR may touch multiple wings — save one drawer per wing if the rationale differs, or one drawer in the most central wing if it's one coherent decision.Search first via
mempalace_searchwith--wingfor the PR's main concept. If anything related surfaces that:- Contradicts the new approach → flag to the user before saving.
- Overlaps substantively → either call
mempalace_kg_invalidateon the old fact and supersede it, or reference the prior drawer ID in the new drawer's content so they're linked.
Save the decision as a drawer via
mempalace_add_drawer:wing: as identified in step 1room: prefer existingdecisionsif it exists; otherwisegeneralor a domain roomsource_file: the PR URL/number, or emptyadded_by:"pr-capture"(so these drawers are grep-able)content: a verbatim markdown block with this exact structure:# <One-line summary of what the PR introduces> **PR**: <number or URL> **Date**: <YYYY-MM-DD — always absolute, convert "today" first> **Wing**: <wing name> ## What <One paragraph: what this PR actually does, not what feature it adds. Be concrete.> ## Why <The real reason. A constraint, an incident, a perf issue, a stakeholder ask, a bug class that kept recurring. NOT "to add feature X" — that's circular.> ## Rejected alternatives - **<Option A>**: <why it lost — be specific> - **<Option B>**: <why it lost> ## Gotchas discovered - <Non-obvious thing hit during implementation that would save future-me time> - <Another one> ## Revisit triggers - <What change in circumstances would make us reconsider this approach?> - <E.g. "if we ever need to support offline writes", "if Node version unlocks", etc.> ## Open questions - <Anything deferred to a follow-up PR or a future decision>
Skip any section that genuinely has nothing to say — don't pad. Better to ship 3 real sections than 6 with filler.
Run
mempalace_check_duplicateon the drawer content (threshold 0.9) before filing. If a near-duplicate already exists, ask the user whether to update or skip — don't blindly file a second copy.Add KG facts via
mempalace_kg_addfor anything durable introduced by the PR:- New dependency:
<wing> uses <library>withvalid_from=<date> - New pattern:
<wing> uses <pattern-name>withsource_closet=<drawer_id> - New constraint:
<wing> requires <thing> - Deprecation: call
mempalace_kg_invalidateon the old triple FIRST withended=<date>, then add the replacement. Prefer stable predicates (uses,requires,deployed_on,replaces). Don't add KG facts for one-off code changes — the drawer already covers them.
- New dependency:
Write a diary entry via
mempalace_diary_write(agent_name="claude-code"or the current agent name), AAAK-style, under ~200 tokens. Format:SESSION:<YYYY-MM-DD>|<wing>|pr#<N>|<short.what>|<key.decision>|<gotcha.if.any>|next:merge+remineRemind the user at the end: "After this PR merges, run
mempalace mine <path>on the affected wing(s) to pull the new code into the palace. Dedup is automatic — only changed files get re-embedded."
What NOT to do:
- Do not save the code diff or file list — that's git's job.
- Do not paste the PR description verbatim — that's GitHub's job.
- Do not save conversation filler, thanks, or "this was hard" reflections — keep the drawer signal-dense.
- Do not save a drawer per file changed — one drawer per rationale.
- Do not invent rejected alternatives that weren't actually considered. Empty section is fine.
Known quirks — do not forget
- UTF-8 on Windows: always
export PYTHONIOENCODING=utf-8before calling the CLI from Bash, otherwise it crashes withcp1252encoding errors on Unicode confidence bars and icons. --yes≠ fully non-interactive: the flag skips entity confirmation but NOT the room confirmation prompt. Pipeprintf '\n'into stdin.- Entity auto-detector is noisy: on code repos it latches onto generic English words ("Transaction", "Functions", "React", "Native"). Always auto-accept with
--yesand then overwriteentities.jsonmanually with the clean single-project entry. mempalace statushas a ~10k drawer display cap: that's a display bug, not a storage limit. The palace can hold far more than 10k. Use it to verify wings exist, not for accurate totals.- Mines on large repos are slow and silent: 10+ minutes for 1000+ files. Stdout is fully buffered when not a TTY — don't interpret empty output as "stuck". Check
~/.mempalace/palace/chroma.sqlite3mtime/size for actual progress. - Do NOT install
hooks/mempal_save_hook.sh: path traversal issue (upstream #121) not yet patched. Rely on this skill + CLAUDE.md instructions instead. - AAAK compression mode is a regression: the README itself acknowledges it scores lower on retrieval than raw mode. The palace is in raw mode; leave it there.
- No concurrent writes: never run two
mempalace minecommands in parallel — they share a single ChromaDB instance and will conflict. - NEVER kill a mine mid-run: ChromaDB's HNSW vector index cannot recover from a killed writer. Killing a mine corrupts the HNSW for the entire palace (not just the wing being mined), making every wing unreadable with
Error loading hnsw index. The only recovery is to wipe~/.mempalace/palace/and re-mine every wing. If a mine is taking too long, let it finish or accept the wipe — do not kill. - MCP server processes hold the HNSW exclusively: before doing any direct-to-chromadb work (bulk delete, compaction, repair), stop any running
python -m mempalace.mcp_serverprocesses. They'll auto-restart on next tool call. Detect withpowershell -NoProfile -Command "Get-CimInstance Win32_Process -Filter 'Name=\"python.exe\"' | Where-Object { \$_.CommandLine -like '*mempalace*mcp_server*' } | Select-Object ProcessId,CommandLine | Format-List". .mypy_cache/is NOT in standard Python .gitignore templates: if a project uses mypy, explicitly add.mypy_cache/,.pytest_cache/, and.ruff_cache/to its.gitignorebefore mining. Otherwise mempalace indexes tens of thousands of.data.jsonfiles per wing. Same caution for any tool-specific cache dir (.next/,.turbo/,.nx/cache,.gradle/caches).- Vectors are NOT in sqlite: the palace
chroma.sqlite3stores metadata and documents; actual embedding vectors live in HNSW.binfiles under<palace>/<uuid>/. Wiping the HNSW dir = losing all vectors = must re-embed everything = full re-mine. - Python via bash on Windows — use quoted heredocs, not
-c: direct chromadb / sqlite operations commonly hit a 3-layer escape minefield (JSON tool-call encoding + bash string escaping + Python backslash literals). The canonical pattern is:
The quoted delimiter ("%USERPROFILE%/pipx/venvs/mempalace/Scripts/python.exe" - <<'PYEOF' import chromadb, os client = chromadb.PersistentClient(path=os.path.expanduser("~/.mempalace/palace")) col = client.get_collection("mempalace_drawers") src = r"C:\path\to\your\project" # raw string — dodges Python's backslash escape print(col.count(), src.replace(chr(92), "/")) PYEOF'PYEOF') is what disables bash expansion. Raw strings (r"...") handle Python's own backslash interpretation. Together they neutralize all layers. Never embed chromadb/sqlite Python inside a double-quotedpython -c "..."on this machine — every session that does gets bitten. For anything >30 lines or that will be re-run, write a file instead.
Quick reference — CLI paths
- Python binary:
%USERPROFILE%/pipx/venvs/mempalace/Scripts/python.exe - CLI shim:
mempalace(on PATH via pipx) - MCP entry:
python -m mempalace.mcp_server - Palace data:
~/.mempalace/palace/ - Knowledge graph:
~/.mempalace/knowledge_graph.sqlite3 - Per-project config:
<project>/mempalace.yamland<project>/entities.json