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.91.0.dev"
__version__ = "0.91.1.dev"
safe_version = __version__

try:
Expand Down
2 changes: 1 addition & 1 deletion aider/coders/agent_coder.py
Original file line number Diff line number Diff line change
Expand Up @@ -374,7 +374,7 @@ async def initialize_mcp_tools(self):
if not local_tools:
return

local_server_config = {"name": "local_tools"}
local_server_config = {"name": "Local"}
local_server = LocalServer(local_server_config)

if not self.mcp_servers:
Expand Down
86 changes: 35 additions & 51 deletions aider/coders/base_coder.py
Original file line number Diff line number Diff line change
Expand Up @@ -799,30 +799,21 @@ def get_files_content(self, fnames=None):
file_tokens = self.main_model.token_count(content)

if file_tokens > self.large_file_token_threshold:
# Truncate the file content
lines = content.splitlines()

# Keep the first and last parts of the file with a marker in between
keep_lines = (
self.large_file_token_threshold // 40
) # Rough estimate of tokens per line
first_chunk = lines[: keep_lines // 2]
last_chunk = lines[-(keep_lines // 2) :]

truncated_content = "\n".join(first_chunk)
truncated_content += (
f"\n\n... [File truncated due to size ({file_tokens} tokens). Use"
" /context-management to toggle truncation off] ...\n\n"
)
truncated_content += "\n".join(last_chunk)
# Instead of truncating, show the file's definitions/structure
file_stub = RepoMap.get_file_stub(fname, self.io)

# Add message about truncation
# Add message about showing definitions instead of full content
self.io.tool_output(
f"⚠️ '{relative_fname}' is very large ({file_tokens} tokens). "
"Use /context-management to toggle truncation off if needed."
)

file_prompt += truncated_content
# Add a message in the content itself so the model knows it's truncated
truncation_note = (
f"\n... [File content truncated due to size ({file_tokens} tokens)."
" Showing structure/definitions only.] ...\n\n"
)
file_prompt += truncation_note + file_stub
else:
file_prompt += content
else:
Expand Down Expand Up @@ -876,30 +867,21 @@ def get_read_only_files_content(self):
file_tokens = self.main_model.token_count(content)

if file_tokens > self.large_file_token_threshold:
# Truncate the file content
lines = content.splitlines()

# Keep the first and last parts of the file with a marker in between
keep_lines = (
self.large_file_token_threshold // 40
) # Rough estimate of tokens per line
first_chunk = lines[: keep_lines // 2]
last_chunk = lines[-(keep_lines // 2) :]

truncated_content = "\n".join(first_chunk)
truncated_content += (
f"\n\n... [File truncated due to size ({file_tokens} tokens). Use"
" /context-management to toggle truncation off] ...\n\n"
)
truncated_content += "\n".join(last_chunk)
# Instead of truncating, show the file's definitions/structure
file_stub = RepoMap.get_file_stub(fname, self.io)

# Add message about truncation
# Add message about showing definitions instead of full content
self.io.tool_output(
f"⚠️ '{relative_fname}' is very large ({file_tokens} tokens). "
"Use /context-management to toggle truncation off if needed."
)

prompt += truncated_content
# Add a message in the content itself so the model knows it's truncated
truncation_note = (
f"\n... [File content truncated due to size ({file_tokens} tokens)."
" Showing structure/definitions only.] ...\n\n"
)
prompt += truncation_note + file_stub
else:
prompt += content
else:
Expand Down Expand Up @@ -1229,8 +1211,9 @@ def init_before_message(self):
self.commit_before_message.append(self.repo.get_head_commit_sha())

async def run(self, with_message=None, preproc=True):
while self.io.confirmation_in_progress:
await asyncio.sleep(0.1) # Yield control and wait briefly
# Wait for confirmation to finish if in progress
if not self.io.confirmation_in_progress_event.is_set():
await self.io.confirmation_in_progress_event.wait()

if self.linear_output:
return await self._run_linear(with_message, preproc)
Expand All @@ -1253,8 +1236,9 @@ async def _run_linear(self, with_message=None, preproc=True):

while True:
try:
if self.commands.cmd_running:
await asyncio.sleep(0.1)
# Wait for commands to finish
if not self.commands.cmd_running_event.is_set():
await self.commands.cmd_running_event.wait()
continue

if not self.suppress_announcements_for_next_prompt:
Expand Down Expand Up @@ -1362,8 +1346,8 @@ async def input_task(self, preproc):
while self.input_running:
try:
# Wait for commands to finish
if self.commands.cmd_running:
await asyncio.sleep(0.1)
if not self.commands.cmd_running_event.is_set():
await self.commands.cmd_running_event.wait()
continue

# Wait for input task completion
Expand All @@ -1372,7 +1356,7 @@ async def input_task(self, preproc):
user_message = self.io.input_task.result()

# Defer to confirmation handler to fix Windows event loop race.
if self.io.confirmation_in_progress:
if not self.io.confirmation_in_progress_event.is_set():
pass
# Set user message for output task
elif not self.io.acknowledge_confirmation():
Expand All @@ -1389,7 +1373,7 @@ async def input_task(self, preproc):

# Check if we should show announcements
if (
not self.io.confirmation_in_progress
self.io.confirmation_in_progress_event.is_set()
and not self.user_message
and not coroutines.is_active(self.io.input_task)
and (not coroutines.is_active(self.io.output_task) or not self.io.placeholder)
Expand Down Expand Up @@ -1427,8 +1411,8 @@ async def output_task(self, preproc):
while self.output_running:
try:
# Wait for commands to finish
if self.commands.cmd_running:
await asyncio.sleep(0.1)
if not self.commands.cmd_running_event.is_set():
await self.commands.cmd_running_event.wait()
continue

# Check if we have a user message to process
Expand Down Expand Up @@ -1530,7 +1514,7 @@ async def preproc_user_input(self, inp):
inp = f"/run {inp[1:]}"

if self.commands.is_run_command(inp):
self.commands.cmd_running = True
self.commands.cmd_running_event.clear() # Command is running

return await self.commands.run(inp)

Expand Down Expand Up @@ -3038,8 +3022,8 @@ async def show_send_output_stream(self, completion):
print(chunk, file=f)

# Check if confirmation is in progress and wait if needed
while self.io.confirmation_in_progress:
await asyncio.sleep(0.1) # Yield control and wait briefly
if not self.io.confirmation_in_progress_event.is_set():
await self.io.confirmation_in_progress_event.wait()

if isinstance(chunk, str):
self.io.tool_error(chunk)
Expand Down Expand Up @@ -3830,7 +3814,7 @@ async def run_shell_commands(self):
accumulated_output = ""

try:
self.commands.cmd_running = True
self.commands.cmd_running_event.clear() # Command is running

for command in self.shell_commands:
if command in done:
Expand All @@ -3842,7 +3826,7 @@ async def run_shell_commands(self):

return accumulated_output
finally:
self.commands.cmd_running = False
self.commands.cmd_running_event.set() # Command finished

async def handle_shell_commands(self, commands_str, group):
commands = commands_str.strip().split(";")
Expand Down
26 changes: 24 additions & 2 deletions aider/commands.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import asyncio
import re
import sys
from pathlib import Path
Expand All @@ -21,6 +22,8 @@ def clone(self):
self.io,
None,
voice_language=self.voice_language,
voice_input_device=self.voice_input_device,
voice_format=self.voice_format,
verify_ssl=self.verify_ssl,
args=self.args,
parser=self.parser,
Expand Down Expand Up @@ -62,7 +65,8 @@ def __init__(

# Store the original read-only filenames provided via args.read
self.original_read_only_fnames = set(original_read_only_fnames or [])
self.cmd_running = False
self.cmd_running_event = asyncio.Event()
self.cmd_running_event.set() # Initially set, meaning no command is running

def is_command(self, inp):
return inp[0] in "/!"
Expand Down Expand Up @@ -108,13 +112,27 @@ async def do_run(self, cmd_name, args):
self.io.tool_output(f"Error: Command {cmd_name} not found.")
return

self.cmd_running_event.clear() # Command is running
try:
# Generate a spreadable kwargs dict with all relevant Commands attributes
kwargs = {
"original_read_only_fnames": self.original_read_only_fnames,
"voice_language": self.voice_language,
"voice_format": self.voice_format,
"voice_input_device": self.voice_input_device,
"verify_ssl": self.verify_ssl,
"parser": self.parser,
"verbose": self.verbose,
"editor": self.editor,
"system_args": self.args,
}

return await CommandRegistry.execute(
cmd_name,
self.io,
self.coder,
args,
original_read_only_fnames=self.original_read_only_fnames,
**kwargs,
)
except ANY_GIT_ERROR as err:
self.io.tool_error(f"Unable to complete {cmd_name}: {err}")
Expand All @@ -124,6 +142,10 @@ async def do_run(self, cmd_name, args):
except Exception as e:
self.io.tool_error(f"Error executing command {cmd_name}: {str(e)}")
return
finally:
self.cmd_running_event.set() # Command finished
if self.coder.tui and self.coder.tui():
self.coder.tui().refresh()

def matching_commands(self, inp):
words = inp.strip().split()
Expand Down
4 changes: 2 additions & 2 deletions aider/commands/reset.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ async def execute(cls, io, coder, args, **kwargs):
# Clear TUI output if available
if coder.tui and coder.tui():
coder.tui().action_clear_output()

io.tool_output("All files dropped and chat history cleared.")
else:
io.tool_output("All files dropped and chat history cleared.")

# Recalculate context block tokens after dropping all files
if hasattr(coder, "use_enhanced_context") and coder.use_enhanced_context:
Expand Down
2 changes: 1 addition & 1 deletion aider/commands/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ class SettingsCommand(BaseCommand):
async def execute(cls, io, coder, args, **kwargs):
# Get parser and args from kwargs or use defaults
parser = kwargs.get("parser")
cmd_args = kwargs.get("args")
cmd_args = kwargs.get("system_args")

if not parser or not cmd_args:
io.tool_error("Settings command requires parser and args context")
Expand Down
4 changes: 2 additions & 2 deletions aider/commands/utils/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,10 +104,10 @@ def format_command_result(io, command_name: str, success_message: str, error: Ex
Formatted result string
"""
if error:
io.tool_error(f"Error in {command_name}: {str(error)}")
io.tool_error(f"\nError in {command_name}: {str(error)}")
return f"Error: {str(error)}"
else:
io.tool_output(f"✅ {success_message}")
io.tool_output(f"\n✅ {success_message}")
return f"Successfully executed {command_name}."


Expand Down
16 changes: 10 additions & 6 deletions aider/io.py
Original file line number Diff line number Diff line change
Expand Up @@ -362,7 +362,8 @@ def __init__(
self.linear = False

# State tracking for confirmation input
self.confirmation_in_progress = False
self.confirmation_in_progress_event = asyncio.Event()
self.confirmation_in_progress_event.set() # Initially set, meaning no confirmation in progress
self.confirmation_acknowledgement = False
self.confirmation_input_active = False
self.saved_input_text = ""
Expand Down Expand Up @@ -939,7 +940,7 @@ def get_continuation(width, line_number, is_soft_wrap):
coder = self.get_coder()

if coder:
await coder.commands.cmd_exit(None)
await coder.commands.do_run("exit", "")
else:
raise SystemExit

Expand Down Expand Up @@ -1081,7 +1082,7 @@ def user_input(self, inp, log_only=True):

if (
len(inp) <= 1
or self.confirmation_in_progress
or not self.confirmation_in_progress_event.is_set()
or self.get_confirmation_acknowledgement()
):
return
Expand Down Expand Up @@ -1153,15 +1154,15 @@ async def confirm_ask(
*args,
**kwargs,
):
self.confirmation_in_progress = True
self.confirmation_in_progress_event.clear() # Confirmation is in progress

try:
return await asyncio.create_task(self._confirm_ask(*args, **kwargs))
except KeyboardInterrupt:
# Re-raise KeyboardInterrupt to allow it to propagate
raise
finally:
self.confirmation_in_progress = False
self.confirmation_in_progress_event.set() # Confirmation finished

async def _confirm_ask(
self,
Expand Down Expand Up @@ -1671,7 +1672,10 @@ def toggle_multiline_mode(self):
)

def append_chat_history(self, text, linebreak=False, blockquote=False, strip=True):
if self.confirmation_in_progress or self.get_confirmation_acknowledgement():
if (
not self.confirmation_in_progress_event.is_set()
or self.get_confirmation_acknowledgement()
):
return

if blockquote:
Expand Down
Loading
Loading