#!/bin/bash # git-safe installer for Claude Code # Usage: curl -fsSL https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/git-safe/install.sh | bash set -euo pipefail HOOK_NAME="git-safe" HOOK_URL="https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/git-safe/hook.sh" HOOK_DIR="$HOME/.claude/$HOOK_NAME" HOOK_PATH="$HOOK_DIR/hook.sh" SETTINGS_FILE="$HOME/.claude/settings.json" echo "Installing $HOOK_NAME for Claude Code..." # Check python3 (needed for settings.json management) if ! command -v python3 >/dev/null 2>&1; then echo "Error: python3 not found. Install Python 3 and try again." >&2 exit 1 fi # Check jq (needed at runtime to parse Claude Code hook input) if ! command -v jq >/dev/null 2>&1; then echo "Error: jq not found. $HOOK_NAME requires jq at runtime." >&2 echo " Install: brew install jq (macOS) or apt install jq (Linux)" >&2 exit 1 fi # Create hook directory mkdir -p "$HOOK_DIR" # Download hook echo " Downloading hook..." if command -v curl &>/dev/null; then curl -fsSL "$HOOK_URL" -o "$HOOK_PATH" elif command -v wget &>/dev/null; then wget -q "$HOOK_URL" -O "$HOOK_PATH" else echo "Error: curl or wget required" >&2 exit 1 fi chmod +x "$HOOK_PATH" # Verify download is not empty if [ ! -s "$HOOK_PATH" ]; then echo "Error: downloaded file is empty. The URL may have changed." >&2 exit 1 fi # Clean up legacy install location if present LEGACY_PATH="$HOME/.claude/hooks/$HOOK_NAME.sh" if [ -f "$LEGACY_PATH" ]; then echo " Migrating from legacy location ($LEGACY_PATH)..." rm -f "$LEGACY_PATH" fi # Handle JSONC comments in settings.json (prevents silent failures) if [ -f "$SETTINGS_FILE" ]; then if ! python3 -c "import json,sys; json.load(open(sys.argv[1]))" "$SETTINGS_FILE" 2>/dev/null; then python3 - "$SETTINGS_FILE" << 'JSONC_FIX' import json, sys, shutil path = sys.argv[1] with open(path) as f: raw = f.read() o, i, n, q = [], 0, len(raw), False while i < n: if q: if raw[i] == '\\' and i+1/dev/null || { echo " Warning: Could not auto-register. Add manually to $SETTINGS_FILE:" echo " hooks.PreToolUse: [{\"matcher\":\"Bash\",\"hooks\":[{\"type\":\"command\",\"command\":\"$HOOK_PATH\"}]}]" } else mkdir -p "$(dirname "$SETTINGS_FILE")" cat > "$SETTINGS_FILE" < (without --staged, discards changes)" echo " git clean -f (deletes untracked files)" echo " git branch -D (use -d for merged branches)" echo " git stash drop/clear" echo "" echo "To allow specific operations, create .git-safe:" echo " allow: push --force" echo " allow: reset --hard" echo "" echo "Config:" echo " Hook: $HOOK_PATH" echo " Disable: GIT_SAFE_DISABLED=1" echo " Debug: GIT_SAFE_LOG=1"