diff --git a/aider/main.py b/aider/main.py index ea344f0ba..6d4cb4435 100644 --- a/aider/main.py +++ b/aider/main.py @@ -49,8 +49,12 @@ def check_config_files_for_yes(config_files): for line in f: if line.strip().startswith("yes:"): print("Configuration error detected.") - print(f"The file {config_file} contains a line starting with 'yes:'") - print("Please replace 'yes:' with 'yes-always:' in this file.") + print( + f"The file {config_file} contains a line starting with 'yes:'" + ) + print( + "Please replace 'yes:' with 'yes-always:' in this file." + ) found = True except Exception: pass @@ -147,7 +151,9 @@ def setup_git(git_root, io): io.tool_warning('Update git name with: git config user.name "Your Name"') if not user_email: git_config.set_value("user", "email", "you@example.com") - io.tool_warning('Update git email with: git config user.email "you@example.com"') + io.tool_warning( + 'Update git email with: git config user.email "you@example.com"' + ) return repo.working_tree_dir @@ -188,7 +194,9 @@ def check_gitignore(git_root, io, ask=True): if ask: io.tool_output("You can skip this check with --no-gitignore") - if not io.confirm_ask(f"Add {', '.join(patterns_to_add)} to .gitignore (recommended)?"): + if not io.confirm_ask( + f"Add {', '.join(patterns_to_add)} to .gitignore (recommended)?" + ): return content += "\n".join(patterns_to_add) + "\n" @@ -391,7 +399,9 @@ def register_litellm_models(git_root, model_metadata_fname, io, verbose=False): model_metadata_files = [] # Add the resource file path - resource_metadata = importlib_resources.files("aider.resources").joinpath("model-metadata.json") + resource_metadata = importlib_resources.files("aider.resources").joinpath( + "model-metadata.json" + ) model_metadata_files.append(str(resource_metadata)) model_metadata_files += generate_search_path_list( @@ -399,7 +409,9 @@ def register_litellm_models(git_root, model_metadata_fname, io, verbose=False): ) try: - model_metadata_files_loaded = models.register_litellm_models(model_metadata_files) + model_metadata_files_loaded = models.register_litellm_models( + model_metadata_files + ) if len(model_metadata_files_loaded) > 0 and verbose: io.tool_output("Loaded model metadata from:") for model_metadata_file in model_metadata_files_loaded: @@ -438,7 +450,9 @@ def sanity_check_repo(repo, io): if bad_ver: io.tool_error("Aider only works with git repos with version number 1 or 2.") - io.tool_output("You may be able to convert your repo: git update-index --index-version=2") + io.tool_output( + "You may be able to convert your repo: git update-index --index-version=2" + ) io.tool_output("Or run aider --no-git to proceed without using git.") io.offer_url(urls.git_index_version, "Open documentation url for more info?") return False @@ -448,6 +462,19 @@ def sanity_check_repo(repo, io): return False +def get_config_file(prefix: Path | None = None): + conf_files = [ + prefix / name if prefix else Path(name) + for name in [".aider.conf.yml", ".aider.conf.yaml"] + if (prefix / name if prefix else Path(name)).exists() + ] + if len(conf_files) > 1: + print( + f"Warning: Both .aider.conf.yml and .aider.conf.yaml are present at {prefix}. Defaulting to .aider.conf.yml." + ) + return conf_files[0] if conf_files else None + + def main(argv=None, input=None, output=None, force_git_root=None, return_coder=False): report_uncaught_exceptions() @@ -461,26 +488,32 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F else: git_root = get_git_root() - conf_fname = Path(".aider.conf.yml") + conf_fname = get_config_file() default_config_files = [] - try: - default_config_files += [conf_fname.resolve()] # CWD - except OSError: - pass + if conf_fname: + try: + default_config_files += [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 + git_conf = get_config_file(Path(git_root)) # git root + if git_conf and git_conf not in default_config_files: + default_config_files.append(git_conf.resolve()) + home_conf = get_config_file(Path.home()) # home dir + if home_conf and home_conf not in default_config_files: + default_config_files.append(home_conf.resolve()) default_config_files = list(map(str, default_config_files)) parser = get_parser(default_config_files, git_root) try: args, unknown = parser.parse_known_args(argv) except AttributeError as e: - if all(word in str(e) for word in ["bool", "object", "has", "no", "attribute", "strip"]): + if all( + word in str(e) + for word in ["bool", "object", "has", "no", "attribute", "strip"] + ): if check_config_files_for_yes(default_config_files): return 1 raise e @@ -625,7 +658,9 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F ) os.environ["OPENAI_API_VERSION"] = args.openai_api_version if args.openai_api_type: - io.tool_warning("--openai-api-type is deprecated, use --set-env OPENAI_API_TYPE=") + io.tool_warning( + "--openai-api-type is deprecated, use --set-env OPENAI_API_TYPE=" + ) os.environ["OPENAI_API_TYPE"] = args.openai_api_type if args.openai_organization_id: io.tool_warning( @@ -633,7 +668,9 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F ) os.environ["OPENAI_ORGANIZATION"] = args.openai_organization_id - analytics = Analytics(logfile=args.analytics_log, permanently_disable=args.analytics_disable) + analytics = Analytics( + logfile=args.analytics_log, permanently_disable=args.analytics_disable + ) if args.analytics is not False: if analytics.need_to_ask(args.analytics): io.tool_output( @@ -749,7 +786,9 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F check_and_load_imports(io, is_first_run, verbose=args.verbose) register_models(git_root, args.model_settings_file, io, verbose=args.verbose) - register_litellm_models(git_root, args.model_metadata_file, io, verbose=args.verbose) + register_litellm_models( + git_root, args.model_metadata_file, io, verbose=args.verbose + ) if args.list_models: models.print_matching_models(io, args.list_models) @@ -778,7 +817,9 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F args.model = selected_model_name # Update args with the selected model # Check if an OpenRouter model was selected/specified but the key is missing - if args.model.startswith("openrouter/") and not os.environ.get("OPENROUTER_API_KEY"): + if args.model.startswith("openrouter/") and not os.environ.get( + "OPENROUTER_API_KEY" + ): io.tool_warning( f"The specified model '{args.model}' requires an OpenRouter API key, which was not" " found." @@ -832,14 +873,16 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F if args.reasoning_effort is not None: # Apply if check is disabled or model explicitly supports it if not args.check_model_accepts_settings or ( - main_model.accepts_settings and "reasoning_effort" in main_model.accepts_settings + main_model.accepts_settings + and "reasoning_effort" in main_model.accepts_settings ): main_model.set_reasoning_effort(args.reasoning_effort) if args.thinking_tokens is not None: # Apply if check is disabled or model explicitly supports it if not args.check_model_accepts_settings or ( - main_model.accepts_settings and "thinking_tokens" in main_model.accepts_settings + main_model.accepts_settings + and "thinking_tokens" in main_model.accepts_settings ): main_model.set_thinking_tokens(args.thinking_tokens) @@ -889,10 +932,14 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F io.tool_output("You can skip this check with --no-show-model-warnings") try: - io.offer_url(urls.model_warnings, "Open documentation url for more info?") + io.offer_url( + urls.model_warnings, "Open documentation url for more info?" + ) io.tool_output() except KeyboardInterrupt: - analytics.event("exit", reason="Keyboard interrupt during model warnings") + analytics.event( + "exit", reason="Keyboard interrupt during model warnings" + ) return 1 repo = None @@ -1110,7 +1157,9 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F io.tool_output(f"Git working dir: {git_root}") if args.stream and args.cache_prompts: - io.tool_warning("Cost estimates may be inaccurate when using streaming and caching.") + io.tool_warning( + "Cost estimates may be inaccurate when using streaming and caching." + ) if args.load: commands.cmd_load(args.load) @@ -1146,7 +1195,9 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F analytics.event("exit", reason="Exit flag set") return - analytics.event("cli session", main_model=main_model, edit_format=main_model.edit_format) + analytics.event( + "cli session", main_model=main_model, edit_format=main_model.edit_format + ) while True: try: @@ -1226,8 +1277,12 @@ def check_and_load_imports(io, is_first_run, verbose=False): load_slow_imports(swallow=False) except Exception as err: io.tool_error(str(err)) - io.tool_output("Error loading required imports. Did you install aider properly?") - io.offer_url(urls.install_properly, "Open documentation url for more info?") + io.tool_output( + "Error loading required imports. Did you install aider properly?" + ) + io.offer_url( + urls.install_properly, "Open documentation url for more info?" + ) sys.exit(1) if verbose: