#!/usr/bin/env bash set -euo pipefail MAX_LOC="${MAX_LOC:-500}" ROOT_DIR="$(git rev-parse --show-toplevel 2>/dev/null || pwd)" EXCEPTIONS_FILE="${ROOT_DIR}/.claude/rules/loc-exceptions.txt" red() { printf '\033[31m%s\033[0m\n' "$*"; } green() { printf '\033[32m%s\033[0m\n' "$*"; } is_exempt() { local file="$1" [[ -f "$EXCEPTIONS_FILE" ]] || return 1 while IFS= read -r line; do [[ -z "$line" ]] && continue [[ "$line" =~ ^# ]] && continue local pattern="${line%%|*}" pattern="$(echo "$pattern" | xargs)" if [[ -n "$pattern" ]] && [[ "$file" == $pattern ]]; then return 0 fi done < "$EXCEPTIONS_FILE" return 1 } count_loc() { [[ -f "$1" ]] && awk 'END { print NR }' "$1" || echo 0 } collect_changed_files() { { git diff --name-only --cached; git diff --name-only; git ls-files --others --exclude-standard; } | awk 'NF' | sort -u } main() { local mode="changed" [[ "${1:-}" == "--changed" ]] && mode="changed" [[ "${1:-}" == "--all" ]] && mode="all" local files=() if [[ "$mode" == "changed" ]]; then IFS=$'\n' read -d '' -r -a files < <(collect_changed_files) else IFS=$'\n' read -d '' -r -a files < <(find "$ROOT_DIR" \( -name '*.py' -o -name '*.go' -o -name '*.ts' -o -name '*.tsx' \) -not -path '*/node_modules/*' -not -path '*/.next/*' -not -path '*/__pycache__/*' -not -path '*/venv/*') fi local failed=0 local violations=0 for file in "${files[@]}"; do [[ -f "$file" ]] || continue is_exempt "$file" && continue local loc loc="$(count_loc "$file")" if (( loc > MAX_LOC )); then red "VIOLATION: $file ($loc LOC > $MAX_LOC)" violations=$((violations + 1)) failed=1 fi done if (( failed )); then red "" red "$violations file(s) exceed ${MAX_LOC} LOC budget." exit 1 fi green "LOC budget check passed." } main "$@"