Skip to content

Instantly share code, notes, and snippets.

@tosin2013
Last active November 23, 2024 17:05
Show Gist options
  • Save tosin2013/e7597e1f4ecabd6f753b3c1b00b45491 to your computer and use it in GitHub Desktop.
Save tosin2013/e7597e1f4ecabd6f753b3c1b00b45491 to your computer and use it in GitHub Desktop.
aider_setup.sh - Aider Setup Script - Streamlined setup for using various AI models with aider-chat, including options for DeepSeek, OpenAI, OpenRouter, and Ollama models.

Aider Setup Script

This Bash script streamlines the setup and execution of Aider, an AI-powered pair programming assistant that operates within your terminal. It automates the installation process, manages virtual environments, and configures Aider with various models and settings.

Prerequisites

  • Python: Ensure Python 3.9 or higher is installed. Verify your version with:

    python3 --version
  • Virtual Environment: The script creates and manages a virtual environment named aider-env in the current directory.

  • API Keys: Depending on the model you intend to use, obtain the necessary API keys (e.g., OpenAI, Anthropic) and have them ready for configuration.

Quick install

curl -OL https://gist.githubusercontent.com/tosin2013/e7597e1f4ecabd6f753b3c1b00b45491/raw/770ae40e0092e08d74f5acd116e36a53152212dc/aider_setup.sh
chmod +x aider_setup.sh
./aider_setup.sh --create-venv

Recommened use the Automated Code Development Assistant for Prompt Engineering

Automated Code Development Assistant

curl -OL https://raw.githubusercontent.com/tosin2013/automated-code-dev-assistant/refs/heads/main/groq_code_development_assistant.py
source aider-env/bin/activate
export GROQ_API_KEY=gsk_XXXXXX
python3 groq_code_development_assistant.py

I normally use this quickscript

#!/bin/bash
source aider-env/bin/activate
export GROQ_API_KEY=gsk_XXXXXX
./aider_setup.sh --use-model  groq/llama-3.1-70b-versatile  --set-api-key groq ${GROQ_API_KEY}

Usage

Execute the script with the desired options:

./aider_setup.sh [options]

Options

  • --create-venv: Create and activate a virtual environment, then install the Aider module.

  • --list-categories: Display all available model categories.

  • --list-models <category>: List all available models within a specified category.

  • --set-api-key <category> <key>: Set the API key for a specific category.

  • --use-model <model>: Start Aider with a specified model.

  • --use-vault: Use Vault to fetch API keys.

  • --gui: Run Aider in your browser.

  • --input-files <file>: Specify the input files (default: files.txt).

  • --package-file <file>: Specify the package file (default: package.json).

  • --prompt-file <file>: Specify the prompt file (default: prompt.txt).

  • --test-cmd <command>: Specify the test command (default: ./micro-agent-start.sh).

  • --max-chat-history-tokens <num>: Set the maximum number of chat history tokens (default: 2500).

  • --edit-format <format>: Set the edit format (default: whole).

  • --editor-edit-format <format>: Set the editor edit format (default: full).

  • --dry-run: Simulate actions without making any changes.

  • --help: Display the help message.

  • --cleanup-venv: Remove the virtual environment.

  • --conventions-file <file>: Specify the conventions file (default: CONVENTIONS.md).

  • --sensitive-files <file>: Specify the sensitive files (default: sensitive_files.txt).

Examples

  1. Create and Activate Virtual Environment:

    ./aider_setup.sh --create-venv
  2. List Available Model Categories:

    ./aider_setup.sh --list-categories
  3. Set API Key for OpenAI:

    ./aider_setup.sh --set-api-key openai YOUR_OPENAI_API_KEY
  4. Start Aider with a Specific Model:

    ./aider_setup.sh --use-model openai/gpt-4
  5. Run Aider in GUI Mode:

    ./aider_setup.sh --use-model openai/gpt-4 --gui

Configuration

The script supports a configuration file located at ~/.aider_config. If present, it loads this file to override default settings. The configuration file should be in the following format:

# ~/.aider_config

INPUT_FILES="custom_files.txt"
PROMPT_FILE="custom_prompt.txt"
CONVENTIONS_FILE="CUSTOM_CONVENTIONS.md"
SENSITIVE_FILES="custom_sensitive_files.txt"
TEST_CMD="./custom_start_script.sh"
MAX_CHAT_HISTORY_TOKENS=3000
EDIT_FORMAT="diff"
EDITOR_EDIT_FORMAT="diff"
LOG_FILE="custom_aider_setup.log"
DRY_RUN=true

Logging

All operations are logged with timestamps in the specified log file (default: aider_setup.log). Review this file for detailed information about the script's execution.

Cleanup

To remove the virtual environment created by the script:

./aider_setup.sh --cleanup-venv

Notes

  • Error Handling: The script employs strict error handling. If any command fails, the script will terminate, and an error message will be logged.

  • Dependencies: Ensure all required dependencies are installed, especially if using the --use-vault option, which requires hcp and jq.

  • API Keys: Secure your API keys appropriately. If using Vault, ensure it is configured correctly to fetch the necessary secrets.

For more information about Aider and its capabilities, visit the official documentation.

#!/bin/bash
# Enable strict error handling
set -euo pipefail
IFS=$'\n\t'
# Export PS4 for debugging (optional)
#export PS4='+(${BASH_SOURCE}:${LINENO}): ${FUNCNAME[0]:+${FUNCNAME[0]}(): }'
# Uncomment the next line to enable debugging
#set -x
# Default configurations
INPUT_FILES="files.txt"
PROMPT_FILE="prompt.txt"
CONVENTIONS_FILE="CONVENTIONS.md"
SENSITIVE_FILES="sensitive_files.txt"
TEST_CMD="./start-script.sh"
MAX_CHAT_HISTORY_TOKENS=2500
EDIT_FORMAT="whole"
EDITOR_EDIT_FORMAT="full"
LOG_FILE="aider_setup.log"
DRY_RUN=false
# python 3.9 or above is required
REQUIRED_PYTHON_VERSION="3.9"
# Load configuration if the file exists
CONFIG_FILE="${HOME}/.aider_config"
if [[ -f "$CONFIG_FILE" ]]; then
source "$CONFIG_FILE"
fi
# Initialize variables
gui_mode=false
use_vault=false
model=""
input_files=${INPUT_FILES}
prompt_file=${PROMPT_FILE}
test_cmd=${TEST_CMD} # Ensure test_cmd is initialized
max_chat_history_tokens=${MAX_CHAT_HISTORY_TOKENS}
edit_format=${EDIT_FORMAT}
editor_edit_format=${EDITOR_EDIT_FORMAT}
log_file=${LOG_FILE}
dry_run=${DRY_RUN}
# Function to log messages
log() {
echo "$(date '+%Y-%m-%d %H:%M:%S') : $*" | tee -a "$LOG_FILE"
}
# Function to display help
display_help() {
cat << EOF
Usage: $(basename "$0") [options]
Options:
--install [model] Install specific aider model
--create-venv Create and activate a virtual environment and install aider module
--list-models [category] List all available models for a category
--list-categories List all available model categories
--use-model <model> Start aider with a specific model
--set-api-key <category> <key> Set the API key for a specific category
--use-vault Use Vault to fetch API keys
--dry-run Simulate actions without making any changes
--gui Run aider in your browser
--input-files <file> Specify input files (default: files.txt)
--prompt-file <file> Specify prompt file (default: prompt.txt)
--test-cmd <command> Specify test command (default: ./micro-agent-start.sh)
--max-chat-history-tokens <num> Set max chat history tokens (default: 2500)
--edit-format <format> Set edit format (default: whole)
--editor-edit-format <format> Set editor edit format (default: full)
--help Display this help message
--cleanup-venv Remove the virtual environment
--conventions-file <file> Specify conventions file (default: CONVENTIONS.md)
--sensitive-files <file> Specify sensitive files (default: sensitive_files.txt)
For more information, visit: https://aider.chat/
EOF
}
# Function to check Python version
check_python_version() {
local python_version
python_version=$(python3 -c 'import sys; print(".".join(map(str, sys.version_info[:2])))')
if [[ $(printf '%s\n' "$python_version" "$REQUIRED_PYTHON_VERSION" | sort -V | head -n1) != "$REQUIRED_PYTHON_VERSION" ]]; then
log "Error: Python $REQUIRED_PYTHON_VERSION or higher is required. Current version: $python_version."
exit 1
fi
}
# Function to create and activate virtual environment
create_venv() {
check_python_version
log "Creating virtual environment..."
python3 -m venv aider-env
if [[ $? -ne 0 ]]; then
log "Error: Failed to create virtual environment."
exit 1
fi
log "Activating virtual environment..."
source aider-env/bin/activate
if [[ $? -ne 0 ]]; then
log "Error: Failed to activate virtual environment."
exit 1
fi
log "Installing necessary Python packages..."
pip install aider-chat questionary inquirer shell-gpt
if [[ $? -ne 0 ]]; then
log "Error: Failed to install Python packages."
exit 1
fi
update_gitignore
}
# Function to activate virtual environment if it exists
activate_venv() {
if [[ -d "aider-env" ]]; then
log "Activating existing virtual environment..."
source aider-env/bin/activate
if [[ $? -ne 0 ]]; then
log "Error: Failed to activate virtual environment."
exit 1
fi
else
log "Virtual environment not found. Creating one..."
create_venv
fi
}
# Function to update .gitignore
update_gitignore() {
local gitignore_file=".gitignore"
if [[ ! -f "$gitignore_file" ]]; then
touch "$gitignore_file"
if [[ $? -ne 0 ]]; then
log "Error: Failed to create .gitignore file."
exit 1
fi
fi
local entries=("aider-env/" "aider-env/bin/activate" "aider_setup.sh")
for entry in "${entries[@]}"; do
grep -qxF "$entry" "$gitignore_file" || echo "$entry" >> "$gitignore_file"
done
log "Updated .gitignore."
}
# Function to list all available categories
list_categories() {
log "Listing available categories:"
echo "openai"
echo "anthropic"
echo "gemini"
echo "groq"
echo "azure"
echo "cohere"
echo "deepseek"
echo "ollama"
echo "openrouter"
}
# Function to list available models for a category
list_models() {
local category=$1
log "Listing available models for category: $category"
aider --models "$category"
if [[ $? -ne 0 ]]; then
log "Error: Failed to list models for category: $category."
exit 1
fi
}
# Function to set API keys (local configuration)
set_api_key() {
local category=$1
local key_value=$2
local key_name=""
case $category in
openai) key_name="OPENAI_API_KEY" ;;
anthropic) key_name="ANTHROPIC_API_KEY" ;;
gemini) key_name="GEMINI_API_KEY" ;;
groq) key_name="GROQ_API_KEY" ;;
azure) key_name="AZURE_API_KEY" ;;
cohere) key_name="COHERE_API_KEY" ;;
deepseek) key_name="DEEPSEEK_API_KEY" ;;
ollama) key_name="OLLAMA_API_KEY" ;;
openrouter) key_name="OPENROUTER_API_KEY" ;;
*) log "Error: Unknown category: $category." ; exit 1 ;;
esac
local model_keys_file=~/model_keys.conf
# Ensure the file exists
touch "$model_keys_file"
if [[ $? -ne 0 ]]; then
log "Error: Failed to create or access $model_keys_file."
exit 1
fi
# Check if the category already exists and update it
if grep -q "^$category:" "$model_keys_file"; then
sed -i "s|^$category:.*|$category:$key_name:$key_value|" "$model_keys_file"
else
echo "$category:$key_name:$key_value" >> "$model_keys_file"
fi
# Secure the keys file
chmod 600 "$model_keys_file"
log "API key for $category set successfully."
}
# Function to login to HCP and initialize Vault secrets
hcp_login() {
log "Logging into HCP..."
hcp auth login
if [[ $? -ne 0 ]]; then
log "Error: Failed to login to HCP."
exit 1
fi
log "Initializing Vault secrets..."
hcp profile init --vault-secrets
if [[ $? -ne 0 ]]; then
log "Error: Failed to initialize Vault secrets."
exit 1
fi
}
# Function to read secret from HCP Vault
load_api_keys_from_hcp_vault() {
local category=$1
local secret_path=""
case $category in
openai) secret_path="openai" ;;
anthropic) secret_path="secret/data/anthropic/anthropic_api_key" ;;
gemini) secret_path="gemini" ;;
groq) secret_path="secret/data/groq/groq_api_key" ;;
azure) secret_path="secret/data/azure/azure_api_key" ;;
cohere) secret_path="secret/data/cohere/cohere_api_key" ;;
deepseek) secret_path="deepseek" ;;
ollama) secret_path="secret/data/ollama/ollama_api_key" ;;
openrouter) secret_path="secret/data/openrouter/openrouter_api_key" ;;
*) log "Error: Unknown category: $category." ; exit 1 ;;
esac
log "Fetching secret for $category from HCP Vault at path '$secret_path'..."
local api_key
api_key=$(hcp vault-secrets secrets open "$secret_path" --format=json | jq -r '.static_version.value')
if [[ -z "$api_key" ]]; then
log "Error: API key for $category not found in HCP Vault at path '$secret_path'."
exit 1
fi
local key_name
key_name=$(echo "${category}_API_KEY" | tr '[:lower:]' '[:upper:]')
export "$key_name"="$api_key"
log "Using $key_name."
}
# Function to load API keys from local configuration
load_api_keys() {
local model_keys_file=~/model_keys.conf
if [[ ! -f "$model_keys_file" ]]; then
log "Error: Local API keys file not found at $model_keys_file."
return 1
fi
while IFS=: read -r category key_name key_value; do
export "$key_name"="$key_value"
done < "$model_keys_file"
log "Loaded API keys from $model_keys_file."
}
# Function to validate file existence
validate_file() {
local file_path=$1
local description=$2
if [[ -n "$file_path" && ! -f "$file_path" ]]; then
log "Error: $description file '$file_path' does not exist."
exit 1
fi
}
# Function to use a specific model
use_model() {
local model=$1
local gui_mode=$2
local use_vault=$3
# Extract category from model name (assuming format category/model)
local model_category="${model%%/*}"
if [[ -z "$model_category" ]]; then
log "Error: Invalid model format '$model'. Expected 'category/model'."
exit 1
fi
if [[ $use_vault == true ]]; then
# Check dependencies
for cmd in hcp jq; do
if ! command -v "$cmd" &> /dev/null; then
log "Error: Required command '$cmd' is not installed. Please install it to proceed."
exit 1
fi
done
# HCP Login and Vault initialization
hcp_login
# Load API keys from HCP Vault
load_api_keys_from_hcp_vault "$model_category"
else
# Load API keys from local configuration
load_api_keys || {
log "Error: Failed to load local API keys."
exit 1
}
fi
local key_name
key_name=$(echo "${model_category}_API_KEY" | tr '[:lower:]' '[:upper:]')
# Verify if the API key is set
if [[ -z ${!key_name} ]]; then
log "Error: Missing API key for $model_category: $key_name."
log "Please set the API key using HCP Vault or local configuration."
exit 1
fi
if [[ $gui_mode == true ]]; then
log "Starting aider in GUI mode with model: $model."
aider --gui --model "$model"
else
log "Starting aider with model: $model."
# Validate required files
validate_file "$input_files" "Input files"
validate_file "$prompt_file" "Prompt"
validate_file "$CONVENTIONS_FILE" "Conventions"
validate_file "$SENSITIVE_FILES" "Sensitive files" # Correct validation for sensitive files
# Validate test command if provided
if [[ -n "$test_cmd" ]]; then
if [[ ! -x "$test_cmd" ]]; then
log "Error: Test command '$test_cmd' is not executable."
exit 1
fi
if [[ ! -f "$test_cmd" ]]; then
log "Error: Test command file '$test_cmd' does not exist."
log "Please create the file with the necessary commands to start the program or run test cases."
exit 1
fi
fi
# Construct the aider command using an array for safety
local aider_cmd=(aider)
# source env
source aider-env/bin/activate
# Append mandatory options
aider_cmd+=( $(cat $input_files)
--architect
--model "$model"
--editor-model "$model"
--auto-commits
--max-chat-history-tokens "$max_chat_history_tokens"
--auto-test
--suggest-shell-commands
--check-update
--edit-format "$edit_format"
--editor-edit-format "$editor_edit_format"
--read "$CONVENTIONS_FILE"
--read "$SENSITIVE_FILES"
--yes
--cache-prompts
--show-diffs
--subtree-only)
# Conditionally append optional options
[[ -n "$prompt_file" ]] && aider_cmd+=(--message-file "$prompt_file")
[[ -n "$test_cmd" ]] && aider_cmd+=(--test-cmd "$test_cmd")
# Optional: Implement dry-run functionality
if [[ $DRY_RUN == true ]]; then
log "Dry run: The following command would be executed:"
printf '%q ' "${aider_cmd[@]}"
echo
else
# Execute the command
"${aider_cmd[@]}"
if [[ $? -ne 0 ]]; then
log "Error: 'aider' command failed."
exit 1
fi
fi
fi
}
# Function to remove virtual environment
cleanup_venv() {
if [[ -d "aider-env" ]]; then
log "Removing virtual environment..."
rm -rf aider-env
if [[ $? -ne 0 ]]; then
log "Error: Failed to remove virtual environment."
exit 1
fi
log "Virtual environment removed successfully."
else
log "No virtual environment found to remove."
fi
}
# Parse arguments
while [[ $# -gt 0 ]]; do
case $1 in
--create-venv)
create_venv
shift
;;
--list-categories)
list_categories
shift
;;
--list-models)
activate_venv
if [[ -z "${2:-}" ]]; then
log "Error: Missing category for --list-models."
display_help
exit 1
fi
list_models "$2"
shift 2
;;
--set-api-key)
if [[ -z "${2:-}" || -z "${3:-}" ]]; then
log "Error: Missing arguments for --set-api-key."
display_help
exit 1
fi
set_api_key "$2" "$3"
shift 3
;;
--use-model)
if [[ -z "${2:-}" ]]; then
log "Error: Missing model for --use-model."
display_help
exit 1
fi
model=$2
shift 2
;;
--use-vault)
use_vault=true
shift
;;
--gui)
gui_mode=true
shift
;;
--input-files)
if [[ -z "${2:-}" ]]; then
log "Error: Missing file for --input-files."
display_help
exit 1
fi
input_files=$2
shift 2
;;
--prompt-file)
if [[ -z "${2:-}" ]]; then
log "Error: Missing file for --prompt-file."
display_help
exit 1
fi
prompt_file=$2
shift 2
;;
--test-cmd)
if [[ -z "${2:-}" ]]; then
log "Error: Missing command for --test-cmd."
display_help
exit 1
fi
test_cmd=$2
shift 2
;;
--max-chat-history-tokens)
if [[ -z "${2:-}" ]]; then
log "Error: Missing number for --max-chat-history-tokens."
display_help
exit 1
fi
max_chat_history_tokens=$2
shift 2
;;
--edit-format)
if [[ -z "${2:-}" ]]; then
log "Error: Missing format for --edit-format."
display_help
exit 1
fi
edit_format=$2
shift 2
;;
--editor-edit-format)
if [[ -z "${2:-}" ]]; then
log "Error: Missing format for --editor-edit-format."
display_help
exit 1
fi
editor_edit_format=$2
shift 2
;;
--dry-run)
DRY_RUN=true
shift
;;
--help)
display_help
exit 0
;;
--cleanup-venv)
cleanup_venv
exit 0
;;
--conventions-file)
if [[ -z "${2:-}" ]]; then
log "Error: Missing file for --conventions-file."
display_help
exit 1
fi
CONVENTIONS_FILE=$2
shift 2
;;
--sensitive-files)
if [[ -z "${2:-}" ]]; then
log "Error: Missing file for --sensitive-files."
display_help
exit 1
fi
SENSITIVE_FILES=$2
shift 2
;;
*)
log "Error: Invalid option: $1. Use --help for usage information."
display_help
exit 1
;;
esac
done
# Ensure .gitignore is updated when script is run
update_gitignore
# Run aider with specified model and mode
if [[ -n "$model" ]]; then
use_model "$model" "$gui_mode" "$use_vault"
else
log "Error: No model specified. Use --use-model <model> to specify a model."
display_help
exit 1
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment