hooks: warn at push time when skill/ changes are not yet mirrored

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).
This commit is contained in:
2026-09-08 23:01:00 +02:00
parent c64c122dd3
commit 2610545c83
3 changed files with 146 additions and 0 deletions
+13
View File
@@ -30,6 +30,19 @@ mounted). `skill/evaluate-extension-usage.py` is referenced by the skill and
must stay alongside it. `install.sh` does not deploy the skill — skill
deployment remains the `skillset` repo's job on a normal workstation.
**Editing `skill/` obliges you to refresh the mirror.** `skillset` keeps a copy at
`skills/pi-extensions/`, and that copy — not this one — is what most consumers
read: every Mac's host-side `~/.agents/skills/pi-extensions` symlinks into it, and
a workstation with only `skillset` cloned has no other copy. The refresh has been
forgotten on two consecutive edits (the mirror drifted 4,890 B behind, then
9,579 B), so `hooks/pre-push` now warns at push time when a pushed `skill/` change
is not yet mirrored, printing the exact `cp`. It **warns rather than blocks** —
the refresh is a commit in another repo and chronologically comes after this push.
Enforcement lives downstream: `skillset`'s own pre-commit gate refuses a commit
that leaves the mirror stale. `./install.sh` activates the hook
(`core.hooksPath=hooks`, per-clone config that cannot be tracked); it is silent
when no `skillset` clone is on disk, since a reminder you cannot act on is noise.
**Install a subset:**
```bash