118bd20fec
The loader that sources ~/.config/pi/.env into every shell was only
living in the myconfigs tor-ms22 backup \u2014 a fresh machine had nowhere
to get it from except copying by hand. Now canonical here.
- extensions/pi/pi-env.zsh: 20-line POSIX-compatible loader
(set -a; source ~/.config/pi/.env; set +a). Works in bash and zsh.
- install.sh install_pi_env_loader:
* oh-my-zsh detected (~/.oh-my-zsh/custom/ exists)
\u2192 cp into that dir (NOT symlink \u2014 that dir is typically part of
a dotfiles backup, and a symlink to mempalace-toolkit would
break when restored on another host).
\u2192 Idempotent: if target content matches repo, says 'already
installed'. If it differs, leaves user edits alone and points
at diff for manual reconcile.
* No oh-my-zsh \u2192 prints source-this-line snippet for ~/.zshrc or
~/.bashrc (derived from $SHELL). Does NOT auto-edit rc files.
- install.sh uninstall: only removes the copy if content still matches
repo. Local edits preserved.
- Docs:
* extensions/pi/README.md Environment setup section rewritten with
both install paths, step 5 of deploy recipe updated.
* AGENTS.md Structure block lists pi-env.zsh.
* Root README repo-contents line mentions it.
Verified on tor-ms22: install fresh \u2192 uninstall (content match \u2192 remove)
\u2192 reinstall \u2192 zsh -ic loads AWS vars correctly. Also tested bash fallback
path via HOME=/tmp/fake-home SHELL=/bin/bash \u2014 prints right .bashrc snippet.
309 lines
12 KiB
Markdown
309 lines
12 KiB
Markdown
# pi ↔ MemPalace extension
|
|
|
|
The canonical source of `~/.pi/agent/extensions/mempalace.ts` — the bridge
|
|
that wires the [MemPalace](https://github.com/MemPalace/mempalace) MCP
|
|
server into the [pi coding-agent](https://github.com/mariozechner/pi-coding-agent)
|
|
harness.
|
|
|
|
`install.sh` at the repo root symlinks `mempalace.ts` from this directory
|
|
into `~/.pi/agent/extensions/` so the live file on every machine tracks
|
|
version control. Works on macOS and Linux (the extension itself is plain
|
|
Node / TypeScript; the symlink is a POSIX `ln -s`).
|
|
|
|
**Jump to:**
|
|
- [Deploying pi on a new machine](#deploying-pi-on-a-new-machine) — step-by-step recipe.
|
|
- [Keybindings (mosh/tmux newline fix)](#keybindings-moshtmux-newline-fix)
|
|
- [Settings template](#settings-template-start-pi-without---model)
|
|
- [Environment setup](#environment-setup)
|
|
|
|
---
|
|
|
|
## Deploying pi on a new machine
|
|
|
|
Full recipe from a clean macOS or Linux box to a working pi+MemPalace
|
|
install with all modifications shipped by this repo and by
|
|
[`myconfigs`](https://gitea.jordbo.se/joakimp/myconfigs). Follow in order.
|
|
|
|
### 0. Prerequisites
|
|
|
|
- Shell: **zsh + oh-my-zsh** (the env loader is `~/.oh-my-zsh/custom/pi-env.zsh`).
|
|
On bash-only hosts, adapt by sourcing `~/.config/pi/.env` from `~/.bashrc`.
|
|
- `git`, `node` ≥ 20, `uv` (for installing mempalace), `tmux` ≥ 3.2.
|
|
- AWS credentials reachable via `AWS_PROFILE` (either `aws configure sso`
|
|
cache or static keys in `~/.aws/credentials`) — **only if** you'll use
|
|
`amazon-bedrock` as pi's provider.
|
|
|
|
### 1. Clone your dotfiles repo and provision
|
|
|
|
Brings `~/.tmux.conf` with CSI-u extended keys, `~/.config/pi/.env`
|
|
(git-crypt encrypted), and `~/.oh-my-zsh/custom/pi-env.zsh`:
|
|
|
|
```bash
|
|
git clone ssh://git@gitea.jordbo.se:2222/joakimp/myconfigs.git ~/src/src_local/myconfigs
|
|
cd ~/src/src_local/myconfigs
|
|
|
|
# Unlock git-crypt so ~/.config/pi/.env decrypts (skip on a box that has
|
|
# never held your git-crypt key; see myconfigs/GIT-CRYPT.md to set up).
|
|
git-crypt unlock ~/path/to/git-crypt-key
|
|
|
|
# Provision — choose the profile matching the box (homelab, work-macos, ...).
|
|
./provision.sh --dry-run --profile homelab # preview
|
|
./provision.sh --profile homelab # apply
|
|
```
|
|
|
|
### 2. Install pi (upstream)
|
|
|
|
```bash
|
|
brew install pi-coding-agent # macOS
|
|
# or: follow https://github.com/mariozechner/pi-coding-agent for Linux
|
|
```
|
|
|
|
First run creates `~/.pi/agent/`.
|
|
|
|
### 3. Install mempalace + the toolkit
|
|
|
|
```bash
|
|
# MemPalace CLI (isolated venv via uv, shim in ~/.local/bin)
|
|
uv tool install mempalace
|
|
|
|
# mempalace-toolkit (this repo) — the bin/ wrappers, the pi extension,
|
|
# keybindings, settings template, and install probes.
|
|
git clone ssh://git@gitea.jordbo.se:2222/joakimp/mempalace-toolkit.git ~/mempalace-toolkit
|
|
cd ~/mempalace-toolkit
|
|
./install.sh
|
|
```
|
|
|
|
`install.sh` detects pi, symlinks `mempalace.ts` + `keybindings.json` into
|
|
`~/.pi/agent/`, installs the companion skill, and runs five probes. The
|
|
AWS probe stays quiet until step 4 selects `amazon-bedrock`.
|
|
|
|
### 4. Bootstrap pi settings (start pi without `--model`)
|
|
|
|
```bash
|
|
cp ~/mempalace-toolkit/extensions/pi/settings.example.json \
|
|
~/.pi/agent/settings.json
|
|
$EDITOR ~/.pi/agent/settings.json
|
|
```
|
|
|
|
Adjust the inference-profile prefix to match your AWS region:
|
|
|
|
| Region | Prefix | Example model ID |
|
|
|---|---|---|
|
|
| eu-west-1 | `eu.` | `eu.anthropic.claude-sonnet-4-6` |
|
|
| us-east-1 | `us.` | `us.anthropic.claude-sonnet-4-6` |
|
|
| non-Bedrock | (none) | `anthropic:claude-sonnet-4-6` |
|
|
|
|
Run `pi --list-models` to confirm what your credentials can actually invoke.
|
|
|
|
### 5. Ensure AWS env vars are live in your shell
|
|
|
|
**On zsh + oh-my-zsh hosts:** step 3's `install.sh` already copied
|
|
`pi-env.zsh` into `~/.oh-my-zsh/custom/`, so every new shell sources
|
|
`~/.config/pi/.env` automatically. Verify:
|
|
|
|
```bash
|
|
exec zsh
|
|
echo "$AWS_PROFILE $AWS_REGION" # should print your values
|
|
```
|
|
|
|
**On bash or plain zsh (no oh-my-zsh):** `install.sh` printed a
|
|
`source <repo>/extensions/pi/pi-env.zsh` snippet — add that one line to
|
|
`~/.bashrc` or `~/.zshrc`, open a fresh shell, verify as above.
|
|
|
|
If vars are empty, check that `~/.config/pi/.env` decrypted
|
|
(`head ~/.config/pi/.env` should show plain text, not binary).
|
|
`git-crypt unlock` in step 1 is the usual culprit when this is empty.
|
|
|
|
### 6. Register mempalace MCP with opencode (if using opencode too)
|
|
|
|
Skip if this box is pi-only. Otherwise see
|
|
[root README § Registering mempalace with opencode](../../README.md#registering-mempalace-with-opencode-or-other-mcp-clients).
|
|
|
|
### 7. First run
|
|
|
|
```bash
|
|
pi # should start with the default model, no --model needed
|
|
```
|
|
|
|
Inside pi, the wake-up auto-injection should print a `mempalace-wakeup`
|
|
system message with palace status and recent diary entries. If it doesn't,
|
|
run `MEMPALACE_EXT_DEBUG=1 pi` to surface `mempalace-mcp` stderr.
|
|
|
|
### Verification checklist
|
|
|
|
```bash
|
|
# Symlinks in place
|
|
ls -la ~/.pi/agent/mempalace.ts ~/.pi/agent/keybindings.json # → repo
|
|
ls -la ~/.agents/skills/opencode-mempalace-bridge/SKILL.md # → repo
|
|
|
|
# Env loaded
|
|
zsh -ic 'echo $AWS_PROFILE $AWS_REGION'
|
|
|
|
# tmux extended keys
|
|
tmux show-options -g | grep extended-keys # csi-u
|
|
|
|
# Palace reachable
|
|
mempalace status
|
|
|
|
# Installer re-run is idempotent
|
|
cd ~/mempalace-toolkit && ./install.sh --yes # all rows should say "already linked"
|
|
```
|
|
|
|
### Uninstall
|
|
|
|
```bash
|
|
cd ~/mempalace-toolkit && ./install.sh --uninstall --yes
|
|
# Leaves mempalace CLI, pi binary, and ~/.config/pi/.env alone —
|
|
# only removes symlinks this repo created.
|
|
```
|
|
|
|
---
|
|
|
|
## What it does
|
|
|
|
1. **Spawns `mempalace-mcp`** as a subprocess and does the MCP stdio
|
|
JSON-RPC handshake (`initialize` + `notifications/initialized` +
|
|
`tools/list`).
|
|
2. **Registers each MCP tool** as a pi tool with its real `inputSchema`
|
|
passed through via `Type.Unsafe(...)` (see gotcha below).
|
|
3. **Wake-up auto-injection** (`before_agent_start`, one-shot per fresh
|
|
session): calls `mempalace_status` + `mempalace_diary_read` and
|
|
injects the result as a `mempalace-wakeup` system message so the
|
|
agent orients itself the way `~/.agents/skills/mempalace/SKILL.md`
|
|
describes. Skipped on resume/fork (context is already in the thread).
|
|
4. **Manual wind-down** via a `/mempalace-diary [topic]` slash command:
|
|
sends a prompt asking the LLM to call `mempalace_diary_write` with
|
|
an AAAK-formatted entry summarizing the session. Not fully auto
|
|
because pi sessions are typically short/tactical and
|
|
`session_shutdown` fires too late to drive another LLM turn.
|
|
|
|
## Fail-soft
|
|
|
|
If `mempalace-mcp` can't be spawned (PATH missing, binary crashes at
|
|
startup, …) the extension logs to stderr and returns early. pi keeps
|
|
working without palace tools rather than refusing to start.
|
|
|
|
## Identity
|
|
|
|
`agent_name` for diary calls comes from `$MEMPALACE_AGENT_NAME`, defaulting
|
|
to `"pi"`. First diary write against that identity creates `wing_<name>`
|
|
in the palace. Set the env var if you want to run pi under a distinct
|
|
identity on a given machine (e.g. `pi-laptop` vs `pi-server`).
|
|
|
|
## Debugging
|
|
|
|
- `MEMPALACE_EXT_DEBUG=1` — surface `mempalace-mcp` stderr into pi's
|
|
stderr. Without this, stderr is drained silently so a misbehaving
|
|
server doesn't flood the TUI.
|
|
- If a tool call fails with a generic "Internal tool error", spawn
|
|
`mempalace-mcp` manually with raw JSON-RPC on stdin to read the
|
|
server-side error — much faster than guessing.
|
|
|
|
## The `Type.Unsafe` gotcha
|
|
|
|
Earlier versions of this extension registered every MCP tool with
|
|
`parameters: Type.Object({}, { additionalProperties: true })`, which
|
|
discarded each tool's real `inputSchema`. The LLM then saw no parameter
|
|
names and had to guess, leading to bugs like `mempalace_diary_read`
|
|
being called with `agent=` instead of the required `agent_name=` and
|
|
crashing the Python server with `TypeError: missing 1 required
|
|
positional argument`.
|
|
|
|
The fix (≈ lines 160-170) is to wrap the incoming JSON Schema with
|
|
`Type.Unsafe<...>(tool.inputSchema)`. TypeBox schemas are plain JSON
|
|
Schema at runtime plus a `Symbol` marker, so wrapping an
|
|
externally-sourced schema with `Unsafe` is sufficient — no conversion
|
|
to a full TypeBox tree is needed, and the LLM now sees every tool's
|
|
real parameter names.
|
|
|
|
If you ever need to re-loosen the schema for debugging, fall back to
|
|
the `Type.Object({}, { additionalProperties: true })` default only for
|
|
that specific tool, not globally.
|
|
|
|
## Keybindings (mosh/tmux newline fix)
|
|
|
|
`keybindings.json` is symlinked so edits flow through git. Default:
|
|
|
|
```json
|
|
{
|
|
"tui.input.newLine": ["shift+enter", "ctrl+j", "alt+j"]
|
|
}
|
|
```
|
|
|
|
Rationale: when pi runs over `kitty → mosh → tmux`, shift+enter doesn't
|
|
forward cleanly (mosh uses vt220-ish emulation, no kitty-keyboard-protocol
|
|
or csi-u extended keys). `ctrl+j` and `alt+j` pass through as plain
|
|
control/meta bytes and give you reliable newline insertion.
|
|
|
|
## Settings template (start pi without `--model`)
|
|
|
|
`settings.example.json` is a template — **not symlinked**. pi rewrites
|
|
its `settings.json` at runtime (`lastChangelogVersion` bumps on upgrade),
|
|
which would dirty a symlinked repo file. Instead, bootstrap with:
|
|
|
|
```bash
|
|
cp /path/to/mempalace-toolkit/extensions/pi/settings.example.json \
|
|
~/.pi/agent/settings.json
|
|
$EDITOR ~/.pi/agent/settings.json
|
|
```
|
|
|
|
The Bedrock inference-profile prefix on model IDs (`eu.`, `us.`) is
|
|
**region-specific** and must match `AWS_REGION` in `~/.config/pi/.env`.
|
|
For a bare Anthropic provider (non-Bedrock) drop the prefix entirely
|
|
and use `anthropic:claude-...`. Run `pi --list-models` to confirm what
|
|
your credentials can actually invoke.
|
|
|
|
`install.sh` warns (non-fatal) if `settings.json` is missing.
|
|
|
|
## Environment setup
|
|
|
|
pi with `defaultProvider=amazon-bedrock` needs `AWS_PROFILE` and
|
|
`AWS_REGION` exported into the shell that launches it. Recommended
|
|
layout (matches the tor-ms22 dotfiles pattern):
|
|
|
|
```
|
|
~/.config/pi/.env ← AWS_PROFILE=..., AWS_REGION=...
|
|
(git-crypt encrypted in dotfiles repo)
|
|
~/.oh-my-zsh/custom/pi-env.zsh ← installed by mempalace-toolkit install.sh
|
|
(sources the .env into every shell)
|
|
```
|
|
|
|
The loader file `pi-env.zsh` is canonical here in `extensions/pi/` and
|
|
installed by `install.sh` in one of two ways:
|
|
|
|
| Detected | Action |
|
|
|---|---|
|
|
| `~/.oh-my-zsh/custom/` exists | `cp` (not symlink) into that directory — auto-loaded by omz on every new shell. cp not symlink because that directory is part of the dotfiles backup, and a symlink into mempalace-toolkit would break when the backup is restored on another host. |
|
|
| No oh-my-zsh | Prints a shell-specific `source <repo>/extensions/pi/pi-env.zsh` snippet for `~/.zshrc` or `~/.bashrc`. Installer does not auto-edit rc files. |
|
|
|
|
The loader itself is POSIX-compatible (`set -a` / `source` / `set +a`),
|
|
so bash users can source it directly — no zsh dependency in the file.
|
|
|
|
Re-runs are idempotent: if the installed copy matches the repo, prints
|
|
"already installed". If it differs, leaves your edits alone and points
|
|
at `diff` for comparison. Uninstall only removes the file if it still
|
|
matches the repo copy; local edits are preserved.
|
|
|
|
Historical note: these vars used to live under a `# Environment variables
|
|
for pi` block inside `~/.config/opencode/.env`. Split out 2026-05-05 so
|
|
each tool owns its own env file. `install.sh` runs a `check_aws_env`
|
|
probe that warns if the vars are missing and points back here.
|
|
|
|
## File layout
|
|
|
|
```
|
|
mempalace-toolkit/
|
|
└── extensions/
|
|
└── pi/
|
|
├── README.md ← this file
|
|
├── mempalace.ts ← symlinked into ~/.pi/agent/extensions/
|
|
├── keybindings.json ← symlinked into ~/.pi/agent/
|
|
├── pi-env.zsh ← cp'd into ~/.oh-my-zsh/custom/ (or source'd manually for bash)
|
|
└── settings.example.json ← template; copy + edit into ~/.pi/agent/
|
|
```
|
|
|
|
`install.sh` detects pi by probing for `~/.pi/agent/extensions/` and
|
|
only creates symlinks when that directory exists. On machines without
|
|
pi the files stay dormant in the repo. Re-runs are idempotent (same
|
|
pattern as `bin/` and `SKILL.md`).
|