# AGENTS.md ## What this is Harness-side bring-up for the [pi coding-agent](https://github.com/earendil-works/pi). Installs three pi-generic config artifacts into the right places and runs a couple of non-halting probes. Does not depend on MemPalace. Sibling to [`opencode-toolkit`](https://gitea.jordbo.se/joakimp/opencode-toolkit) — same shape: one `install.sh` at the root, cp-or-symlink the config files, probe the environment, compose independently of the palace memory layer. Read [`README.md`](README.md) first for the user-facing walk-through. This file is for agents modifying the repo. ## Structure ``` install.sh # Idempotent installer (symlink + cp + probes) pi-env.zsh # Shell loader sourcing ~/.config/pi/.env (POSIX-compatible) keybindings.json # ~/.pi/agent/keybindings.json — mosh/tmux newline fix settings.example.json # Template for ~/.pi/agent/settings.json (copy + edit) README.md # User-facing quickstart + new-machine deploy recipe. AGENTS.md # This file. LICENSE # MIT. ``` ## Conventions - **One install step per artifact**, with a matching uninstall step. - **Symlink what's safe to symlink, cp what's part of a dotfiles backup, template what the harness rewrites.** - `keybindings.json` → symlink. pi doesn't rewrite it; the symlink lets edits flow through git without a re-install step. - `pi-env.zsh` → `cp` into `~/.oh-my-zsh/custom/`. That dir is typically part of a dotfiles backup (e.g. `myconfigs`'s `rsync_copy.sh`); a symlink into this repo's absolute path breaks when the backup is restored on another host. - `settings.example.json` → **not installed**. pi rewrites `~/.pi/agent/settings.json` at runtime (`lastChangelogVersion` bumps on upgrade), so any symlink back to the repo would dirty the working tree. Installer prints the `cp` command and walks away. - **Probes warn, never halt.** `warn` + `return 0`. The installer's only hard-fail path is "pi isn't installed at all" — that's an early `exit 4` from `require_pi_installed` because there's nothing useful to do without `~/.pi/agent/`. - **Non-destructive.** Existing real files at symlink destinations get backed up with a timestamp (`.bak.YYYYMMDD-HHMMSS`) before linking. The shell loader's cp path refuses to clobber local edits: detects drift via `cmp -s`, prints a `diff` hint, leaves the file alone. - **Do not auto-edit rc files.** If oh-my-zsh isn't present, print the `source` snippet the user manually adds to `~/.zshrc` or `~/.bashrc`. Invasive shell-rc edits belong in a dotfiles installer, not here. - **No hard dependency on mempalace.** If the surface ever grows mempalace-aware (unlikely — those pieces live in `mempalace-toolkit` on purpose), it must degrade gracefully when mempalace is absent. - **POSIX-compatible shell glue.** `set -a` / `source` / `set +a` work in bash and zsh both. Don't add zsh-only constructs to files that may end up sourced from `~/.bashrc`. ## What `install.sh` does Hard-required: - `require_pi_installed` — aborts with exit 4 if `~/.pi/agent/` is missing. Creates `~/.pi/agent/extensions/` proactively so a later `mempalace-toolkit` install has somewhere to symlink into. Always (once pi is present): - Symlinks `keybindings.json` into `~/.pi/agent/`. Backs up any pre-existing real file. oh-my-zsh path (gated on `~/.oh-my-zsh/custom/` existing): - Copies `pi-env.zsh` into that directory. Drift-safe — prints a `diff` hint if installed content differs from repo, doesn't clobber. No oh-my-zsh path: - Prints a shell-specific source snippet for `~/.zshrc` or `~/.bashrc` (selected from `$SHELL`). User pastes manually. Probes (non-halting): - `~/.pi/agent/settings.json` exists. Without it pi refuses to start without `--provider`/`--model`. Prints the `cp settings.example.json` command. - `AWS_PROFILE` + `AWS_REGION` are set, **only if** `settings.json` selects `amazon-bedrock`. Silent for non-Bedrock providers. Silent if settings.json is missing (check_pi_settings already handled that case). ## Adding a new artifact Follow the same shape `keybindings.json` / `pi-env.zsh` / `settings.example.json` use: 1. **One file at repo root.** Flat layout; no `extensions/` subdir yet. Revisit if five+ artifacts ever accumulate. 2. **Choose symlink / cp / template** per the conventions above. Document the choice in a comment next to the variable declarations at the top of `install.sh`. 3. **One install step + one uninstall step** in `install.sh`, guarded by `link_if_into_repo` (for symlinks) or `cmp -s` (for copies). Never remove user edits silently. 4. **One probe per external dependency** the artifact implies. `warn` + `return 0`. 5. **Update this file's Structure block** and `README.md`. ## Adding mempalace-aware functionality Don't. Those pieces belong in `mempalace-toolkit/extensions/pi/`: - The mempalace MCP bridge TypeScript extension (`mempalace.ts`) — imports pi's `ExtensionAPI`, registers MCP tools, injects wake-up context. - The `install_pi_extension` function that symlinks it in. - Any probes that check for mempalace being reachable *through* pi. That boundary is load-bearing for `opencode-devbox`'s slim container path: a container built with `INSTALL_MEMPALACE=false` should install pi-toolkit cleanly and get a functional pi without dragging in chromadb + embedding models (~300 MB). ## Testing No framework. Manual: ```bash ./install.sh --help # flags ./install.sh --yes # fresh install ./install.sh --yes # re-run (idempotent) ./install.sh --uninstall --yes # remove ./install.sh --yes # reinstall ``` oh-my-zsh fallback simulation: ```bash SHELL=/bin/bash HOME=/tmp/fake ./install.sh --yes # should print ~/.bashrc snippet ``` Environment verification (after install + `exec zsh` or new shell): ```bash zsh -ic 'echo $AWS_PROFILE $AWS_REGION' # should print values from ~/.config/pi/.env ``` Drift simulation: ```bash echo "# local edit" >> ~/.oh-my-zsh/custom/pi-env.zsh ./install.sh --yes # should warn "differs from repo" and leave alone ``` ## Gotchas - **pi must be installed upstream first.** `~/.pi/agent/` is created by pi on first run, not by this installer. Missing → exit 4 with pointer to https://github.com/earendil-works/pi. - **`settings.example.json` is region-specific.** The shipped default uses `eu.` Bedrock inference-profile prefixes. Users on us-east need to swap to `us.` before first run, or pi will reject the model ID. - **Bedrock is not the only provider.** If the user is on bare Anthropic or OpenAI, `AWS_PROFILE` / `AWS_REGION` are irrelevant. The probe gates on `grep -q '"amazon-bedrock"' settings.json` to stay quiet in those cases. - **Mosh strips modifiers upstream of tmux.** The keybindings file's `ctrl+j` / `alt+j` fallbacks are specifically because mosh's vt220-ish emulation does not forward `Shift+Enter` as CSI-u — tmux's `extended-keys csi-u` setting doesn't help over mosh (only helps on direct ssh / local / WireGuard). Documented in README. - **The shell loader runs on every shell start.** Keep it fast. If it grows beyond a few lines, gate on `[[ $- == *i* ]]` so non-interactive shells skip the work. - **`~/.config/pi/.env` should be chmod 600.** It's plaintext on disk; git-crypt encryption only applies in the dotfiles repo. The installer does not enforce this because it doesn't ship the file. ## Related repos - [`mempalace-toolkit`](https://gitea.jordbo.se/joakimp/mempalace-toolkit) — adds a pi↔mempalace MCP bridge on top of this toolkit. Installs the `mempalace.ts` extension into `~/.pi/agent/extensions/`. Optional. - [`opencode-toolkit`](https://gitea.jordbo.se/joakimp/opencode-toolkit) — sibling pattern for the opencode coding-agent. Same install.sh shape. - [`opencode-devbox`](https://gitea.jordbo.se/joakimp/opencode-devbox) — Docker containers; composes any subset of these toolkits via independent install.sh invocations. - [`myconfigs`](https://gitea.jordbo.se/joakimp/myconfigs) — dotfiles repo where `~/.config/pi/.env` is tracked (git-crypt encrypted). ## History Split out of `mempalace-toolkit/extensions/pi/` on 2026-05-05. Previously the pi-generic config artifacts (env loader, keybindings, settings template) lived alongside the `mempalace.ts` MCP bridge inside mempalace-toolkit because that's where the pattern first crystallized. The split was motivated by `opencode-devbox`'s mempalace opt-out and future pi-option: a container built with mempalace off should still be able to install a functional pi, and that dependency asymmetry is cleanest when pi's own config lives in its own repo. Mirrors the 2026-05-05 split of `opencode-toolkit` out of the same parent.