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.9.dev"
__version__ = "0.88.10.dev"
safe_version = __version__

try:
Expand Down
6 changes: 6 additions & 0 deletions aider/args.py
Original file line number Diff line number Diff line change
Expand Up @@ -758,6 +758,12 @@ def get_parser(default_config_files, git_root):
),
default=False,
)
group.add_argument(
"--debug",
action="store_true",
help="Turn on verbose debugging (default: False)",
default=False,
)
##########
group = parser.add_argument_group("Voice settings")
group.add_argument(
Expand Down
54 changes: 32 additions & 22 deletions aider/coders/base_coder.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@
from aider.repo import ANY_GIT_ERROR, GitRepo
from aider.repomap import RepoMap
from aider.run_cmd import run_cmd
from aider.utils import format_content, format_messages, format_tokens, is_image_file
from aider.utils import format_tokens, is_image_file

from ..dump import dump # noqa: F401
from .chat_chunks import ChatChunks
Expand Down Expand Up @@ -870,6 +870,8 @@ def get_repo_map(self, force_refresh=False):
if not self.repo_map:
return

self.io.update_spinner("Updating repo map")

cur_msg_text = self.get_cur_message_text()
mentioned_fnames = self.get_file_mentions(cur_msg_text)
mentioned_idents = self.get_ident_mentions(cur_msg_text)
Expand All @@ -887,6 +889,10 @@ def _include_in_map(abs_path):
parts = Path(rel).parts
if ".meta" in parts or ".docs" in parts:
return False
if ".min." in parts[-1]:
return False
if self.repo.git_ignored_file(abs_path):
return False
return True

all_abs_files = {p for p in all_abs_files if _include_in_map(p)}
Expand Down Expand Up @@ -921,6 +927,7 @@ def _include_in_map(abs_path):
all_abs_files,
)

self.io.update_spinner(self.io.last_spinner_text)
return repo_content

def get_repo_messages(self):
Expand Down Expand Up @@ -970,7 +977,7 @@ def get_chat_files_messages(self):
files_content = self.gpt_prompts.files_content_prefix
files_content += self.get_files_content()
files_reply = self.gpt_prompts.files_content_assistant_reply
elif self.get_repo_map() and self.gpt_prompts.files_no_full_files_with_repo_map:
elif self.gpt_prompts.files_no_full_files_with_repo_map:
files_content = self.gpt_prompts.files_no_full_files_with_repo_map
files_reply = self.gpt_prompts.files_no_full_files_with_repo_map_reply
else:
Expand Down Expand Up @@ -1125,15 +1132,12 @@ async def _run_patched(self, with_message=None, preproc=True):
return self.partial_response_content

user_message = None
self.user_message = ""
await self.io.cancel_input_task()
await self.io.cancel_processing_task()

while True:
try:
if self.commands.cmd_running:
await asyncio.sleep(0.1)
continue

if (
not self.io.confirmation_in_progress
and not user_message
Expand All @@ -1156,6 +1160,19 @@ async def _run_patched(self, with_message=None, preproc=True):
# Yield Control so input can actually get properly set up
await asyncio.sleep(0)

if self.user_message:
self.io.processing_task = asyncio.create_task(
self._processing_logic(self.user_message, preproc)
)

self.user_message = ""
# Start spinner for processing task
self.io.start_spinner("Processing...")

if self.commands.cmd_running:
await asyncio.sleep(0.1)
continue

tasks = set()

if self.io.processing_task:
Expand Down Expand Up @@ -1189,8 +1206,9 @@ async def _run_patched(self, with_message=None, preproc=True):
self.io.stop_spinner()

try:
user_message = self.io.input_task.result()
await self.io.cancel_input_task()
if self.io.input_task:
user_message = self.io.input_task.result()
await self.io.cancel_input_task()

if self.commands.is_run_command(user_message):
self.commands.cmd_running = True
Expand Down Expand Up @@ -1237,12 +1255,7 @@ async def _run_patched(self, with_message=None, preproc=True):
self.io.stop_spinner()

if user_message and not self.io.acknowledge_confirmation():
self.io.processing_task = asyncio.create_task(
self._processing_logic(user_message, preproc)
)

# Start spinner for processing task
self.io.start_spinner("Processing...")
self.user_message = user_message

self.io.ring_bell()
user_message = None
Expand All @@ -1263,6 +1276,8 @@ async def _run_patched(self, with_message=None, preproc=True):
await self.io.cancel_processing_task()

async def _processing_logic(self, user_message, preproc):
await asyncio.sleep(0.1)

try:
self.compact_context_completed = False
await self.compact_context_if_needed()
Expand Down Expand Up @@ -1894,7 +1909,8 @@ async def send_message(self, inp):
dict(role="user", content=inp),
]

chunks = self.format_messages()
loop = asyncio.get_running_loop()
chunks = await loop.run_in_executor(None, self.format_messages)
messages = chunks.all_messages()

if not await self.check_tokens(messages):
Expand Down Expand Up @@ -2704,8 +2720,6 @@ async def send(self, messages, model=None, functions=None, tools=None):
self.partial_response_function_call = dict()
self.partial_response_tool_calls = []

self.io.log_llm_history("TO LLM", format_messages(messages))

completion = None

try:
Expand Down Expand Up @@ -2738,11 +2752,6 @@ async def send(self, messages, model=None, functions=None, tools=None):
self.keyboard_interrupt()
raise kbi
finally:
self.io.log_llm_history(
"LLM RESPONSE",
format_content("ASSISTANT", self.partial_response_content),
)

self.preprocess_response()

if self.partial_response_content:
Expand Down Expand Up @@ -3127,6 +3136,7 @@ def show_usage_report(self):
self.total_tokens_received += self.message_tokens_received

self.io.tool_output(self.usage_report)
self.io.rule()

prompt_tokens = self.message_tokens_sent
completion_tokens = self.message_tokens_received
Expand Down
13 changes: 6 additions & 7 deletions aider/coders/navigator_coder.py
Original file line number Diff line number Diff line change
Expand Up @@ -2298,25 +2298,24 @@ def get_directory_structure(self):
current = current[part]

# Function to recursively print the tree
def print_tree(node, prefix="- ", indent=" ", path=""):
def print_tree(node, prefix="- ", indent=" ", current_path=""):
lines = []
# First print all directories
dirs = sorted([k for k in node.keys() if k != "."])
for i, dir_name in enumerate(dirs):
full_path = f"{path}/{dir_name}" if path else dir_name
lines.append(f"{prefix}{full_path}/")
# Only print the current directory name, not the full path
lines.append(f"{prefix}{dir_name}/")
sub_lines = print_tree(
node[dir_name], prefix=prefix, indent=indent, path=full_path
node[dir_name], prefix=prefix, indent=indent, current_path=dir_name
)
for sub_line in sub_lines:
lines.append(f"{indent}{sub_line}")

# Then print all files
if "." in node:
for file_name in sorted(node["."]):
lines.append(
f"{prefix}{path}/{file_name}" if path else f"{prefix}{file_name}"
)
# Only print the current file name, not the full path
lines.append(f"{prefix}{file_name}")

return lines

Expand Down
3 changes: 3 additions & 0 deletions aider/io.py
Original file line number Diff line number Diff line change
Expand Up @@ -509,6 +509,9 @@ def start_spinner(self, text, update_last_text=True):
self.fallback_spinner = Spinner(text)
self.fallback_spinner.step()

def update_spinner(self, text):
self.spinner_text = text

def stop_spinner(self):
"""Stop the spinner."""
self.spinner_running = False
Expand Down
43 changes: 42 additions & 1 deletion aider/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -470,6 +470,37 @@ def expand_glob_patterns(patterns, root="."):
return expanded_files


PROJECT_ROOT = os.path.abspath(os.path.dirname(__file__))
log_file = None
file_blacklist = ["get_bottom_toolbar", "<genexpr>"]


def custom_tracer(frame, event, arg):
# Get the absolute path of the file where the code is executing
filename = os.path.abspath(frame.f_code.co_filename)

# --- THE FILTERING LOGIC ---
# Only proceed if the file path is INSIDE the project root
if not filename.startswith(PROJECT_ROOT):
return None # Returning None means no local trace function for this scope

if filename.endswith("repo.py"):
return None

# If it's your code, trace the call
if event == "call":
func_name = frame.f_code.co_name
line_no = frame.f_lineno

if func_name not in file_blacklist:
log_file.write(
f"-> CALL (My Code): {func_name}() in {os.path.basename(filename)}:{line_no}\n"
)

# Must return the trace function (or a local one) for subsequent events
return custom_tracer


def main(argv=None, input=None, output=None, force_git_root=None, return_coder=False):
return asyncio.run(main_async(argv, input, output, force_git_root, return_coder))

Expand Down Expand Up @@ -529,6 +560,11 @@ async def main_async(argv=None, input=None, output=None, force_git_root=None, re
# Parse again to include any arguments that might have been defined in .env
args = parser.parse_args(argv)

if args.debug:
global log_file
log_file = open(".aider-debug.log", "w", buffering=1)
sys.settrace(custom_tracer)

if args.shell_completions:
# Ensure parser.prog is set for shtab, though it should be by default
parser.prog = "aider"
Expand Down Expand Up @@ -1182,7 +1218,7 @@ def get_io(pretty):
io.tool_output()
try:
await coder.run(with_message=args.message)
except (SwitchCoder, KeyboardInterrupt):
except (SwitchCoder, KeyboardInterrupt, SystemExit):
pass
analytics.event("exit", reason="Completed --message")
return
Expand All @@ -1192,6 +1228,8 @@ def get_io(pretty):
message_from_file = io.read_text(args.message_file)
io.tool_output()
await coder.run(with_message=message_from_file)
except (SwitchCoder, KeyboardInterrupt, SystemExit):
pass
except FileNotFoundError:
io.tool_error(f"Message file not found: {args.message_file}")
analytics.event("exit", reason="Message file not found")
Expand Down Expand Up @@ -1235,6 +1273,9 @@ def get_io(pretty):

if switch.kwargs.get("show_announcements") is False:
coder.suppress_announcements_for_next_prompt = True
except SystemExit:
analytics.event("exit", reason="/exit command")
return


def is_first_run_of_new_version(io, verbose=False):
Expand Down
15 changes: 9 additions & 6 deletions aider/mcp/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,12 +51,15 @@ async def connect(self):
)

try:
stdio_transport = await self.exit_stack.enter_async_context(stdio_client(server_params))
read, write = stdio_transport
session = await self.exit_stack.enter_async_context(ClientSession(read, write))
await session.initialize()
self.session = session
return session
with open(".aider-mcp-errors.log", "w") as err_file:
stdio_transport = await self.exit_stack.enter_async_context(
stdio_client(server_params, errlog=err_file)
)
read, write = stdio_transport
session = await self.exit_stack.enter_async_context(ClientSession(read, write))
await session.initialize()
self.session = session
return session
except Exception as e:
logging.error(f"Error initializing server {self.name}: {e}")
await self.disconnect()
Expand Down
Loading
Loading