#!/usr/bin/env bash # Push-time reminder: skill/ changed, so the skillset MIRROR needs refreshing. # # WHY THIS EXISTS # skill/ is canonical for the pi-extensions agent skill, but it is not the copy # most consumers read. skillset mirrors it at skills/pi-extensions/, and every # Mac's host-side ~/.agents/skills/pi-extensions symlinks into that mirror. So a # skill/ edit that is pushed without refreshing the mirror is invisible to the # people it was written for. # # That has now happened on two consecutive edits: the mirror sat 4890 B behind, # then 9579 B behind. Both times the edit was correct and the omission was the # follow-up step in another repo. # # WHAT IT DOES # On push, if the pushed commits touch skill/, compare the PUSHED content # against the mirror on disk. If they differ, print the refresh commands. # # It WARNS, it does not block. The direction rule is "edit upstream, THEN # refresh" -- the refresh is chronologically after this push, and it is a commit # in a different repo, so refusing this push would be both wrong-ordered and # unfixable from here. Enforcement lives downstream, in skillset's own # pre-commit gate, which refuses a commit that leaves the mirror stale. # # Silent when the mirror already matches, and silent when no skillset clone is # on disk -- a machine without one cannot act on the reminder, and a warning it # cannot act on is noise that trains people to skim hook output. # # ACTIVATION (per clone, cannot be tracked in git) # ./install.sh -- or -- git config core.hooksPath hooks # # ESCAPE HATCH # git push --no-verify set -euo pipefail tag="[mirror]" MIRROR_SUBPATH="skills/pi-extensions" ZERO="0000000000000000000000000000000000000000" # Locate the skillset clone. Explicit override, then the layouts this fleet has. skillset="" repo_root="$(git rev-parse --show-toplevel)" for cand in "${PI_EXTENSIONS_SKILLSET:-}" "$repo_root/../skillset" "/workspace/skillset"; do [ -n "$cand" ] || continue if [ -d "$cand/$MIRROR_SUBPATH" ]; then skillset="$(cd "$cand" && pwd)"; break; fi done [ -n "$skillset" ] || exit 0 # nothing actionable on this machine # pre-push feeds ref updates on stdin: touched="" pushed_sha="" while read -r _local_ref local_sha _remote_ref remote_sha; do [ -n "${local_sha:-}" ] || continue [ "$local_sha" = "$ZERO" ] && continue # branch deletion if [ "$remote_sha" = "$ZERO" ]; then # New ref on the remote: consider commits not already on any remote, so a # first push of a branch does not diff against the whole of history. range_args=("$local_sha" "--not" "--remotes=origin") else range_args=("$remote_sha..$local_sha") fi if git log --format= --name-only "${range_args[@]}" -- skill/ 2>/dev/null | grep -q .; then touched="yes" pushed_sha="$local_sha" fi done [ -n "$touched" ] || exit 0 # Compare what is being PUSHED (committed content at that sha) against the mirror # on disk -- the file consumers actually read. Not the worktree: uncommitted local # edits are not what this push publishes. differ="" while read -r path; do base="$(basename "$path")" mirror_file="$skillset/$MIRROR_SUBPATH/$base" if [ ! -f "$mirror_file" ]; then differ="${differ}${base} (missing from mirror)"$'\n' continue fi if ! git show "$pushed_sha:$path" 2>/dev/null | diff -q - "$mirror_file" >/dev/null 2>&1; then differ="${differ}${base}"$'\n' fi done < <(git ls-tree -r --name-only "$pushed_sha" skill/) [ -n "$differ" ] || exit 0 # mirror already refreshed; nothing to say short="$(git rev-parse --short "$pushed_sha")" echo "$tag this push changes skill/, and skillset's mirror does not match it yet." >&2 echo "$tag" >&2 printf '%s' "$differ" | sed 's/^/'"$tag"' differs: /' >&2 echo "$tag" >&2 echo "$tag Consumers read the MIRROR, not this repo: each Mac's host-side" >&2 echo "$tag ~/.agents/skills/pi-extensions symlinks into it, and a workstation with" >&2 echo "$tag only skillset cloned has no other copy. Refresh it:" >&2 echo "$tag" >&2 echo "$tag cp $repo_root/skill/SKILL.md $repo_root/skill/evaluate-extension-usage.py $skillset/$MIRROR_SUBPATH/" >&2 echo "$tag sed -i 's/^# Last refreshed from: .*/# Last refreshed from: pi-extensions $short/' $skillset/$MIRROR_SUBPATH/.skill-source" >&2 echo "$tag git -C $skillset commit -m 'skills(pi-extensions): refresh mirror from upstream $short' $MIRROR_SUBPATH" >&2 echo "$tag" >&2 echo "$tag Not blocking this push -- the refresh comes after it. skillset's own" >&2 echo "$tag pre-commit gate will refuse a commit that leaves the mirror stale." >&2 exit 0