Skip to content

Instantly share code, notes, and snippets.

@tosin2013
Last active August 7, 2025 16:06
Show Gist options
  • Select an option

  • Save tosin2013/7b5d1140dbd23161321c9d73a2535ea0 to your computer and use it in GitHub Desktop.

Select an option

Save tosin2013/7b5d1140dbd23161321c9d73a2535ea0 to your computer and use it in GitHub Desktop.
aider-make.sh
#!/bin/bash
# aider-make: AI-powered Makefile automation with aider integration
# Author: Generated for automation of make targets with AI assistance
# Version: 1.0.0
set -euo pipefail # Exit on error, undefined vars, pipe failures
# =============================================================================
# CONFIGURATION VARIABLES (User Configurable)
# =============================================================================
# Default model to use with aider (can be overridden)
DEFAULT_MODEL="${AIDER_MODEL:-deepseek}"
# API key for the model (can be set via environment or command line)
# Will be detected automatically based on model or prompted if needed
DEFAULT_API_KEY="${AIDER_API_KEY:-}"
# API base URL for OpenAI compatible endpoints
DEFAULT_API_BASE="${AIDER_API_BASE:-${OPENAI_API_BASE:-}}"
# Maximum number of fix attempts before giving up
MAX_FIX_ATTEMPTS="${AIDER_MAKE_MAX_ATTEMPTS:-3}"
# Extra arguments to pass to aider
AIDER_EXTRA_ARGS="${AIDER_MAKE_EXTRA_ARGS:---yes --auto-commits --disable-playwright}"
# Makefile name to look for
MAKEFILE_NAME="Makefile"
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
PURPLE='\033[0;35m'
CYAN='\033[0;36m'
NC='\033[0m' # No Color
# =============================================================================
# GLOBAL VARIABLES
# =============================================================================
SCRIPT_NAME="$(basename "$0")"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
WORK_DIR="$(pwd)"
LOG_DIR="${WORK_DIR}/.aider-make"
LOG_FILE="${LOG_DIR}/aider-make.log"
# Command line options
MODEL="$DEFAULT_MODEL"
API_KEY="$DEFAULT_API_KEY"
API_BASE="$DEFAULT_API_BASE"
TARGET=""
LIST_TARGETS=false
DRY_RUN=false
ARCHITECT_MODE=false
VERBOSE=false
HELP=false
# =============================================================================
# UTILITY FUNCTIONS
# =============================================================================
# Print colored output
print_color() {
local color=$1
shift
echo -e "${color}$*${NC}"
}
# Print info message
info() {
print_color "$BLUE" "[INFO] $*"
}
# Print success message
success() {
print_color "$GREEN" "[SUCCESS] $*"
}
# Print warning message
warn() {
print_color "$YELLOW" "[WARNING] $*"
}
# Print error message
error() {
print_color "$RED" "[ERROR] $*"
}
# Print debug message (only if verbose)
debug() {
if [[ "$VERBOSE" == true ]]; then
print_color "$PURPLE" "[DEBUG] $*"
fi
}
# Log message to file
log() {
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
echo "[$timestamp] $*" >> "$LOG_FILE"
}
# Setup logging directory
setup_logging() {
mkdir -p "$LOG_DIR"
touch "$LOG_FILE"
log "=== aider-make session started ==="
log "Working directory: $WORK_DIR"
log "Model: $MODEL"
}
# =============================================================================
# HELP AND USAGE
# =============================================================================
show_help() {
cat << EOF
$SCRIPT_NAME - AI-powered Makefile automation with aider integration
USAGE:
$SCRIPT_NAME [OPTIONS] [TARGET]
DESCRIPTION:
This script integrates aider CLI with Makefile automation to provide
AI-powered assistance for running make targets and automatically fixing
failing tests or build issues.
OPTIONS:
-h, --help Show this help message
-l, --list List available make targets
-m, --model MODEL Specify AI model to use (default: $DEFAULT_MODEL)
-k, --api-key KEY Specify API key for the model
-b, --api-base URL Specify API base URL for OpenAI compatible endpoints
-a, --architect Start aider in architect mode
-d, --dry-run Show what would be done without executing
-v, --verbose Enable verbose output
--max-attempts N Maximum fix attempts (default: $MAX_FIX_ATTEMPTS)
EXAMPLES:
$SCRIPT_NAME # Interactive mode - show targets and let user choose
$SCRIPT_NAME test # Run 'make test' with AI assistance
$SCRIPT_NAME --list # List all available make targets
$SCRIPT_NAME --model gpt-4 test # Use GPT-4 model for assistance
$SCRIPT_NAME --dry-run build # Show what would happen without executing
# OpenAI compatible API examples:
$SCRIPT_NAME --model gpt-4 --api-base http://localhost:1234/v1 --api-key sk-xxx test
$SCRIPT_NAME --model llama3 --api-base https://api.together.xyz/v1 test
ENVIRONMENT VARIABLES:
AIDER_MODEL # Default model to use
AIDER_API_KEY # Generic API key (fallback)
AIDER_API_BASE # API base URL for OpenAI compatible endpoints
# Provider-specific API keys (recommended):
OPENAI_API_KEY # For OpenAI models (gpt-*, o1-*, o3-*)
OPENAI_API_BASE # For OpenAI compatible endpoints
ANTHROPIC_API_KEY # For Anthropic models (claude-*, sonnet*)
GEMINI_API_KEY # For Google Gemini models
DEEPSEEK_API_KEY # For DeepSeek models
AIDER_MAKE_MAX_ATTEMPTS # Maximum fix attempts
AIDER_MAKE_EXTRA_ARGS # Extra arguments for aider
CONFIGURATION:
You can also create a .aider-make.conf file in your project root with:
MODEL=deepseek
API_KEY=your_key_here
API_BASE=https://api.openai.com/v1
MAX_ATTEMPTS=3
For different providers, use the appropriate environment variables:
export OPENAI_API_KEY=your_openai_key # For OpenAI models
export ANTHROPIC_API_KEY=your_anthropic_key # For Claude models
export GEMINI_API_KEY=your_gemini_key # For Gemini models
export DEEPSEEK_API_KEY=your_deepseek_key # For DeepSeek models
export OPENAI_API_BASE=https://your-endpoint.com/v1 # For custom endpoints
EOF
}
# =============================================================================
# API KEY MANAGEMENT
# =============================================================================
prompt_for_api_key() {
local suggested_env_var=""
local provider_name=""
# Suggest appropriate environment variable based on model
case "$MODEL" in
gpt-*|o1-*|o3-*)
suggested_env_var="OPENAI_API_KEY"
provider_name="OpenAI"
;;
claude-*|sonnet*)
suggested_env_var="ANTHROPIC_API_KEY"
provider_name="Anthropic"
;;
gemini*)
suggested_env_var="GEMINI_API_KEY"
provider_name="Google Gemini"
;;
deepseek*)
suggested_env_var="DEEPSEEK_API_KEY"
provider_name="DeepSeek"
;;
*)
# Try to infer from model name
local provider=$(echo "$MODEL" | cut -d'/' -f1)
if [[ "$provider" != "$MODEL" ]]; then
suggested_env_var="${provider^^}_API_KEY"
provider_name="$provider"
else
suggested_env_var="OPENAI_API_KEY"
provider_name="OpenAI-compatible"
fi
;;
esac
warn "No API key found for model '$MODEL'"
info "You can either:"
info " 1. Set the environment variable: export $suggested_env_var=your_key_here"
info " 2. Use the --api-key option: --api-key your_key_here"
info " 3. Enter the API key now (will not be saved)"
echo
# In dry-run mode, don't prompt
if [[ "$DRY_RUN" == true ]]; then
warn "DRY RUN: Would prompt for API key"
return 0
fi
# Prompt for API key
echo -n "Enter your $provider_name API key (or 'q' to quit): "
read -r -s user_api_key
echo # New line after hidden input
if [[ "$user_api_key" == "q" || "$user_api_key" == "quit" ]]; then
info "Exiting..."
exit 0
fi
if [[ -z "$user_api_key" ]]; then
error "No API key provided. Cannot continue."
exit 1
fi
API_KEY="$user_api_key"
success "API key accepted"
# Suggest setting environment variable for future use
info "For future use, consider setting: export $suggested_env_var=your_key_here"
}
# =============================================================================
# DEPENDENCY CHECKING
# =============================================================================
check_dependencies() {
info "Checking dependencies..."
# Check if we're in a directory with a Makefile
if [[ ! -f "$MAKEFILE_NAME" && ! -f "makefile" && ! -f "GNUmakefile" ]]; then
error "No Makefile found in current directory"
error "Please run this script from a directory containing a Makefile"
return 1
fi
# Determine which Makefile to use
if [[ -f "GNUmakefile" ]]; then
MAKEFILE_NAME="GNUmakefile"
elif [[ -f "$MAKEFILE_NAME" ]]; then
MAKEFILE_NAME="Makefile"
else
MAKEFILE_NAME="makefile"
fi
debug "Using Makefile: $MAKEFILE_NAME"
# Check if make is available
if ! command -v make &> /dev/null; then
error "make command not found. Please install make."
return 1
fi
# Check if aider is available
if ! command -v aider &> /dev/null; then
warn "aider not found. Attempting to install..."
if ! install_aider; then
error "Failed to install aider. Please install manually:"
error " python -m pip install aider-install"
error " aider-install"
return 1
fi
fi
# Validate API key if model requires it
if [[ -z "$API_KEY" ]]; then
# Check if we have provider-specific environment variables
local found_env_key=""
case "$MODEL" in
gpt-*|o1-*|o3-*)
[[ -n "${OPENAI_API_KEY:-}" ]] && found_env_key="OPENAI_API_KEY"
;;
claude-*|sonnet*)
[[ -n "${ANTHROPIC_API_KEY:-}" ]] && found_env_key="ANTHROPIC_API_KEY"
;;
gemini*)
[[ -n "${GEMINI_API_KEY:-}" ]] && found_env_key="GEMINI_API_KEY"
;;
deepseek*)
[[ -n "${DEEPSEEK_API_KEY:-}" ]] && found_env_key="DEEPSEEK_API_KEY"
;;
*)
# Try to find any common API key environment variables
if [[ -n "${OPENAI_API_KEY:-}" ]]; then
found_env_key="OPENAI_API_KEY"
elif [[ -n "${ANTHROPIC_API_KEY:-}" ]]; then
found_env_key="ANTHROPIC_API_KEY"
elif [[ -n "${GEMINI_API_KEY:-}" ]]; then
found_env_key="GEMINI_API_KEY"
elif [[ -n "${DEEPSEEK_API_KEY:-}" ]]; then
found_env_key="DEEPSEEK_API_KEY"
fi
;;
esac
# If we found an environment variable, use it
if [[ -n "$found_env_key" ]]; then
API_KEY="${!found_env_key}"
debug "Using API key from $found_env_key environment variable"
else
# No API key found, prompt user for it
prompt_for_api_key
fi
fi
# Log API configuration for debugging
if [[ -n "$API_BASE" ]]; then
debug "Using custom API base: $API_BASE"
log "API base configured: $API_BASE"
fi
success "Dependencies check completed"
return 0
}
# Install aider if not present
install_aider() {
info "Installing aider..."
# Check if python is available
if ! command -v python3 &> /dev/null && ! command -v python &> /dev/null; then
error "Python not found. Please install Python first."
return 1
fi
# Try to install aider
local python_cmd="python3"
if ! command -v python3 &> /dev/null; then
python_cmd="python"
fi
if $python_cmd -m pip install aider-install; then
if aider-install; then
success "aider installed successfully"
return 0
else
error "aider-install failed"
return 1
fi
else
error "Failed to install aider-install package"
return 1
fi
}
# =============================================================================
# MAKEFILE PARSING
# =============================================================================
parse_makefile_targets() {
info "Parsing Makefile targets..."
local targets=()
local makefile="$MAKEFILE_NAME"
# Extract targets using regex pattern
# This pattern matches lines that start with target names followed by colon
while IFS= read -r line; do
# Skip comments and empty lines
[[ "$line" =~ ^[[:space:]]*# ]] && continue
[[ -z "${line// }" ]] && continue
# Extract target names (handle multiple targets on one line)
if [[ "$line" =~ ^([a-zA-Z0-9_.-]+[[:space:]]*)+: ]]; then
local target_line="${line%%:*}"
# Split multiple targets
IFS=' ' read -ra target_array <<< "$target_line"
for target in "${target_array[@]}"; do
# Skip internal targets and variables
if [[ ! "$target" =~ ^\. ]] && [[ ! "$target" =~ = ]]; then
targets+=("$target")
fi
done
fi
done < "$makefile"
# Handle included makefiles
while IFS= read -r include_line; do
if [[ "$include_line" =~ ^include[[:space:]]+(.+)$ ]]; then
local included_file="${BASH_REMATCH[1]}"
# Expand environment variables in the path
included_file=$(eval echo "$included_file")
if [[ -f "$included_file" ]]; then
debug "Processing included makefile: $included_file"
while IFS= read -r line; do
[[ "$line" =~ ^[[:space:]]*# ]] && continue
[[ -z "${line// }" ]] && continue
if [[ "$line" =~ ^([a-zA-Z0-9_.-]+[[:space:]]*)+: ]]; then
local target_line="${line%%:*}"
IFS=' ' read -ra target_array <<< "$target_line"
for target in "${target_array[@]}"; do
if [[ ! "$target" =~ ^\. ]] && [[ ! "$target" =~ = ]]; then
targets+=("$target")
fi
done
fi
done < "$included_file"
fi
fi
done < "$makefile"
# Remove duplicates and sort
printf '%s\n' "${targets[@]}" | sort -u
}
# =============================================================================
# TARGET DISPLAY AND SELECTION
# =============================================================================
display_targets() {
local targets=("$@")
if [[ ${#targets[@]} -eq 0 ]]; then
warn "No targets found in Makefile"
return 1
fi
print_color "$CYAN" "\nAvailable make targets:"
print_color "$CYAN" "======================="
for i in "${!targets[@]}"; do
printf "%3d) %s\n" $((i + 1)) "${targets[i]}"
done
echo
}
select_target() {
local targets=("$@")
while true; do
echo -n "Select target (number or name, 'q' to quit): "
read -r selection
# Handle quit
if [[ "$selection" == "q" || "$selection" == "quit" ]]; then
info "Exiting..."
exit 0
fi
# Handle numeric selection
if [[ "$selection" =~ ^[0-9]+$ ]]; then
local index=$((selection - 1))
if [[ $index -ge 0 && $index -lt ${#targets[@]} ]]; then
echo "${targets[index]}"
return 0
else
error "Invalid selection. Please choose 1-${#targets[@]}"
continue
fi
fi
# Handle name selection
for target in "${targets[@]}"; do
if [[ "$target" == "$selection" ]]; then
echo "$target"
return 0
fi
done
error "Target '$selection' not found. Please try again."
done
}
# =============================================================================
# MAKE EXECUTION
# =============================================================================
run_make_target() {
local target="$1"
local output_file="${LOG_DIR}/make_output_${target}_$(date +%s).log"
info "Running 'make $target'..."
log "Executing: make $target"
log "Output will be saved to: $output_file"
if [[ "$DRY_RUN" == true ]]; then
info "DRY RUN: Would execute 'make $target'"
return 0
fi
local start_time=$(date +%s)
local exit_code=0
# Run make and capture output
if make "$target" 2>&1 | tee "$output_file"; then
exit_code=0
local end_time=$(date +%s)
local duration=$((end_time - start_time))
success "make $target completed successfully in ${duration}s"
log "make $target completed successfully in ${duration}s"
else
exit_code=$?
local end_time=$(date +%s)
local duration=$((end_time - start_time))
error "make $target failed with exit code $exit_code after ${duration}s"
log "make $target failed with exit code $exit_code after ${duration}s"
log "Output file for analysis: $output_file"
# Store the output file path for analysis
echo "$output_file"
fi
return $exit_code
}
# =============================================================================
# FAILURE ANALYSIS
# =============================================================================
analyze_failure() {
local output_file="$1"
local target="$2"
info "Analyzing failure output from: $output_file"
if [[ ! -f "$output_file" ]]; then
error "Output file not found: $output_file"
return 1
fi
local file_size=$(wc -l < "$output_file")
debug "Output file contains $file_size lines"
# Extract relevant error information
local error_summary=""
local error_context=""
# Look for common error patterns with more comprehensive extraction
if grep -q "error:" "$output_file"; then
error_context=$(grep -A 5 -B 2 "error:" "$output_file" | head -30)
debug "Found 'error:' pattern"
elif grep -q "Error:" "$output_file"; then
error_context=$(grep -A 5 -B 2 "Error:" "$output_file" | head -30)
debug "Found 'Error:' pattern"
elif grep -q "FAILED" "$output_file"; then
error_context=$(grep -A 5 -B 2 "FAILED" "$output_file" | head -30)
debug "Found 'FAILED' pattern"
elif grep -q "FAIL" "$output_file"; then
error_context=$(grep -A 5 -B 2 "FAIL" "$output_file" | head -30)
debug "Found 'FAIL' pattern"
elif grep -q "AssertionError\|Exception\|Traceback" "$output_file"; then
error_context=$(grep -A 10 -B 2 "AssertionError\|Exception\|Traceback" "$output_file" | head -40)
debug "Found Python exception pattern"
else
# Get last 30 lines as context for unknown failures
error_context=$(tail -30 "$output_file")
debug "Using last 30 lines as error context"
fi
# Determine failure type
local failure_type="unknown"
if grep -q "test.*fail\|FAIL\|FAILED\|AssertionError" "$output_file"; then
failure_type="test"
elif grep -q "compile\|compilation\|syntax" "$output_file"; then
failure_type="compilation"
elif grep -q "lint\|style\|format" "$output_file"; then
failure_type="linting"
elif grep -q "missing\|not found\|No such file" "$output_file"; then
failure_type="dependency"
elif grep -q "ImportError\|ModuleNotFoundError" "$output_file"; then
failure_type="import"
fi
info "Failure type detected: $failure_type"
log "Failure analysis - Type: $failure_type"
log "Error context length: $(echo "$error_context" | wc -l) lines"
# Save error context to a separate file for debugging
local error_context_file="${LOG_DIR}/error_context_${target}_$(date +%s).log"
echo "$error_context" > "$error_context_file"
debug "Error context saved to: $error_context_file"
# Return the error context for aider
echo "$error_context"
}
# =============================================================================
# AIDER INTEGRATION
# =============================================================================
fix_with_aider() {
local target="$1"
local error_context="$2"
local attempt="$3"
info "Attempting to fix issues with aider (attempt $attempt/$MAX_FIX_ATTEMPTS)..."
if [[ "$DRY_RUN" == true ]]; then
info "DRY RUN: Would run aider to fix issues"
return 0
fi
# Create a temporary file for the error context to avoid command line issues
local temp_message_file="${LOG_DIR}/aider_message_${target}_${attempt}_$(date +%s).txt"
# Construct the message for aider and write to temp file
cat > "$temp_message_file" << EOF
The make target '$target' failed with the following output:
$error_context
Please analyze the failure and fix the underlying issues in the code. Focus on:
1. Understanding the root cause of the failure
2. Making the minimal necessary changes to fix the issue
3. Ensuring the fix doesn't break other functionality
4. Following best practices for the detected programming language/framework
If this is a test failure, fix the code being tested rather than changing the test unless the test itself is clearly wrong.
EOF
# Construct aider command
local aider_cmd="aider"
# Add model specification
if [[ -n "$MODEL" ]]; then
aider_cmd="$aider_cmd --model $MODEL"
fi
# Set up environment variables for API configuration
local env_vars=()
# Add API base URL if provided (for OpenAI compatible endpoints)
if [[ -n "$API_BASE" ]]; then
env_vars+=("OPENAI_API_BASE=$API_BASE")
fi
# Set API key environment variable based on model/provider
if [[ -n "$API_KEY" ]]; then
# If API_BASE is set, assume OpenAI compatible API
if [[ -n "$API_BASE" ]]; then
env_vars+=("OPENAI_API_KEY=$API_KEY")
else
# Set provider-specific environment variables
case "$MODEL" in
gpt-*|o1-*|o3-*)
env_vars+=("OPENAI_API_KEY=$API_KEY")
;;
claude-*|sonnet*)
env_vars+=("ANTHROPIC_API_KEY=$API_KEY")
;;
gemini*)
env_vars+=("GEMINI_API_KEY=$API_KEY")
;;
deepseek*)
env_vars+=("DEEPSEEK_API_KEY=$API_KEY")
;;
*)
# For unknown models, try to infer from model name
local provider=$(echo "$MODEL" | cut -d'/' -f1 | tr '[:lower:]' '[:upper:]')
if [[ "$provider" != "$MODEL" ]]; then
env_vars+=("${provider}_API_KEY=$API_KEY")
else
# Default to OpenAI format
env_vars+=("OPENAI_API_KEY=$API_KEY")
fi
;;
esac
fi
fi
# Add environment variables to the command using --set-env
for env_var in "${env_vars[@]}"; do
aider_cmd="$aider_cmd --set-env $env_var"
done
# Add architect mode if requested
if [[ "$ARCHITECT_MODE" == true ]]; then
aider_cmd="$aider_cmd --architect"
fi
# Add extra arguments
aider_cmd="$aider_cmd $AIDER_EXTRA_ARGS"
# Use --message-file instead of --message to avoid command line parsing issues
aider_cmd="$aider_cmd --message-file \"$temp_message_file\""
debug "Executing: $aider_cmd"
debug "Message file: $temp_message_file"
log "Aider command: $aider_cmd"
log "Message file: $temp_message_file"
# Execute aider and capture output
local aider_output_file="${LOG_DIR}/aider_output_${target}_${attempt}_$(date +%s).log"
local aider_exit_code=0
info "Running aider (output logged to: $aider_output_file)..."
if eval "$aider_cmd" 2>&1 | tee "$aider_output_file"; then
success "aider completed successfully"
log "aider fix attempt $attempt completed successfully"
aider_exit_code=0
else
aider_exit_code=$?
error "aider failed with exit code $aider_exit_code"
log "aider fix attempt $attempt failed with exit code $aider_exit_code"
log "aider output saved to: $aider_output_file"
fi
# Clean up temporary message file
rm -f "$temp_message_file"
return $aider_exit_code
}
# =============================================================================
# MAIN WORKFLOW
# =============================================================================
main_workflow() {
local target="$1"
# If no target specified, show interactive menu
if [[ -z "$target" ]]; then
local targets_array
mapfile -t targets_array < <(parse_makefile_targets)
if [[ ${#targets_array[@]} -eq 0 ]]; then
error "No targets found in Makefile"
return 1
fi
display_targets "${targets_array[@]}"
target=$(select_target "${targets_array[@]}")
fi
info "Selected target: $target"
log "Selected target: $target"
# Run the make target
local output_file
if output_file=$(run_make_target "$target"); then
# Success - we're done
success "Target '$target' completed successfully!"
return 0
else
# Failure - try to fix with aider
warn "Target '$target' failed. Attempting to fix with AI assistance..."
local attempt=1
while [[ $attempt -le $MAX_FIX_ATTEMPTS ]]; do
# Analyze the failure
local error_context
error_context=$(analyze_failure "$output_file" "$target")
if [[ -z "$error_context" ]]; then
error "Could not extract error context from output"
return 1
fi
# Try to fix with aider
if fix_with_aider "$target" "$error_context" "$attempt"; then
info "Fix applied. Retrying make target..."
# Retry the make target
if output_file=$(run_make_target "$target"); then
success "Target '$target' now passes after AI fix!"
success "Fixed in $attempt attempt(s)"
return 0
else
warn "Target still failing after fix attempt $attempt"
((attempt++))
fi
else
error "aider fix attempt $attempt failed"
((attempt++))
fi
done
error "Failed to fix target '$target' after $MAX_FIX_ATTEMPTS attempts"
error "You may need to manually investigate the issue"
return 1
fi
}
# =============================================================================
# COMMAND LINE PARSING
# =============================================================================
parse_arguments() {
while [[ $# -gt 0 ]]; do
case $1 in
-h|--help)
HELP=true
shift
;;
-l|--list)
LIST_TARGETS=true
shift
;;
-m|--model)
MODEL="$2"
shift 2
;;
-k|--api-key)
API_KEY="$2"
shift 2
;;
-b|--api-base)
API_BASE="$2"
shift 2
;;
-a|--architect)
ARCHITECT_MODE=true
shift
;;
-d|--dry-run)
DRY_RUN=true
shift
;;
-v|--verbose)
VERBOSE=true
shift
;;
--max-attempts)
MAX_FIX_ATTEMPTS="$2"
shift 2
;;
-*)
error "Unknown option: $1"
show_help
exit 1
;;
*)
if [[ -z "$TARGET" ]]; then
TARGET="$1"
else
error "Multiple targets specified: '$TARGET' and '$1'"
exit 1
fi
shift
;;
esac
done
}
# =============================================================================
# CONFIGURATION FILE LOADING
# =============================================================================
load_config_file() {
local config_file=".aider-make.conf"
if [[ -f "$config_file" ]]; then
info "Loading configuration from $config_file"
# Source the config file safely
while IFS='=' read -r key value; do
# Skip comments and empty lines
[[ "$key" =~ ^[[:space:]]*# ]] && continue
[[ -z "${key// }" ]] && continue
# Remove leading/trailing whitespace
key=$(echo "$key" | xargs)
value=$(echo "$value" | xargs)
# Set variables if not already set by command line or environment
case "$key" in
MODEL)
[[ "$MODEL" == "$DEFAULT_MODEL" ]] && MODEL="$value"
;;
API_KEY)
[[ "$API_KEY" == "$DEFAULT_API_KEY" ]] && API_KEY="$value"
;;
API_BASE)
[[ "$API_BASE" == "$DEFAULT_API_BASE" ]] && API_BASE="$value"
;;
MAX_ATTEMPTS)
[[ "$MAX_FIX_ATTEMPTS" == "${AIDER_MAKE_MAX_ATTEMPTS:-3}" ]] && MAX_FIX_ATTEMPTS="$value"
;;
esac
done < "$config_file"
debug "Configuration loaded from $config_file"
fi
}
# =============================================================================
# MAIN FUNCTION
# =============================================================================
main() {
# Parse command line arguments
parse_arguments "$@"
# Load configuration file
load_config_file
# Show help if requested
if [[ "$HELP" == true ]]; then
show_help
exit 0
fi
# Setup logging
setup_logging
# Check dependencies
if ! check_dependencies; then
exit 1
fi
# List targets if requested
if [[ "$LIST_TARGETS" == true ]]; then
info "Available make targets:"
parse_makefile_targets
exit 0
fi
# Run main workflow
if main_workflow "$TARGET"; then
success "aider-make completed successfully!"
exit 0
else
error "aider-make failed"
exit 1
fi
}
# =============================================================================
# SCRIPT ENTRY POINT
# =============================================================================
# Only run main if script is executed directly (not sourced)
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
main "$@"
fi
@tosin2013
Copy link
Author

tosin2013 commented Aug 7, 2025

curl -OL https://gist.githubusercontent.com/tosin2013/7b5d1140dbd23161321c9d73a2535ea0/raw/f4c27b968cce890c52cf6da4e9ff2dc2f0fcde02/aider-make.sh
chmod +x aider-make.sh
./aider-make.sh --help

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment