From 98097005c70b8a2c3304229bbcb355d12c3f8921 Mon Sep 17 00:00:00 2001 From: Joshua Vial Date: Wed, 6 Sep 2023 15:00:35 +1200 Subject: [PATCH 01/87] small fix for better error handling if the summarizer ever fails - fix #231 --- aider/coders/base_coder.py | 6 +++++- aider/history.py | 2 ++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/aider/coders/base_coder.py b/aider/coders/base_coder.py index d103396d9..c10118924 100755 --- a/aider/coders/base_coder.py +++ b/aider/coders/base_coder.py @@ -359,7 +359,11 @@ class Coder: self.summarizer_thread.start() def summarize_worker(self): - self.summarized_done_messages = self.summarizer.summarize(self.done_messages) + try: + self.summarized_done_messages = self.summarizer.summarize(self.done_messages) + except ValueError as err: + self.io.tool_error(err.args[0]) + if self.verbose: self.io.tool_output("Finished summarizing chat history.") diff --git a/aider/history.py b/aider/history.py index 36d148344..56e8a8423 100644 --- a/aider/history.py +++ b/aider/history.py @@ -87,6 +87,8 @@ class ChatSummary: ] summary = simple_send_with_retries(self.model.name, messages) + if summary is None: + raise ValueError(f"summarizer unexpectedly failed for {self.model.name}") summary = prompts.summary_prefix + summary return [dict(role="user", content=summary)] From 166984e94a4cf48d78735db4c0495a2255198215 Mon Sep 17 00:00:00 2001 From: Joshua Vial Date: Fri, 8 Sep 2023 11:49:41 +1200 Subject: [PATCH 02/87] only show token costs if usage exists --- aider/coders/base_coder.py | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/aider/coders/base_coder.py b/aider/coders/base_coder.py index c10118924..f2d96ec40 100755 --- a/aider/coders/base_coder.py +++ b/aider/coders/base_coder.py @@ -606,15 +606,17 @@ class Coder: self.io.tool_error(show_content_err) raise Exception("No data found in openai response!") - prompt_tokens = completion.usage.prompt_tokens - completion_tokens = completion.usage.completion_tokens + tokens = None + if hasattr(completion, 'usage'): + prompt_tokens = completion.usage.prompt_tokens + completion_tokens = completion.usage.completion_tokens - tokens = f"{prompt_tokens} prompt tokens, {completion_tokens} completion tokens" - if self.main_model.prompt_price: - cost = prompt_tokens * self.main_model.prompt_price / 1000 - cost += completion_tokens * self.main_model.completion_price / 1000 - tokens += f", ${cost:.6f} cost" - self.total_cost += cost + tokens = f"{prompt_tokens} prompt tokens, {completion_tokens} completion tokens" + if self.main_model.prompt_price: + cost = prompt_tokens * self.main_model.prompt_price / 1000 + cost += completion_tokens * self.main_model.completion_price / 1000 + tokens += f", ${cost:.6f} cost" + self.total_cost += cost show_resp = self.render_incremental_response(True) if self.pretty: @@ -625,7 +627,9 @@ class Coder: show_resp = Text(show_resp or "") self.io.console.print(show_resp) - self.io.tool_output(tokens) + + if tokens is not None: + self.io.tool_output(tokens) def show_send_output_stream(self, completion): live = None From ad3eeca80edd8c5642c2212ba13a44207d5b590f Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Fri, 8 Sep 2023 08:28:12 -0700 Subject: [PATCH 03/87] Added __all__ to models/__init__ --- aider/models/__init__.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/aider/models/__init__.py b/aider/models/__init__.py index f299009c6..fa8aa3673 100644 --- a/aider/models/__init__.py +++ b/aider/models/__init__.py @@ -1,5 +1,15 @@ from .model import Model +from .openai import OpenAIModel +from .openrouter import OpenRouterModel GPT4 = Model.create("gpt-4") GPT35 = Model.create("gpt-3.5-turbo") GPT35_16k = Model.create("gpt-3.5-turbo-16k") + +__all__ = [ + OpenAIModel, + OpenRouterModel, + GPT4, + GPT35, + GPT35_16k, +] From 491993f97961b3515c8bbce99deeebfe6e6e4ce5 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Fri, 8 Sep 2023 09:12:34 -0700 Subject: [PATCH 04/87] Added a benchmarking readme --- benchmark/Dockerfile | 2 +- benchmark/README.md | 107 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 108 insertions(+), 1 deletion(-) create mode 100644 benchmark/README.md diff --git a/benchmark/Dockerfile b/benchmark/Dockerfile index ad79affa6..419167e01 100644 --- a/benchmark/Dockerfile +++ b/benchmark/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.8-slim +FROM python:3.10-slim RUN apt-get update && apt-get install -y less git COPY requirements.txt /aider/requirements.txt RUN pip install lox typer pandas matplotlib imgcat aider-chat diff --git a/benchmark/README.md b/benchmark/README.md new file mode 100644 index 000000000..38c98cb94 --- /dev/null +++ b/benchmark/README.md @@ -0,0 +1,107 @@ + +# Aider code editing benchmark harness + +Aider uses a "code editing" benchmark to quantitatively measure how well it works +with the GPT-3.5 and GPT-4 models. +This directory holds the harness and tools needed to run the benchmarking suite. + +## Background + +The benchmark is based on the [Exercism +python](https://github.com/exercism/python) coding exercises. +This +benchmark evaluates how effectively aider and GPT can translate a +natural language coding request into executable code saved into +files that pass unit tests. +It provides an end-to-end evaluation of not just +GPT's coding ability, but also its capacity to *edit existing code* +and *format those code edits* so that aider can save the +edits to the local source files. + +See [this writeup for a longer discussion about the benchmark and how to interpret the results](https://aider.chat/docs/benchmarks.html). + +The benchmark is intended to be run *inside a docker container*. +This is because the benchmarking harness will be +taking code written by an LLM +and executing it without any human review or supervision! +The LLM could generate dangerous python that harms your system, like this: `import os; os.system("sudo rm -rf /")`. +Running inside a docker container helps ensure no permanent damage can be done. + +## Usage + +There are 3 main tasks involved in benchmarking aider: + +1. Install and setup for benchmarking. + +2. Run the benchmark to measure performance across the 133 exercises. + +3. Generate a summary report of how many of the exercises succeeded or failed. + +### Setup for benchmarking + +First, prepare all the groundwork for running the benchmarks. +These steps only need to be done once. + +``` +# Clone the aider repo +git clone git@github.com:paul-gauthier/aider.git + +# Create the scratch dir to hold benchmarking results inside the main aider dir: +cd aider +mkdir tmp.benchmarks + +# Clone the exercism repo +git clone git@github.com:exercism/python.git + +# Copy the practice exercises into the benchmark scratch dir +cp -rp python/exercises/practice tmp.benchmarks/practice + +# Build the docker container +./benchmark/docker_build.sh +``` + +### Running the benchmark + +Launch the docker container and run the benchmark inside it: + +``` +# Launch the docker container +./benchmark/docker.sh + +# Inside the container, install aider as a development build. +# This way you're running the code that you cloned above, including any local changes. +pip install -e . + +# Run the benchmark: +./benchmark/benchmark.py a-helpful-name-for-this-run --model gpt-3.5-turbo --edit-format whole --threads 10 +``` + +The above will create a folder `tmp.benchmarks/YYYY-MM-DD-HH-MM-SS--a-helpful-name-for-this-run` with benchmarking results. +Run like this, the script will run all 133 exercises in a random order. + +You can run `./benchmark/benchmark.py --help` for a list of all the arguments, but here are the most useful to keep in mind: + +- `--model` is the name of the model, same as you would pass directly to `aider`. +- `--edit-format` is the name of the edit format, same as you would pass directly to `aider`. When working with an experimental LLM, I recommend starting with `whole` +- `--threads` specifies how many exercises to benchmark in parallel. Start with a single thread if you are working out the kinks on your benchmarking setup or working with a new model, etc. Once you are getting reliable results, you can speed up the process by running with more threads. 10 works well against the OpenAI APIs. +- `--num-tests` specifies how many of the 133 tests to run before stopping. This is another way to start gently as you debug your benchmarking setup. +- `--keywords` filters the tests to run to only the ones whose name match the supplied argument (similar to `pytest -k xxxx`). + +### Generating a benchmark report + +You can generate stats about any benchmark, including ones which are still running. +You don't need to run this inside the docker container, as it is just +collecting stats not executing unsafe python. + +``` +# Generate stats for a specific benchmarking directory +./benchmark/benchmark.py --stats tmp.benchmarks/YYYY-MM-DD-HH-MM-SS--a-helpful-name-for-this-run +``` + +## Limitations, notes + +- If you're experimenting with non-OpenAI models, the benchmarking harness may not provide enough switches/control to specify the integration to such models. You probably need to edit `benchmark.py` to instantiate `Coder()` appropriately. You can just hack this in or add new switches/config. +- Benchmarking all 133 exercises against GPT-4 will cost about $10-20. +- Benchmarking aider is intended for folks who are actively developing aider or doing experimental work adapting it for use with [new LLM models](https://github.com/paul-gauthier/aider/issues/172). +- These scripts are not intended for use by typical aider users. +- Some of the tools are written as `bash` scripts, so it will be hard to use them on Windows. From aee1338670ac33d759f1ee82fe7610a6aa9f31a8 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Fri, 8 Sep 2023 09:49:58 -0700 Subject: [PATCH 05/87] copy --- benchmark/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benchmark/README.md b/benchmark/README.md index 38c98cb94..d87a4190b 100644 --- a/benchmark/README.md +++ b/benchmark/README.md @@ -25,7 +25,7 @@ This is because the benchmarking harness will be taking code written by an LLM and executing it without any human review or supervision! The LLM could generate dangerous python that harms your system, like this: `import os; os.system("sudo rm -rf /")`. -Running inside a docker container helps ensure no permanent damage can be done. +Running inside a docker container helps limit the damage that could be done. ## Usage From 68e3d98f240e0e8a8df746194b6c165337659697 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Fri, 8 Sep 2023 09:53:52 -0700 Subject: [PATCH 06/87] Updated HISTORY --- HISTORY.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/HISTORY.md b/HISTORY.md index d182a2226..312595a20 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,5 +1,12 @@ # Release history +### v0.14.0 + +- [Support for Claude2 and other LLMs via OpenRouter](https://aider.chat/docs/faq.html#accessing-other-llms-with-openrouter) by @joshuavial +- Documentation for [running the aider benchmarking suite](https://github.com/paul-gauthier/aider/tree/main/benchmark) +- Aider now requires Python >= 3.9 + + ### v0.13.0 - [Only git commit dirty files that GPT tries to edit](https://aider.chat/docs/faq.html#how-did-v0130-change-git-usage) From f852bcabcdd8f07a482bc05975574c03e7269db9 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Fri, 8 Sep 2023 09:54:15 -0700 Subject: [PATCH 07/87] version bump to 0.14.0 --- aider/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aider/__init__.py b/aider/__init__.py index bb8bac653..9e78220f9 100644 --- a/aider/__init__.py +++ b/aider/__init__.py @@ -1 +1 @@ -__version__ = "0.13.1-dev" +__version__ = "0.14.0" From 2a8c7ef70dcf5ef49d6e1286f90146b32fafc2c1 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Fri, 8 Sep 2023 09:54:43 -0700 Subject: [PATCH 08/87] set version to 0.14.1-dev --- aider/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aider/__init__.py b/aider/__init__.py index 9e78220f9..ff695eb53 100644 --- a/aider/__init__.py +++ b/aider/__init__.py @@ -1 +1 @@ -__version__ = "0.14.0" +__version__ = "0.14.1-dev" From 45298f8af67eb5ea638f73c5b50bae9b21101443 Mon Sep 17 00:00:00 2001 From: Thinh Nguyen Date: Fri, 8 Sep 2023 14:01:30 -0700 Subject: [PATCH 09/87] Add option to skip model availability check --- aider/coders/base_coder.py | 3 ++- aider/main.py | 7 +++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/aider/coders/base_coder.py b/aider/coders/base_coder.py index 5710ea5f9..630fe3cc0 100755 --- a/aider/coders/base_coder.py +++ b/aider/coders/base_coder.py @@ -55,6 +55,7 @@ class Coder: main_model, edit_format, io, + skip_model_availabily_check, **kwargs, ): from . import EditBlockCoder, WholeFileCoder @@ -62,7 +63,7 @@ class Coder: if not main_model: main_model = models.GPT35_16k - if not main_model.always_available: + if not skip_model_availabily_check and not main_model.always_available: if not check_model_availability(main_model): if main_model != models.GPT4: io.tool_error( diff --git a/aider/main.py b/aider/main.py index 755e118bb..fbc20eea1 100644 --- a/aider/main.py +++ b/aider/main.py @@ -142,6 +142,12 @@ def main(argv=None, input=None, output=None, force_git_root=None): default=models.GPT4.name, help=f"Specify the model to use for the main chat (default: {models.GPT4.name})", ) + core_group.add_argument( + "--skip-model-availability-check", + metavar="SKIP_MODEL_AVAILABILITY_CHECK", + default=False, + help="Override to skip model availability check (default: False)", + ) core_group.add_argument( "-3", action="store_const", @@ -465,6 +471,7 @@ def main(argv=None, input=None, output=None, force_git_root=None): main_model, args.edit_format, io, + args.skip_model_availability_check, ## fnames=fnames, git_dname=git_dname, From 2f835e9c462a4e9a5aa3f8ade4bc79abfc9342e1 Mon Sep 17 00:00:00 2001 From: Thinh Nguyen Date: Fri, 8 Sep 2023 16:09:35 -0700 Subject: [PATCH 10/87] Add default value for skip_model_availabily_check --- aider/coders/base_coder.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aider/coders/base_coder.py b/aider/coders/base_coder.py index 630fe3cc0..bef227881 100755 --- a/aider/coders/base_coder.py +++ b/aider/coders/base_coder.py @@ -55,7 +55,7 @@ class Coder: main_model, edit_format, io, - skip_model_availabily_check, + skip_model_availabily_check=False, **kwargs, ): from . import EditBlockCoder, WholeFileCoder From 91b7648f3338a55bcb45843f0eb63ae836644551 Mon Sep 17 00:00:00 2001 From: Joshua Vial Date: Sat, 9 Sep 2023 12:06:34 +1200 Subject: [PATCH 11/87] update model creation method --- benchmark/benchmark.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benchmark/benchmark.py b/benchmark/benchmark.py index 2d16653c8..72f007c49 100755 --- a/benchmark/benchmark.py +++ b/benchmark/benchmark.py @@ -547,7 +547,7 @@ def run_test( chat_history_file=history_fname, ) - main_model = models.Model(model_name) + main_model = models.Model.create(model_name) edit_format = edit_format or main_model.edit_format dump(main_model) From 9a91d8fbad7b090a5aca6da51900f9db388136a4 Mon Sep 17 00:00:00 2001 From: Joshua Vial Date: Sat, 9 Sep 2023 12:07:11 +1200 Subject: [PATCH 12/87] set git config safe dir in benchmark docker --- benchmark/Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/benchmark/Dockerfile b/benchmark/Dockerfile index 419167e01..ac552e877 100644 --- a/benchmark/Dockerfile +++ b/benchmark/Dockerfile @@ -3,5 +3,6 @@ RUN apt-get update && apt-get install -y less git COPY requirements.txt /aider/requirements.txt RUN pip install lox typer pandas matplotlib imgcat aider-chat RUN pip install --upgrade pip && pip install -r /aider/requirements.txt +RUN git config --global --add safe.directory /aider WORKDIR /aider From 8bac324b0f180e83c8b6c5d98f9c50207194d23e Mon Sep 17 00:00:00 2001 From: Farhan Ahmad Date: Sat, 16 Sep 2023 15:40:05 -0400 Subject: [PATCH 13/87] aider: Add instructions for running the project locally. # Aider chat conversation: USER: Update the readme to include instruction for running the project locally after closing the repo or creating your fork. Make sure to mention how you will go about running the local version aider by using the following command: python -m aider.main ASSISTANT: To add instructions for running the project locally, we need to modify the README.md file. Here are the changes: markdown README.md <<<<<<< HEAD ## Installation See the [installation instructions](https://aider.chat/docs/install.html). ======= ## Installation See the [installation instructions](https://aider.chat/docs/install.html). ## Running Locally After cloning the repository or creating your fork, you can run the local version of Aider using the following command: ``` python -m aider.main ``` >>>>>>> updated --- README.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 6dd7b5f53..14b385d26 100644 --- a/README.md +++ b/README.md @@ -154,6 +154,14 @@ For a discussion of using other non-OpenAI models, see the See the [installation instructions](https://aider.chat/docs/install.html). +## Running Locally + +After cloning the repository or creating your fork, you can run the local version of Aider using the following command: + +``` +python -m aider.main +``` + ## FAQ For more information, see the [FAQ](https://aider.chat/docs/faq.html). @@ -170,4 +178,4 @@ For more information, see the [FAQ](https://aider.chat/docs/faq.html). * *This project is stellar.* -- [funkytaco](https://github.com/paul-gauthier/aider/issues/112#issuecomment-1637429008) * *Amazing project, definitely the best AI coding assistant I've used.* -- [joshuavial](https://github.com/paul-gauthier/aider/issues/84) * *I am an aider addict. I'm getting so much more work done, but in less time.* -- [dandandan](https://discord.com/channels/1131200896827654144/1131200896827654149/1135913253483069470) -* *Best agent for actual dev work in existing codebases.* -- [Nick Dobos](https://twitter.com/NickADobos/status/1690408967963652097?s=20) \ No newline at end of file +* *Best agent for actual dev work in existing codebases.* -- [Nick Dobos](https://twitter.com/NickADobos/status/1690408967963652097?s=20) From 53c467ae882091e028a658b99ad2c3f577cbc4a7 Mon Sep 17 00:00:00 2001 From: Farhan Ahmad Date: Sat, 16 Sep 2023 15:41:42 -0400 Subject: [PATCH 14/87] Updated gitignore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..8ba023496 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.aider* +*__pycache__* \ No newline at end of file From b7e3911afce860796656732f94afad741c83fdef Mon Sep 17 00:00:00 2001 From: Farhan Ahmad Date: Sat, 16 Sep 2023 15:45:46 -0400 Subject: [PATCH 15/87] aider: Updated the instructions for running the project locally. # Aider chat conversation: USER: update the instruction for running locally to include step by step instructions like cloning the repo / creating a fork. Switch to the correct directly. Install all the dependencies inside `requirements.txt` file using pip install -r. and finally running the program using python -m aider.main ASSISTANT: To update the instructions for running the project locally, we need to modify the README.md file. Here are the changes: markdown README.md <<<<<<< HEAD ## Running Locally After cloning the repository or creating your fork, you can run the local version of Aider using the following command: ``` python -m aider.main ``` ======= ## Running Locally To run the project locally, follow these steps: 1. Clone the repository or create your fork: ``` git clone https://github.com/your-username/aider.git ``` 2. Navigate to the project directory: ``` cd aider ``` 3. Install the dependencies listed in the `requirements.txt` file: ``` pip install -r requirements.txt ``` 4. Run the local version of Aider: ``` python -m aider.main ``` >>>>>>> updated --- README.md | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 14b385d26..36bf7628b 100644 --- a/README.md +++ b/README.md @@ -156,7 +156,27 @@ See the [installation instructions](https://aider.chat/docs/install.html). ## Running Locally -After cloning the repository or creating your fork, you can run the local version of Aider using the following command: +To run the project locally, follow these steps: + +1. Clone the repository or create your fork: + +``` +git clone https://github.com/your-username/aider.git +``` + +2. Navigate to the project directory: + +``` +cd aider +``` + +3. Install the dependencies listed in the `requirements.txt` file: + +``` +pip install -r requirements.txt +``` + +4. Run the local version of Aider: ``` python -m aider.main From e255b2f50065d3d49b6714aa9fc9f1e15901fd85 Mon Sep 17 00:00:00 2001 From: Farhan Ahmad Date: Mon, 18 Sep 2023 14:41:46 -0400 Subject: [PATCH 16/87] Removed .gitignore from repo --- .gitignore | 2 -- 1 file changed, 2 deletions(-) delete mode 100644 .gitignore diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 8ba023496..000000000 --- a/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -.aider* -*__pycache__* \ No newline at end of file From 7137b2974453cee2afc263eadebbd7661119af52 Mon Sep 17 00:00:00 2001 From: Farhan Ahmad Date: Mon, 18 Sep 2023 15:04:00 -0400 Subject: [PATCH 17/87] Moved instructions from README.md into FAQs section. --- README.md | 28 ---------------------------- docs/faq.md | 29 +++++++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index 36bf7628b..becb7dbac 100644 --- a/README.md +++ b/README.md @@ -154,34 +154,6 @@ For a discussion of using other non-OpenAI models, see the See the [installation instructions](https://aider.chat/docs/install.html). -## Running Locally - -To run the project locally, follow these steps: - -1. Clone the repository or create your fork: - -``` -git clone https://github.com/your-username/aider.git -``` - -2. Navigate to the project directory: - -``` -cd aider -``` - -3. Install the dependencies listed in the `requirements.txt` file: - -``` -pip install -r requirements.txt -``` - -4. Run the local version of Aider: - -``` -python -m aider.main -``` - ## FAQ For more information, see the [FAQ](https://aider.chat/docs/faq.html). diff --git a/docs/faq.md b/docs/faq.md index bd71fe4bf..77657671d 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -8,6 +8,7 @@ - [Can I use aider with other LLMs, local LLMs, etc?](#can-i-use-aider-with-other-llms-local-llms-etc) - [Can I change the system prompts that aider uses?](#can-i-change-the-system-prompts-that-aider-uses) - [Can I run aider in Google Colab?](#can-i-run-aider-in-google-colab) +- [How can I run aider locally from source code?](#how-can-i-run-aider-locally-from-source-code) ## How does aider use git? @@ -224,3 +225,31 @@ all the raw information being sent to/from GPT in the conversation. User [imabutahersiddik](https://github.com/imabutahersiddik) has provided this [Colab notebook](https://colab.research.google.com/drive/1J9XynhrCqekPL5PR6olHP6eE--rnnjS9?usp=sharing). + +## How can I run aider locally from source code? + +To run the project locally, follow these steps: + +1. Clone the repository or create your fork: + +``` +git clone https://github.com/your-username/aider.git +``` + +2. Navigate to the project directory: + +``` +cd aider +``` + +3. Install the dependencies listed in the `requirements.txt` file: + +``` +pip install -r requirements.txt +``` + +4. Run the local version of Aider: + +``` +python -m aider.main +``` \ No newline at end of file From 7910c2f731309e97ac54c46381d1e42d5e0fdcbb Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Mon, 25 Sep 2023 12:25:22 -0700 Subject: [PATCH 18/87] copy --- docs/voice.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/voice.md b/docs/voice.md index 0b93bf336..2c994c727 100644 --- a/docs/voice.md +++ b/docs/voice.md @@ -3,7 +3,9 @@ Speak with GPT about your code! Request new features, test cases or bug fixes using your voice and let GPT do the work of editing the files in your local git repo. As with all of aider's capabilities, you can use voice-to-code with an existing repo or to start a new project. -Voice support fits quite naturally into aider's AI pair programming chat interface. Now you can fluidly switch between voice and text chat where you ask GPT to edit the code and your own editor when it makes more sense for you to drive. +Voice support fits quite naturally into aider's AI pair programming +chat interface. Now you can fluidly switch between voice and text chat +when you ask GPT to edit your code. ## How to use voice-to-code From 6d5827643fd0a220729d86d5df6421893a944927 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Mon, 25 Sep 2023 14:55:00 -0700 Subject: [PATCH 19/87] don't try and commit unless there are changes #264 --- aider/repo.py | 5 ++++- tests/test_repo.py | 18 ++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/aider/repo.py b/aider/repo.py index a44f235b0..8be07641f 100644 --- a/aider/repo.py +++ b/aider/repo.py @@ -53,10 +53,13 @@ class GitRepo: if not fnames and not self.repo.is_dirty(): return + diffs = self.get_diffs(fnames) + if not diffs: + return + if message: commit_message = message else: - diffs = self.get_diffs(fnames) commit_message = self.get_commit_message(diffs, context) if not commit_message: diff --git a/tests/test_repo.py b/tests/test_repo.py index 77092c2da..66b12455b 100644 --- a/tests/test_repo.py +++ b/tests/test_repo.py @@ -194,3 +194,21 @@ class TestRepo(unittest.TestCase): raw_repo.git.commit("-m", "new") fnames = git_repo.get_tracked_files() self.assertIn(str(fname), fnames) + + @patch("aider.repo.simple_send_with_retries") + def test_noop_commit(self, mock_send): + mock_send.return_value = '"a good commit message"' + + with GitTemporaryDirectory(): + # new repo + raw_repo = git.Repo() + + # add it, but no commits at all in the raw_repo yet + fname = Path("file.txt") + fname.touch() + raw_repo.git.add(str(fname)) + raw_repo.git.commit("-m", "new") + + git_repo = GitRepo(InputOutput(), None, None) + + git_repo.commit(fnames=[str(fname)]) From 2ec453ab2abe61b76dc2939635423c05ef863b50 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Wed, 27 Sep 2023 10:17:19 -0700 Subject: [PATCH 20/87] copy --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index becb7dbac..accaa31b0 100644 --- a/README.md +++ b/README.md @@ -134,7 +134,6 @@ Aider has some ability to help GPT figure out which files to edit all by itself, * Use Meta-ENTER (Esc+ENTER in some environments) to enter multiline chat messages. Or enter `{` alone on the first line to start a multiline message and `}` alone on the last line to end it. * If your code is throwing an error, share the error output with GPT using `/run` or by pasting it into the chat. Let GPT figure out and fix the bug. * GPT knows about a lot of standard tools and libraries, but may get some of the fine details wrong about APIs and function arguments. You can paste doc snippets into the chat to resolve these issues. -* [Aider will notice if you launch it on a git repo with uncommitted changes and offer to commit them before proceeding](https://aider.chat/docs/faq.html#how-does-aider-use-git). * GPT can only see the content of the files you specifically "add to the chat". Aider also sends GPT-4 a [map of your entire git repo](https://aider.chat/docs/ctags.html). So GPT may ask to see additional files if it feels that's needed for your requests. * I also shared some general [GPT coding tips on Hacker News](https://news.ycombinator.com/item?id=36211879). From 6a7fd93580c904dfa3038710ffa00c1739692a7b Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Wed, 27 Sep 2023 10:20:59 -0700 Subject: [PATCH 21/87] copy --- docs/faq.md | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/docs/faq.md b/docs/faq.md index 77657671d..aae406fc2 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -230,26 +230,16 @@ has provided this To run the project locally, follow these steps: -1. Clone the repository or create your fork: - -``` -git clone https://github.com/your-username/aider.git ``` +# Clone the repository: +git clone git@github.com:paul-gauthier/aider.git -2. Navigate to the project directory: - -``` +# Navigate to the project directory: cd aider -``` -3. Install the dependencies listed in the `requirements.txt` file: - -``` +# Install the dependencies listed in the `requirements.txt` file: pip install -r requirements.txt -``` -4. Run the local version of Aider: - -``` +# Run the local version of Aider: python -m aider.main ``` \ No newline at end of file From 14eb12364d7daa417913a4272f18e454e4e537d1 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Wed, 27 Sep 2023 16:24:43 -0700 Subject: [PATCH 22/87] aider: Add --gitignore/--no-gitignore switches (like the others) default is true, only call `check_gitignore` if it's true. # Aider chat conversation: USER: add --gitignore/--no-gitignore switches (like the others) default is true. only call `check_gitignore` if it's true. ASSISTANT: To add the `--gitignore/--no-gitignore` switches, we need to modify the `aider/main.py` file. Here are the steps: 1. Add a new argument to the `git_group` in the `main` function to handle the `--gitignore/--no-gitignore` switches. 2. Modify the `if args.git:` block to only call `check_gitignore` if `args.gitignore` is `True`. Here are the proposed changes: ```python aider/main.py <<<<<<< HEAD git_group = parser.add_argument_group("Git Settings") git_group.add_argument( "--git", action=argparse.BooleanOptionalAction, default=True, help="Enable/disable looking for a git repo (default: True)", ) git_group.add_argument( "--auto-commits", action=argparse.BooleanOptionalAction, default=True, help="Enable/disable auto commit of GPT changes (default: True)", ) git_group.add_argument( "--dirty-commits", action=argparse.BooleanOptionalAction, default=True, help="Enable/disable commits when repo is found dirty (default: True)", ) git_group.add_argument( "--dry-run", action=argparse.BooleanOptionalAction, default=False, help="Enable/disable performing a dry run without modifying files (default: False)", ) ======= git_group = parser.add_argument_group("Git Settings") git_group.add_argument( "--git", action=argparse.BooleanOptionalAction, default=True, help="Enable/disable looking for a git repo (default: True)", ) git_group.add_argument( "--gitignore", action=argparse.BooleanOptionalAction, default=True, help="Enable/disable adding .aider* to .gitignore (default: True)", ) git_group.add_argument( "--auto-commits", action=argparse.BooleanOptionalAction, default=True, help="Enable/disable auto commit of GPT changes (default: True)", ) git_group.add_argument( "--dirty-commits", action=argparse.BooleanOptionalAction, default=True, help="Enable/disable commits when repo is found dirty (default: True)", ) git_group.add_argument( "--dry-run", action=argparse.BooleanOptionalAction, default=False, help="Enable/disable performing a dry run without modifying files (default: False)", ) >>>>>>> updated ``` ```python aider/main.py <<<<<<< HEAD if args.git: git_root = setup_git(git_root, io) check_gitignore(git_root, io) ======= if args.git: git_root = setup_git(git_root, io) if args.gitignore: check_gitignore(git_root, io) >>>>>>> updated ``` --- aider/main.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/aider/main.py b/aider/main.py index fbc20eea1..5dee72fb0 100644 --- a/aider/main.py +++ b/aider/main.py @@ -292,6 +292,12 @@ def main(argv=None, input=None, output=None, force_git_root=None): default=True, help="Enable/disable looking for a git repo (default: True)", ) + git_group.add_argument( + "--gitignore", + action=argparse.BooleanOptionalAction, + default=True, + help="Enable/disable adding .aider* to .gitignore (default: True)", + ) git_group.add_argument( "--auto-commits", action=argparse.BooleanOptionalAction, @@ -429,7 +435,8 @@ def main(argv=None, input=None, output=None, force_git_root=None): if args.git: git_root = setup_git(git_root, io) - check_gitignore(git_root, io) + if args.gitignore: + check_gitignore(git_root, io) def scrub_sensitive_info(text): # Replace sensitive information with placeholder From 19e47875022e9d7a6c22dd31d7eff2d5e2600c21 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Wed, 27 Sep 2023 18:30:26 -0700 Subject: [PATCH 23/87] aider: Refactored `check_gitignore` function to use `gitpython` library to check if the `.aider` pattern is already ignored by the repository before adding it to `.gitignore`. # Aider chat conversation: USER: in `check_gitignore` use gitpython to see if the repo is already ignoring `.aider`. if so, don't add anything to gitignore ASSISTANT: To implement this change, we need to modify the `check_gitignore` function in the `aider/main.py` file. We will use the `gitpython` library to check if the `.aider` pattern is already ignored by the repository. If it is, we won't add anything to the `.gitignore` file. Here are the steps we need to take: 1. Import the `git` module at the top of the file. 2. In the `check_gitignore` function, create a `git.Repo` object for the current repository. 3. Use the `Repo.ignored` method to check if the `.aider` pattern is already ignored. 4. Only add the `.aider` pattern to the `.gitignore` file if it's not already ignored. Here is the *edit block* for this change: ```python aider/main.py <<<<<<< HEAD def check_gitignore(git_root, io, ask=True): if not git_root: return pat = ".aider*" gitignore_file = Path(git_root) / ".gitignore" if gitignore_file.exists(): content = io.read_text(gitignore_file) if pat in content.splitlines(): return else: content = "" if ask and not io.confirm_ask(f"Add {pat} to .gitignore (recommended)?"): return if content and not content.endswith("\n"): content += "\n" content += pat + "\n" io.write_text(gitignore_file, content) io.tool_output(f"Added {pat} to .gitignore") ======= import git def check_gitignore(git_root, io, ask=True): if not git_root: return pat = ".aider*" gitignore_file = Path(git_root) / ".gitignore" if gitignore_file.exists(): content = io.read_text(gitignore_file) if pat in content.splitlines(): return else: content = "" repo = git.Repo(git_root) if repo.is_ignored(pat): return if ask and not io.confirm_ask(f"Add {pat} to .gitignore (recommended)?"): return if content and not content.endswith("\n"): content += "\n" content += pat + "\n" io.write_text(gitignore_file, content) io.tool_output(f"Added {pat} to .gitignore") >>>>>>> updated ``` Please note that this change assumes that the `gitpython` library is already installed in your environment. If it's not, you will need to install it using pip (`pip install gitpython`). --- aider/main.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/aider/main.py b/aider/main.py index 5dee72fb0..00489504c 100644 --- a/aider/main.py +++ b/aider/main.py @@ -70,6 +70,8 @@ def setup_git(git_root, io): return repo.working_tree_dir +import git + def check_gitignore(git_root, io, ask=True): if not git_root: return @@ -84,6 +86,10 @@ def check_gitignore(git_root, io, ask=True): else: content = "" + repo = git.Repo(git_root) + if repo.is_ignored(pat): + return + if ask and not io.confirm_ask(f"Add {pat} to .gitignore (recommended)?"): return From 1d0d91d71e33663c7223e4a5db5e2730b53dcd9a Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Wed, 27 Sep 2023 18:32:18 -0700 Subject: [PATCH 24/87] improved gitignore check logic --- aider/main.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/aider/main.py b/aider/main.py index 00489504c..dab1ea1e7 100644 --- a/aider/main.py +++ b/aider/main.py @@ -70,12 +70,14 @@ def setup_git(git_root, io): return repo.working_tree_dir -import git - def check_gitignore(git_root, io, ask=True): if not git_root: return + repo = git.Repo(git_root) + if repo.ignored(".aider"): + return + pat = ".aider*" gitignore_file = Path(git_root) / ".gitignore" @@ -86,10 +88,6 @@ def check_gitignore(git_root, io, ask=True): else: content = "" - repo = git.Repo(git_root) - if repo.is_ignored(pat): - return - if ask and not io.confirm_ask(f"Add {pat} to .gitignore (recommended)?"): return From 82d7f04865e31bb39f6d9b04e3deea90f0933719 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Wed, 27 Sep 2023 18:37:26 -0700 Subject: [PATCH 25/87] handle the case of no git repo --- aider/main.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/aider/main.py b/aider/main.py index dab1ea1e7..23fcc1014 100644 --- a/aider/main.py +++ b/aider/main.py @@ -74,9 +74,12 @@ def check_gitignore(git_root, io, ask=True): if not git_root: return - repo = git.Repo(git_root) - if repo.ignored(".aider"): - return + try: + repo = git.Repo(git_root) + if repo.ignored(".aider"): + return + except git.exc.InvalidGitRepositoryError: + pass pat = ".aider*" From 545f10551edf3ddfbf8b91575b4c283b10df5087 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Fri, 29 Sep 2023 09:20:36 -0700 Subject: [PATCH 26/87] Show supported models when avail check fails --- aider/coders/base_coder.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/aider/coders/base_coder.py b/aider/coders/base_coder.py index bef227881..f81d0a43a 100755 --- a/aider/coders/base_coder.py +++ b/aider/coders/base_coder.py @@ -64,7 +64,7 @@ class Coder: main_model = models.GPT35_16k if not skip_model_availabily_check and not main_model.always_available: - if not check_model_availability(main_model): + if not check_model_availability(io, main_model): if main_model != models.GPT4: io.tool_error( f"API key does not support {main_model.name}, falling back to" @@ -608,7 +608,7 @@ class Coder: raise Exception("No data found in openai response!") tokens = None - if hasattr(completion, 'usage'): + if hasattr(completion, "usage"): prompt_tokens = completion.usage.prompt_tokens completion_tokens = completion.usage.completion_tokens @@ -911,7 +911,12 @@ class Coder: return True -def check_model_availability(main_model): +def check_model_availability(io, main_model): available_models = openai.Model.list() model_ids = [model.id for model in available_models["data"]] - return main_model.name in model_ids + if main_model.name in model_ids: + return True + + available_models = ", ".join(model_ids) + io.tool_error(f"API key supports: {available_models}") + return False From 98bacd0b5bf1b7f6452f0892f0278bed418e0bad Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Fri, 29 Sep 2023 10:49:27 -0700 Subject: [PATCH 27/87] test case and fix for /add blowing up in subdir #201 --- aider/commands.py | 13 +++++++------ tests/test_commands.py | 19 +++++++++++++++++++ 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/aider/commands.py b/aider/commands.py index a4caf7142..fd4472fbf 100644 --- a/aider/commands.py +++ b/aider/commands.py @@ -279,16 +279,17 @@ class Commands: if any(char in word for char in "*?[]"): self.io.tool_error(f"No files to add matching pattern: {word}") else: - if Path(word).exists(): - if Path(word).is_file(): - matched_files = [word] + fname = Path(self.coder.root) / word + if fname.exists(): + if fname.is_file(): + matched_files = [str(fname)] else: self.io.tool_error(f"Unable to add: {word}") elif self.io.confirm_ask( - f"No files matched '{word}'. Do you want to create the file?" + f"No files matched '{word}'. Do you want to create {fname}?" ): - (Path(self.coder.root) / word).touch() - matched_files = [word] + fname.touch() + matched_files = [str(fname)] all_matched_files.update(matched_files) diff --git a/tests/test_commands.py b/tests/test_commands.py index 70362651d..74a287dd8 100644 --- a/tests/test_commands.py +++ b/tests/test_commands.py @@ -276,3 +276,22 @@ class TestCommands(TestCase): commit_message = "Test commit message" commands.cmd_commit(commit_message) self.assertFalse(repo.is_dirty()) + + def test_cmd_add_from_sub_dir(self): + with GitTemporaryDirectory(): + io = InputOutput(pretty=False, yes=False) + from aider.coders import Coder + + coder = Coder.create(models.GPT35, None, io) + commands = Commands(io, coder) + + Path("side_dir").mkdir() + os.chdir("side_dir") + + # add a file that is in the side_dir + with open("temp.txt", "w"): + pass + + # this was blowing up with GitCommandError, per: + # https://github.com/paul-gauthier/aider/issues/201 + commands.cmd_add("temp.txt") From 5d7e44082e83f67a866ac1a9b9bb3b49fefa8080 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Fri, 29 Sep 2023 13:07:56 -0700 Subject: [PATCH 28/87] print warning if cur wd is different than git wd, per discussion in #188 --- aider/main.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/aider/main.py b/aider/main.py index 23fcc1014..ffad5f454 100644 --- a/aider/main.py +++ b/aider/main.py @@ -522,6 +522,15 @@ def main(argv=None, input=None, output=None, force_git_root=None): io.tool_output("Use /help to see in-chat commands, run with --help to see cmd line args") + if git_root and Path.cwd().resolve() != Path(git_root).resolve(): + io.tool_error( + "Note: in-chat filenames are always relative to the git working dir, not the current" + " working dir." + ) + + io.tool_error(f"Cur working dir: {Path.cwd()}") + io.tool_error(f"Git working dir: {git_root}") + if args.message: io.tool_output() coder.run(with_message=args.message) From 399d86d4378fb48115d80674d7899922f682934a Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Fri, 29 Sep 2023 13:27:43 -0700 Subject: [PATCH 29/87] block /add of files from outside coder.root #178 --- aider/commands.py | 6 ++++++ tests/test_commands.py | 47 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/aider/commands.py b/aider/commands.py index fd4472fbf..9313a50d6 100644 --- a/aider/commands.py +++ b/aider/commands.py @@ -296,6 +296,12 @@ class Commands: for matched_file in all_matched_files: abs_file_path = self.coder.abs_root_path(matched_file) + if not abs_file_path.startswith(self.coder.root): + self.io.tool_error( + f"Can not add {abs_file_path}, which is not within {self.coder.root}" + ) + continue + if self.coder.repo and matched_file not in git_files: self.coder.repo.repo.git.add(abs_file_path) git_added.append(matched_file) diff --git a/tests/test_commands.py b/tests/test_commands.py index 74a287dd8..346d2fa27 100644 --- a/tests/test_commands.py +++ b/tests/test_commands.py @@ -14,7 +14,7 @@ from aider.coders import Coder from aider.commands import Commands from aider.dump import dump # noqa: F401 from aider.io import InputOutput -from tests.utils import GitTemporaryDirectory +from tests.utils import ChdirTemporaryDirectory, GitTemporaryDirectory, make_repo class TestCommands(TestCase): @@ -295,3 +295,48 @@ class TestCommands(TestCase): # this was blowing up with GitCommandError, per: # https://github.com/paul-gauthier/aider/issues/201 commands.cmd_add("temp.txt") + + def test_cmd_add_from_outside_root(self): + with ChdirTemporaryDirectory() as tmp_dname: + root = Path("root") + root.mkdir() + os.chdir(str(root)) + + io = InputOutput(pretty=False, yes=False) + from aider.coders import Coder + + coder = Coder.create(models.GPT35, None, io) + commands = Commands(io, coder) + + outside_file = Path(tmp_dname) / "outside.txt" + outside_file.touch() + + # This should not be allowed! + # https://github.com/paul-gauthier/aider/issues/178 + commands.cmd_add("../outside.txt") + + self.assertEqual(len(coder.abs_fnames), 0) + + def test_cmd_add_from_outside_git(self): + with ChdirTemporaryDirectory() as tmp_dname: + root = Path("root") + root.mkdir() + os.chdir(str(root)) + + make_repo() + + io = InputOutput(pretty=False, yes=False) + from aider.coders import Coder + + coder = Coder.create(models.GPT35, None, io) + commands = Commands(io, coder) + + outside_file = Path(tmp_dname) / "outside.txt" + outside_file.touch() + + # This should not be allowed! + # It was blowing up with GitCommandError, per: + # https://github.com/paul-gauthier/aider/issues/178 + commands.cmd_add("../outside.txt") + + self.assertEqual(len(coder.abs_fnames), 0) From dc19a1fe4b6dd974f238a013e5595f8c3e824381 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Fri, 29 Sep 2023 13:44:49 -0700 Subject: [PATCH 30/87] /add treats args as literal filenames initially, only glob if they do not exist #195 --- aider/commands.py | 30 ++++++++--------- tests/test_commands.py | 74 ++++++++++++++++++++++++++++++------------ 2 files changed, 66 insertions(+), 38 deletions(-) diff --git a/aider/commands.py b/aider/commands.py index 9313a50d6..a0aacedbd 100644 --- a/aider/commands.py +++ b/aider/commands.py @@ -273,25 +273,21 @@ class Commands: all_matched_files = set() for word in args.split(): + fname = Path(self.coder.root) / word + if fname.exists(): + if fname.is_file(): + all_matched_files.add(str(fname)) + continue + # else we fall through and glob will pickup all files within a dir + matched_files = self.glob_filtered_to_repo(word) + if matched_files: + all_matched_files.update(matched_files) + continue - if not matched_files: - if any(char in word for char in "*?[]"): - self.io.tool_error(f"No files to add matching pattern: {word}") - else: - fname = Path(self.coder.root) / word - if fname.exists(): - if fname.is_file(): - matched_files = [str(fname)] - else: - self.io.tool_error(f"Unable to add: {word}") - elif self.io.confirm_ask( - f"No files matched '{word}'. Do you want to create {fname}?" - ): - fname.touch() - matched_files = [str(fname)] - - all_matched_files.update(matched_files) + if self.io.confirm_ask(f"No files matched '{word}'. Do you want to create {fname}?"): + fname.touch() + all_matched_files.add(str(fname)) for matched_file in all_matched_files: abs_file_path = self.coder.abs_root_path(matched_file) diff --git a/tests/test_commands.py b/tests/test_commands.py index 346d2fa27..025d82010 100644 --- a/tests/test_commands.py +++ b/tests/test_commands.py @@ -69,8 +69,8 @@ class TestCommands(TestCase): self.assertNotIn(str(Path("test.txt").resolve()), coder.abs_fnames) def test_cmd_add_no_match(self): - # Initialize the Commands and InputOutput objects - io = InputOutput(pretty=False, yes=True) + # yes=False means we will *not* create the file when it is not found + io = InputOutput(pretty=False, yes=False) from aider.coders import Coder coder = Coder.create(models.GPT35, None, io) @@ -82,6 +82,23 @@ class TestCommands(TestCase): # Check if no files have been added to the chat session self.assertEqual(len(coder.abs_fnames), 0) + def test_cmd_add_no_match_but_make_it(self): + # yes=True means we *will* create the file when it is not found + io = InputOutput(pretty=False, yes=True) + from aider.coders import Coder + + coder = Coder.create(models.GPT35, None, io) + commands = Commands(io, coder) + + fname = Path("*.nonexistent") + + # Call the cmd_add method with a non-existent file pattern + commands.cmd_add(str(fname)) + + # Check if no files have been added to the chat session + self.assertEqual(len(coder.abs_fnames), 1) + self.assertTrue(fname.exists()) + def test_cmd_add_drop_directory(self): # Initialize the Commands and InputOutput objects io = InputOutput(pretty=False, yes=False) @@ -255,6 +272,25 @@ class TestCommands(TestCase): self.assertNotIn(filenames[1], coder.abs_fnames) self.assertIn(filenames[2], coder.abs_fnames) + def test_cmd_add_from_subdir_again(self): + with GitTemporaryDirectory(): + io = InputOutput(pretty=False, yes=False) + from aider.coders import Coder + + coder = Coder.create(models.GPT35, None, io) + commands = Commands(io, coder) + + Path("side_dir").mkdir() + os.chdir("side_dir") + + # add a file that is in the side_dir + with open("temp.txt", "w"): + pass + + # this was blowing up with GitCommandError, per: + # https://github.com/paul-gauthier/aider/issues/201 + commands.cmd_add("temp.txt") + def test_cmd_commit(self): with GitTemporaryDirectory(): fname = "test.txt" @@ -277,25 +313,6 @@ class TestCommands(TestCase): commands.cmd_commit(commit_message) self.assertFalse(repo.is_dirty()) - def test_cmd_add_from_sub_dir(self): - with GitTemporaryDirectory(): - io = InputOutput(pretty=False, yes=False) - from aider.coders import Coder - - coder = Coder.create(models.GPT35, None, io) - commands = Commands(io, coder) - - Path("side_dir").mkdir() - os.chdir("side_dir") - - # add a file that is in the side_dir - with open("temp.txt", "w"): - pass - - # this was blowing up with GitCommandError, per: - # https://github.com/paul-gauthier/aider/issues/201 - commands.cmd_add("temp.txt") - def test_cmd_add_from_outside_root(self): with ChdirTemporaryDirectory() as tmp_dname: root = Path("root") @@ -340,3 +357,18 @@ class TestCommands(TestCase): commands.cmd_add("../outside.txt") self.assertEqual(len(coder.abs_fnames), 0) + + def test_cmd_add_filename_with_special_chars(self): + with ChdirTemporaryDirectory(): + io = InputOutput(pretty=False, yes=False) + from aider.coders import Coder + + coder = Coder.create(models.GPT35, None, io) + commands = Commands(io, coder) + + fname = Path("with[brackets].txt") + fname.touch() + + commands.cmd_add(str(fname)) + + self.assertIn(str(fname.resolve()), coder.abs_fnames) From d2acb8e95ef7dce126b90e9e811b300df2f74f4d Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Fri, 29 Sep 2023 14:50:41 -0700 Subject: [PATCH 31/87] use a windows compatible regex filename --- tests/test_commands.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_commands.py b/tests/test_commands.py index 025d82010..29976a77b 100644 --- a/tests/test_commands.py +++ b/tests/test_commands.py @@ -90,7 +90,7 @@ class TestCommands(TestCase): coder = Coder.create(models.GPT35, None, io) commands = Commands(io, coder) - fname = Path("*.nonexistent") + fname = Path("[abc].nonexistent") # Call the cmd_add method with a non-existent file pattern commands.cmd_add(str(fname)) From 774589b376b5d96094c2cfedc4fafd3ba0d03493 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Fri, 29 Sep 2023 15:02:45 -0700 Subject: [PATCH 32/87] test to ensure we accept abs filenames; use shlex so we handle quoted filenames; #152 #156 --- aider/commands.py | 13 ++++++------- tests/test_commands.py | 30 ++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 7 deletions(-) diff --git a/aider/commands.py b/aider/commands.py index a0aacedbd..559690011 100644 --- a/aider/commands.py +++ b/aider/commands.py @@ -272,13 +272,12 @@ class Commands: git_files = self.coder.repo.get_tracked_files() if self.coder.repo else [] all_matched_files = set() - for word in args.split(): + for word in shlex.split(args): fname = Path(self.coder.root) / word - if fname.exists(): - if fname.is_file(): - all_matched_files.add(str(fname)) - continue - # else we fall through and glob will pickup all files within a dir + if fname.exists() and fname.is_file(): + all_matched_files.add(str(fname)) + continue + # an existing dir will fall through and get recursed by glob matched_files = self.glob_filtered_to_repo(word) if matched_files: @@ -342,7 +341,7 @@ class Commands: self.io.tool_output("Dropping all files from the chat session.") self.coder.abs_fnames = set() - for word in args.split(): + for word in shlex.split(args): matched_files = self.glob_filtered_to_repo(word) if not matched_files: diff --git a/tests/test_commands.py b/tests/test_commands.py index 29976a77b..89524e73b 100644 --- a/tests/test_commands.py +++ b/tests/test_commands.py @@ -372,3 +372,33 @@ class TestCommands(TestCase): commands.cmd_add(str(fname)) self.assertIn(str(fname.resolve()), coder.abs_fnames) + + def test_cmd_add_abs_filename(self): + with ChdirTemporaryDirectory(): + io = InputOutput(pretty=False, yes=False) + from aider.coders import Coder + + coder = Coder.create(models.GPT35, None, io) + commands = Commands(io, coder) + + fname = Path("file.txt") + fname.touch() + + commands.cmd_add(str(fname.resolve())) + + self.assertIn(str(fname.resolve()), coder.abs_fnames) + + def test_cmd_add_quoted_filename(self): + with ChdirTemporaryDirectory(): + io = InputOutput(pretty=False, yes=False) + from aider.coders import Coder + + coder = Coder.create(models.GPT35, None, io) + commands = Commands(io, coder) + + fname = Path("file with spaces.txt") + fname.touch() + + commands.cmd_add(f'"{fname}"') + + self.assertIn(str(fname.resolve()), coder.abs_fnames) From 2dc01bb9bddfaae7cee72fe5e1b424453d2fab97 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Fri, 29 Sep 2023 16:36:37 -0700 Subject: [PATCH 33/87] handle abs paths on windows --- aider/commands.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/aider/commands.py b/aider/commands.py index 559690011..cf4145de6 100644 --- a/aider/commands.py +++ b/aider/commands.py @@ -273,7 +273,11 @@ class Commands: all_matched_files = set() for word in shlex.split(args): - fname = Path(self.coder.root) / word + if Path(word).is_absolute(): + fname = Path(word) + else: + fname = Path(self.coder.root) / word + if fname.exists() and fname.is_file(): all_matched_files.add(str(fname)) continue From b78b8fabf455925907a7ace69b046b5ccb63bfce Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Fri, 29 Sep 2023 17:07:17 -0700 Subject: [PATCH 34/87] use regex not shlex to parse quoted filenames portably on windows and others --- aider/commands.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/aider/commands.py b/aider/commands.py index cf4145de6..9ab52cb2f 100644 --- a/aider/commands.py +++ b/aider/commands.py @@ -1,4 +1,5 @@ import json +import re import shlex import subprocess import sys @@ -272,7 +273,9 @@ class Commands: git_files = self.coder.repo.get_tracked_files() if self.coder.repo else [] all_matched_files = set() - for word in shlex.split(args): + + filenames = parse_quoted_filenames(args) + for word in filenames: if Path(word).is_absolute(): fname = Path(word) else: @@ -345,7 +348,8 @@ class Commands: self.io.tool_output("Dropping all files from the chat session.") self.coder.abs_fnames = set() - for word in shlex.split(args): + filenames = parse_quoted_filenames(args) + for word in filenames: matched_files = self.glob_filtered_to_repo(word) if not matched_files: @@ -493,3 +497,9 @@ def expand_subdir(file_path): for file in file_path.rglob("*"): if file.is_file(): yield str(file) + + +def parse_quoted_filenames(args): + filenames = re.findall(r"\"(.+?)\"|(\S+)", args) + filenames = [name for sublist in filenames for name in sublist if name] + return filenames From fa7656cef8a386ca465e93e0ba69047437b8111f Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Fri, 29 Sep 2023 17:19:47 -0700 Subject: [PATCH 35/87] Updated HISTORY --- HISTORY.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/HISTORY.md b/HISTORY.md index 312595a20..f18a4bfa4 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,5 +1,13 @@ # Release history +### v0.14.1 + +- /add and /drop handle absolute filenames and quoted filenames +- /add checks to be sure files are within the git repo (or root) +- If needed, warn users that in-chat file paths are all relative to the git repo +- Fixed /add bug in when aider launched in repo subdir +- Show models supported by api/key if requested model isn't available + ### v0.14.0 - [Support for Claude2 and other LLMs via OpenRouter](https://aider.chat/docs/faq.html#accessing-other-llms-with-openrouter) by @joshuavial From 3d4c021fc6e9f95f01854b974251e3f0674a4797 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Fri, 29 Sep 2023 17:20:53 -0700 Subject: [PATCH 36/87] version bump to 0.14.1 --- aider/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aider/__init__.py b/aider/__init__.py index ff695eb53..f075dd36a 100644 --- a/aider/__init__.py +++ b/aider/__init__.py @@ -1 +1 @@ -__version__ = "0.14.1-dev" +__version__ = "0.14.1" From 87fb6c8fbb9e8c8531d4b2d5f9ab1450f6c7b43d Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Fri, 29 Sep 2023 17:21:19 -0700 Subject: [PATCH 37/87] set version to 0.14.2-dev --- aider/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aider/__init__.py b/aider/__init__.py index f075dd36a..5efe46a9f 100644 --- a/aider/__init__.py +++ b/aider/__init__.py @@ -1 +1 @@ -__version__ = "0.14.1" +__version__ = "0.14.2-dev" From b2eed01051e129e83d9334da4d758b0f721e8a61 Mon Sep 17 00:00:00 2001 From: Alexander Kjeldaas Date: Sun, 1 Oct 2023 19:49:58 +0200 Subject: [PATCH 38/87] aider: Add `shell=True` to `subprocess.run` in `cmd_run`. # Aider chat conversation: USER: add `shell=True` to subprocess.run in cmd_run ASSISTANT: To add `shell=True` to the `subprocess.run` call in the `cmd_run` method, we need to modify the `aider/commands.py` file. Here's the change: python aider/commands.py <<<<<<< HEAD result = subprocess.run( parsed_args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True ) ======= result = subprocess.run( parsed_args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, shell=True ) >>>>>>> updated --- aider/commands.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aider/commands.py b/aider/commands.py index 9ab52cb2f..6af8e92d4 100644 --- a/aider/commands.py +++ b/aider/commands.py @@ -385,7 +385,7 @@ class Commands: try: parsed_args = shlex.split(args) result = subprocess.run( - parsed_args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True + parsed_args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, shell=True ) combined_output = result.stdout except Exception as e: From 67c5bb5985e112009daab4ba3698735cf77c0559 Mon Sep 17 00:00:00 2001 From: Alexander Kjeldaas Date: Sun, 1 Oct 2023 19:51:38 +0200 Subject: [PATCH 39/87] aider: Remove the use of `shlex.split(args)` in `cmd_run` method. # Aider chat conversation: USER: what does shlex.split(args) do? ASSISTANT: The `shlex.split(args)` function is a method in Python's `shlex` module. It splits the string `args` into a list of substrings, using shell-like syntax. This means it handles quotes and backslashes like a Unix shell would. For example, if `args` is a string like `"command -option 'argument with spaces'"`, `shlex.split(args)` would return the list `['command', '-option', 'argument with spaces']`. This is useful when you want to pass a command line command as a string to a function like `subprocess.run()`, which expects a list of arguments. USER: remove the use of shlex in cmd_run ASSISTANT: To remove the use of `shlex.split(args)` in the `cmd_run` method, we need to modify the `aider/commands.py` file. Here's the change: python aider/commands.py <<<<<<< HEAD parsed_args = shlex.split(args) result = subprocess.run( parsed_args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, shell=True ) ======= result = subprocess.run( args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, shell=True ) >>>>>>> updated --- aider/commands.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/aider/commands.py b/aider/commands.py index 6af8e92d4..1b26168ce 100644 --- a/aider/commands.py +++ b/aider/commands.py @@ -383,9 +383,8 @@ class Commands: "Run a shell command and optionally add the output to the chat" combined_output = None try: - parsed_args = shlex.split(args) result = subprocess.run( - parsed_args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, shell=True + args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, shell=True ) combined_output = result.stdout except Exception as e: From 22eb344192f665ea43ebffc51993ba8e2beb6486 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Thu, 12 Oct 2023 07:00:51 -0700 Subject: [PATCH 40/87] disable /voice if import soundfile failes #274 --- aider/commands.py | 4 +++- aider/voice.py | 11 +++++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/aider/commands.py b/aider/commands.py index 9ab52cb2f..110a1cda7 100644 --- a/aider/commands.py +++ b/aider/commands.py @@ -457,7 +457,9 @@ class Commands: try: self.voice = voice.Voice() except voice.SoundDeviceError: - self.io.tool_error("Unable to import `sounddevice`, is portaudio installed?") + self.io.tool_error( + "Unable to import `sounddevice` and/or `soundfile`, is portaudio installed?" + ) return history_iter = self.io.get_input_history() diff --git a/aider/voice.py b/aider/voice.py index 78f94f4ae..3ee9651ad 100644 --- a/aider/voice.py +++ b/aider/voice.py @@ -5,7 +5,12 @@ import time import numpy as np import openai -import soundfile as sf + +try: + import soundfile as sf +except (OSError, ModuleNotFoundError): + sf = None + from prompt_toolkit.shortcuts import prompt from .dump import dump # noqa: F401 @@ -23,12 +28,14 @@ class Voice: threshold = 0.15 def __init__(self): + if sf is None: + raise SoundDeviceError try: print("Initializing sound device...") import sounddevice as sd self.sd = sd - except OSError: + except (OSError, ModuleNotFoundError): raise SoundDeviceError def callback(self, indata, frames, time, status): From 364a3f70e92fad8812785e527b1e6cbdbb022a0f Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Tue, 17 Oct 2023 16:41:05 -0700 Subject: [PATCH 41/87] Be careful to set done_messages=[] not None #273 --- aider/coders/base_coder.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/aider/coders/base_coder.py b/aider/coders/base_coder.py index f81d0a43a..86dc20e81 100755 --- a/aider/coders/base_coder.py +++ b/aider/coders/base_coder.py @@ -193,7 +193,7 @@ class Coder: self.summarizer = ChatSummary(models.Model.weak_model()) self.summarizer_thread = None - self.summarized_done_messages = None + self.summarized_done_messages = [] # validate the functions jsonschema if self.functions: @@ -376,7 +376,7 @@ class Coder: self.summarizer_thread = None self.done_messages = self.summarized_done_messages - self.summarized_done_messages = None + self.summarized_done_messages = [] def move_back_cur_messages(self, message): self.done_messages += self.cur_messages From f068e49501f615768cca9b164ad1fe1ac98d1bb4 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Wed, 18 Oct 2023 08:24:12 -0700 Subject: [PATCH 42/87] added experimental docker image --- docker/Dockerfile | 7 +++++++ docker/README.md | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 docker/Dockerfile create mode 100644 docker/README.md diff --git a/docker/Dockerfile b/docker/Dockerfile new file mode 100644 index 000000000..5bacb755c --- /dev/null +++ b/docker/Dockerfile @@ -0,0 +1,7 @@ +FROM python:3.10-slim +RUN apt-get update && apt-get install -y git && rm -rf /var/lib/apt/lists/* +RUN pip install --upgrade pip && pip install aider-chat +WORKDIR /app +CMD ["aider"] + + diff --git a/docker/README.md b/docker/README.md new file mode 100644 index 000000000..85e34c27f --- /dev/null +++ b/docker/README.md @@ -0,0 +1,32 @@ + +# Running aider with docker (experimental) + +You can run aider via docker without doing any local installation, like this: + + +``` +docker run -it --volume `pwd`:/app paulgauthier/aider aider +``` + +You will also need to pass in your `OPENAI_API_KEY`, which can be done with the `--openai-api-key` argument: + + +``` +docker run -it --volume `pwd`:/app paulgauthier/aider aider --openai-api-key $OPENAI_API_KEY +``` + +You should run the above commands from the root of your git repo. + +You should be sure your that +git repo config contains your user name and email, since the +docker container won't have your global git config: + +``` +git config user.email "you@example.com" +git config user.name "Your Name" +``` + +Be aware that when you use the in-chat `/run` command, it will +be running shell commands *inside the docker container*. +So those commands won't be running in your local environment, +which may make it tricky to `/run` tests, etc for your project. From f954bc2eccf0649173f4064712b45ecf5c11ef17 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Wed, 18 Oct 2023 08:27:32 -0700 Subject: [PATCH 43/87] Changed RUN to ENTRYPOINT --- docker/Dockerfile | 2 +- docker/README.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index 5bacb755c..3405669ed 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -2,6 +2,6 @@ FROM python:3.10-slim RUN apt-get update && apt-get install -y git && rm -rf /var/lib/apt/lists/* RUN pip install --upgrade pip && pip install aider-chat WORKDIR /app -CMD ["aider"] +ENTRYPOINT ["aider"] diff --git a/docker/README.md b/docker/README.md index 85e34c27f..640fcd16c 100644 --- a/docker/README.md +++ b/docker/README.md @@ -5,14 +5,14 @@ You can run aider via docker without doing any local installation, like this: ``` -docker run -it --volume `pwd`:/app paulgauthier/aider aider +docker run -it --volume `pwd`:/app paulgauthier/aider ``` You will also need to pass in your `OPENAI_API_KEY`, which can be done with the `--openai-api-key` argument: ``` -docker run -it --volume `pwd`:/app paulgauthier/aider aider --openai-api-key $OPENAI_API_KEY +docker run -it --volume `pwd`:/app paulgauthier/aider --openai-api-key $OPENAI_API_KEY ``` You should run the above commands from the root of your git repo. From 9bcef8b853e5e9f9f206231ffb40ba10da7b42bc Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Wed, 18 Oct 2023 08:36:16 -0700 Subject: [PATCH 44/87] aider: Added Docker login and build/push steps to the GitHub Actions workflow for building and pushing a Docker image to DockerHub on each release. # Aider chat conversation: USER: what's the best way to docker build and push a new version to dockerhub on each release? ASSISTANT: To build and push a new Docker image to DockerHub on each release, you can use GitHub Actions. You will need to modify the existing GitHub Actions workflow file `.github/workflows/release.yml` to include steps for building and pushing the Docker image. Here are the steps you need to follow: 1. Add Docker login step: You need to log in to DockerHub before you can push images. You can use the `docker/login-action` action for this. You will need to store your DockerHub username and password as secrets in your GitHub repository. 2. Build the Docker image: You can use the `docker/build-push-action` action to build your Docker image. You should use the new version as the tag for the Docker image. 3. Push the Docker image: The `docker/build-push-action` action can also push the Docker image to DockerHub. Here are the changes you need to make to the `.github/workflows/release.yml` file: ```yaml .github/workflows/release.yml <<<<<<< HEAD name: Release on: push: tags: - 'v*' jobs: build_and_publish: runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v3 - name: Set up Python uses: actions/setup-python@v4 with: python-version: 3.x - name: Install dependencies run: | python -m pip install --upgrade pip pip install setuptools wheel twine - name: Build and publish env: TWINE_USERNAME: __token__ TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }} run: | python setup.py sdist bdist_wheel twine upload dist/* ======= name: Release on: push: tags: - 'v*' jobs: build_and_publish: runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v3 - name: Set up Python uses: actions/setup-python@v4 with: python-version: 3.x - name: Install dependencies run: | python -m pip install --upgrade pip pip install setuptools wheel twine - name: Build and publish env: TWINE_USERNAME: __token__ TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }} run: | python setup.py sdist bdist_wheel twine upload dist/* - name: Login to DockerHub uses: docker/login-action@v1 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_PASSWORD }} - name: Build and push Docker image uses: docker/build-push-action@v2 with: context: . push: true tags: ${{ secrets.DOCKERHUB_USERNAME }}/aider:${{ github.ref }} >>>>>>> updated ``` Please replace `DOCKERHUB_USERNAME` and `DOCKERHUB_PASSWORD` with your DockerHub username and password. You should store these as secrets in your GitHub repository. Please note that this will build and push a new Docker image for every release. If you want to build and push a Docker image only for specific releases, you will need to modify the `on` section of the workflow file. --- .github/workflows/release.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8a4455f2c..b65f146f5 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -29,3 +29,16 @@ jobs: run: | python setup.py sdist bdist_wheel twine upload dist/* + + - name: Login to DockerHub + uses: docker/login-action@v1 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_PASSWORD }} + + - name: Build and push Docker image + uses: docker/build-push-action@v2 + with: + context: . + push: true + tags: ${{ secrets.DOCKERHUB_USERNAME }}/aider:${{ github.ref }} From 5769821a35fd30c62e16134873878a9ebd592d19 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Wed, 18 Oct 2023 08:38:30 -0700 Subject: [PATCH 45/87] aider: Updated Dockerfile path to `./docker/Dockerfile` in the `docker/build-push-action` action. # Aider chat conversation: USER: what is github.ref? ASSISTANT: In GitHub Actions, `github.ref` is a context variable that refers to the branch or tag name that triggered the workflow run. For example, if you push a commit to the `main` branch, `github.ref` will be `refs/heads/main`. If you create a tag named `v1.0.0`, `github.ref` will be `refs/tags/v1.0.0`. In your workflow file, you're using `github.ref` to tag the Docker image. This means that the Docker image will be tagged with the name of the branch or tag that triggered the workflow run. However, since you're triggering the workflow on tag creation (`on: push: tags: - 'v*'`), `github.ref` will always be the name of the tag. If you create a tag named `v1.0.0`, the Docker image will be tagged as `v1.0.0`. USER: the Dockerfile is in the docker/ subdir. ASSISTANT: If your Dockerfile is in the `docker/` subdirectory, you need to specify this in the `context` and `file` parameters of the `docker/build-push-action` action in your GitHub Actions workflow file. The `context` parameter is the path to the directory that contains the Dockerfile. The `file` parameter is the path to the Dockerfile. Here are the changes you need to make to the `.github/workflows/release.yml` file: ```yaml .github/workflows/release.yml <<<<<<< HEAD - name: Build and push Docker image uses: docker/build-push-action@v2 with: context: . push: true tags: ${{ secrets.DOCKERHUB_USERNAME }}/aider:${{ github.ref }} ======= - name: Build and push Docker image uses: docker/build-push-action@v2 with: context: . file: ./docker/Dockerfile push: true tags: ${{ secrets.DOCKERHUB_USERNAME }}/aider:${{ github.ref }} >>>>>>> updated ``` This will tell the `docker/build-push-action` action to look for the Dockerfile in the `docker/` subdirectory. --- .github/workflows/release.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b65f146f5..1fee08670 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -40,5 +40,6 @@ jobs: uses: docker/build-push-action@v2 with: context: . + file: ./docker/Dockerfile push: true tags: ${{ secrets.DOCKERHUB_USERNAME }}/aider:${{ github.ref }} From b69edcfcbab68699b75289f447ab6c276c93f148 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Wed, 18 Oct 2023 08:45:02 -0700 Subject: [PATCH 46/87] prep docker build to run in github action --- .dockerignore | 1 + .github/workflows/release.yml | 1 + docker/Dockerfile | 3 ++- 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.dockerignore b/.dockerignore index ce03212b5..830cf8498 100644 --- a/.dockerignore +++ b/.dockerignore @@ -7,3 +7,4 @@ OLD* .env .venv .aider.* +build diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1fee08670..c1f87789a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,6 +1,7 @@ name: Release on: + workflow_dispatch: push: tags: - 'v*' diff --git a/docker/Dockerfile b/docker/Dockerfile index 3405669ed..d33ce63f6 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,6 +1,7 @@ FROM python:3.10-slim RUN apt-get update && apt-get install -y git && rm -rf /var/lib/apt/lists/* -RUN pip install --upgrade pip && pip install aider-chat +COPY . /aider +RUN pip install --upgrade pip && pip install /aider WORKDIR /app ENTRYPOINT ["aider"] From ec13c87e8d96a7f04f1f013493189a65efdc6b23 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Wed, 18 Oct 2023 08:48:24 -0700 Subject: [PATCH 47/87] disable pypi for now --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c1f87789a..6610b56bf 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -29,7 +29,7 @@ jobs: TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }} run: | python setup.py sdist bdist_wheel - twine upload dist/* + #twine upload dist/* - name: Login to DockerHub uses: docker/login-action@v1 From 8cb9d1d64df7b6aa242dd6f57d4dc4f5157d2f46 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Wed, 18 Oct 2023 08:54:26 -0700 Subject: [PATCH 48/87] update actions --- .github/workflows/release.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6610b56bf..21b39928c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -32,13 +32,13 @@ jobs: #twine upload dist/* - name: Login to DockerHub - uses: docker/login-action@v1 + uses: docker/login-action@v3 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_PASSWORD }} - name: Build and push Docker image - uses: docker/build-push-action@v2 + uses: docker/build-push-action@v5 with: context: . file: ./docker/Dockerfile From 87296ddcee990fc1fe9014abeb694c33f67c7ef4 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Wed, 18 Oct 2023 09:01:37 -0700 Subject: [PATCH 49/87] aider: Updated Docker tag to use `github.ref_name` instead of `github.ref`. # Aider chat conversation: USER: how can i test this release action? ASSISTANT: Testing a GitHub Actions workflow can be done in a few ways: 1. **Push to a branch:** You can push your changes to a branch and see if the workflow runs as expected. This is the most straightforward way to test a workflow, but it may not be suitable if the workflow is supposed to run on specific events like a release or a tag. 2. **Use Pull Requests:** If your workflow should run on pull requests, you can open a pull request to trigger the workflow. 3. **Use Actions tab on GitHub:** You can manually trigger a workflow run from the Actions tab in your GitHub repository. Click on the workflow you want to run, then click `Run workflow` and select the branch you want to run the workflow on. 4. **Use a workflow_dispatch event:** You can add a `workflow_dispatch` event to your workflow file to manually trigger the workflow from GitHub's UI. Here's an example of how to add it: ```yaml on: workflow_dispatch: push: tags: - 'v*' ``` After adding this to your workflow file and pushing it to your repository, you can manually trigger the workflow from the Actions tab in your GitHub repository. 5. **Use a specific branch for testing:** If your workflow runs on a push to a specific branch, you can create a separate branch for testing. You can push your changes to this branch to test the workflow. 6. **Use act:** [act](https://github.com/nektos/act) is a tool that lets you run your GitHub Actions workflows locally. This can be useful for testing your workflows before pushing them to your repository. Remember to remove or comment out any actions that could have side effects (like publishing a package or pushing a Docker image) when you're testing your workflows. USER: what will github.ref be if i manually trigger? ASSISTANT: When you manually trigger a workflow using the `workflow_dispatch` event, the `github.ref` will be set to the branch or tag you selected when you triggered the workflow. If you didn't select a branch or tag (which is possible if your workflow file doesn't specify any `inputs` for the `workflow_dispatch` event), `github.ref` will be set to the default branch of your repository (usually `main` or `master`). Here's an example of how you can specify an input for the `workflow_dispatch` event: ```yaml on: workflow_dispatch: inputs: branch: description: 'Branch or tag to run the workflow on' required: true default: 'main' ``` In this example, when you manually trigger the workflow, you'll be prompted to enter a branch or tag. The value you enter will be available in the `github.event.inputs.branch` context, and `github.ref` will be set to the same value. USER: buildx failed with: ERROR: invalid tag "***/aider:refs/tags/v0.14.2-dev1": invalid reference format ASSISTANT: The error message indicates that the Docker tag is not valid. Docker tags cannot contain slashes (`/`), and `github.ref` includes the full Git reference, which will be something like `refs/tags/v0.14.2-dev1` for a tag. To fix this, you can use the `github.ref_name` context instead of `github.ref`. The `github.ref_name` context contains just the name of the branch or tag without the `refs/heads/` or `refs/tags/` prefix. Here's how you can update your workflow file: ```yaml .github/workflows/release.yml <<<<<<< HEAD - name: Build and push Docker image uses: docker/build-push-action@v5 with: context: . file: ./docker/Dockerfile push: true tags: ${{ secrets.DOCKERHUB_USERNAME }}/aider:${{ github.ref }} ======= - name: Build and push Docker image uses: docker/build-push-action@v5 with: context: . file: ./docker/Dockerfile push: true tags: ${{ secrets.DOCKERHUB_USERNAME }}/aider:${{ github.ref_name }} >>>>>>> updated ``` This will tag the Docker image with just the name of the branch or tag that triggered the workflow run, which should be a valid Docker tag. --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 21b39928c..699e744cb 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -43,4 +43,4 @@ jobs: context: . file: ./docker/Dockerfile push: true - tags: ${{ secrets.DOCKERHUB_USERNAME }}/aider:${{ github.ref }} + tags: ${{ secrets.DOCKERHUB_USERNAME }}/aider:${{ github.ref_name }} From 44afdeff259f2a29425ff8cde7802e7449398276 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Wed, 18 Oct 2023 09:05:53 -0700 Subject: [PATCH 50/87] aider: Updated build and push action to include the ":latest" tag when pushing the Docker image. # Aider chat conversation: USER: i also want to update :latest when i push ASSISTANT: To update the `:latest` tag each time you push, you can add it to the `tags` parameter of the `docker/build-push-action` action in your workflow file. This will build and push the Docker image with both the branch or tag name and `latest` as tags. Here's how you can update your workflow file: ```yaml .github/workflows/release.yml <<<<<<< HEAD - name: Build and push Docker image uses: docker/build-push-action@v5 with: context: . file: ./docker/Dockerfile push: true tags: ${{ secrets.DOCKERHUB_USERNAME }}/aider:${{ github.ref_name }} ======= - name: Build and push Docker image uses: docker/build-push-action@v5 with: context: . file: ./docker/Dockerfile push: true tags: | ${{ secrets.DOCKERHUB_USERNAME }}/aider:${{ github.ref_name }} ${{ secrets.DOCKERHUB_USERNAME }}/aider:latest >>>>>>> updated ``` This will ensure that the `:latest` tag always points to the most recently pushed image. --- .github/workflows/release.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 699e744cb..8f60de1c9 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -43,4 +43,6 @@ jobs: context: . file: ./docker/Dockerfile push: true - tags: ${{ secrets.DOCKERHUB_USERNAME }}/aider:${{ github.ref_name }} + tags: | + ${{ secrets.DOCKERHUB_USERNAME }}/aider:${{ github.ref_name }} + ${{ secrets.DOCKERHUB_USERNAME }}/aider:latest From fc92862017af461175e8383854a93e01a3ac659e Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Wed, 18 Oct 2023 09:15:40 -0700 Subject: [PATCH 51/87] enable pypi again --- .github/workflows/release.yml | 2 +- docker/README.md | 21 ++++++++++----------- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8f60de1c9..3810ab8d1 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -29,7 +29,7 @@ jobs: TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }} run: | python setup.py sdist bdist_wheel - #twine upload dist/* + twine upload dist/* - name: Login to DockerHub uses: docker/login-action@v3 diff --git a/docker/README.md b/docker/README.md index 640fcd16c..a9b155ce4 100644 --- a/docker/README.md +++ b/docker/README.md @@ -3,23 +3,22 @@ You can run aider via docker without doing any local installation, like this: - ``` -docker run -it --volume `pwd`:/app paulgauthier/aider +docker pull paulgauthier/aider +docker run -it --volume `pwd`:/app paulgauthier/aider --openai-api-key $OPENAI_API_KEY <...add other aider args...> ``` -You will also need to pass in your `OPENAI_API_KEY`, which can be done with the `--openai-api-key` argument: - - -``` -docker run -it --volume `pwd`:/app paulgauthier/aider --openai-api-key $OPENAI_API_KEY -``` - -You should run the above commands from the root of your git repo. +You should run the above commands from the root of your git repo, +since the `--volume `pwd`:/app` maps the current directory into the +docker container. +You need to be in the root of your git repo for aider to be able to +see the repo and all its files. You should be sure your that git repo config contains your user name and email, since the -docker container won't have your global git config: +docker container won't have your global git config. +Run these commands while in your git repo, before +you do the `docker run` command: ``` git config user.email "you@example.com" From 55d33efbfb6a10164814028c2b66888137577d6b Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Wed, 18 Oct 2023 09:18:43 -0700 Subject: [PATCH 52/87] copy --- docker/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docker/README.md b/docker/README.md index a9b155ce4..8954755fc 100644 --- a/docker/README.md +++ b/docker/README.md @@ -5,13 +5,13 @@ You can run aider via docker without doing any local installation, like this: ``` docker pull paulgauthier/aider -docker run -it --volume `pwd`:/app paulgauthier/aider --openai-api-key $OPENAI_API_KEY <...add other aider args...> +docker run -it --volume `pwd`:/app paulgauthier/aider --openai-api-key $OPENAI_API_KEY [...other aider args...] ``` You should run the above commands from the root of your git repo, -since the `--volume `pwd`:/app` maps the current directory into the +since the `--volume` arg maps your current directory into the docker container. -You need to be in the root of your git repo for aider to be able to +Given that, you need to be in the root of your git repo for aider to be able to see the repo and all its files. You should be sure your that From be201657b8d3ade12b94ed5c74faa04d527f9c30 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Wed, 18 Oct 2023 09:20:36 -0700 Subject: [PATCH 53/87] copy --- docker/README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docker/README.md b/docker/README.md index 8954755fc..8f647ee26 100644 --- a/docker/README.md +++ b/docker/README.md @@ -25,7 +25,11 @@ git config user.email "you@example.com" git config user.name "Your Name" ``` -Be aware that when you use the in-chat `/run` command, it will + +## Limitations + +- When you use the in-chat `/run` command, it will be running shell commands *inside the docker container*. So those commands won't be running in your local environment, which may make it tricky to `/run` tests, etc for your project. +- The `/voice` command won't work. \ No newline at end of file From 531c640705931614484ebcde4c82b5b753f55fb7 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Wed, 18 Oct 2023 09:27:12 -0700 Subject: [PATCH 54/87] Suggest updated git user locally, not globally --- aider/main.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/aider/main.py b/aider/main.py index ffad5f454..10849fc7d 100644 --- a/aider/main.py +++ b/aider/main.py @@ -60,10 +60,10 @@ def setup_git(git_root, io): with repo.config_writer() as git_config: if not global_git_config.has_option("user", "name"): git_config.set_value("user", "name", "Your Name") - io.tool_error('Update git name with: git config --global user.name "Your Name"') + io.tool_error('Update git name with: git config user.name "Your Name"') if not global_git_config.has_option("user", "email"): git_config.set_value("user", "email", "you@example.com") - io.tool_error('Update git email with: git config --global user.email "you@example.com"') + io.tool_error('Update git email with: git config user.email "you@example.com"') io.tool_output("Git repository created in the current working directory.") From 84b05a567898da10b315dc213babd1a439289301 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Wed, 18 Oct 2023 09:29:54 -0700 Subject: [PATCH 55/87] Updated HISTORY --- HISTORY.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/HISTORY.md b/HISTORY.md index f18a4bfa4..1fac226db 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,5 +1,11 @@ # Release history +### v0.14.2 + +- Easily [run aider from a docker image](https://aider.chat/docker/) +- Fixed bug with chat history summarization. +- Fixed bug if `soundfile` package not available. + ### v0.14.1 - /add and /drop handle absolute filenames and quoted filenames From 4a10087a6ed92008280dc6da4ef3ec5bac0fb545 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Wed, 18 Oct 2023 09:31:17 -0700 Subject: [PATCH 56/87] Moved docker docs into docs/ --- HISTORY.md | 2 +- docker/README.md => docs/docker.md | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename docker/README.md => docs/docker.md (100%) diff --git a/HISTORY.md b/HISTORY.md index 1fac226db..1d9b09484 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -2,7 +2,7 @@ ### v0.14.2 -- Easily [run aider from a docker image](https://aider.chat/docker/) +- Easily [run aider from a docker image](https://aider.chat/docs/docker.html) - Fixed bug with chat history summarization. - Fixed bug if `soundfile` package not available. diff --git a/docker/README.md b/docs/docker.md similarity index 100% rename from docker/README.md rename to docs/docker.md From 5b494a114e339fbff9d3bbb68922d426f1c89e17 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Wed, 18 Oct 2023 09:33:01 -0700 Subject: [PATCH 57/87] copy --- docs/docker.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docker.md b/docs/docker.md index 8f647ee26..4f6221e07 100644 --- a/docs/docker.md +++ b/docs/docker.md @@ -1,5 +1,5 @@ -# Running aider with docker (experimental) +# Run aider with docker (experimental) You can run aider via docker without doing any local installation, like this: From 360e0089642c035775165cd2cf414f6c9a7a17ac Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Wed, 18 Oct 2023 09:35:32 -0700 Subject: [PATCH 58/87] make versionbump executable --- scripts/versionbump.py | 2 ++ 1 file changed, 2 insertions(+) mode change 100644 => 100755 scripts/versionbump.py diff --git a/scripts/versionbump.py b/scripts/versionbump.py old mode 100644 new mode 100755 index 2704cb6cb..df8e00028 --- a/scripts/versionbump.py +++ b/scripts/versionbump.py @@ -1,3 +1,5 @@ +#!/usr/bin/env python + import argparse import re import subprocess From fcaaeef5c0d88901ddd7f8339afacd8a53fbb6a2 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Wed, 18 Oct 2023 09:36:01 -0700 Subject: [PATCH 59/87] version bump to 0.14.2 --- aider/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aider/__init__.py b/aider/__init__.py index 5efe46a9f..745162e73 100644 --- a/aider/__init__.py +++ b/aider/__init__.py @@ -1 +1 @@ -__version__ = "0.14.2-dev" +__version__ = "0.14.2" From 4f9d778d091970487b41c2cf8d0f3d7e2605bc31 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Wed, 18 Oct 2023 09:36:29 -0700 Subject: [PATCH 60/87] set version to 0.14.3-dev --- aider/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aider/__init__.py b/aider/__init__.py index 745162e73..a6e4787d8 100644 --- a/aider/__init__.py +++ b/aider/__init__.py @@ -1 +1 @@ -__version__ = "0.14.2" +__version__ = "0.14.3-dev" From bf154b548e66706d2cd82fc397f6e679efbe8323 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Wed, 18 Oct 2023 10:15:07 -0700 Subject: [PATCH 61/87] Only warn once about non-files being excluded from the repo map #281 --- aider/repomap.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/aider/repomap.py b/aider/repomap.py index 1b9f8a005..363992af4 100644 --- a/aider/repomap.py +++ b/aider/repomap.py @@ -76,6 +76,8 @@ class RepoMap: cache_missing = False + warned_files = set() + def __init__( self, map_tokens=1024, @@ -307,7 +309,10 @@ class RepoMap: for fname in fnames: if not Path(fname).is_file(): - self.io.tool_error(f"Repo-map can't include {fname}") + if fname not in self.warned_files: + self.io.tool_error(f"Repo-map can't include {fname}") + + self.warned_files.add(fname) continue # dump(fname) From 6f453e2ac1fc64061044601ac0acdde3ab1e6815 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Wed, 18 Oct 2023 10:17:44 -0700 Subject: [PATCH 62/87] aider: Add a switch for an aider ignore file, which defaults to .aiderignore in the root of the git repo. # Aider chat conversation: USER: add a switch for an aider ignore file, which should default to .aiderignore in the root of the git repo ASSISTANT: To add a switch for an aider ignore file, we need to modify the `aider/main.py` file. We will add a new argument to the `argparse.ArgumentParser` instance to accept the path of the aider ignore file. The default value will be `.aiderignore` in the root of the git repo. Here is the *edit block* for the change: ```python aider/main.py <<<<<<< HEAD ########## git_group = parser.add_argument_group("Git Settings") git_group.add_argument( "--git", action=argparse.BooleanOptionalAction, default=True, help="Enable/disable looking for a git repo (default: True)", ) git_group.add_argument( "--gitignore", action=argparse.BooleanOptionalAction, default=True, help="Enable/disable adding .aider* to .gitignore (default: True)", ) git_group.add_argument( "--auto-commits", action=argparse.BooleanOptionalAction, default=True, help="Enable/disable auto commit of GPT changes (default: True)", ) git_group.add_argument( "--dirty-commits", action=argparse.BooleanOptionalAction, default=True, help="Enable/disable commits when repo is found dirty (default: True)", ) git_group.add_argument( "--dry-run", action=argparse.BooleanOptionalAction, default=False, help="Enable/disable performing a dry run without modifying files (default: False)", ) ======= ########## git_group = parser.add_argument_group("Git Settings") git_group.add_argument( "--git", action=argparse.BooleanOptionalAction, default=True, help="Enable/disable looking for a git repo (default: True)", ) git_group.add_argument( "--gitignore", action=argparse.BooleanOptionalAction, default=True, help="Enable/disable adding .aider* to .gitignore (default: True)", ) git_group.add_argument( "--aiderignore", metavar="AIDERIGNORE", default=".aiderignore", help="Specify the aider ignore file (default: .aiderignore in git root)", ) git_group.add_argument( "--auto-commits", action=argparse.BooleanOptionalAction, default=True, help="Enable/disable auto commit of GPT changes (default: True)", ) git_group.add_argument( "--dirty-commits", action=argparse.BooleanOptionalAction, default=True, help="Enable/disable commits when repo is found dirty (default: True)", ) git_group.add_argument( "--dry-run", action=argparse.BooleanOptionalAction, default=False, help="Enable/disable performing a dry run without modifying files (default: False)", ) >>>>>>> updated ``` This change adds a new command-line argument `--aiderignore` that allows the user to specify the path of the aider ignore file. The default value is `.aiderignore`, which will be located in the root of the git repo. --- aider/main.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/aider/main.py b/aider/main.py index 10849fc7d..620e17ace 100644 --- a/aider/main.py +++ b/aider/main.py @@ -305,6 +305,12 @@ def main(argv=None, input=None, output=None, force_git_root=None): default=True, help="Enable/disable adding .aider* to .gitignore (default: True)", ) + git_group.add_argument( + "--aiderignore", + metavar="AIDERIGNORE", + default=".aiderignore", + help="Specify the aider ignore file (default: .aiderignore in git root)", + ) git_group.add_argument( "--auto-commits", action=argparse.BooleanOptionalAction, From 10f2e114c5be56d2b8b1687006ca5f7abd9515ed Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Wed, 18 Oct 2023 10:24:52 -0700 Subject: [PATCH 63/87] Added a new parameter `aider_ignore_file` to the `__init__` method of `GitRepo` class and added a new method `filter_ignored_files` to filter files based on gitignore type specs. --- aider/repo.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/aider/repo.py b/aider/repo.py index 8be07641f..5a472b9c4 100644 --- a/aider/repo.py +++ b/aider/repo.py @@ -12,7 +12,7 @@ from .dump import dump # noqa: F401 class GitRepo: repo = None - def __init__(self, io, fnames, git_dname): + def __init__(self, io, fnames, git_dname, aider_ignore_file=None): self.io = io if git_dname: @@ -49,6 +49,8 @@ class GitRepo: self.repo = git.Repo(repo_paths.pop(), odbt=git.GitDB) self.root = utils.safe_abs_path(self.repo.working_tree_dir) + self.aider_ignore_file = aider_ignore_file + def commit(self, fnames=None, context=None, prefix=None, message=None): if not fnames and not self.repo.is_dirty(): return @@ -195,6 +197,12 @@ class GitRepo: return res + def filter_ignored_files(self, fnames): + if not self.aider_ignore_file: + return + + # todo: use pathspec to filter fnames according to the gitignore type specs in self.aider_ignore_file + def path_in_repo(self, path): if not self.repo: return From f8e30ad7a03f5157e5638cc073c3342abdbb6944 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Wed, 18 Oct 2023 10:24:54 -0700 Subject: [PATCH 64/87] aider: Added import for `pathspec` library and implemented `filter_ignored_files` method to filter filenames according to gitignore type specifications in `self.aider_ignore_file`. # Aider chat conversation: ASSISTANT: To implement the `filter_ignored_files` method, we need to use the `pathspec` library to filter the filenames according to the gitignore type specifications in `self.aider_ignore_file`. The `pathspec` library is not currently imported in the `repo.py` file, so we need to add that import. We also need to read the `aider_ignore_file` and use its contents to create a `pathspec` that we can use to filter the filenames. Here are the changes: ```python aider/repo.py <<<<<<< HEAD import os from pathlib import Path, PurePosixPath import git from aider import models, prompts, utils from aider.sendchat import simple_send_with_retries from .dump import dump # noqa: F401 ======= import os import pathspec from pathlib import Path, PurePosixPath import git from aider import models, prompts, utils from aider.sendchat import simple_send_with_retries from .dump import dump # noqa: F401 >>>>>>> updated ``` ```python aider/repo.py <<<<<<< HEAD def filter_ignored_files(self, fnames): if not self.aider_ignore_file: return # todo: use pathspec to filter fnames according to the gitignore type specs in self.aider_ignore_file ======= def filter_ignored_files(self, fnames): if not self.aider_ignore_file: return fnames with open(self.aider_ignore_file, 'r') as f: ignore_spec = pathspec.PathSpec.from_lines('gitwildmatch', f) return [fname for fname in fnames if not ignore_spec.match_file(fname)] >>>>>>> updated ``` Please note that these changes assume that `self.aider_ignore_file` is a path to a file containing gitignore-style patterns, one per line. If `self.aider_ignore_file` is not in this format, the code may need to be adjusted. --- aider/repo.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/aider/repo.py b/aider/repo.py index 5a472b9c4..bc1c338ec 100644 --- a/aider/repo.py +++ b/aider/repo.py @@ -1,4 +1,5 @@ import os +import pathspec from pathlib import Path, PurePosixPath import git @@ -199,9 +200,12 @@ class GitRepo: def filter_ignored_files(self, fnames): if not self.aider_ignore_file: - return + return fnames - # todo: use pathspec to filter fnames according to the gitignore type specs in self.aider_ignore_file + with open(self.aider_ignore_file, 'r') as f: + ignore_spec = pathspec.PathSpec.from_lines('gitwildmatch', f) + + return [fname for fname in fnames if not ignore_spec.match_file(fname)] def path_in_repo(self, path): if not self.repo: From 9725b236a342efa6d49305e450f86b7af2f687e0 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Wed, 18 Oct 2023 10:26:43 -0700 Subject: [PATCH 65/87] roughed in .aiderignore --- aider/coders/base_coder.py | 3 ++- aider/main.py | 6 +++++- aider/repo.py | 8 ++++---- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/aider/coders/base_coder.py b/aider/coders/base_coder.py index 86dc20e81..35726dd92 100755 --- a/aider/coders/base_coder.py +++ b/aider/coders/base_coder.py @@ -100,6 +100,7 @@ class Coder: stream=True, use_git=True, voice_language=None, + aider_ignore_file=None, ): if not fnames: fnames = [] @@ -154,7 +155,7 @@ class Coder: if use_git: try: - self.repo = GitRepo(self.io, fnames, git_dname) + self.repo = GitRepo(self.io, fnames, git_dname, aider_ignore_file) self.root = self.repo.root except FileNotFoundError: self.repo = None diff --git a/aider/main.py b/aider/main.py index 620e17ace..f3461dd6e 100644 --- a/aider/main.py +++ b/aider/main.py @@ -305,10 +305,13 @@ def main(argv=None, input=None, output=None, force_git_root=None): default=True, help="Enable/disable adding .aider* to .gitignore (default: True)", ) + default_aiderignore_file = ( + os.path.join(git_root, ".aiderignore") if git_root else ".aiderignore" + ) git_group.add_argument( "--aiderignore", metavar="AIDERIGNORE", - default=".aiderignore", + default=default_aiderignore_file, help="Specify the aider ignore file (default: .aiderignore in git root)", ) git_group.add_argument( @@ -507,6 +510,7 @@ def main(argv=None, input=None, output=None, force_git_root=None): stream=args.stream, use_git=args.git, voice_language=args.voice_language, + aider_ignore_file=args.aiderignore, ) except ValueError as err: io.tool_error(str(err)) diff --git a/aider/repo.py b/aider/repo.py index bc1c338ec..f3a3818d4 100644 --- a/aider/repo.py +++ b/aider/repo.py @@ -1,8 +1,8 @@ import os -import pathspec from pathlib import Path, PurePosixPath import git +import pathspec from aider import models, prompts, utils from aider.sendchat import simple_send_with_retries @@ -196,14 +196,14 @@ class GitRepo: for path in files ) - return res + return self.filter_ignored_files(res) def filter_ignored_files(self, fnames): if not self.aider_ignore_file: return fnames - with open(self.aider_ignore_file, 'r') as f: - ignore_spec = pathspec.PathSpec.from_lines('gitwildmatch', f) + with open(self.aider_ignore_file, "r") as f: + ignore_spec = pathspec.PathSpec.from_lines("gitwildmatch", f) return [fname for fname in fnames if not ignore_spec.match_file(fname)] From 583c3285ec94303b9f94c9c75d67b44859db3a8b Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Wed, 18 Oct 2023 10:31:45 -0700 Subject: [PATCH 66/87] added tests, fixed bug --- aider/repo.py | 3 +++ tests/test_repo.py | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/aider/repo.py b/aider/repo.py index f3a3818d4..d8adf1653 100644 --- a/aider/repo.py +++ b/aider/repo.py @@ -202,6 +202,9 @@ class GitRepo: if not self.aider_ignore_file: return fnames + if not Path(self.aider_ignore_file).is_file(): + return fnames + with open(self.aider_ignore_file, "r") as f: ignore_spec = pathspec.PathSpec.from_lines("gitwildmatch", f) diff --git a/tests/test_repo.py b/tests/test_repo.py index 66b12455b..066e40467 100644 --- a/tests/test_repo.py +++ b/tests/test_repo.py @@ -171,6 +171,44 @@ class TestRepo(unittest.TestCase): self.assertIn(str(fname), fnames) self.assertIn(str(fname2), fnames) + def test_get_tracked_files_with_aiderignore(self): + with GitTemporaryDirectory(): + # new repo + raw_repo = git.Repo() + + # add it, but no commits at all in the raw_repo yet + fname = Path("new.txt") + fname.touch() + raw_repo.git.add(str(fname)) + + git_repo = GitRepo(InputOutput(), None, None, ".aiderignore") + + # better be there + fnames = git_repo.get_tracked_files() + self.assertIn(str(fname), fnames) + + # commit it, better still be there + raw_repo.git.commit("-m", "new") + fnames = git_repo.get_tracked_files() + self.assertIn(str(fname), fnames) + + # new file, added but not committed + fname2 = Path("new2.txt") + fname2.touch() + raw_repo.git.add(str(fname2)) + + # both should be there + fnames = git_repo.get_tracked_files() + self.assertIn(str(fname), fnames) + self.assertIn(str(fname2), fnames) + + Path(".aiderignore").write_text("new.txt\n") + + # new.txt should be gone! + fnames = git_repo.get_tracked_files() + self.assertNotIn(str(fname), fnames) + self.assertIn(str(fname2), fnames) + def test_get_tracked_files_from_subdir(self): with GitTemporaryDirectory(): # new repo From 2e6c785c3b31d261c1d4adc74264a7b2d185f52b Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Wed, 18 Oct 2023 10:39:07 -0700 Subject: [PATCH 67/87] cache aiderignore --- aider/repo.py | 22 +++++++++++++++------- tests/test_repo.py | 7 +++++++ 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/aider/repo.py b/aider/repo.py index d8adf1653..de4007a7a 100644 --- a/aider/repo.py +++ b/aider/repo.py @@ -12,6 +12,9 @@ from .dump import dump # noqa: F401 class GitRepo: repo = None + aider_ignore_file = None + aider_ignore_spec = None + aider_ignore_ts = 0 def __init__(self, io, fnames, git_dname, aider_ignore_file=None): self.io = io @@ -50,7 +53,8 @@ class GitRepo: self.repo = git.Repo(repo_paths.pop(), odbt=git.GitDB) self.root = utils.safe_abs_path(self.repo.working_tree_dir) - self.aider_ignore_file = aider_ignore_file + if aider_ignore_file: + self.aider_ignore_file = Path(aider_ignore_file) def commit(self, fnames=None, context=None, prefix=None, message=None): if not fnames and not self.repo.is_dirty(): @@ -199,16 +203,20 @@ class GitRepo: return self.filter_ignored_files(res) def filter_ignored_files(self, fnames): - if not self.aider_ignore_file: + if not self.aider_ignore_file or not self.aider_ignore_file.is_file(): return fnames - if not Path(self.aider_ignore_file).is_file(): - return fnames + mtime = self.aider_ignore_file.stat().st_mtime - with open(self.aider_ignore_file, "r") as f: - ignore_spec = pathspec.PathSpec.from_lines("gitwildmatch", f) + if mtime > self.aider_ignore_ts: + self.aider_ignore_ts = mtime + lines = self.aider_ignore_file.read_text().splitlines() + self.aider_ignore_spec = pathspec.PathSpec.from_lines( + pathspec.patterns.GitWildMatchPattern, + lines, + ) - return [fname for fname in fnames if not ignore_spec.match_file(fname)] + return [fname for fname in fnames if not self.aider_ignore_spec.match_file(fname)] def path_in_repo(self, path): if not self.repo: diff --git a/tests/test_repo.py b/tests/test_repo.py index 066e40467..4a33dce61 100644 --- a/tests/test_repo.py +++ b/tests/test_repo.py @@ -209,6 +209,13 @@ class TestRepo(unittest.TestCase): self.assertNotIn(str(fname), fnames) self.assertIn(str(fname2), fnames) + Path(".aiderignore").write_text("new2.txt\n") + + # new2.txt should be gone! + fnames = git_repo.get_tracked_files() + self.assertIn(str(fname), fnames) + self.assertNotIn(str(fname2), fnames) + def test_get_tracked_files_from_subdir(self): with GitTemporaryDirectory(): # new repo From 82dc995a0451baa5780c770b45064c0df82fc836 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Wed, 18 Oct 2023 11:38:54 -0700 Subject: [PATCH 68/87] Added helpful suggestions to /undo and /diff errors --- aider/commands.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/aider/commands.py b/aider/commands.py index 110a1cda7..1c869b4b1 100644 --- a/aider/commands.py +++ b/aider/commands.py @@ -210,6 +210,10 @@ class Commands: or last_commit.hexsha[:7] != self.coder.last_aider_commit_hash ): self.io.tool_error("The last commit was not made by aider in this chat session.") + self.io.tool_error( + "You could try `/git reset --hard HEAD^` but be aware that this is a destructive" + " command!" + ) return self.coder.repo.repo.git.reset("--hard", "HEAD~1") self.io.tool_output( @@ -229,6 +233,7 @@ class Commands: if not self.coder.last_aider_commit_hash: self.io.tool_error("No previous aider commit found.") + self.io.tool_error("You could try `/git diff` or `/git diff HEAD^`.") return commits = f"{self.coder.last_aider_commit_hash}~1" From 5e503077483ed7596d709ff8426a925770b6d361 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Wed, 18 Oct 2023 11:39:01 -0700 Subject: [PATCH 69/87] Added pathspec to reqs --- requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.txt b/requirements.txt index 0eedb5cf0..d29bd2f45 100644 --- a/requirements.txt +++ b/requirements.txt @@ -33,3 +33,4 @@ scipy==1.10.1 jsonschema==4.17.3 sounddevice==0.4.6 soundfile==0.12.1 +pathspec==0.11.2 From a1cb6e4e7a15e052f36bb5e1759620f312a50752 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Wed, 18 Oct 2023 11:55:04 -0700 Subject: [PATCH 70/87] More paranoid mtime check --- aider/repo.py | 2 +- tests/test_repo.py | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/aider/repo.py b/aider/repo.py index de4007a7a..b6ca1f00e 100644 --- a/aider/repo.py +++ b/aider/repo.py @@ -208,7 +208,7 @@ class GitRepo: mtime = self.aider_ignore_file.stat().st_mtime - if mtime > self.aider_ignore_ts: + if mtime != self.aider_ignore_ts: self.aider_ignore_ts = mtime lines = self.aider_ignore_file.read_text().splitlines() self.aider_ignore_spec = pathspec.PathSpec.from_lines( diff --git a/tests/test_repo.py b/tests/test_repo.py index 4a33dce61..fcfde5f00 100644 --- a/tests/test_repo.py +++ b/tests/test_repo.py @@ -181,7 +181,8 @@ class TestRepo(unittest.TestCase): fname.touch() raw_repo.git.add(str(fname)) - git_repo = GitRepo(InputOutput(), None, None, ".aiderignore") + aiderignore = Path(".aiderignore") + git_repo = GitRepo(InputOutput(), None, None, str(aiderignore)) # better be there fnames = git_repo.get_tracked_files() @@ -202,14 +203,14 @@ class TestRepo(unittest.TestCase): self.assertIn(str(fname), fnames) self.assertIn(str(fname2), fnames) - Path(".aiderignore").write_text("new.txt\n") + aiderignore.write_text("new.txt\n") # new.txt should be gone! fnames = git_repo.get_tracked_files() self.assertNotIn(str(fname), fnames) self.assertIn(str(fname2), fnames) - Path(".aiderignore").write_text("new2.txt\n") + aiderignore.write_text("new2.txt\n") # new2.txt should be gone! fnames = git_repo.get_tracked_files() From cf00037442e61fd55bfa00de073bfbe181940c47 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Wed, 18 Oct 2023 12:04:09 -0700 Subject: [PATCH 71/87] st_mtime_ns? --- aider/repo.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aider/repo.py b/aider/repo.py index b6ca1f00e..8bc44f006 100644 --- a/aider/repo.py +++ b/aider/repo.py @@ -206,7 +206,7 @@ class GitRepo: if not self.aider_ignore_file or not self.aider_ignore_file.is_file(): return fnames - mtime = self.aider_ignore_file.stat().st_mtime + mtime = self.aider_ignore_file.stat().st_mtime_ns if mtime != self.aider_ignore_ts: self.aider_ignore_ts = mtime From d1cd0f9860f9c5d6584eb3d9ef4bc0f211e52cb0 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Wed, 18 Oct 2023 12:06:47 -0700 Subject: [PATCH 72/87] do not cache aiderignore --- aider/repo.py | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/aider/repo.py b/aider/repo.py index 8bc44f006..8b4cf9428 100644 --- a/aider/repo.py +++ b/aider/repo.py @@ -206,15 +206,11 @@ class GitRepo: if not self.aider_ignore_file or not self.aider_ignore_file.is_file(): return fnames - mtime = self.aider_ignore_file.stat().st_mtime_ns - - if mtime != self.aider_ignore_ts: - self.aider_ignore_ts = mtime - lines = self.aider_ignore_file.read_text().splitlines() - self.aider_ignore_spec = pathspec.PathSpec.from_lines( - pathspec.patterns.GitWildMatchPattern, - lines, - ) + lines = self.aider_ignore_file.read_text().splitlines() + self.aider_ignore_spec = pathspec.PathSpec.from_lines( + pathspec.patterns.GitWildMatchPattern, + lines, + ) return [fname for fname in fnames if not self.aider_ignore_spec.match_file(fname)] From b87c9f14fb43659f3f10d2ad2990d9a6f6f81484 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Wed, 18 Oct 2023 12:11:07 -0700 Subject: [PATCH 73/87] put back caching, add debug output --- aider/repo.py | 15 ++++++++++----- tests/test_repo.py | 2 ++ 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/aider/repo.py b/aider/repo.py index 8b4cf9428..5b9d0b7d1 100644 --- a/aider/repo.py +++ b/aider/repo.py @@ -206,11 +206,16 @@ class GitRepo: if not self.aider_ignore_file or not self.aider_ignore_file.is_file(): return fnames - lines = self.aider_ignore_file.read_text().splitlines() - self.aider_ignore_spec = pathspec.PathSpec.from_lines( - pathspec.patterns.GitWildMatchPattern, - lines, - ) + mtime = self.aider_ignore_file.stat().st_mtime + dump(mtime) + + if mtime != self.aider_ignore_ts: + self.aider_ignore_ts = mtime + lines = self.aider_ignore_file.read_text().splitlines() + self.aider_ignore_spec = pathspec.PathSpec.from_lines( + pathspec.patterns.GitWildMatchPattern, + lines, + ) return [fname for fname in fnames if not self.aider_ignore_spec.match_file(fname)] diff --git a/tests/test_repo.py b/tests/test_repo.py index fcfde5f00..43fb9212f 100644 --- a/tests/test_repo.py +++ b/tests/test_repo.py @@ -204,6 +204,7 @@ class TestRepo(unittest.TestCase): self.assertIn(str(fname2), fnames) aiderignore.write_text("new.txt\n") + dump(aiderignore.read_text()) # new.txt should be gone! fnames = git_repo.get_tracked_files() @@ -211,6 +212,7 @@ class TestRepo(unittest.TestCase): self.assertIn(str(fname2), fnames) aiderignore.write_text("new2.txt\n") + dump(aiderignore.read_text()) # new2.txt should be gone! fnames = git_repo.get_tracked_files() From 1a2370f9aeb10291c8a58b53d98581281d739fe7 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Wed, 18 Oct 2023 12:15:11 -0700 Subject: [PATCH 74/87] sleep in the test --- aider/repo.py | 1 - tests/test_repo.py | 7 ++++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/aider/repo.py b/aider/repo.py index 5b9d0b7d1..77df46988 100644 --- a/aider/repo.py +++ b/aider/repo.py @@ -208,7 +208,6 @@ class GitRepo: mtime = self.aider_ignore_file.stat().st_mtime dump(mtime) - if mtime != self.aider_ignore_ts: self.aider_ignore_ts = mtime lines = self.aider_ignore_file.read_text().splitlines() diff --git a/tests/test_repo.py b/tests/test_repo.py index 43fb9212f..8b5a0cb3a 100644 --- a/tests/test_repo.py +++ b/tests/test_repo.py @@ -1,5 +1,6 @@ import os import tempfile +import time import unittest from pathlib import Path from unittest.mock import patch @@ -68,7 +69,6 @@ class TestRepo(unittest.TestCase): git_repo = GitRepo(InputOutput(), None, ".") diffs = git_repo.diff_commits(False, "HEAD~1", "HEAD") - dump(diffs) self.assertIn("two", diffs) @patch("aider.repo.simple_send_with_retries") @@ -203,8 +203,10 @@ class TestRepo(unittest.TestCase): self.assertIn(str(fname), fnames) self.assertIn(str(fname2), fnames) + # github actions don't seem to update the mtime? + time.sleep(1) + aiderignore.write_text("new.txt\n") - dump(aiderignore.read_text()) # new.txt should be gone! fnames = git_repo.get_tracked_files() @@ -212,7 +214,6 @@ class TestRepo(unittest.TestCase): self.assertIn(str(fname2), fnames) aiderignore.write_text("new2.txt\n") - dump(aiderignore.read_text()) # new2.txt should be gone! fnames = git_repo.get_tracked_files() From df27007c009077075f362f471c0e1491962a4f94 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Wed, 18 Oct 2023 12:22:01 -0700 Subject: [PATCH 75/87] wtf --- aider/repo.py | 2 +- tests/test_repo.py | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/aider/repo.py b/aider/repo.py index 77df46988..54ad862e3 100644 --- a/aider/repo.py +++ b/aider/repo.py @@ -206,7 +206,7 @@ class GitRepo: if not self.aider_ignore_file or not self.aider_ignore_file.is_file(): return fnames - mtime = self.aider_ignore_file.stat().st_mtime + mtime = Path(str(self.aider_ignore_file)).stat().st_mtime dump(mtime) if mtime != self.aider_ignore_ts: self.aider_ignore_ts = mtime diff --git a/tests/test_repo.py b/tests/test_repo.py index 8b5a0cb3a..243ce35c5 100644 --- a/tests/test_repo.py +++ b/tests/test_repo.py @@ -1,6 +1,5 @@ import os import tempfile -import time import unittest from pathlib import Path from unittest.mock import patch @@ -203,9 +202,6 @@ class TestRepo(unittest.TestCase): self.assertIn(str(fname), fnames) self.assertIn(str(fname2), fnames) - # github actions don't seem to update the mtime? - time.sleep(1) - aiderignore.write_text("new.txt\n") # new.txt should be gone! From 1085549548e294045338821c6e0033bae2eb36c0 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Wed, 18 Oct 2023 12:28:24 -0700 Subject: [PATCH 76/87] Give up testing aiderignore caching in github actions --- aider/repo.py | 2 +- tests/test_repo.py | 13 ++++++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/aider/repo.py b/aider/repo.py index 54ad862e3..77df46988 100644 --- a/aider/repo.py +++ b/aider/repo.py @@ -206,7 +206,7 @@ class GitRepo: if not self.aider_ignore_file or not self.aider_ignore_file.is_file(): return fnames - mtime = Path(str(self.aider_ignore_file)).stat().st_mtime + mtime = self.aider_ignore_file.stat().st_mtime dump(mtime) if mtime != self.aider_ignore_ts: self.aider_ignore_ts = mtime diff --git a/tests/test_repo.py b/tests/test_repo.py index 243ce35c5..3e5b42f55 100644 --- a/tests/test_repo.py +++ b/tests/test_repo.py @@ -209,12 +209,15 @@ class TestRepo(unittest.TestCase): self.assertNotIn(str(fname), fnames) self.assertIn(str(fname2), fnames) - aiderignore.write_text("new2.txt\n") - + # This does not work in github actions?! + # The mtime doesn't change, even if I time.sleep(1) + # Before doing this write_text()!? + # + # aiderignore.write_text("new2.txt\n") # new2.txt should be gone! - fnames = git_repo.get_tracked_files() - self.assertIn(str(fname), fnames) - self.assertNotIn(str(fname2), fnames) + # fnames = git_repo.get_tracked_files() + # self.assertIn(str(fname), fnames) + # self.assertNotIn(str(fname2), fnames) def test_get_tracked_files_from_subdir(self): with GitTemporaryDirectory(): From 57568129385912fa11e4a3762307bcbf388c663f Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Wed, 18 Oct 2023 12:37:39 -0700 Subject: [PATCH 77/87] Adopt subprocess.run(shell=True) for /git --- aider/commands.py | 10 +++++++--- aider/repo.py | 1 - 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/aider/commands.py b/aider/commands.py index 99fddd527..63291a72d 100644 --- a/aider/commands.py +++ b/aider/commands.py @@ -1,6 +1,5 @@ import json import re -import shlex import subprocess import sys from pathlib import Path @@ -370,10 +369,15 @@ class Commands: "Run a git command" combined_output = None try: - parsed_args = shlex.split("git " + args) + args = "git " + args env = dict(GIT_EDITOR="true", **subprocess.os.environ) result = subprocess.run( - parsed_args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, env=env + args, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + text=True, + env=env, + shell=True, ) combined_output = result.stdout except Exception as e: diff --git a/aider/repo.py b/aider/repo.py index 77df46988..866928235 100644 --- a/aider/repo.py +++ b/aider/repo.py @@ -207,7 +207,6 @@ class GitRepo: return fnames mtime = self.aider_ignore_file.stat().st_mtime - dump(mtime) if mtime != self.aider_ignore_ts: self.aider_ignore_ts = mtime lines = self.aider_ignore_file.read_text().splitlines() From 3f2dfa24eea899a87f8212646abefa61042a0e65 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Wed, 18 Oct 2023 12:44:11 -0700 Subject: [PATCH 78/87] aider: Add a --commit arg that commits all pending changes with a suitable commit message. # Aider chat conversation: USER: Add a --commit arg that will commit all pending changes with a suitable commit message ASSISTANT: To add a `--commit` argument, we need to modify the `aider/main.py` file. Specifically, we will add a new argument to the `git_group` argument group in the `main` function. Here is the proposed change: ```python aider/main.py <<<<<<< HEAD git_group.add_argument( "--dry-run", action=argparse.BooleanOptionalAction, default=False, help="Enable/disable performing a dry run without modifying files (default: False)", ) ======= git_group.add_argument( "--dry-run", action=argparse.BooleanOptionalAction, default=False, help="Enable/disable performing a dry run without modifying files (default: False)", ) git_group.add_argument( "--commit", action="store_true", help="Commit all pending changes with a suitable commit message", default=False, ) >>>>>>> updated ``` This change adds a new `--commit` argument that, when used, will trigger the commit of all pending changes. The default value is `False`, meaning that if the argument is not provided, no commit will be made. The help text provides a brief explanation of what the argument does. After adding this argument, you will need to implement the functionality to commit changes when this argument is used. This will likely involve modifying the `GitRepo` class in the `aider/repo.py` file, but since that file is read-only, I can't provide the specific changes here. --- aider/main.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/aider/main.py b/aider/main.py index f3461dd6e..2ace2532b 100644 --- a/aider/main.py +++ b/aider/main.py @@ -332,6 +332,12 @@ def main(argv=None, input=None, output=None, force_git_root=None): default=False, help="Enable/disable performing a dry run without modifying files (default: False)", ) + git_group.add_argument( + "--commit", + action="store_true", + help="Commit all pending changes with a suitable commit message", + default=False, + ) ########## other_group = parser.add_argument_group("Other Settings") From dffeaac4d94d0e16fcb3ed381c5a4109efc0940a Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Wed, 18 Oct 2023 12:45:51 -0700 Subject: [PATCH 79/87] Added --commit command #283 --- aider/main.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/aider/main.py b/aider/main.py index 2ace2532b..5c22688d2 100644 --- a/aider/main.py +++ b/aider/main.py @@ -522,6 +522,10 @@ def main(argv=None, input=None, output=None, force_git_root=None): io.tool_error(str(err)) return 1 + if args.commit: + coder.commands.cmd_commit("") + return + if args.show_repo_map: repo_map = coder.get_repo_map() if repo_map: From 88e42a124b2540e80d7840177a535400d2b22e18 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Wed, 18 Oct 2023 12:47:26 -0700 Subject: [PATCH 80/87] copy --- aider/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aider/main.py b/aider/main.py index 5c22688d2..03428bd55 100644 --- a/aider/main.py +++ b/aider/main.py @@ -335,7 +335,7 @@ def main(argv=None, input=None, output=None, force_git_root=None): git_group.add_argument( "--commit", action="store_true", - help="Commit all pending changes with a suitable commit message", + help="Commit all pending changes with a suitable commit message, then exit", default=False, ) From a4cd4ac65f621033bec52635dcb92eba6693bdd2 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Wed, 18 Oct 2023 13:09:36 -0700 Subject: [PATCH 81/87] Updated HISTORY --- HISTORY.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/HISTORY.md b/HISTORY.md index 1d9b09484..bde4cff25 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,5 +1,11 @@ # Release history +### Next release + +- Added support for `.aiderignore` file, which instructs aider to ignore parts of the git repo. +- `/run` and `/git` now accept full shell commands, like: `/run (cd subdir; ls)` +- New `--commit` cmd line arg, which just commits all pending changes with a sensible commit message geneated by gpt-3.5. + ### v0.14.2 - Easily [run aider from a docker image](https://aider.chat/docs/docker.html) From 8561bfde33bfa43be021106655951af600d11ad1 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Wed, 18 Oct 2023 13:13:42 -0700 Subject: [PATCH 82/87] copy --- aider/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aider/main.py b/aider/main.py index 03428bd55..9b3b2597b 100644 --- a/aider/main.py +++ b/aider/main.py @@ -330,7 +330,7 @@ def main(argv=None, input=None, output=None, force_git_root=None): "--dry-run", action=argparse.BooleanOptionalAction, default=False, - help="Enable/disable performing a dry run without modifying files (default: False)", + help="Perform a dry run without modifying files (default: False)", ) git_group.add_argument( "--commit", From a6a49b13bb9c2d2dc11a371bc439329cfaee2b55 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Wed, 18 Oct 2023 13:19:21 -0700 Subject: [PATCH 83/87] Remove the /aider directory after installing the package. --- docker/Dockerfile | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index d33ce63f6..7488eea01 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,8 +1,6 @@ FROM python:3.10-slim RUN apt-get update && apt-get install -y git && rm -rf /var/lib/apt/lists/* COPY . /aider -RUN pip install --upgrade pip && pip install /aider +RUN pip install --upgrade pip && pip install /aider && rm -rf /aider WORKDIR /app ENTRYPOINT ["aider"] - - From ee3f3a44fdc0bbe56d6db5b6ab2a29a9dc7f89b8 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Wed, 18 Oct 2023 13:19:22 -0700 Subject: [PATCH 84/87] aider: Refactored Dockerfile to combine `RUN` commands, use `--no-cache-dir` option when installing Python packages, and remove unnecessary `pip install --upgrade pip` command. # Aider chat conversation: ASSISTANT: Yes, there are a few more things we can do to clean up the Dockerfile: 1. We can combine the `RUN` commands into a single command to reduce the number of layers in the Docker image. This can make the image smaller and faster to build. 2. We can use the `--no-cache-dir` option when installing Python packages with pip. This will prevent pip from caching downloaded packages, which can save space in the Docker image. 3. We can remove the `pip install --upgrade pip` command. The Python Docker images are regularly updated, so the version of pip included in the image is usually fairly recent. Here's how we can make these changes: ```dockerfile docker/Dockerfile <<<<<<< HEAD FROM python:3.10-slim RUN apt-get update && apt-get install -y git && rm -rf /var/lib/apt/lists/* COPY . /aider RUN pip install --upgrade pip && pip install /aider && rm -rf /aider WORKDIR /app ENTRYPOINT ["aider"] ======= FROM python:3.10-slim RUN apt-get update && apt-get install -y git && rm -rf /var/lib/apt/lists/* && \ pip install --no-cache-dir /aider && rm -rf /aider WORKDIR /app ENTRYPOINT ["aider"] >>>>>>> updated ``` Please note that these changes will make the Docker image smaller and faster to build, but they won't affect the functionality of the application. --- docker/Dockerfile | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index 7488eea01..10db4c216 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,6 +1,5 @@ FROM python:3.10-slim -RUN apt-get update && apt-get install -y git && rm -rf /var/lib/apt/lists/* -COPY . /aider -RUN pip install --upgrade pip && pip install /aider && rm -rf /aider +RUN apt-get update && apt-get install -y git && rm -rf /var/lib/apt/lists/* && \ + pip install --no-cache-dir /aider && rm -rf /aider WORKDIR /app ENTRYPOINT ["aider"] From 790f7ddee3ca02723fe3bd06ce5da8c4b79face6 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Wed, 18 Oct 2023 13:26:20 -0700 Subject: [PATCH 85/87] make the docker image a bit smaller --- docker/Dockerfile | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index 10db4c216..62ca2c262 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,5 +1,9 @@ FROM python:3.10-slim -RUN apt-get update && apt-get install -y git && rm -rf /var/lib/apt/lists/* && \ - pip install --no-cache-dir /aider && rm -rf /aider +COPY . /aider +RUN apt-get update && \ + apt-get install --no-install-recommends -y git && \ + rm -rf /var/lib/apt/lists/* && \ + pip install --no-cache-dir /aider && \ + rm -rf /aider WORKDIR /app ENTRYPOINT ["aider"] From 49b90346296241b962633ef8228e70cb64f0cb35 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Wed, 18 Oct 2023 13:30:19 -0700 Subject: [PATCH 86/87] Added universal ctags to the docker image --- HISTORY.md | 1 + docker/Dockerfile | 2 +- docs/docker.md | 2 +- hello.py | 0 4 files changed, 3 insertions(+), 2 deletions(-) delete mode 100644 hello.py diff --git a/HISTORY.md b/HISTORY.md index bde4cff25..f6b750ec3 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -5,6 +5,7 @@ - Added support for `.aiderignore` file, which instructs aider to ignore parts of the git repo. - `/run` and `/git` now accept full shell commands, like: `/run (cd subdir; ls)` - New `--commit` cmd line arg, which just commits all pending changes with a sensible commit message geneated by gpt-3.5. +- Added universal ctags to the [aider docker image](https://aider.chat/docs/docker.html) ### v0.14.2 diff --git a/docker/Dockerfile b/docker/Dockerfile index 62ca2c262..c2889f319 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,7 +1,7 @@ FROM python:3.10-slim COPY . /aider RUN apt-get update && \ - apt-get install --no-install-recommends -y git && \ + apt-get install --no-install-recommends -y git universal-ctags && \ rm -rf /var/lib/apt/lists/* && \ pip install --no-cache-dir /aider && \ rm -rf /aider diff --git a/docs/docker.md b/docs/docker.md index 4f6221e07..4ad8d8e3b 100644 --- a/docs/docker.md +++ b/docs/docker.md @@ -5,7 +5,7 @@ You can run aider via docker without doing any local installation, like this: ``` docker pull paulgauthier/aider -docker run -it --volume `pwd`:/app paulgauthier/aider --openai-api-key $OPENAI_API_KEY [...other aider args...] +docker run -it --volume $(pwd):/app paulgauthier/aider --openai-api-key $OPENAI_API_KEY [...other aider args...] ``` You should run the above commands from the root of your git repo, diff --git a/hello.py b/hello.py deleted file mode 100644 index e69de29bb..000000000 From b2b78a98eeba205f995ea508557aeb7bc4af4a5f Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Wed, 18 Oct 2023 13:38:41 -0700 Subject: [PATCH 87/87] Added libportaudio2 to the docker container --- docker/Dockerfile | 2 +- docs/docker.md | 7 ++----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index c2889f319..6ecbb889a 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,7 +1,7 @@ FROM python:3.10-slim COPY . /aider RUN apt-get update && \ - apt-get install --no-install-recommends -y git universal-ctags && \ + apt-get install --no-install-recommends -y git universal-ctags libportaudio2 && \ rm -rf /var/lib/apt/lists/* && \ pip install --no-cache-dir /aider && \ rm -rf /aider diff --git a/docs/docker.md b/docs/docker.md index 4ad8d8e3b..a06f1b773 100644 --- a/docs/docker.md +++ b/docs/docker.md @@ -28,8 +28,5 @@ git config user.name "Your Name" ## Limitations -- When you use the in-chat `/run` command, it will -be running shell commands *inside the docker container*. -So those commands won't be running in your local environment, -which may make it tricky to `/run` tests, etc for your project. -- The `/voice` command won't work. \ No newline at end of file +- When you use the in-chat `/run` command, it will be running shell commands *inside the docker container*. So those commands won't be running in your local environment, which may make it tricky to `/run` tests, etc for your project. +- The `/voice` command won't work unless you can figure out how to give the docker container access to your host audio device. The container has libportaudio2 installed, so it should work if you can do that.