#!/usr/bin/env bash
# _git-guard-shim.sh — PATH shim for `git`. Blocks the SAME destructive verbs as
# main-checkout-guard.sh (git restore / checkout -- / checkout . / stash push,pop,
# drop,clear / clean -f* / reset --hard) when they target the LIVE deploy clone
# (OVERDECK_DEPLOY_DIR), or the SHARED main checkout of a repo that has opted into
# worktree isolation (a <main_root>/.worktrees dir).
#
# WHY A PATH SHIM, NOT JUST A CLAUDE HOOK: Claude Code's PreToolUse hook
# (main-checkout-guard.sh) covers only Claude's own Bash tool. codex (`cdx exec`,
# used by /ask-codex) and cursor-agent run their own internal git calls with no
# equivalent hook. PATH is inherited across every exec boundary — the same
# rationale _tmpjail-shim.sh documents for wrapping claude/codex/cursor-agent
# themselves — so wrapping `git` here catches all three runtimes, and anything
# else spawned underneath them, without depending on per-runtime cooperation.
#
# WHY GATED ON AGENT_BUILD_SCOPE_ACTIVE: that var is exported unconditionally by
# _agent-build-scope for every launch of the wrapped claude/codex/cursor-agent
# CLIs, and inherited by every process they spawn. A human's own interactive
# shell never sets it, so a person typing `git restore` by hand is never
# affected — only git commands running inside an agent-launched process tree are.
#
# GIT_GUARD_SHIM_MARKER OD_PATH_SHIM_MARKER — identify any copy of this shim to
# install-git-guard-real and to the sibling shims' PATH resolvers.
#
# The real git is PINNED at install time (install-git-guard-real), never searched
# for at run time: a shim that looks nothing up cannot resolve into a copy of
# itself. Every unresolvable state below exits non-zero; none re-invokes `git`.
set -e

name="${0##*/}"
SHIM_DIR="$(cd "$(dirname "$(readlink -f -- "${BASH_SOURCE[0]}")")" && pwd)"

GUARD_LIB="$SHIM_DIR/../lib/shim-guard.sh"
if [[ ! -r "$GUARD_LIB" ]]; then
  echo "$name: git-guard shim cannot read $GUARD_LIB — refusing" >&2
  exit 78
fi
# shellcheck source=../lib/shim-guard.sh
source "$GUARD_LIB"
shim_guard_enter "$name"

if ! shim_pin_read git; then
  echo "$name: git-guard shim has no usable pinned git (looked in: $SHIM_PIN_TRIED) — run $SHIM_DIR/install-git-guard-real" >&2
  exit 78
fi
real="$SHIM_PIN_REAL"
shim_guard_clear "$name"

if [[ "$SHIM_REENTRY" == 1 ]]; then
  exec "$real" "$@"
fi

# Every pattern wt_has_destructive_git matches requires one of these literals in argv.
# With none present the guard cannot fire, so the path resolution below — 3 realpath and
# 2 perl starts on every git an agent runs — is skipped.
case "$*" in
  *restore*|*checkout*|*stash*|*clean*|*reset*) ;;
  *) exec "$real" "$@" ;;
esac

if [[ "${AGENT_BUILD_SCOPE_ACTIVE:-}" == "1" ]]; then
  LIB="$SHIM_DIR/../hooks/lib/worktree-guard-lib.sh"
  if [[ -r "$LIB" ]]; then
    # shellcheck source=../hooks/lib/worktree-guard-lib.sh
    source "$LIB"
    export WT_GIT_BIN="$real"

    # Every path compared below goes through wt_resolve: ~/.claude/bin, ~/.claude/hooks
    # and ~/.claude/lib are symlinks INTO the deploy clone, and an unresolved compare
    # lets a redirect through one of them miss both roots entirely.
    EFFECTIVE_DIR="$(wt_resolve "$PWD")"
    while IFS= read -r target; do
      [[ -z "$target" ]] && continue
      [[ "$target" != /* ]] && target="$EFFECTIVE_DIR/$target"
      EFFECTIVE_DIR="$(wt_resolve "$target")"
    done < <(wt_git_dir_targets "$*")

    DEPLOY_ROOT="$(wt_deploy_root)"

    if wt_has_destructive_git "git $*"; then
      if wt_is_within "$EFFECTIVE_DIR" "$DEPLOY_ROOT"; then
        cat >&2 <<MSG
git-guard: this command discards/overwrites working-tree state in the LIVE deploy clone
($DEPLOY_ROOT), which every agent's hooks, skills and landers execute from, and which
the next deploy rebuilds from main. Change the repo and land it instead:
  ~/.claude/bin/od-worktree add <slug>
  edit under the worktree, then ship.sh land wt/<slug> <worktree>
MSG
        exit 77
      fi

      if MAIN_ROOT=$(wt_main_root "$EFFECTIVE_DIR") \
        && [[ -d "$MAIN_ROOT/.worktrees" ]] \
        && wt_is_main_checkout_path "$EFFECTIVE_DIR" "$MAIN_ROOT"; then
        cat >&2 <<MSG
git-guard: this command discards/overwrites working-tree state in the SHARED main
checkout ($MAIN_ROOT) — it can silently destroy another agent session's uncommitted
edits. Work in a claimed worktree instead:
  ~/.claude/bin/od-worktree add <slug>
  cd $MAIN_ROOT/.worktrees/<slug>
MSG
        exit 77
      fi
    fi
  fi
fi

exec "$real" "$@"
