# pi-extensions Custom and modified extensions for the [pi coding-agent](https://github.com/mariozechner/pi-coding-agent). This repo is the single source of truth for extensions that aren't suitable for general publishing — personal workflow tweaks, modified versions of built-in examples, and extensions written for specific infrastructure. Symlinked into `~/.pi/agent/extensions/` so pi loads them automatically. Part of the same family as [`pi-toolkit`](https://gitea.jordbo.se/joakimp/pi-toolkit) (bring-up) and [`skillset`](https://gitea.jordbo.se/joakimp/skillset) (agent skills). --- ## Install ```bash git clone ssh://git@gitea.jordbo.se:2222/joakimp/pi-extensions.git ~/src/src_local/pi-extensions cd ~/src/src_local/pi-extensions chmod +x install.sh ./install.sh ``` Each `.ts` file in `extensions/` is symlinked into `~/.pi/agent/extensions/`. Existing real files are backed up with a timestamp. Re-runs are idempotent. **Install a subset:** ```bash ./install.sh --only ssh-controlmaster # just this one ./install.sh --only "ssh-controlmaster,other" # explicit list ./install.sh --skip "git-checkpoint" # all except these ``` `--only` and `--skip` accept comma-separated names without the `.ts` suffix. `--only` takes precedence if both are given. ### Alternative: pi install (local path) Because `package.json` declares a `pi` manifest, you can also register this repo as a pi package: ```bash pi install ~/src/src_local/pi-extensions ``` This makes pi manage the extension loading directly. The `install.sh` approach (symlinks) and `pi install` are mutually exclusive for the same extension — pick one per machine. ### Uninstall ```bash ./install.sh --uninstall ``` Removes symlinks that point into this repo. Your own files in `~/.pi/agent/extensions/` are never touched. --- ## Extensions ### `ssh-controlmaster.ts` Transparent SSH remote execution via a persistent ControlMaster socket. When launched with `--ssh user@host`, all of pi's native file and shell tools (`read`, `write`, `edit`, `bash`) are transparently redirected to execute on the remote machine. One SSH connection is established at session start; all subsequent tool calls multiplex over it via a Unix socket. Much faster than the plain `ssh.ts` example which opens a new connection per tool call. **Use cases:** - Diagnose and fix issues on a remote server without installing pi there - Work on Proxmox hosts, LXC containers, or ephemeral VMs - Any machine you have SSH key access to but don't own **Usage:** ```bash # Key-based auth (normal) pi --ssh user@192.168.1.10 # Explicit remote path (skips the initial pwd call) pi --ssh root@proxmox-node:/etc/pve # Password auth — prompts before connecting pi --ssh user@host --ssh-ask-pass # Try without modifying your global install pi -e ~/src/src_local/pi-extensions/extensions/ssh-controlmaster.ts --ssh user@host ``` **Requirements:** - SSH key-based auth (preferred), or password auth via `--ssh-ask-pass` (see below) - `bash` available on the remote > **Note on `--ssh-ask-pass`:** The password is prompted via pi's input dialog > before the SSH connection is opened. Input is **not masked** — the password > is visible while typing. It is passed to SSH via a temporary `SSH_ASKPASS` > script (`/tmp/pi-askpass-.sh`, `chmod 700`) which is deleted > immediately after the master is established. **How it works:** 1. On `session_start`, runs `ssh -G ` to read the effective config for that host 2. If `~/.ssh/config` already configures `ControlMaster auto` or `yes` for the host, the existing system socket is reused — no second connection is opened and pi does **not** tear down the master on exit (it was the system's to manage) 3. Otherwise pi establishes its own master: `ssh -fN -o ControlMaster=yes -o ControlPersist=yes -o ControlPath=/tmp/pi-cm-.sock ` and shuts it down cleanly on exit 4. All tool calls multiplex over the socket with `-o ControlMaster=no -o ControlPath=` — near-zero per-call overhead 5. The system prompt is patched to tell the LLM it's operating on ` (via SSH ControlMaster: )` 6. User `!` shell commands are also routed over SSH The status bar shows `⚡ own master` or `⚡ system master` so you can see which path was taken. **Status bar:** Shows `SSH ⚡ user@host:/path` when the master is ready, `⟳ connecting…` during setup, and an error state if the master fails to start. **Path mapping:** Paths are rewritten by replacing the local `cwd` with the remote `cwd`. This means pi should be started from a directory that maps cleanly to a path on the remote. Use the `user@host:/explicit/path` form when the remote path differs significantly from your local working directory. --- ### `confirm-destructive.ts` Confirmation gates for dangerous bash commands and destructive session actions. Always-on — no flag needed. **Bash commands intercepted:** - Recursive removes (`rm -rf`, `rm -r`, etc.) - Any `sudo` command - `chmod`/`chown 777` - `dd if=` (disk operations) - `mkfs` (format filesystem) - `git push --force` / `git push -f` - Writes to `/dev/*` - `truncate --size 0` In non-interactive mode (e.g. `pi -p`) dangerous commands are blocked outright rather than prompted. **Session actions gated:** - `/new` — confirms before clearing the session - `/resume` — confirms before switching away if the current session has messages - `/fork` — always confirms --- ### `git-checkpoint.ts` Creates a git stash checkpoint at the start of each turn, keyed to the session entry ID. If you `/fork` from a past entry, you're offered the option to restore the code to that point. Silently skips when the working directory isn't inside a git repo, or when there are no changes to stash. Status bar shows `⎇ N checkpoints` during active sessions. **Notes:** - Uses `git stash create` — non-destructive, doesn't touch your working tree - Stash objects persist in the git repo even after pi exits, so you can apply them manually with `git stash apply ` if needed - Checkpoints are in-memory per session — the entry→ref mapping is lost on restart, but the underlying stash objects remain --- ### `notify.ts` Sends a native terminal notification when the agent finishes and is waiting for input. Only fires when the agent ran for longer than the threshold (default 8 seconds) — quick responses are silently skipped. **Terminal support:** - Kitty (`KITTY_WINDOW_ID`) → OSC 99 - Windows Terminal / WSL (`WT_SESSION`) → Windows toast - Everything else (iTerm2, WezTerm, Ghostty) → OSC 777 **Flag:** ```bash pi --notify-min-secs 15 # only notify for tasks over 15 seconds pi --notify-min-secs 0 # notify on every agent completion ``` ### `ext-toggle.ts` Registers `/ext` — a slash command that lists extensions in `~/.pi/agent/extensions/` and toggles individual ones on/off without leaving the TUI. **How it works:** pi auto-discovers `*.ts` only. Toggling renames a file (or symlink) between `name.ts` and `name.ts.off`, so a disabled extension is invisible to the loader. After a toggle, the extension calls `ctx.reload()` so the change takes effect immediately — no restart needed. **Usage:** ``` /ext # opens a picker; ● = active, ○ = disabled ``` **Notes:** - Subdirectory-style extensions (`name/index.ts`) are listed read-only — v1 doesn't toggle them. Move the directory aside manually if needed. - `install.sh --uninstall` cleans up both `.ts` and `.ts.off` symlinks pointing into this repo, so a disabled extension won't be left behind. ## Adding a new extension 1. Drop a `.ts` file into `extensions/` 2. Re-run `./install.sh` — it picks up the new file and symlinks it 3. In a running pi session, `/reload` is enough; no restart needed 4. (or, with `ext-toggle` installed: `/ext` to disable noisy ones at runtime) Each extension is a TypeScript module loaded by [jiti](https://github.com/unjs/jiti) — no compilation step. See the [pi extensions docs](https://github.com/mariozechner/pi-coding-agent/blob/main/docs/extensions.md) and the [built-in examples](https://github.com/mariozechner/pi-coding-agent/tree/main/examples/extensions) for the API surface. --- ## Deploying on a new machine ```bash # 1. Prerequisites: pi installed, SSH key auth working pi --help # creates ~/.pi/agent/ on first run # 2. Clone and install git clone ssh://git@gitea.jordbo.se:2222/joakimp/pi-extensions.git ~/src/src_local/pi-extensions cd ~/src/src_local/pi-extensions && ./install.sh # 3. Verify ls -la ~/.pi/agent/extensions/ # should show symlinks into this repo ``` --- ## Related repos - [`pi-toolkit`](https://gitea.jordbo.se/joakimp/pi-toolkit) — pi bring-up: settings template, keybindings, shell env loader - [`mempalace-toolkit`](https://gitea.jordbo.se/joakimp/mempalace-toolkit) — persistent memory layer for pi via MemPalace MCP - [`skillset`](https://gitea.jordbo.se/joakimp/skillset) — agent skills for pi, opencode, and Claude ## License MIT — see [`LICENSE`](LICENSE).