Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions aider/args.py
Original file line number Diff line number Diff line change
Expand Up @@ -791,8 +791,8 @@ def get_parser(default_config_files, git_root):
is_config_file=True,
metavar="CONFIG_FILE",
help=(
"Specify the config file (default: search for .aider.conf.yml in git root, cwd"
" or home directory)"
"Specify the config file (default: search for .aider.conf.yml in git root, cwd, home"
" dir, and for conf.yml in ~/.config/aider)"
),
).complete = shtab.FILE
# This is a duplicate of the argument in the preparser and is a no-op by this time of
Expand Down
2 changes: 1 addition & 1 deletion aider/args_formatter.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ def _format_text(self, text):
##########################################################
# Sample .aider.conf.yml
# This file lists *all* the valid configuration entries.
# Place in your home dir, or at the root of your git repo.
# Place in your home dir, git repo root, or $XDG_CONFIG_HOME/aider/conf.yml.
##########################################################

# Note: You can only put OpenAI and Anthropic API keys in the YAML
Expand Down
35 changes: 33 additions & 2 deletions aider/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,17 @@ def parse_lint_cmds(lint_cmds, io):
def generate_search_path_list(default_file, git_root, command_line_file):
files = []
files.append(Path.home() / default_file) # homedir

xdg_config_home = os.environ.get("XDG_CONFIG_HOME")
if not xdg_config_home:
xdg_config_home = Path.home() / ".config"
else:
xdg_config_home = Path(xdg_config_home)
xdg_aider_dir = xdg_config_home / "aider"
files.append(xdg_aider_dir / default_file)
if default_file.startswith("."):
files.append(xdg_aider_dir / default_file[1:])

if git_root:
files.append(Path(git_root) / default_file) # git root
files.append(default_file)
Expand Down Expand Up @@ -465,15 +476,35 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F

default_config_files = []
try:
default_config_files += [conf_fname.resolve()] # CWD
default_config_files.append(conf_fname.resolve()) # CWD
except OSError:
pass

if git_root:
git_conf = Path(git_root) / conf_fname # git root
if git_conf not in default_config_files:
default_config_files.append(git_conf)
default_config_files.append(Path.home() / conf_fname) # homedir

xdg_config_home = os.environ.get("XDG_CONFIG_HOME")
if not xdg_config_home:
xdg_config_home = Path.home() / ".config"
else:
xdg_config_home = Path(xdg_config_home)
xdg_aider_dir = xdg_config_home / "aider"

# Non-dotted version, higher precedence
xdg_conf_no_dot = xdg_aider_dir / "conf.yml"
if xdg_conf_no_dot not in default_config_files:
default_config_files.append(xdg_conf_no_dot)

# Dotted version
xdg_conf_dot = xdg_aider_dir / conf_fname
if xdg_conf_dot not in default_config_files:
default_config_files.append(xdg_conf_dot)

home_conf = Path.home() / conf_fname
if home_conf not in default_config_files:
default_config_files.append(home_conf) # homedir
default_config_files = list(map(str, default_config_files))

parser = get_parser(default_config_files, git_root)
Expand Down
13 changes: 7 additions & 6 deletions aider/website/docs/config/aider_conf.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,15 @@ description: How to configure aider with a YAML config file.

# YAML config file

Most of aider's options can be set in an `.aider.conf.yml` file.
Aider will look for a this file in these locations:
Most of aider's options can be set in a YAML config file.
Aider will look for config files in these locations:

- Your home directory.
- The root of your git repo.
- The current directory.
- Your home directory (`~/.aider.conf.yml`).
- Your XDG config home in `$XDG_CONFIG_HOME/aider/`. Aider will look for `conf.yml` and `.aider.conf.yml`. If `$XDG_CONFIG_HOME` is not set, it defaults to `~/.config`.
- The root of your git repo (`.aider.conf.yml`).
- The current directory (`.aider.conf.yml`).

If the files above exist, they will be loaded in that order. Files loaded last will take priority.
If the files above exist, they will be loaded in that order. Settings from files loaded later take priority.

You can also specify the `--config <filename>` parameter, which will only load the one config file.

Expand Down
28 changes: 25 additions & 3 deletions tests/basic/test_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -533,8 +533,18 @@ def test_yaml_config_file_loading(self):
cwd_config = cwd / ".aider.conf.yml"
named_config = git_dir / "named.aider.conf.yml"

# Create XDG config files
xdg_config_home = fake_home / ".config"
os.environ["XDG_CONFIG_HOME"] = str(xdg_config_home)
xdg_aider_dir = xdg_config_home / "aider"
xdg_aider_dir.mkdir(parents=True)
xdg_config_dot = xdg_aider_dir / ".aider.conf.yml"
xdg_config_no_dot = xdg_aider_dir / "conf.yml"

cwd_config.write_text("model: gpt-4-32k\nmap-tokens: 4096\n")
git_config.write_text("model: gpt-4\nmap-tokens: 2048\n")
xdg_config_no_dot.write_text("model: xdg-no-dot\nmap-tokens: 600\n")
xdg_config_dot.write_text("model: xdg-dot\nmap-tokens: 500\n")
home_config.write_text("model: gpt-3.5-turbo\nmap-tokens: 1024\n")
named_config.write_text("model: gpt-4-1106-preview\nmap-tokens: 8192\n")

Expand All @@ -555,8 +565,6 @@ def test_yaml_config_file_loading(self):
# Test loading from current working directory
main(["--yes", "--exit"], input=DummyInput(), output=DummyOutput())
_, kwargs = MockCoder.call_args
print("kwargs:", kwargs) # Add this line for debugging
self.assertIn("main_model", kwargs, "main_model key not found in kwargs")
self.assertEqual(kwargs["main_model"].name, "gpt-4-32k")
self.assertEqual(kwargs["map_tokens"], 4096)

Expand All @@ -567,10 +575,24 @@ def test_yaml_config_file_loading(self):
self.assertEqual(kwargs["main_model"].name, "gpt-4")
self.assertEqual(kwargs["map_tokens"], 2048)

# Test loading from home directory
# Test loading from XDG (no dot)
git_config.unlink()
main(["--yes", "--exit"], input=DummyInput(), output=DummyOutput())
_, kwargs = MockCoder.call_args
self.assertEqual(kwargs["main_model"].name, "xdg-no-dot")
self.assertEqual(kwargs["map_tokens"], 600)

# Test loading from XDG (dot)
xdg_config_no_dot.unlink()
main(["--yes", "--exit"], input=DummyInput(), output=DummyOutput())
_, kwargs = MockCoder.call_args
self.assertEqual(kwargs["main_model"].name, "xdg-dot")
self.assertEqual(kwargs["map_tokens"], 500)

# Test loading from home directory
xdg_config_dot.unlink()
main(["--yes", "--exit"], input=DummyInput(), output=DummyOutput())
_, kwargs = MockCoder.call_args
self.assertEqual(kwargs["main_model"].name, "gpt-3.5-turbo")
self.assertEqual(kwargs["map_tokens"], 1024)

Expand Down