Editing skill/ here is only half the job: skillset mirrors it at
skills/pi-extensions/, and that copy is what most consumers actually read --
each Mac's host-side ~/.agents/skills/pi-extensions symlinks into it, and a
workstation with only skillset cloned has no other copy. The refresh is a manual
cp in another repo and it has now been forgotten on two consecutive edits, so
the mirror drifted 4890 B behind, then 9579 B.
hooks/pre-push compares the PUSHED content (git show <sha>:skill/...) against the
mirror on disk and prints the exact cp/sed/commit sequence when they differ.
Pushed content rather than the worktree: uncommitted local edits are not what
this push publishes.
It warns and exits 0 rather than blocking, for two reasons that are not
squeamishness: the direction rule is "edit upstream, THEN refresh", so the
refresh legitimately comes after this push, and it is a commit in a different
repo that cannot be made from here. Enforcement belongs downstream and already
exists -- skillset's pre-commit gate refuses a commit that leaves the mirror
stale. This hook only shortens time-to-detection from "next skillset commit" to
"seconds, to the person who caused it".
Silent when the mirror already matches, when the push does not touch skill/, on
branch deletions, and when no skillset clone is on disk -- a reminder that cannot
be acted on is noise that trains people to skim hook output.
install.sh activates it (core.hooksPath=hooks, per-clone config that cannot be
tracked), preserves a foreign hooksPath rather than clobbering it, and does NOT
undo the activation on --uninstall: removing a safety gate as a side effect of
uninstalling extensions would be a surprise in the wrong direction.
Verified: all three activate_hooks branches in a throwaway repo, and five
pre-push scenarios driven through the real stdin protocol (stale -> warns,
in-sync -> silent, non-skill push -> silent, branch deletion -> silent, no
skillset clone -> silent).
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). Affected packages:
@mariozechner/pi-coding-agent -> @earendil-works/pi-coding-agent
@mariozechner/pi-tui -> @earendil-works/pi-tui
@mariozechner/pi-ai -> @earendil-works/pi-ai
@mariozechner/pi-agent-core -> @earendil-works/pi-agent-core
The old @mariozechner/* packages are deprecated on npm with the
explicit message 'please use @earendil-works/pi-coding-agent instead
going forward', and the version stream has moved on (old top-out
0.73.1; new currently 0.74.0). Anyone npm-installing the old names
gets a deprecation warning + a stale binary.
Sweep:
- All 7 extension TypeScript files: import statements updated.
- README, AGENTS, install.sh: textual references and the github.com/
mariozechner/pi-coding-agent URL pointed at github.com/earendil-works/
pi (the new monorepo root; coding-agent now lives at
packages/coding-agent inside it).
- Bun build of mcp-loader, ext-toggle, ssh-controlmaster verified clean.
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. Historical CHANGELOG
entries are untouched.
When linking, check for <name>.ts.off pointing into this repo and skip
relinking if found. Means a previously /ext-disabled extension stays
disabled across install.sh re-runs (e.g. when adding a new extension).
README + AGENTS updated with the new behavior.
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.
macOS ships bash 3.2 which does not support associative arrays (declare -A,
bash 4+ only). Replace INSTALL_SET associative array with a space-delimited
string and an in_install_set() helper function. All operations preserved:
add, skip/remove, empty-check, iteration, membership test.