From 83109ed7ef6e3bcb0e09ac2d0d59e38395da0a0e Mon Sep 17 00:00:00 2001 From: Dustin Washington Date: Wed, 3 Dec 2025 20:41:54 -0500 Subject: [PATCH 1/6] Bump Version --- aider/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aider/__init__.py b/aider/__init__.py index 8ef542b15ff..fbbf12541b9 100644 --- a/aider/__init__.py +++ b/aider/__init__.py @@ -1,6 +1,6 @@ from packaging import version -__version__ = "0.88.37.dev" +__version__ = "0.88.38.dev" safe_version = __version__ try: From e47d9b8f498628ec81c7b7e0a012203de4f8b703 Mon Sep 17 00:00:00 2001 From: Dustin Washington Date: Wed, 3 Dec 2025 20:47:27 -0500 Subject: [PATCH 2/6] #219: Tool calls will be empty on model error which breaks secondary requests --- aider/helpers/requests.py | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/aider/helpers/requests.py b/aider/helpers/requests.py index e61c1728cde..230f89a1012 100644 --- a/aider/helpers/requests.py +++ b/aider/helpers/requests.py @@ -7,17 +7,19 @@ def thought_signature(model, messages): for msg in messages: if "tool_calls" in msg: tool_calls = msg["tool_calls"] - for call in tool_calls: - if not call: - continue - - # Check if thought signature is missing in extra_content.google.thought_signature - if "provider_specific_fields" not in call: - call["provider_specific_fields"] = {} - if "thought_signature" not in call["provider_specific_fields"]: - call["provider_specific_fields"][ - "thought_signature" - ] = "skip_thought_signature_validator" + + if tool_calls: + for call in tool_calls: + if not call: + continue + + # Check if thought signature is missing in extra_content.google.thought_signature + if "provider_specific_fields" not in call: + call["provider_specific_fields"] = {} + if "thought_signature" not in call["provider_specific_fields"]: + call["provider_specific_fields"][ + "thought_signature" + ] = "skip_thought_signature_validator" if "function_call" in msg: call = msg["function_call"] From 86f3a992bd39623990cd15a1bf42e8e739a659f1 Mon Sep 17 00:00:00 2001 From: Dustin Washington Date: Wed, 3 Dec 2025 21:20:50 -0500 Subject: [PATCH 3/6] #214, #217: Keep removed files from crashing system --- aider/coders/base_coder.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/aider/coders/base_coder.py b/aider/coders/base_coder.py index 52b5970d612..fd8320bdc25 100755 --- a/aider/coders/base_coder.py +++ b/aider/coders/base_coder.py @@ -685,7 +685,9 @@ def show_pretty(self): def get_abs_fnames_content(self): # Sort files by last modified time (earliest first, latest last) - sorted_fnames = sorted(self.abs_fnames, key=lambda fname: os.path.getmtime(fname)) + sorted_fnames = sorted( + self.abs_fnames, key=lambda fname: os.path.exists(fname) and os.path.getmtime(fname) + ) for fname in sorted_fnames: content = self.io.read_text(fname) @@ -743,8 +745,9 @@ def get_files_content(self, fnames=None): file_times = [] for fname in fnames: try: - mtime = os.path.getmtime(fname) - file_times.append((fname, mtime)) + if os.path.exists(fname): + mtime = os.path.getmtime(fname) + file_times.append((fname, mtime)) except OSError: # Skip files that can't be accessed continue @@ -840,7 +843,10 @@ def get_files_content(self, fnames=None): def get_read_only_files_content(self): prompt = "" # Sort read-only files by last modified time (earliest first, latest last) - sorted_fnames = sorted(self.abs_read_only_fnames, key=lambda fname: os.path.getmtime(fname)) + sorted_fnames = sorted( + self.abs_read_only_fnames, + key=lambda fname: os.path.exists(fname) and os.path.getmtime(fname), + ) # Handle regular read-only files for fname in sorted_fnames: @@ -890,7 +896,8 @@ def get_read_only_files_content(self): # Sort stub files by last modified time (earliest first, latest last) sorted_stub_fnames = sorted( - self.abs_read_only_stubs_fnames, key=lambda fname: os.path.getmtime(fname) + self.abs_read_only_stubs_fnames, + key=lambda fname: os.path.exists(fname) and os.path.getmtime(fname), ) # Handle stub files From be9b026ce211b20dfd1de51bc25e8aa6ff2e58fc Mon Sep 17 00:00:00 2001 From: Dustin Washington Date: Wed, 3 Dec 2025 22:02:08 -0500 Subject: [PATCH 4/6] #72: Add global command prefix --- aider/args.py | 5 +++++ aider/coders/base_coder.py | 4 ++++ aider/tools/command.py | 3 +++ aider/tools/command_interactive.py | 4 ++++ 4 files changed, 16 insertions(+) diff --git a/aider/args.py b/aider/args.py index 928a7328148..91137539a9c 100644 --- a/aider/args.py +++ b/aider/args.py @@ -965,6 +965,11 @@ def get_parser(default_config_files, git_root): " specified, a default command for your OS may be used." ), ) + group.add_argument( + "--command-prefix", + default=None, + help="Specify a command prefix for all commands (useful for sandboxing)", + ) group.add_argument( "--detect-urls", action=argparse.BooleanOptionalAction, diff --git a/aider/coders/base_coder.py b/aider/coders/base_coder.py index fd8320bdc25..8ab6030b957 100755 --- a/aider/coders/base_coder.py +++ b/aider/coders/base_coder.py @@ -3837,6 +3837,10 @@ async def handle_shell_commands(self, commands_str, group): if not command or command.startswith("#"): continue + if command and getattr(self.args, "command_prefix", None): + command_prefix = getattr(self.args, "command_prefix", None) + command = f"{command_prefix} {command}" + self.io.tool_output() self.io.tool_output(f"Running {command}") # Add the command to input history diff --git a/aider/tools/command.py b/aider/tools/command.py index 7c539c7f9fe..cfed959bc65 100644 --- a/aider/tools/command.py +++ b/aider/tools/command.py @@ -32,6 +32,9 @@ async def execute(cls, coder, command_string): # Ask for confirmation before executing. # allow_never=True enables the 'Always' option. # confirm_ask handles remembering the 'Always' choice based on the subject. + if command_string and getattr(coder.args, "command_prefix", None): + command_prefix = getattr(coder.args, "command_prefix", None) + command_string = f"{command_prefix} {command_string}" confirmed = ( True diff --git a/aider/tools/command_interactive.py b/aider/tools/command_interactive.py index 5d25a36f1af..1ccb621f7e7 100644 --- a/aider/tools/command_interactive.py +++ b/aider/tools/command_interactive.py @@ -31,6 +31,10 @@ async def execute(cls, coder, command_string): Execute an interactive shell command using run_cmd (which uses pexpect/PTY). """ try: + if command_string and getattr(coder.args, "command_prefix", None): + command_prefix = getattr(coder.args, "command_prefix", None) + command_string = f"{command_prefix} {command_string}" + confirmed = ( True if coder.skip_cli_confirmations From 7d87dad967f3f7f148447117ba4a175b47727039 Mon Sep 17 00:00:00 2001 From: Dustin Washington Date: Wed, 3 Dec 2025 22:49:02 -0500 Subject: [PATCH 5/6] #206: Must check absolute file path to match drops appropriately --- aider/commands.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aider/commands.py b/aider/commands.py index 0f3b63205d7..73a702334ed 100644 --- a/aider/commands.py +++ b/aider/commands.py @@ -1149,7 +1149,7 @@ async def cmd_drop(self, args=""): matched_files = [ self.coder.get_rel_fname(f) for f in self.coder.abs_fnames - if expanded_word in f + if self.coder.abs_root_path(expanded_word) in f ] if not matched_files: From f7af02d2adf743d7f591b3d5cf48c79f7e4179fd Mon Sep 17 00:00:00 2001 From: Dustin Washington Date: Wed, 3 Dec 2025 23:07:16 -0500 Subject: [PATCH 6/6] #214, #217: Don't yield non-existant files --- aider/coders/base_coder.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/aider/coders/base_coder.py b/aider/coders/base_coder.py index 8ab6030b957..c55d4ddb69b 100755 --- a/aider/coders/base_coder.py +++ b/aider/coders/base_coder.py @@ -686,7 +686,8 @@ def show_pretty(self): def get_abs_fnames_content(self): # Sort files by last modified time (earliest first, latest last) sorted_fnames = sorted( - self.abs_fnames, key=lambda fname: os.path.exists(fname) and os.path.getmtime(fname) + list(filter(lambda f: os.path.exists(f), self.abs_fnames)), + key=lambda fname: os.path.getmtime(fname), ) for fname in sorted_fnames: @@ -844,8 +845,8 @@ def get_read_only_files_content(self): prompt = "" # Sort read-only files by last modified time (earliest first, latest last) sorted_fnames = sorted( - self.abs_read_only_fnames, - key=lambda fname: os.path.exists(fname) and os.path.getmtime(fname), + list(filter(lambda f: os.path.exists(f), self.abs_read_only_fnames)), + key=lambda fname: os.path.getmtime(fname), ) # Handle regular read-only files @@ -896,8 +897,8 @@ def get_read_only_files_content(self): # Sort stub files by last modified time (earliest first, latest last) sorted_stub_fnames = sorted( - self.abs_read_only_stubs_fnames, - key=lambda fname: os.path.exists(fname) and os.path.getmtime(fname), + list(filter(lambda f: os.path.exists(f), self.abs_read_only_stubs_fnames)), + key=lambda fname: os.path.getmtime(fname), ) # Handle stub files