#!/usr/bin/env python3 """Refuse a change to plugin/ that leaves the plugin's version where it was. Claude Code offers an installed plugin an update only when `version` in plugin/.claude-plugin/plugin.json changes. A hook change without a new version never reaches a machine that installed the plugin before it. Compares what is staged with origin/main, so one bump covers every commit on a branch. Run by prek on changes under plugin/, or by hand. """ import json import subprocess import sys MANIFEST = "plugin/.claude-plugin/plugin.json" BASE = "origin/main" def git(*args): return subprocess.run( ["git", *args], capture_output=True, text=True, check=True ).stdout def version_at(revision): return json.loads(git("show", f"{revision}:{MANIFEST}"))["version"] def main(): try: base = git("merge-base", "HEAD", BASE).strip() except subprocess.CalledProcessError: print(f"check-plugin-version: no {BASE} to compare with, so not checked") return 0 changed = [ path for path in git("diff", "--cached", "--name-only", base, "--", "plugin/").split() if path != MANIFEST ] if not changed: return 0 before = version_at(base) after = version_at("") if after != before: return 0 print( f"plugin/ changed since {BASE}, but {MANIFEST} is still version {before}.\n" "Claude Code offers an update only when the version changes.\n" "Changed: " + ", ".join(changed) ) return 1 if __name__ == "__main__": sys.exit(main())