Skip to content

Instantly share code, notes, and snippets.

@loopyd
Last active December 6, 2023 10:05
Show Gist options
  • Save loopyd/f34ed5d96c7363b80ea84137560553e7 to your computer and use it in GitHub Desktop.
Save loopyd/f34ed5d96c7363b80ea84137560553e7 to your computer and use it in GitHub Desktop.
[bash] Aider Linux Tool - aider is AI pair programming in your terminal: https://github.com/paul-gauthier/aider | This is a run script for aider that streamlines its workflow by automating it in a conda environment.
#!/bin/bash
export SCRIPT_PATH=$(dirname "$(realpath "$0")" )
function __usage() {
BASENAME=$(basename "$0")
printf "%0.s=" {1..80}
printf "\n%-10s%-60s%-10s\n" "$BASENAME" "Aider Linux CLI tool" "v0.0.1"
printf "%0.s-" {1..80}
printf "\n\n%s" "This is a CLI tool for managing Aider projects. It streamlines the aider workflow and allows a developer to iterate fast." | fold -w 75 | awk '{ print " " $0 }'
printf "\n %-10s%s\n" "Usage:" "$BASENAME [options] <project>"
printf "\n %-20s %-40s %-20s\n" "Argument" "Description" "Env"
printf "\n %s\n\n" "Tool Options:"
printf " %-20s %-40s %-20s\n" "-h | --help" "Display this help message, and exit" "n/a"
printf "\n %s\n\n" "Project Options:"
printf " %-20s %-40s %-20s\n" "-p | --project" "The name of the project to run" "PROJECT"
printf "\n %s\n\n" "Execution Options:"
printf " %-20s %-40s %-20s\n" "-e | --exec" "Execute from the given file. If" "EXEC_PATH"
printf " %-20s %-40s %-20s\n" " " "specified, will attempt to infer" ""
printf " %-20s %-40s %-20s\n" " " "what type of request is being made" ""
printf " %-20s %-40s %-20s\n" " " "and execute that request" ""
printf " %-20s %-40s %-20s\n" " " "accordingly." ""
printf " %-20s %-40s %-20s\n" "-a | --args" "Execute with the given command line" "EXEC_ARGS"
printf " %-20s %-40s %-20s\n" " " "arguments. If specified, will add" ""
printf " %-20s %-40s %-20s\n" " " "these arguments to the inferred" ""
printf " %-20s %-40s %-20s\n" " " "request." ""
printf "\n %s\n" "Default Behavior:"
printf "\n%s" "If the execution method cannot be inferred (no file extension) then the tool guesses by default at attempting to run the specified entrypoint as a python module." | fold -w 70 | awk '{ print " " $0 }'
printf "\n"
printf "%0.s-" {1..80}
printf "\n\n"
exit 0
}
function __parse_args() {
local ARGS=("$@")
SHORT_OPTIONS="hp:e:a:"
LONG_OPTIONS="help,project:,exec:,args:"
OPTS=$(getopt -o "$SHORT_OPTIONS" --long "$LONG_OPTIONS" -n "$(basename "$0")" -- "${ARGS[@]}")
# shellcheck disable=SC2181
if [ $? != 0 ]; then
echo "Failed parsing options: $OPTS"
exit 1
fi
eval set -- "$OPTS"
while true; do
case "$1" in
-h | --help)
__usage
;;
-p | --project)
if [[ -z "$PROJECT" ]]; then
PROJECT=$2
shift 2
continue
fi
;;
-e | --exec)
if [[ -z "$EXEC_PATH" ]]; then
EXEC_PATH=$2
shift 2
continue
fi
;;
-a | --args)
if [[ -z "$EXEC_ARGS" ]]; then
EXEC_ARGS=$2
shift 2
continue
fi
;;
--)
shift
break
;;
*)
echo "Unknown option: $1"
exit 1
esac
done
if [[ -z "$PROJECT" ]] && [[ -n "$EXEC_ARGS" ]]; then
echo "Arguments specified, but no project name specified. (Try --help)"
exit 1
fi
if [[ -z "$PROJECT" ]]; then
echo "Project name is required, but was not specified. (Try --help)"
exit 1
fi
}
function __check_aider_config() {
if [[ ! -f "$SCRIPT_PATH/aider.config.yml" ]]; then
echo "Could not find aider.config.yml in: $SCRIPT_PATH"
return 1
fi
return 0
}
function __conda_hooked() {
local CMD="$*"
eval "$(conda shell.bash hook)"
conda $CMD
}
function __conda_exists() {
local ENV_NAME=$1
if __conda_hooked env list | grep -q "$ENV_NAME"; then
return 0
else
return 1
fi
}
function __in_conda_env() {
if [ -n "$CONDA_DEFAULT_ENV" ]; then
return 0
else
return 1
fi
}
function __conda_leave() {
if __in_conda_env; then
echo "Deactivating conda environment: $CONDA_DEFAULT_ENV"
__conda_hooked deactivate
return 1
else
return 0
fi
}
function __conda_enter() {
local ENV_NAME=$1
if ! __conda_exists "$ENV_NAME"; then
__conda_create "$ENV_NAME" "environment.yml"
fi
__conda_leave
echo "Activating conda environment: $ENV_NAME"
__conda_hooked activate "$ENV_NAME" >/dev/null 2>&1
return 1
}
function __conda_remove()
{
local ENV_NAME=$1
if ! __conda_exists "$ENV_NAME"; then
return 0
fi
__conda_leave
echo "Removing conda environment: $ENV_NAME"
__conda_hooked env remove -n "$ENV_NAME" >/dev/null 2>&1
return 1
}
function __conda_create() {
local ENV_NAME=$1
local YML_FILE=$2
if __conda_exists "$ENV_NAME"; then
if __in_conda_env; then
__conda_leave
fi
__conda_remove "$ENV_NAME"
fi
echo "Creating conda environment: $ENV_NAME"
__conda_hooked env create -f "$YML_FILE" >/dev/null 2>&1
return 1
}
__find_aider_project() {
local PROJECT=$1
local PROJECT_PATH
PROJECT_PATH=$(find "$SCRIPT_PATH" -type d -name "$PROJECT" | head -n 1)
if [[ -z "$PROJECT_PATH" ]]; then
echo "Could not find project: $PROJECT"
exit 1
fi
echo "$PROJECT_PATH"
}
__run_aider() {
local MY_PROJECT=$1
local PROJECT_PATH
if ! __check_aider_config; then
echo "Could not find aider.config.yml in: $SCRIPT_PATH"
exit 1
fi
PROJECT_PATH=$(__find_aider_project "$MY_PROJECT")
echo "Running aider for project: $MY_PROJECT"
pushd "$PROJECT_PATH" >/dev/null 2>&1 || exit
aider -c "$SCRIPT_PATH/aider.config.yml"
popd >/dev/null 2>&1 || exit
}
__execute_project() {
local MY_PROJECT="$1"
local ENTRYPOINT="$2"
local EXEC_ARGS="$3"
local PROJECT_PATH
local CMD=()
PROJECT_PATH=$(__find_aider_project "$MY_PROJECT")
pushd "$PROJECT_PATH" >/dev/null 2>&1 || exit
if [[ "$ENTRYPOINT" == *.py ]]; then
CMD=("python" "$ENTRYPOINT" "$EXEC_ARGS")
fi
if [[ "$ENTRYPOINT" == *.sh ]]; then
CMD=("bash" "$ENTRYPOINT" "$EXEC_ARGS")
fi
if [[ "$ENTRYPOINT" != *.* ]]; then
echo "Warning: Assuming entrypoint: $ENTRYPOINT is a python module."
CMD=("python" "-m" "$ENTRYPOINT" "$EXEC_ARGS")
fi
echo "Executing command: ${CMD[*]}"
${CMD[*]}
LASTEXITCODE=$?
if [[ $LASTEXITCODE -ne 0 ]]; then
echo "Execute command returned exit code: $LASTEXITCODE"
return $LASTEXITCODE
fi
popd >/dev/null 2>&1 || exit
return 0
}
function __exit_trap() {
if [[ $? -ne 0 ]]; then
local BASENAME
BASENAME=$(basename "$0")
echo "$BASENAME failed with code: $?"
fi
export SCRIPT_PATH=
__conda_leave
}
trap __exit_trap EXIT
__parse_args "$@"
__conda_enter "aider"
if [[ -n "$EXEC_PATH" ]]; then
__execute_project "$PROJECT" "$EXEC_PATH" "$EXEC_ARGS"
exit 0
else
__run_aider "$PROJECT"
fi
__conda_leave
@loopyd
Copy link
Author

loopyd commented Nov 30, 2023

Installing the Aider Tool

1. Install anaconda3

You can follow the instructions at: https://docs.anaconda.com/free/anaconda/install/linux/

I have also included this snippet that installs anaconda3 systemwide to a prefix that you can specify by editing CONDA_PREFIX, creates a python group and adds the user to that group, then adjusts the permissions and initializes anaconda3 for the bash shell.

CONDA_PREFIX="/usr/local/anaconda3"
groupadd python
usermod -aG python $USER
installer_file=$(mktemp -q).sh
# Install dependencies (ubuntu | debian)
apt-get install libgl1-mesa-glx libegl1-mesa libxrandr2 libxrandr2 libxss1 libxcursor1 libxcomposite1 libasound2 libxi6 libxtst6
curl -SL https://repo.anaconda.com/archive/Anaconda3-2023.09-0-Linux-x86_64.sh -- >${installer_file}
chmod +x "${installer_file}"
${installer_file} -p "${CONDA_PREFIX}"
sudo chown -R root:python "${CONDA_PREFIX}"
sudo chmod -R g+rwx "${CONDA_PREFIX}"
conda init bash -q --system
source .bashrc
eval $(conda shell.bash hook)

2. Make a new folder and dump this script into it.

Here's a little bash snippet that does that.

mkdir AiderProjects
pushd AiderProjects
cat <<'EOF' >./run.sh
{ paste the RAW here } 
EOF
chmod +x ./run.sh
popd

3. Create an environment.yml file for anaconda, in the folder.

Here's a bash heredoc that does that.

pushd AiderProjects
cat <<'EOF' >./environment.yml
name: aider
channels:
  - defaults
  - conda-forge
prefix: ./.conda
dependencies:
  - python=3.11.6
  - pip
  - setuptools
  - wheel
  - aider-chat
EOF
popd

4. Add an aider.config.yml file.

You can get an API key from: https://platform.openai.com/api-keys

If this is your first time, you're probably on a prepaid account and have to top up. You can do so for your account at: https://platform.openai.com/account/billing/overview . Just click Add to Credit Balance.

I highly recommend topping up $100 or more, because aider uses a lot of credits, especially if you're using the new gpt-4-1106-preview (gpt-4-turbo) research preview. You'll need to have access to gpt-4 and have a plus subscription active to get access to this model.

pushd AiderProjects
cat <<'EOF' >./aider.config.yml
openai-api-base: "https://api.openai.com/v1"
openai-api-key: "your_api_key"
model: "gpt-4-1106-preview"
EOF
popd

5. Create a project folder

Once again, a little more bash. In your AiderProjects folder, you'll make a new project folder for your project.

pushd AiderProjects
mkdir myNewProject
popd

Running the Script

A. Working with your project.

You'll always be in the base AiderProjects folder when running this script, as its also where you placed your files whilst following the installation steps.

cd AiderProjects
./run.sh --project myNewProject

And you'll be dropped into aider for the first time, loaded in a fresh anaconda3 environment created automatically for it! Each time you run the script; you'll be using this environment.

B. Executing code from your project.

You'll always be in the base AiderProjects folder when running this script, as its also where you placed your files whilst following the installation steps.

You can select an entrypoint with the -e | --execargument (see--helpfor more details). For example, to runmain.py``:

cd AiderProjects
./run.sh --project myNewProject --exec main.py

And to run a shell script that may contain some environment initialization logic of your own (primarily for other languages). Say that you've got the script in the scripts folder of your project. You can do:

cd AiderProjects
./run.sh --project myNewProject --exec scripts/run.sh

If you want to add command-line arguments, you can use the -a | --args argument. Like so:

cd AiderProjects
./run.sh --project myNewProject --exec scripts/run.sh --args "--foo --bar --pi 3.141"

Getting Help

The tool includes a help page. You can pass the -h | --help command line option to see it.

If you have any issues, please comment them below!

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