Last active
December 6, 2023 10:05
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.2. Make a new folder and dump this script into it.
Here's a little bash snippet that does that.
3. Create an
environment.yml
file for anaconda, in the folder.Here's a bash heredoc that does that.
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.
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.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 run
main.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: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!