8a4279f773
Pi moved to its new home at earendil-works on 2026-05-07 (https://pi.dev/news/2026/5/7/pi-has-a-new-home). URL substitution: https://github.com/mariozechner/pi-coding-agent -> https://github.com/earendil-works/pi (coding-agent now lives at packages/coding-agent inside the new monorepo root, but linking to the root is the canonical reference per the announcement post). Brew install references (`brew install pi-coding-agent`) left as-is: the homebrew formula still works at 0.73.1 and a tap update is tracked upstream at earendil-works/pi#2755.
277 lines
11 KiB
Bash
Executable File
277 lines
11 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# install.sh — install pi-toolkit
|
|
#
|
|
# Harness-side bring-up for the pi coding-agent (https://github.com/earendil-works/pi):
|
|
# - pi-env.zsh : shell loader sourcing ~/.config/pi/.env so AWS_PROFILE
|
|
# and AWS_REGION are in every shell that launches pi.
|
|
# - keybindings.json : mosh/tmux-friendly newline bindings
|
|
# (shift+enter, ctrl+j, alt+j).
|
|
# - settings.example.json : template for ~/.pi/agent/settings.json so pi
|
|
# starts without --provider/--model. Copy + edit.
|
|
#
|
|
# No dependency on MemPalace. For the palace bridge see
|
|
# https://gitea.jordbo.se/joakimp/mempalace-toolkit (optional).
|
|
#
|
|
# Idempotent. Safe to re-run. Non-destructive on drift: if installed content
|
|
# differs from repo, leaves user edits alone and points at diff.
|
|
|
|
set -euo pipefail
|
|
|
|
# ── locate self ──────────────────────────────────────
|
|
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
|
|
|
# ── targets ──────────────────────────────────────────
|
|
PI_EXT_DEST_DIR="${HOME}/.pi/agent/extensions" # existence = pi is installed
|
|
PI_AGENT_DIR="${HOME}/.pi/agent"
|
|
|
|
# keybindings — symlinked (pi doesn't rewrite it at runtime)
|
|
PI_KEYS_SRC="${SCRIPT_DIR}/keybindings.json"
|
|
PI_KEYS_DEST="${HOME}/.pi/agent/keybindings.json"
|
|
|
|
# settings template — NOT installed, only referenced (pi rewrites settings.json)
|
|
PI_SETTINGS_EXAMPLE="${SCRIPT_DIR}/settings.example.json"
|
|
PI_SETTINGS_DEST="${HOME}/.pi/agent/settings.json"
|
|
|
|
# shell env loader — cp'd into oh-my-zsh custom dir (portability over symlink)
|
|
PI_ENV_SRC="${SCRIPT_DIR}/pi-env.zsh"
|
|
PI_ENV_OMZ_DEST="${HOME}/.oh-my-zsh/custom/pi-env.zsh"
|
|
|
|
# ── args ─────────────────────────────────────────────
|
|
ACTION="install"
|
|
ASSUME_YES="no"
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--uninstall) ACTION="uninstall"; shift ;;
|
|
-y|--yes) ASSUME_YES="yes"; shift ;;
|
|
-h|--help)
|
|
cat <<EOF
|
|
install.sh — install pi-toolkit
|
|
|
|
Usage:
|
|
./install.sh # install (interactive confirm)
|
|
./install.sh --yes # install without prompt
|
|
./install.sh --uninstall # remove symlinks and copied loaders
|
|
|
|
What install does:
|
|
- Requires pi installed (~/.pi/agent/ must exist). Aborts cleanly otherwise.
|
|
- Symlinks keybindings.json into ~/.pi/agent/keybindings.json.
|
|
- If oh-my-zsh is present (~/.oh-my-zsh/custom/), copies pi-env.zsh there
|
|
so every new zsh shell sources ~/.config/pi/.env. Uses cp (not symlink)
|
|
for dotfile-backup portability. Prints source snippet otherwise.
|
|
- Probes (non-halting warnings): settings.json present, and AWS_PROFILE /
|
|
AWS_REGION set if settings.json selects amazon-bedrock.
|
|
|
|
What uninstall does:
|
|
- Removes keybindings.json symlink if it points into this repo.
|
|
- Removes pi-env.zsh copy ONLY if its content still matches the repo.
|
|
- Never touches settings.json (it's your file, not managed here).
|
|
|
|
No dependency on MemPalace. For memory integration see:
|
|
https://gitea.jordbo.se/joakimp/mempalace-toolkit
|
|
EOF
|
|
exit 0 ;;
|
|
*) echo "Unknown flag: $1" >&2; exit 2 ;;
|
|
esac
|
|
done
|
|
|
|
# ── helpers ──────────────────────────────────────────
|
|
ok() { printf ' \e[32m✓\e[0m %s\n' "$*"; }
|
|
note() { printf '==> %s\n' "$*"; }
|
|
warn() { printf ' \e[33m!\e[0m %s\n' "$*" >&2; }
|
|
err() { printf ' \e[31m✗\e[0m %s\n' "$*" >&2; }
|
|
|
|
confirm() {
|
|
[[ "$ASSUME_YES" == "yes" ]] && return 0
|
|
read -r -p "Proceed? [y/N] " ans
|
|
[[ "$ans" =~ ^[Yy]$ ]]
|
|
}
|
|
|
|
link_if_into_repo() {
|
|
# Return 0 if $1 is a symlink pointing into $SCRIPT_DIR
|
|
local target
|
|
[[ -L "$1" ]] || return 1
|
|
target=$(readlink -f "$1")
|
|
[[ "$target" == "$SCRIPT_DIR"/* ]]
|
|
}
|
|
|
|
require_pi_installed() {
|
|
if [[ ! -d "$PI_AGENT_DIR" ]]; then
|
|
err "pi not detected at $PI_AGENT_DIR"
|
|
printf ' Install pi first: https://github.com/earendil-works/pi\n'
|
|
printf ' Re-run this installer after `pi` has been started at least once\n'
|
|
printf ' (first run creates ~/.pi/agent/).\n'
|
|
exit 4
|
|
fi
|
|
# Ensure the extensions dir exists so pi loads any extensions we may add
|
|
# later (e.g. mempalace-toolkit's mempalace.ts). pi creates this on first
|
|
# extension install but we may race ahead of that.
|
|
mkdir -p "$PI_EXT_DEST_DIR"
|
|
ok "pi detected at $PI_AGENT_DIR"
|
|
}
|
|
|
|
# ── install ──────────────────────────────────────────
|
|
install_keybindings() {
|
|
# Generic mosh/tmux newline fix. Symlinked so edits flow through git.
|
|
note "Linking keybindings.json → $PI_KEYS_DEST"
|
|
if [[ -e "$PI_KEYS_DEST" || -L "$PI_KEYS_DEST" ]]; then
|
|
if link_if_into_repo "$PI_KEYS_DEST"; then
|
|
ok "pi keybindings already linked"
|
|
return 0
|
|
fi
|
|
# Back up any existing real file / foreign symlink.
|
|
local backup="${PI_KEYS_DEST}.bak.$(date +%Y%m%d-%H%M%S)"
|
|
mv "$PI_KEYS_DEST" "$backup"
|
|
warn "Existing $PI_KEYS_DEST backed up to $backup"
|
|
fi
|
|
ln -s "$PI_KEYS_SRC" "$PI_KEYS_DEST"
|
|
ok "Linked keybindings.json → $PI_KEYS_SRC"
|
|
}
|
|
|
|
install_env_loader() {
|
|
# Copy pi-env.zsh into ~/.oh-my-zsh/custom/ (auto-loaded by omz).
|
|
# cp not symlink because that dir is typically part of a dotfiles backup
|
|
# and a symlink into this repo breaks on restore to another host.
|
|
# No oh-my-zsh → print a source snippet for manual rc-file addition.
|
|
|
|
if [[ -d "$HOME/.oh-my-zsh/custom" ]]; then
|
|
note "Installing pi-env.zsh into ~/.oh-my-zsh/custom/"
|
|
if [[ -f "$PI_ENV_OMZ_DEST" ]]; then
|
|
if cmp -s "$PI_ENV_SRC" "$PI_ENV_OMZ_DEST"; then
|
|
ok "pi-env.zsh already installed (content matches repo)"
|
|
return 0
|
|
fi
|
|
warn "$PI_ENV_OMZ_DEST exists and differs from repo copy"
|
|
printf ' Leaving your edits alone. Compare with:\n'
|
|
printf ' diff %q %q\n' "$PI_ENV_OMZ_DEST" "$PI_ENV_SRC"
|
|
printf ' To adopt the repo version: rm that file and re-run install.sh\n'
|
|
return 0
|
|
fi
|
|
cp "$PI_ENV_SRC" "$PI_ENV_OMZ_DEST"
|
|
ok "Copied pi-env.zsh → $PI_ENV_OMZ_DEST"
|
|
printf ' Open a new shell (or `exec zsh`) to load AWS_PROFILE / AWS_REGION.\n'
|
|
return 0
|
|
fi
|
|
|
|
# No oh-my-zsh — print shell-specific snippet.
|
|
note "oh-my-zsh not detected — manual shell setup needed"
|
|
local shell_name
|
|
shell_name="$(basename "${SHELL:-bash}")"
|
|
local rc_file
|
|
case "$shell_name" in
|
|
zsh) rc_file="~/.zshrc" ;;
|
|
bash) rc_file="~/.bashrc" ;;
|
|
*) rc_file="~/.${shell_name}rc # adjust for your shell" ;;
|
|
esac
|
|
printf ' Add this line to %s so every shell sources ~/.config/pi/.env:\n' "$rc_file"
|
|
printf ' source %q\n' "$PI_ENV_SRC"
|
|
printf ' (the loader itself is POSIX-compatible — works in bash and zsh.)\n'
|
|
}
|
|
|
|
# ── Probes ───────────────────────────────────────────
|
|
# Never halt. warn + return 0 — mirrors opencode-toolkit and mempalace-toolkit.
|
|
check_pi_settings() {
|
|
# If settings.json missing, pi refuses to start without --provider/--model
|
|
# on every run. Template exists in this repo; point at the cp command.
|
|
if [[ -f "$PI_SETTINGS_DEST" ]]; then
|
|
ok "pi settings.json present at $PI_SETTINGS_DEST"
|
|
return 0
|
|
fi
|
|
|
|
warn "pi settings.json NOT found at $PI_SETTINGS_DEST"
|
|
printf ' Without it, pi must be invoked with --provider/--model every run.\n'
|
|
printf ' Bootstrap from the shipped template:\n'
|
|
printf ' cp %q %q\n' "$PI_SETTINGS_EXAMPLE" "$PI_SETTINGS_DEST"
|
|
printf ' $EDITOR %q # adjust region prefix + model IDs\n' "$PI_SETTINGS_DEST"
|
|
printf ' See README.md for the eu./us./anthropic: prefix rules.\n'
|
|
}
|
|
|
|
check_aws_env() {
|
|
# Only warn if settings.json selects amazon-bedrock. If pi uses a
|
|
# non-Bedrock provider (bare anthropic, openai, ...) AWS creds are
|
|
# irrelevant and this probe would be noise. If settings.json doesn't
|
|
# exist yet, check_pi_settings already told the user to bootstrap it
|
|
# — we can't know which provider they'll pick, so stay quiet here.
|
|
[[ -f "$PI_SETTINGS_DEST" ]] || return 0
|
|
grep -q '"amazon-bedrock"' "$PI_SETTINGS_DEST" 2>/dev/null || return 0
|
|
|
|
if [[ -n "${AWS_PROFILE:-}" && -n "${AWS_REGION:-}" ]]; then
|
|
ok "AWS env present (AWS_PROFILE=$AWS_PROFILE, AWS_REGION=$AWS_REGION)"
|
|
return 0
|
|
fi
|
|
|
|
warn "AWS_PROFILE and/or AWS_REGION not set in this shell"
|
|
printf ' pi with defaultProvider=amazon-bedrock needs both to invoke Bedrock.\n'
|
|
printf ' Recommended layout (matches tor-ms22 dotfiles pattern):\n'
|
|
printf ' ~/.config/pi/.env # AWS_PROFILE=..., AWS_REGION=...\n'
|
|
printf ' The pi-env.zsh loader (installed above) sources it automatically.\n'
|
|
printf ' Open a fresh shell to pick up the vars after creating the file.\n'
|
|
}
|
|
|
|
do_install() {
|
|
echo
|
|
echo "pi-toolkit installer"
|
|
echo "Repository: $SCRIPT_DIR"
|
|
echo
|
|
require_pi_installed
|
|
echo
|
|
echo "==> Installation plan:"
|
|
echo " Symlink keybindings.json → $PI_KEYS_DEST"
|
|
if [[ -d "$HOME/.oh-my-zsh/custom" ]]; then
|
|
echo " Copy pi-env.zsh → $PI_ENV_OMZ_DEST"
|
|
else
|
|
echo " Print manual source-snippet instructions for pi-env.zsh"
|
|
fi
|
|
echo
|
|
confirm || { echo "Aborted."; exit 0; }
|
|
echo
|
|
install_keybindings
|
|
echo
|
|
install_env_loader
|
|
echo
|
|
check_pi_settings
|
|
echo
|
|
check_aws_env
|
|
echo
|
|
ok "Done."
|
|
}
|
|
|
|
# ── uninstall ────────────────────────────────────────
|
|
do_uninstall() {
|
|
echo
|
|
echo "pi-toolkit uninstaller"
|
|
echo "Repository: $SCRIPT_DIR"
|
|
echo
|
|
confirm || { echo "Aborted."; exit 0; }
|
|
echo
|
|
|
|
note "Removing pi keybindings symlink"
|
|
if link_if_into_repo "$PI_KEYS_DEST"; then
|
|
rm "$PI_KEYS_DEST"
|
|
ok "Removed pi keybindings symlink"
|
|
else
|
|
ok "No pi keybindings symlink to remove"
|
|
fi
|
|
|
|
echo
|
|
note "Removing pi-env.zsh loader (oh-my-zsh path)"
|
|
# Only remove if content still matches the repo copy — user may have
|
|
# local edits we shouldn't silently discard.
|
|
if [[ -f "$PI_ENV_OMZ_DEST" ]] && cmp -s "$PI_ENV_SRC" "$PI_ENV_OMZ_DEST"; then
|
|
rm "$PI_ENV_OMZ_DEST"
|
|
ok "Removed $PI_ENV_OMZ_DEST"
|
|
elif [[ -f "$PI_ENV_OMZ_DEST" ]]; then
|
|
warn "$PI_ENV_OMZ_DEST differs from repo copy — left alone"
|
|
else
|
|
ok "No pi-env.zsh loader to remove"
|
|
fi
|
|
|
|
echo
|
|
ok "Done."
|
|
}
|
|
|
|
case "$ACTION" in
|
|
install) do_install ;;
|
|
uninstall) do_uninstall ;;
|
|
esac
|