There are many “AI coding” tools, including GitHub’s Copilot – but none of them are able to work with multiple files at a time. AIDER project can! It is a “GPT-powered coding in your terminal”.

GitHub: https://github.com/paul-gauthier/aider

Let’s start by creating a new environment with Anaconda.
If you’d like to learn how to use Anaconda properly, here’s a free course: https://learning.anaconda.cloud/get-started-with-anaconda

Assuming you have it installed and ready: start Anaconda Terminal and run this command:

conda create --name aider python=3.11.3

TIP:
If you don’t have ANACONDA, I recommend you get it installed before testing anything. It will save you a ton of trouble in the future.

It will ask if you want to install all necessary packages – type y and press ENTER.

When it finishes installing, start the environment by running this command:

conda activate aider

Your (base) environment should change to (aider), which means you are in the environment we have created. Because it’s completely isolated, it will not cause conflicts between Python packages and any other stuff you may have installed on your system. It’s a good practice to always use dedicated environment for your project.

Together with Python comes a command-line package management called pip. We will use it to install (almost) everything we need.
Run this code in your Anaconda terminal (make sure you do that in the (aider) environment!):

pip install aider-chat

It needs to grab a few things from the Internet so get a drink and wait. If everything goes well, it should install without any issues.
When it does, you can move to the next step.

Now we need to add our OpenAI Api key to the environment. I’m running this on Windows 11 so it will look like this:

set OPENAI_API_KEY=YoueKeyHere

If you use Linux or macOS, thank use “export” instead of “set”:

export OPENAI_API_KEY=YourKeyHere

Don’t worry if you have problems with this – you can add openai-api-key: sk-... in an .aider.config.yml file.

If you don’t have an OpenAI API key, you can get it from https://platform.openai.com/account/api-keys. It’s a paid service so you may need to use subscribe with your payment details.
OpenAI charges for usage so don’t worry, it’s not expensive. I’m playing with this API for weeks and I didn’t get charged yet.

If you want to work with bigger projects (that have multiple files), you will need to install universal ctags. It’s easy on Linux or macOS as you can use Homebrew and run brew install universal-ctags.
I’m on Windows, so it’s a little more complicated…

I’m too lazy to compile my own version, so I have found precompiled Windows version here.

To use Aider, start your Anaconda terminal and activate the environment:

conda activate aider

Navigate to your main drive and create a new folder. I’m going to call mine “aider”:

cd\
mkdir aider
cd aider

Let’s try to build something with Aider now! To start a fresh project, type: aider main.py. If everything goes well, you should see a green line with your file name:

Let’s try to build a game… The simplest thing I could come up with is Tic Tac Toe.

I’m lazy so I will not explain the rules, or give any details. Here’s my first prompt:
“Create a GUI game of Tic, Tac, Toe.”

TIP:
By default, you can’t use copy & paste in Anaconda terminal.
To change it, right-click on the title bar and go to Properties > Options and select “Use Ctrl+Shift+C/V as Copy/Paste”

Here’s the code it created:

import tkinter as tk
from tkinter import messagebox

class TicTacToe:
    def __init__(self):
        self.current_player = "X"
        self.board = [["" for _ in range(3)] for _ in range(3)]
        self.root = tk.Tk()
        self.root.title("Tic Tac Toe")
        self.buttons = [[None for _ in range(3)] for _ in range(3)]
        self.create_board()

    def create_board(self):
        for i in range(3):
            for j in range(3):
                button = tk.Button(self.root, text="", width=10, height=5,
                                   command=lambda row=i, col=j: self.make_move(row, col))
                button.grid(row=i, column=j)
                self.buttons[i][j] = button

    def make_move(self, row, col):
        if self.board[row][col] == "":
            self.board[row][col] = self.current_player
            self.buttons[row][col].config(text=self.current_player)
            if self.check_winner():
                messagebox.showinfo("Game Over", f"Player {self.current_player} wins!")
                self.root.quit()
            elif self.check_draw():
                messagebox.showinfo("Game Over", "It's a draw!")
                self.root.quit()
            else:
                self.current_player = "O" if self.current_player == "X" else "X"

    def check_winner(self):
        for i in range(3):
            if self.board[i][0] == self.board[i][1] == self.board[i][2] != "":
                return True
            if self.board[0][i] == self.board[1][i] == self.board[2][i] != "":
                return True
        if self.board[0][0] == self.board[1][1] == self.board[2][2] != "":
            return True
        if self.board[0][2] == self.board[1][1] == self.board[2][0] != "":
            return True
        return False

    def check_draw(self):
        for row in self.board:
            if "" in row:
                return False
        return True

    def start(self):
        self.root.mainloop()
        
if __name__ == "__main__":
    game = TicTacToe()
    game.start()

Our fresh environment doesn’t have Tkinter so we need to install it. I will use pip again:

pip install tk

When it finishes installing (and assuming there are no errors), we can try to run our program:

python main.py

Here it is! Our first “game”!

It’s super simple, but it was a test to see if our Aider installation works.

With a couple more prompts it updated the code with some basic computer opponent, score table, etc. It’s super simple and glitchy – but I know it works.
Very rarely the code will work exactly as intended. In most cases, requires some basic understanding of the coding language to investigate errors and tweak some logic problems.

Keep in mind that it’s still an LLM (Large Language Model) and it doesn’t “understand” the code it’s creating. We are calling it “AI”, but it’s nowhere close to becoming self-aware 😉

The more obscure problem you are trying to solve, the less likely you get a correct solution. If you working with a common problem, it means there is a lot of content about it and more data for OpenAI to use. If your problem is relatively new or relies on solutions that appeared in the last ~2 years – it will struggle to provide a correct answer.

Where Aider shines is the ability to use multiple files from your project (or git repo) as input and work with existing code. It’s not a zero-code approach, but it can speed up a lot of work, give you quick examples or help with solving errors in your code. I will be following aider project and maybe try to build something more complex with it.

Just to show a completely different way of using this, I used this prompt in a new session: Example HTML and Ajax code showing how to read and update SQLite database without refreshing the page and aider created 3 files and some explanations and instructions on how to make it work:

Create an HTML file (e.g., ajaxexample.html) with the following content:

HTML – ajaxexample.html
<!DOCTYPE html>
<html>
<head>
  <title>AJAX Example</title>
  <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
  <script>
    $(document).ready(function() {
      // Function to fetch data from the database
      function fetchData() {
        $.ajax({
          url: 'fetchdata.php',
          type: 'GET',
          success: function(response) {
            // Process the response and update the page
            // with the fetched data
            $('#data').html(response);
          }
        });
      }

      // Function to update data in the database
      function updateData() {
        var newData = $('#newData').val();

        $.ajax({
          url: 'updatedata.php',
          type: 'POST',
          data: { newData: newData },
          success: function(response) {
            // Process the response and update the page
            // with the updated data
            fetchData();
          }
        });
      }

      // Fetch initial data on page load
      fetchData();

      // Bind the updateData function to the button click event
      $('#updateButton').click(function() {
        updateData();
      });
    });
  </script>
</head>
<body>
  <h1>AJAX Example</h1>
  <div id="data"></div>
  <input type="text" id="newData" placeholder="Enter new data">
  <button id="updateButton">Update</button>
</body>
</html>

Create a server-side script (e.g., fetchdata.php) that fetches data from the database and returns it as a response. Here’s an example using PHP:

PHP – fetchdata.php
<?php
// Connect to the SQLite database
$db = new SQLite3('database.db');

// Fetch data from the database
$result = $db->query('SELECT * FROM table_name');

// Process the fetched data
$data = '';
while ($row = $result->fetchArray()) {
  $data .= '<p>' . $row['column_name'] . '</p>';
}

// Close the database connection
$db->close();

// Return the fetched data as the response
echo $data;
?>

Create another server-side script (e.g., updatedata.php) that updates the data in the database based on the received input. Here’s an example using PHP:

PHP – updatedata.php
<?php
// Connect to the SQLite database
$db = new SQLite3('database.db');

// Get the new data from the request
$newData = $_POST['newData'];

// Update the data in the database
$db->exec("UPDATE table_name SET column_name = '$newData'");

// Close the database connection
$db->close();

// Return a success message as the response
echo 'Data updated successfully';
?>

Make sure to replace 'database.db' with the actual path to your SQLite database file, and 'table_name' and 'column_name' with the appropriate table and column names in your database.

This example demonstrates how to use AJAX to fetch data from the database and update it without refreshing the page. The fetchData function sends a GET request to fetchdata.php, which retrieves the data from the database and returns it as a response. The updateData function sends a POST request to updatedata.php, which updates the data in the database based on the input and returns a success message as a response.

It’s not perfect, it requires some understanding of the code and the ability to troubleshoot, but it can make your life much easier. Well done Paul Gauthier!