Add ext-toggle extension and /ext slash command

extensions/ext-toggle.ts:
  /ext lists ~/.pi/agent/extensions/ with active/disabled markers
  and toggles individual extensions by renaming between name.ts and
  name.ts.off (pi only auto-discovers *.ts). Calls ctx.reload() so the
  change takes effect without restarting pi.

  Subdirectory-style extensions (name/index.ts) are listed read-only
  in v1 — toggling a directory cleanly is more work than the rename
  trick is worth.

install.sh:
  --uninstall now matches both *.ts and *.ts.off symlinks pointing
  into this repo, so a disabled extension is still cleaned up.

README.md / AGENTS.md:
  Document ext-toggle alongside the others; AGENTS notes the API
  surface used (registerCommand, ui.select/confirm/notify, reload)
  and the rename-not-delete design decision.
This commit is contained in:
2026-05-07 20:26:41 +02:00
parent 9218fe512c
commit d2b2b3fb43
4 changed files with 233 additions and 7 deletions
+38
View File
@@ -21,6 +21,7 @@ extensions/
confirm-destructive.ts # Confirm before dangerous bash commands and session actions
git-checkpoint.ts # Git stash checkpoint per turn, restorable on /fork
notify.ts # Native terminal notification when agent finishes
ext-toggle.ts # /ext slash command — list & toggle extensions at runtime
install.sh # Idempotent installer — symlinks extensions/ into ~/.pi/agent/extensions/
package.json # pi package manifest — enables `pi install /path` as an alternative
README.md # User-facing docs.
@@ -159,6 +160,43 @@ Terminal detection order: `KITTY_WINDOW_ID` → OSC 99 (Kitty) →
Notification text: `Pi — Done (Ns)` where N is the rounded elapsed seconds.
### `ext-toggle.ts`
Registers `/ext` slash command. Lists files in `~/.pi/agent/extensions/`,
shows `` (active) / `` (disabled) plus dir/symlink hints, and lets the
user toggle individual extensions by renaming them between `name.ts` and
`name.ts.off`. Calls `ctx.reload()` after a toggle so the change takes
effect without restarting pi.
**Key design decisions:**
- **Rename, not delete.** Disabling a built-in produces a `name.ts.off`
symlink/file that's invisible to pi's `*.ts` discovery glob but trivially
reversible. No state stored elsewhere.
- **Symlink-friendly.** `fs.renameSync` renames the symlink itself; the repo
target is untouched. Toggling an extension installed by this repo is
reversible without re-running `install.sh`.
- **Subdir extensions are read-only in v1.** `name/index.ts` shapes show up in
the listing with a `[dir]` tag but cannot be toggled — the cleanest disable
for a directory would need a hidden-prefix or move-aside dance that adds
more failure modes than it's worth for now.
- **`install.sh --uninstall` matches both `*.ts` and `*.ts.off`.** Means a
disabled extension is still cleaned up on uninstall, regardless of which
state it was left in.
- **Listing is recomputed on every `/ext` invocation.** No cache, no event
subscription — cheap enough for the tens of files this directory will ever
contain.
**API used:**
- `pi.registerCommand(name, { description, handler })` — registers `/ext`.
- `ctx.ui.select(title, items)` — picker; returns selected string or `undefined`.
- `ctx.ui.confirm(title, message)` — yes/no dialog returning `boolean`.
- `ctx.ui.notify(message, level)` — transient toast.
- `ctx.reload()` — reloads extensions/skills/prompts/themes; same as `/reload`.
No flags, no `agent_*` event handlers — fully passive until `/ext` is invoked.
No framework. Manual:
```bash