#!/bin/bash # gemini-wrapper - Token-aware wrapper for gemini command # Location: ~/.claude/scripts/gemini-wrapper # # This wrapper automatically manages --all-files flag based on project token count # Usage: gemini-wrapper [all gemini options] # Note: Executes in current working directory set -e # Configuration DEFAULT_TOKEN_LIMIT=2000000 TOKEN_LIMIT=${GEMINI_TOKEN_LIMIT:-$DEFAULT_TOKEN_LIMIT} # Colors for output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' NC='\033[0m' # No Color # Function to count tokens (approximate: chars/4) - optimized version count_tokens() { local total_chars=0 local file_count=0 # Use single find with bulk wc for better performance # Common source file extensions local extensions="py js ts tsx jsx java cpp c h rs go md txt json yaml yml xml html css scss sass php rb sh bash" # Build find command with extension patterns local find_cmd="find . -type f \(" local first=true for ext in $extensions; do if [[ "$first" == true ]]; then find_cmd+=" -name \"*.$ext\"" first=false else find_cmd+=" -o -name \"*.$ext\"" fi done find_cmd+=" \)" # Exclude common build/cache directories find_cmd+=" -not -path \"*/node_modules/*\"" find_cmd+=" -not -path \"*/.git/*\"" find_cmd+=" -not -path \"*/dist/*\"" find_cmd+=" -not -path \"*/build/*\"" find_cmd+=" -not -path \"*/.next/*\"" find_cmd+=" -not -path \"*/.nuxt/*\"" find_cmd+=" -not -path \"*/target/*\"" find_cmd+=" -not -path \"*/vendor/*\"" find_cmd+=" -not -path \"*/__pycache__/*\"" find_cmd+=" -not -path \"*/.cache/*\"" find_cmd+=" 2>/dev/null" # Use efficient bulk processing with wc if command -v wc >/dev/null 2>&1; then # Try bulk wc first - much faster for many files local wc_output wc_output=$(eval "$find_cmd" | xargs wc -c 2>/dev/null | tail -n 1) # Parse the total line (last line of wc output when processing multiple files) if [[ -n "$wc_output" && "$wc_output" =~ ^[[:space:]]*([0-9]+)[[:space:]]+total[[:space:]]*$ ]]; then total_chars="${BASH_REMATCH[1]}" file_count=$(eval "$find_cmd" | wc -l 2>/dev/null || echo 0) else # Fallback: single file processing while IFS= read -r file; do if [[ -f "$file" && -r "$file" ]]; then local chars=$(wc -c < "$file" 2>/dev/null || echo 0) total_chars=$((total_chars + chars)) file_count=$((file_count + 1)) fi done < <(eval "$find_cmd") fi else # No wc available - fallback method while IFS= read -r file; do if [[ -f "$file" && -r "$file" ]]; then local chars=$(stat -c%s "$file" 2>/dev/null || echo 0) total_chars=$((total_chars + chars)) file_count=$((file_count + 1)) fi done < <(eval "$find_cmd") fi local estimated_tokens=$((total_chars / 4)) echo "$estimated_tokens $file_count" } # Parse arguments to check for flags has_all_files=false has_approval_mode=false args=() # Check for existing flags for arg in "$@"; do case "$arg" in "--all-files") has_all_files=true args+=("$arg") ;; --approval-mode*) has_approval_mode=true args+=("$arg") ;; *) args+=("$arg") ;; esac done # Analyze current working directory echo -e "${GREEN}📁 Analyzing current directory: $(pwd)${NC}" >&2 # Count tokens (in the target directory if -c was used) echo -e "${YELLOW}🔍 Analyzing project size...${NC}" >&2 read -r token_count file_count <<< "$(count_tokens)" echo -e "${YELLOW}📊 Project stats: ~${token_count} tokens across ${file_count} files${NC}" >&2 # Decision logic for --all-files flag if [[ $token_count -lt $TOKEN_LIMIT ]]; then if [[ "$has_all_files" == false ]]; then echo -e "${GREEN}✅ Small project (${token_count} < ${TOKEN_LIMIT} tokens): Adding --all-files${NC}" >&2 args=("--all-files" "${args[@]}") else echo -e "${GREEN}✅ Small project (${token_count} < ${TOKEN_LIMIT} tokens): Keeping --all-files${NC}" >&2 fi else if [[ "$has_all_files" == true ]]; then echo -e "${RED}⚠️ Large project (${token_count} >= ${TOKEN_LIMIT} tokens): Removing --all-files to avoid token limits${NC}" >&2 echo -e "${YELLOW}💡 Consider using specific @{patterns} for targeted analysis${NC}" >&2 # Remove --all-files from args new_args=() for arg in "${args[@]}"; do if [[ "$arg" != "--all-files" ]]; then new_args+=("$arg") fi done args=("${new_args[@]}") else echo -e "${RED}⚠️ Large project (${token_count} >= ${TOKEN_LIMIT} tokens): Avoiding --all-files${NC}" >&2 echo -e "${YELLOW}💡 Consider using specific @{patterns} for targeted analysis${NC}" >&2 fi fi # Auto-add approval-mode if not specified if [[ "$has_approval_mode" == false ]]; then # Check if this is an analysis task (contains words like "analyze", "review", "understand") prompt_text="${args[*]}" if [[ "$prompt_text" =~ (analyze|analysis|review|understand|inspect|examine) ]]; then echo -e "${GREEN}📋 Analysis task detected: Adding --approval-mode default${NC}" >&2 args=("--approval-mode" "default" "${args[@]}") else echo -e "${YELLOW}⚡ Execution task detected: Adding --approval-mode yolo${NC}" >&2 args=("--approval-mode" "yolo" "${args[@]}") fi fi # Show final command (for transparency) echo -e "${YELLOW}🚀 Executing: gemini ${args[*]}${NC}" >&2 # Execute gemini with adjusted arguments (we're already in the right directory) gemini "${args[@]}"