#!/usr/bin/env bash # Pre-commit hook: enforce autoresearch repo invariants. # Install: git config core.hooksPath .githooks set -euo pipefail FROZEN_FILES=("program.md" "eval.py" "meta_journal.md") GLOBAL_FILES=("RESEARCH_JOURNAL.md" "results.tsv") YELLOW='\033[1;33m' RED='\033[0;31m' NC='\033[0m' # no color CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "unknown") IS_WORKTREE=$(git rev-parse --git-dir | grep -q "worktrees" && echo "yes" || echo "no") META_MODE="${META_MODE:-0}" # ── 1. Warn on direct commits to main ──────────────────────────────────────── if [[ "$CURRENT_BRANCH" == "main" && "$META_MODE" != "1" ]]; then echo -e "${YELLOW}[hook] WARNING: committing directly to main.${NC}" echo " Agents should work in worktrees: just worktree {slug}" echo " To suppress: META_MODE=1 git commit ..." echo "" echo -n " Are you sure? (y/N) " read -r reply < /dev/tty if [[ "$reply" != "y" && "$reply" != "Y" ]]; then echo "Aborted." exit 1 fi fi # ── 2. Protect FROZEN files ─────────────────────────────────────────────────── if [[ "$META_MODE" != "1" ]]; then for f in "${FROZEN_FILES[@]}"; do if git diff --cached --name-only | grep -qx "$f"; then echo -e "${RED}[hook] ERROR: '$f' is FROZEN.${NC}" echo " Only edit in meta-mode: META_MODE=1 git commit ..." echo " See meta_journal.md for instructions." exit 1 fi done fi # ── 3. Warn about GLOBAL files committed from worktrees ─────────────────────── if [[ "$IS_WORKTREE" == "yes" || "$CURRENT_BRANCH" != "main" ]]; then for f in "${GLOBAL_FILES[@]}"; do if git diff --cached --name-only | grep -qx "$f"; then echo -e "${YELLOW}[hook] WARNING: '$f' is a GLOBAL file.${NC}" echo " It should only be committed from main (not from worktree branches)." echo " Agents: append to the root project copy instead:" echo " echo '...' >> \$(git rev-parse --show-toplevel)/$f" echo "" echo -n " Commit anyway? (y/N) " read -r reply < /dev/tty if [[ "$reply" != "y" && "$reply" != "Y" ]]; then echo "Aborted. Unstage with: git reset HEAD $f" exit 1 fi fi done fi exit 0