Skip to content
Merged
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
2 changes: 1 addition & 1 deletion aider/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from packaging import version

__version__ = "0.88.37.dev"
__version__ = "0.88.38.dev"
safe_version = __version__

try:
Expand Down
5 changes: 5 additions & 0 deletions aider/args.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
22 changes: 17 additions & 5 deletions aider/coders/base_coder.py
Original file line number Diff line number Diff line change
Expand Up @@ -685,7 +685,10 @@ 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(
list(filter(lambda f: os.path.exists(f), self.abs_fnames)),
key=lambda fname: os.path.getmtime(fname),
)

for fname in sorted_fnames:
content = self.io.read_text(fname)
Expand Down Expand Up @@ -743,8 +746,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
Expand Down Expand Up @@ -840,7 +844,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(
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
for fname in sorted_fnames:
Expand Down Expand Up @@ -890,7 +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.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
Expand Down Expand Up @@ -3830,6 +3838,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
Expand Down
2 changes: 1 addition & 1 deletion aider/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
24 changes: 13 additions & 11 deletions aider/helpers/requests.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
Expand Down
3 changes: 3 additions & 0 deletions aider/tools/command.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions aider/tools/command_interactive.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading