Commit graph

890 commits

Author SHA1 Message Date
Paul Gauthier
f266c87e59 normalize /add paths 2023-07-07 16:41:29 -07:00
Paul Gauthier
02e9602d36 fixed get_files_content with multiple files 2023-07-07 16:35:01 -07:00
Paul Gauthier
15336eeb04 added missing fence closing 2023-07-07 16:32:49 -07:00
Paul Gauthier
fe959be85d Pass unicode encodings into ctags 2023-07-07 15:08:40 -07:00
Paul Gauthier
031c2bc34b provide default git name/email; fix tests 2023-07-07 15:02:56 -07:00
Paul Gauthier
451929f2ca aider: Added code to check if name and email are set in git configuration and set example values if not.
# Context:
USER: can i pass email and name to repo.init ?
ASSISTANT: Yes, you can pass the `name` and `email` to `git.Repo.init()`, but not directly. The `git.Repo.init()` function initializes a new repository, but it doesn't handle the configuration of user details.

To set the `name` and `email`, you need to modify the configuration of the repository after it has been initialized. This can be done using the `git.config_writer()` function.

Here's how you can do it:

```python
repo = git.Repo.init(path)
with repo.config_writer() as git_config:
    git_config.set_value("user", "name", "Your Name")
    git_config.set_value("user", "email", "your.email@example.com")
```

In the context of your `aider/main.py` file, you can add these changes after the repository initialization. Here's the modification:

```python
aider/main.py
<<<<<<< ORIGINAL
    if not git_root and args.git:
        if io.confirm_ask("
^C KeyboardInterrupt
USER: can up check if they are set first, and if not put in example values?
ASSISTANT: Sure, you can check if the `name` and `email` are already set in the git configuration before setting the example values. Here's how you can do it:

```python
repo = git.Repo.init(path)
with repo.config_writer() as git_config:
    if not git_config.has_option("user", "name"):
        git_config.set_value("user", "name", "Example Name")
    if not git_config.has_option("user", "email"):
        git_config.set_value("user", "email", "example.email@example.com")
```

In the context of your `aider/main.py` file, you can add these changes after the repository initialization. Here's the modification:

```python
aider/main.py
<<<<<<< ORIGINAL
    if not git_root and args.git:
        if io.confirm_ask("No git repo found, create one to track GPT's changes (recommended)?"):
            git.Repo.init(os.getcwd())
            io.tool_output("Git repository created in the current working directory.")
=======
    if not git_root and args.git:
        if io.confirm_ask("No git repo found, create one to track GPT's changes (recommended)?"):
            repo = git.Repo.init(os.getcwd())
            with repo.config_writer() as git_config:
                if not git_config.has_option("user", "name"):
                    git_config.set_value("user", "name", "Example Name")
                if not git_config.has_option("user", "email"):
                    git_config.set_value("user", "email", "example.email@example.com")
            io.tool_output("Git repository created in the current working directory.")
>>>>>>> UPDATED
```

This code will check if the `name` and `email` are already set in the git configuration. If they are not set, it will set them to "Example Name" and "example.email@example.com" respectively.
2023-07-07 13:45:57 -07:00
Paul Gauthier
cffeb275a6 cleanup 2023-07-07 12:27:13 -07:00
Paul Gauthier
552bdc4f71 handle diff output for new file 2023-07-07 11:58:27 -07:00
Paul Gauthier
a764efa82b fix test 2023-07-07 11:25:41 -07:00
Paul Gauthier
b5c497b6a6 Allow adding of existing non-git files 2023-07-07 11:21:55 -07:00
Paul Gauthier
63d98b30d8 honor --no-git 2023-07-07 11:13:52 -07:00
Paul Gauthier
181062dc06 confirm_ask 2023-07-07 11:12:36 -07:00
Paul Gauthier
95c1aa2f2e aider: Initialized a new git repository in the current working directory and output a message to inform the user.
# Context:
USER: do the TODO
ASSISTANT: To clarify, are you referring to the TODO comment in the `main.py` file? The comment is:

```python
    if not git_root:
        if io.ask_confirm("Create git repo (recommended)?"):
            # TODO: create at cwd
            pass
```

It seems like the task is to create a git repository in the current working directory if one does not already exist. Is that correct?
USER: yes
ASSISTANT: To create a git repository in the current working directory, we need to use the `git` module's `Repo.init` method. Here's how to modify the `main.py` file:

```python
aider/main.py
<<<<<<< ORIGINAL
    if not git_root:
        if io.ask_confirm("Create git repo (recommended)?"):
            # TODO: create at cwd
            pass
=======
    if not git_root:
        if io.ask_confirm("Create git repo (recommended)?"):
            git.Repo.init(os.getcwd())
            io.tool_output("Git repository created in the current working directory.")
>>>>>>> UPDATED
```

This change will initialize a new git repository in the current working directory when the user confirms. It also outputs a message to inform the user that the repository has been created.
2023-07-07 11:10:25 -07:00
Paul Gauthier
b75cfaac28 Look for conf yml in cwd, gitroot and homedir 2023-07-07 11:08:34 -07:00
Paul Gauthier
fb5af6ec8b catch and report json errors parsing ctags output #63 2023-07-07 09:54:50 -07:00
Paul Gauthier
033541d01f Merge branch 'main' into glob-add 2023-07-07 09:10:56 -07:00
Paul Gauthier
1654ee422f add test for conf yml 2023-07-07 09:07:02 -07:00
Paul Gauthier
61e9003ce0 handle valerr in render_incremental_response 2023-07-07 08:29:53 -07:00
Paul Gauthier
75b533b4c3 commit new files to git 2023-07-07 08:24:44 -07:00
Paul Gauthier
6a0cff50f8 filter against git 2023-07-06 18:09:26 -07:00
Paul Gauthier
cb2b2c5ce3 Merge branch 'main' into glob-add 2023-07-06 17:53:46 -07:00
Paul Gauthier
42977230b1 Mark gpt-4 as always_available to skip the availability check on startup 2023-07-06 17:44:12 -07:00
Paul Gauthier
c45074b937 version bump to 0.8.0 2023-07-06 14:08:47 -07:00
Paul Gauthier
95a777a48c More accurate /tokens 2023-07-06 13:35:17 -07:00
Paul Gauthier
6cb4428cb7 Merge branch 'main' into encodings 2023-07-06 13:33:53 -07:00
Paul Gauthier
1291c2424a better message for no /ls output 2023-07-06 12:52:33 -07:00
Paul Gauthier
610dd223b5 added test for choose_fence 2023-07-06 11:08:13 -07:00
Paul Gauthier
3c443d68d0 working test 2023-07-06 10:55:38 -07:00
Paul Gauthier
d2c13b2976 aider: Refactored cmd_drop method to match glob pattern against the filename only, not the full file path.
# Context:
USER: I ran this command:

 pytest tests/test_commands.py

And got this output:

============================= test session starts ==============================
platform darwin -- Python 3.11.3, pytest-7.3.1, pluggy-1.2.0
rootdir: /Users/gauthier/Projects/aider
configfile: pytest.ini
collected 3 items

tests/test_commands.py ..F                                               [100%]

=================================== FAILURES ===================================
________________ TestCommands.test_cmd_drop_with_glob_patterns _________________

self = <tests.test_commands.TestCommands testMethod=test_cmd_drop_with_glob_patterns>

    def test_cmd_drop_with_glob_patterns(self):
        # Initialize the Commands and InputOutput objects
        io = InputOutput(pretty=False, yes=True)
        from aider.coders import Coder

        coder = Coder.create(models.GPT35, None, io, openai_api_key="deadbeef")
        commands = Commands(io, coder)

        with open("test1.py", "w") as f:
            f.write("print('test1')")
        with open("test2.py", "w") as f:
            f.write("print('test2')")

        # Add some files to the chat session
        commands.cmd_add("*.py")

        # Call the cmd_drop method with a glob pattern
        commands.cmd_drop("*2.py")

        # Check if the Python files have been removed from the chat session
>       self.assertIn(os.path.abspath("test1.py"), coder.abs_fnames)
E       AssertionError: '/private/var/folders/49/kxrdwwbx0h9bchx99397477c0000gn/T/tmpijmz7big/test1.py' not found in set()

tests/test_commands.py:81: AssertionError
----------------------------- Captured stdout call -----------------------------
Model: gpt-3.5-turbo
Git repo: none
Repo-map: disabled
Added test1.py to the chat
Added test2.py to the chat
Removed test2.py from the chat
Removed test1.py from the chat
=========================== short test summary info ============================
FAILED tests/test_commands.py::TestCommands::test_cmd_drop_with_glob_patterns
========================= 1 failed, 2 passed in 0.98s ==========================

ASSISTANT: It seems like the test `test_cmd_drop_with_glob_patterns` is failing because the `cmd_drop` method is removing all files that match the glob pattern "*2.py", which includes "test1.py" and "test2.py". This is because the glob pattern is being matched against the full file path, not just the filename.

To fix this, we need to modify the `cmd_drop` method in the `Commands` class in the `aider/commands.py` file. Specifically, we need to change the way we match the glob pattern against the file paths.

Here are the changes:

```python
aider/commands.py
<<<<<<< ORIGINAL
    def cmd_drop(self, args):
        "Remove matching files from the chat session"

        if not args.strip():
            self.io.tool_output("Dropping all files from the chat session.")
            self.coder.abs_fnames = set()

        for word in args.split():
            matched_files = [
                file
                for file in self.coder.abs_fnames
                if any(glob.fnmatch.fnmatch(os.path.relpath(file, self.coder.root), word) for file in self.coder.abs_fnames)
            ]
            if not matched_files:
                self.io.tool_error(f"No files matched '{word}'")

            for matched_file in matched_files:
                relative_fname = os.path.relpath(matched_file, self.coder.root)
                self.coder.abs_fnames.remove(matched_file)
                self.io.tool_output(f"Removed {relative_fname} from the chat")
=======
    def cmd_drop(self, args):
        "Remove matching files from the chat session"

        if not args.strip():
            self.io.tool_output("Dropping all files from the chat session.")
            self.coder.abs_fnames = set()

        for word in args.split():
            matched_files = [
                file
                for file in self.coder.abs_fnames
                if glob.fnmatch.fnmatch(os.path.basename(file), word)
            ]
            if not matched_files:
                self.io.tool_error(f"No files matched '{word}'")

            for matched_file in matched_files:
                relative_fname = os.path.relpath(matched_file, self.coder.root)
                self.coder.abs_fnames.remove(matched_file)
                self.io.tool_output(f"Removed {relative_fname} from the chat")
>>>>>>> UPDATED
```

This change will ensure that the glob pattern is matched against the filename only, not the full file path. This should fix the failing test.
2023-07-05 21:42:36 -07:00
Paul Gauthier
ad5cdc463a aider: Refactored cmd_drop to use glob for matching files against the given word.
# Context:
USER: have `cmd_drop` also glob against the `files`
ASSISTANT: To implement this change, we need to modify the `aider/commands.py` file.

Here's the step-by-step process:

1. In the `cmd_drop` method, use the `glob.glob` function to match the word against the `files`.
2. If no files match, print an error message.
3. If files match, remove them from the chat session.

Here's the code change:

```python
aider/commands.py
<<<<<<< ORIGINAL
    def cmd_drop(self, args):
        "Remove matching files from the chat session"

        if not args.strip():
            self.io.tool_output("Dropping all files from the chat session.")
            self.coder.abs_fnames = set()

        for word in args.split():
            matched_files = [
                file
                for file in self.coder.abs_fnames
                if word.lower() in os.path.relpath(file, self.coder.root).lower()
            ]
            if not matched_files:
                self.io.tool_error(f"No files matched '{word}'")

            for matched_file in matched_files:
                relative_fname = os.path.relpath(matched_file, self.coder.root)
                self.coder.abs_fnames.remove(matched_file)
                self.io.tool_output(f"Removed {relative_fname} from the chat")
=======
    def cmd_drop(self, args):
        "Remove matching files from the chat session"

        if not args.strip():
            self.io.tool_output("Dropping all files from the chat session.")
            self.coder.abs_fnames = set()

        for word in args.split():
            matched_files = [
                file
                for file in self.coder.abs_fnames
                if any(glob.fnmatch.fnmatch(os.path.relpath(file, self.coder.root), word) for file in self.coder.abs_fnames)
            ]
            if not matched_files:
                self.io.tool_error(f"No files matched '{word}'")

            for matched_file in matched_files:
                relative_fname = os.path.relpath(matched_file, self.coder.root)
                self.coder.abs_fnames.remove(matched_file)
                self.io.tool_output(f"Removed {relative_fname} from the chat")
>>>>>>> UPDATED
```
Please let me know if you need any further changes.
2023-07-05 21:38:44 -07:00
Paul Gauthier
5cd7d9d743 aider: aider: Modified cmd_add to skip file creation if word contains glob characters.
# Context:
USER: if word doesn't match anything, don't ask to add it if it contains glob characters
ASSISTANT: To implement this change, we need to modify the `aider/commands.py` file.

Here's the step-by-step process:

1. In the `cmd_add` method, before asking to create a file, check if the word contains any glob characters (`*`, `?`, `[`, `]`).
2. If it does, skip the creation process and print an error message.

Here's the code change:

```python
aider/commands.py
<<<<<<< ORIGINAL
            if not matched_files:
                if self.coder.repo is not None:
                    create_file = self.io.confirm_ask(
                        (
                            f"No files matched '{word}'. Do you want to create the file and add it"
                            " to git?"
                        ),
                    )
                else:
                    create_file = self.io.confirm_ask(
                        f"No files matched '{word}'. Do you want to create the file?"
                    )

                if create_file:
                    with open(os.path.join(self.coder.root, word), "w"):
                        pass
                    matched_files = [word]
                    if self.coder.repo is not None:
                        self.coder.repo.git.add(os.path.join(self.coder.root, word))
                        commit_message = f"aider: Created and added {word} to git."
                        self.coder.repo.git.commit("-m", commit_message, "--no-verify")
                else:
                    self.io.tool_error(f"No files matched '{word}'")
=======
            if not matched_files:
                if any(char in word for char in "*?[]"):
                    self.io.tool_error(f"No files matched '{word}' and it contains glob characters")
                else:
                    if self.coder.repo is not None:
                        create_file = self.io.confirm_ask(
                            (
                                f"No files matched '{word}'. Do you want to create the file and add it"
                                " to git?"
                            ),
                        )
                    else:
                        create_file = self.io.confirm_ask(
                            f"No files matched '{word}'. Do you want to create the file?"
                        )

                    if create_file:
                        with open(os.path.join(self.coder.root, word), "w"):
                            pass
                        matched_files = [word]
                        if self.coder.repo is not None:
                            self.coder.repo.git.add(os.path.join(self.coder.root, word))
                            commit_message = f"aider: Created and added {word} to git."
                            self.coder.repo.git.commit("-m", commit_message, "--no-verify")
                    else:
                        self.io.tool_error(f"No files matched '{word}'")
>>>>>>> UPDATED
```
Please let me know if you need any further changes.
2023-07-05 21:37:22 -07:00
Paul Gauthier
09605788d5 cleanup 2023-07-05 21:33:59 -07:00
Paul Gauthier
d97bf17e19 aider: Moved the import statement for the glob module to the top of the aider/commands.py file.
# Context:
USER: I ran this command:

 pytest tests/test_commands.py

And got this output:

============================= test session starts ==============================
platform darwin -- Python 3.11.3, pytest-7.3.1, pluggy-1.2.0
rootdir: /Users/gauthier/Projects/aider
configfile: pytest.ini
collected 1 item

tests/test_commands.py F                                                 [100%]

=================================== FAILURES ===================================
_________________ TestCommands.test_cmd_add_with_glob_patterns _________________

self = <tests.test_commands.TestCommands testMethod=test_cmd_add_with_glob_patterns>

    def test_cmd_add_with_glob_patterns(self):
        # Initialize the Commands and InputOutput objects
        io = InputOutput(pretty=False, yes=True)
        from aider.coders import Coder

        coder = Coder.create(models.GPT35, None, io, openai_api_key="deadbeef")
        commands = Commands(io, coder)

        # Create some test files
        with open("test1.py", "w") as f:
            f.write("print('test1')")
        with open("test2.py", "w") as f:
            f.write("print('test2')")
        with open("test.txt", "w") as f:
            f.write("test")

        # Call the cmd_add method with a glob pattern
>       commands.cmd_add("*.py")

tests/test_commands.py:38:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <aider.commands.Commands object at 0x1113b2250>, args = '*.py'

    def cmd_add(self, args):
        "Add matching files to the chat session using glob patterns"

        added_fnames = []
        for pattern in args.split():
>           matched_files = glob.glob(pattern, recursive=True)
E           NameError: name 'glob' is not defined

aider/commands.py:226: NameError
----------------------------- Captured stdout call -----------------------------
Model: gpt-3.5-turbo
Git repo: none
Repo-map: disabled
=========================== short test summary info ============================
FAILED tests/test_commands.py::TestCommands::test_cmd_add_with_glob_patterns
============================== 1 failed in 0.92s ===============================

ASSISTANT: The error message indicates that the `glob` module is not defined in the `cmd_add` method of the `Commands` class in the `aider/commands.py` file. This is because the `glob` module is imported inside the `Commands` class, but it should be imported at the top of the file.

Here are the changes you need to make:

```python
aider/commands.py
<<<<<<< ORIGINAL
import json
import os
import shlex
import subprocess
import sys

import git
import tiktoken
from prompt_toolkit.completion import Completion

from aider import prompts, utils

class Commands:
    import glob
=======
import json
import os
import shlex
import subprocess
import sys
import glob

import git
import tiktoken
from prompt_toolkit.completion import Completion

from aider import prompts, utils

class Commands:
>>>>>>> UPDATED
```

This change moves the `import glob` statement from inside the `Commands` class to the top of the file, which is the standard place for import statements in Python. This will make the `glob` module available to all methods in the `Commands` class.
2023-07-05 21:30:57 -07:00
Paul Gauthier
874b5f8c7a aider: The cmd_add method in the Commands class has been modified to allow the /add command to accept glob patterns.
# Context:
USER: Feature Description
Currently, the /add function accepts directory paths to add the entire directory or spefic file paths to the program's context. A useful enhancement would be to support the use of regex or glob expressions with the /add function. This would give users more flexibility and control to specify exactly which files to include.

Proposed Solution
Allow the /add function to interpret glob or regex patterns. For example, a user could use /add src/*.py to include only Python files from the src directory or /add src/[A-C]* to add only files that start with A, B, or C.

Expected Behavior
When a user runs /add with a regex or glob pattern, the program should add to the context only the files that match the pattern. The program should handle non-matching files or non-existent paths gracefully, preferably by ignoring them and printing a warning message.

Alternatives Considered
An alternative could be providing an additional function, say /addPattern, that accepts regex/glob patterns. This approach would leave the existing /add functionality unchanged for users who don't need the enhanced functionality.

Additional Context
Adding this feature would boost the flexibility of the /add function, making the program more convenient to use for a variety of tasks. Users could, for instance, exclude certain types of files or select a subset of files based on their naming conventions.
ASSISTANT: To implement this feature, we need to modify the `cmd_add` method in the `Commands` class in the `aider/commands.py` file.

The changes will involve:
1. Interpreting the input argument as a glob pattern.
2. Using the `glob` module to find all files that match the pattern.
3. Adding the matched files to the chat session.

Here is the proposed change:

```python
aider/commands.py
<<<<<<< ORIGINAL
    def cmd_add(self, args):
        "Add matching files to the chat session"

        added_fnames = []
        files = self.coder.get_all_relative_files()
        for word in args.split():
            matched_files = [file for file in files if word in file]
=======
    import glob

    def cmd_add(self, args):
        "Add matching files to the chat session using glob patterns"

        added_fnames = []
        for pattern in args.split():
            matched_files = glob.glob(pattern, recursive=True)
>>>>>>> UPDATED
```

This change will allow the `/add` command to accept glob patterns. For example, `/add src/*.py` will add all Python files in the `src` directory to the chat session.
2023-07-05 21:28:53 -07:00
Paul Gauthier
391088fd14 add self.dry_run 2023-07-05 21:22:12 -07:00
Paul Gauthier
85c3bb8f12 rely on io.dry_run 2023-07-05 21:21:37 -07:00
Paul Gauthier
4ee5c49a04 have io.write_text handle dry_run 2023-07-05 21:20:22 -07:00
Paul Gauthier
b46dffbb20 refactor to use io.write_text 2023-07-05 21:18:19 -07:00
Paul Gauthier
0ea52b412f cleanup 2023-07-05 21:11:46 -07:00
Paul Gauthier
1808c47d96 aider: Added write_text function to InputOutput class in aider/io.py.
# Context:
USER: add a write_text func
ASSISTANT: To add a `write_text` function to the `InputOutput` class in the `aider/io.py` file, we need to modify the `aider/io.py` file.

Here is the change:

```python
aider/io.py
<<<<<<< ORIGINAL

    def read_text(self, filename):
        try:
            with open(filename, "r", encoding=self.encoding) as f:
                return f.read()
        except (FileNotFoundError, UnicodeError) as e:
            self.tool_error(f"{filename}: {e}")
            return
=======

    def read_text(self, filename):
        try:
            with open(filename, "r", encoding=self.encoding) as f:
                return f.read()
        except (FileNotFoundError, UnicodeError) as e:
            self.tool_error(f"{filename}: {e}")
            return

    def write_text(self, filename, content):
        try:
            with open(filename, "w", encoding=self.encoding) as f:
                f.write(content)
        except (FileNotFoundError, UnicodeError) as e:
            self.tool_error(f"{filename}: {e}")
            return
>>>>>>> UPDATED
```

This `write_text` function takes a filename and content as arguments, opens the file in write mode, and writes the content to the file. If there is a `FileNotFoundError` or `UnicodeError`, it calls the `tool_error` method with the error message.
2023-07-05 21:11:30 -07:00
Paul Gauthier
de02038a7e remove ref to quoted_file 2023-07-05 21:08:49 -07:00
Paul Gauthier
7c56dcb16f noop 2023-07-05 21:02:40 -07:00
Paul Gauthier
3897d0a665 removed recheck_abs_fnames 2023-07-05 21:01:26 -07:00
Paul Gauthier
c2c80bbd3c do not /add files that fail to decode 2023-07-05 21:00:10 -07:00
Paul Gauthier
ca45db2898 use utf-8 2023-07-05 20:51:35 -07:00
Paul Gauthier
7542449629 refactor to use io.read_text 2023-07-05 20:49:04 -07:00
Paul Gauthier
d30fb94c72 refactor to use io.read_text 2023-07-05 20:48:09 -07:00
Paul Gauthier
60619f263c refactor to use io.read_text 2023-07-05 20:47:44 -07:00
Paul Gauthier
c8fb1ddaa7 refactor to use io.read_text 2023-07-05 20:46:15 -07:00
Paul Gauthier
413ddb84df refactor to use io.read_text 2023-07-05 20:33:46 -07:00