Files
pi-toolkit/AGENTS.md
T
joakimp 4b4b76e80e feat(pi-atelier): ship Status Rail defaults, cp'd not symlinked
Adds pi-atelier.json (defaults for the pi-atelier extension) and an
install/uninstall step for it, so the config survives a devbox volume wipe and
applies to pi on the host too — ~/.pi/agent is a named volume in a container,
which the extension's own config path alone does not outlive.

cp-if-absent rather than a symlink, unlike keybindings.json/AGENTS.md: the
extension rewrites this exact path when the user saves from its menu
(src/config.ts writeJsonAtomic -> "<path>.<pid>.tmp" then rename). rename(2)
REPLACES a symlink with a regular file instead of following it, so a link would
detach on the first menu save and the repo copy would quietly stop applying.
Verified empirically before choosing the idiom, not assumed.

Drift is respected in both directions, matching the pi-env.zsh shape: install
never clobbers an existing file (warns + prints a diff hint), uninstall removes
it only while its content still matches the repo. All five paths exercised
against an isolated HOME: fresh copy, idempotent re-run, drift-preserved
install, drift-preserved uninstall, matched-content uninstall.

Values chosen for this setup and validated against the extension's own
validateConfig (no warnings, no coercion):
- segments drop "brand" (decoration, and first in the drop order anyway)
- density compact — the rail relayouts at 132/96/72/56 cols and these sessions
  run over plain SSH at unknown width
- contextWarning/Danger 60/85, earlier than the 70/90 default because
  defaultModel is opus-5 at xhigh thinking with obsmem compaction behind it
- showSidebarToolNames true — MCP/tool-heavy sessions, names beat "something
  is running"
- completionNotifications false — deliverSystemNotification only spawns for
  darwin/win32 and returns undefined on linux, and a container has no desktop
  session to reach anyway

Docs: file inventory + install/uninstall tables in README.md and AGENTS.md,
including why the symlink idiom does not apply here.
2026-07-29 23:42:05 +02:00

219 lines
10 KiB
Markdown

# 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
pi-atelier.json # ~/.pi/agent/pi-atelier.json (cp) — Status Rail defaults
pi-global-AGENTS.md # ~/.pi/agent/AGENTS.md (symlink) — global agent instructions
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.
- `pi-atelier.json``cp` **if absent**. The pi-atelier extension
rewrites this exact path when the user saves from its menu, using
write-temp-then-`rename`. `rename(2)` replaces a symlink with a
regular file instead of following it, so a symlink would detach
silently on the first menu save and the repo copy would quietly stop
applying. Verified, not assumed. Drift is respected in both
directions: install won't clobber an existing file, uninstall only
removes one whose content still matches the repo.
- `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.
## Documentation drift sweep
Before committing any non-trivial change, check that prose still matches code. Drift hotspots in this repo:
- `README.md` — install instructions, the file/artifact table, the testing recipe. The installer's behaviour and the README's description of it must stay in lockstep — the README is what users follow.
- `AGENTS.md` (this file) — the `Conventions` section codifies symlink-vs-cp-vs-template decisions per artifact; if you change how an artifact is installed, update the conventions too.
- `install.sh` — contains its own usage block at the top; keep the `--help` output and the README in sync.
- `settings.example.json` — the canonical template for pi settings; if pi adds a new top-level setting that this toolkit blesses with defaults, update the example here.
Quick triage: `git diff --name-only HEAD | xargs -I{} grep -l 'thing-you-changed' README.md AGENTS.md install.sh`.