diff --git a/st-export.py b/st-export.py index 2b86b6b..d0f5ddf 100755 --- a/st-export.py +++ b/st-export.py @@ -115,7 +115,40 @@ def export_personas() -> int: return len(out.get("personas") or {}) +HOOK = """#!/usr/bin/env bash +# Installed by st-export.py --install-hook. +# Regenerates _text/ from the PNGs and settings.json, then stages it, so the +# readable copy can never drift from the artifact SillyTavern actually loads. +set -e +cd "$(git rev-parse --show-toplevel)" +python3 ./st-export.py >/dev/null +git add -A _text +""" + + +def install_hook() -> int: + import subprocess + root = subprocess.run(["git", "rev-parse", "--git-dir"], capture_output=True, + text=True).stdout.strip() + if not root: + print("not a git repository") + return 1 + path = os.path.join(root, "hooks", "pre-commit") + os.makedirs(os.path.dirname(path), exist_ok=True) + open(path, "w").write(HOOK) + os.chmod(path, 0o755) + print(f"armed: {path}") + # ⚠ BLOCKING ON PURPOSE, unlike the halogen repo's advisory doc-check. That + # one reports a judgement call; this one regenerates a derived file. If it + # fails, _text/ would silently describe a card that no longer exists — and a + # stale derived copy is worse than no copy, because it reads as current. + print("⚠ runs on every commit; a failure blocks it, which is the point") + return 0 + + def main() -> int: + if "--install-hook" in sys.argv: + return install_hook() os.makedirs(OUT, exist_ok=True) cards = export_cards() personas = export_personas()