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.13.dev"
__version__ = "0.88.14.dev"
safe_version = __version__

try:
Expand Down
5 changes: 5 additions & 0 deletions aider/coders/agent_coder.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
from aider.mcp.server import LocalServer
from aider.repo import ANY_GIT_ERROR

# Import tool modules for registry
# Import tool modules for registry
from aider.tools import (
command,
Expand All @@ -32,8 +33,10 @@
delete_lines,
extract_lines,
finished,
git_branch,
git_diff,
git_log,
git_remote,
git_show,
git_status,
grep,
Expand Down Expand Up @@ -159,8 +162,10 @@ def _build_tool_registry(self):
delete_lines,
extract_lines,
finished,
git_branch,
git_diff,
git_log,
git_remote,
git_show,
git_status,
grep,
Expand Down
18 changes: 7 additions & 11 deletions aider/coders/base_coder.py
Original file line number Diff line number Diff line change
Expand Up @@ -1097,8 +1097,7 @@ async def _run_linear(self, with_message=None, preproc=True):
self.show_announcements()
self.suppress_announcements_for_next_prompt = True

self.io.input_task = asyncio.create_task(self.get_input())
await asyncio.sleep(0)
await self.io.recreate_input()
await self.io.input_task
user_message = self.io.input_task.result()

Expand Down Expand Up @@ -1161,10 +1160,7 @@ async def _run_patched(self, with_message=None, preproc=True):
# Stop spinner before showing announcements or getting input
self.io.stop_spinner()
self.copy_context()
self.io.input_task = asyncio.create_task(self.get_input())

# Yield Control so input can actually get properly set up
await asyncio.sleep(0)
await self.io.recreate_input()

if self.user_message:
self.io.processing_task = asyncio.create_task(
Expand Down Expand Up @@ -1232,11 +1228,8 @@ async def _run_patched(self, with_message=None, preproc=True):
tasks.add(self.io.processing_task)

# We just did a confirmation so add a new input task
if (
not self.io.input_task
and self.io.get_confirmation_acknowledgement()
):
self.io.input_task = asyncio.create_task(self.get_input())
if self.io.get_confirmation_acknowledgement():
await self.io.recreate_input()
tasks.add(self.io.input_task)

done, pending = await asyncio.wait(
Expand Down Expand Up @@ -1331,6 +1324,9 @@ async def preproc_user_input(self, inp):
if not inp:
return

# Strip whitespace from beginning and end
inp = inp.strip()

if self.commands.is_command(inp):
if inp[0] in "!":
inp = f"/run {inp[1:]}"
Expand Down
34 changes: 18 additions & 16 deletions aider/io.py
Original file line number Diff line number Diff line change
Expand Up @@ -677,9 +677,6 @@ def rule(self):
print()

def interrupt_input(self):
if self.input_task and not self.input_task.done():
self.input_task.cancel()

if self.prompt_session and self.prompt_session.app:
# Store any partial input before interrupting
self.placeholder = self.prompt_session.app.current_buffer.text
Expand All @@ -695,6 +692,14 @@ def reject_outstanding_confirmations(self):
# This method is now a no-op since we removed the confirmation_future logic
pass

async def recreate_input(self, future=None):
if not self.input_task or self.input_task.done() or self.input_task.cancelled():
coder = self.coder() if self.coder else None

if coder:
self.input_task = asyncio.create_task(coder.get_input())
await asyncio.sleep(0)

async def get_input(
self,
root,
Expand Down Expand Up @@ -1023,11 +1028,15 @@ def ai_output(self, content):
hist = "\n" + content.strip() + "\n\n"
self.append_chat_history(hist)

async def offer_url(self, url, prompt="Open URL for more info?", allow_never=True):
async def offer_url(
self, url, prompt="Open URL for more info?", allow_never=True, acknowledge=False
):
"""Offer to open a URL in the browser, returns True if opened."""
if url in self.never_prompts:
return False
if await self.confirm_ask(prompt, subject=url, allow_never=allow_never):
if await self.confirm_ask(
prompt, subject=url, allow_never=allow_never, acknowledge=acknowledge
):
webbrowser.open(url)
return True
return False
Expand Down Expand Up @@ -1068,6 +1077,7 @@ async def _confirm_ask(
group=None,
group_response=None,
allow_never=False,
acknowledge=False,
):
self.num_user_asks += 1

Expand Down Expand Up @@ -1126,16 +1136,7 @@ async def _confirm_ask(
while True:
try:
if self.prompt_session:
if (
not self.input_task
or self.input_task.done()
or self.input_task.cancelled()
):
coder = self.coder() if self.coder else None

if coder:
self.input_task = asyncio.create_task(coder.get_input())
await asyncio.sleep(0)
await self.recreate_input()

if (
self.input_task
Expand Down Expand Up @@ -1168,7 +1169,8 @@ async def _confirm_ask(
good = any(valid_response.startswith(res) for valid_response in valid_responses)

if good:
self.set_confirmation_acknowledgement()
if not acknowledge:
self.set_confirmation_acknowledgement()
self.start_spinner(self.last_spinner_text)
break

Expand Down
31 changes: 18 additions & 13 deletions aider/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -971,19 +971,6 @@ def get_io(pretty):
analytics.event("exit", reason="Invalid lint command format")
return 1

if args.show_model_warnings:
problem = await models.sanity_check_models(io, main_model)
if problem:
analytics.event("model warning", main_model=main_model)
io.tool_output("You can skip this check with --no-show-model-warnings")

try:
await 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")
return 1

repo = None
if args.git:
try:
Expand Down Expand Up @@ -1113,6 +1100,24 @@ def get_io(pretty):
preserve_todo_list=args.preserve_todo_list,
linear_output=args.linear_output,
)

if args.show_model_warnings:
problem = await models.sanity_check_models(io, main_model)
if problem:
analytics.event("model warning", main_model=main_model)
io.tool_output("You can skip this check with --no-show-model-warnings")

try:
await io.offer_url(
urls.model_warnings,
"Open documentation url for more info?",
acknowledge=True,
)
io.tool_output()
except KeyboardInterrupt:
analytics.event("exit", reason="Keyboard interrupt during model warnings")
return 1

except UnknownEditFormat as err:
io.tool_error(str(err))
await io.offer_url(urls.edit_formats, "Open documentation about edit formats?")
Expand Down
16 changes: 16 additions & 0 deletions aider/sessions.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
from pathlib import Path
from typing import Dict, List, Optional

from aider import models


class SessionManager:
"""Manages chat session saving, listing, and loading."""
Expand Down Expand Up @@ -128,6 +130,9 @@ def _build_session_data(self, session_name) -> Dict:
"version": 1,
"session_name": session_name,
"model": self.coder.main_model.name,
"weak_model": self.coder.main_model.weak_model.name,
"editor_model": self.coder.main_model.editor_model.name,
"editor_edit_format": self.coder.main_model.editor_edit_format,
"edit_format": self.coder.edit_format,
"chat_history": {
"done_messages": self.coder.done_messages,
Expand Down Expand Up @@ -207,6 +212,17 @@ def _apply_session_data(self, session_data: Dict, session_file: Path) -> bool:
else:
self.io.tool_warning(f"File not found, skipping: {rel_fname}")

if session_data.get("model"):
self.coder.main_model = models.Model(
session_data.get("model", self.coder.args.model),
weak_model=session_data.get("weak_model", self.coder.args.weak_model),
editor_model=session_data.get("editor_model", self.coder.args.editor_model),
editor_edit_format=session_data.get(
"editor_edit_format", self.coder.args.editor_edit_format
),
verbose=self.coder.args.verbose,
)

# Load settings
settings = session_data.get("settings", {})
if "auto_commits" in settings:
Expand Down
4 changes: 4 additions & 0 deletions aider/tools/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@
delete_lines,
extract_lines,
finished,
git_branch,
git_diff,
git_log,
git_remote,
git_show,
git_status,
grep,
Expand Down Expand Up @@ -43,8 +45,10 @@
delete_lines,
extract_lines,
finished,
git_branch,
git_diff,
git_log,
git_remote,
git_show,
git_status,
grep,
Expand Down
7 changes: 1 addition & 6 deletions aider/tools/command.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
# Import necessary functions
import asyncio

from aider.run_cmd import run_cmd_subprocess

schema = {
Expand Down Expand Up @@ -46,10 +44,7 @@ async def _execute_command(coder, command_string):
)
)

if not coder.io.input_task or coder.io.input_task.done() or coder.io.input_task.cancelled():
coder.io.input_task = asyncio.create_task(coder.get_input())

await asyncio.sleep(0)
await coder.io.recreate_input()

if not confirmed:
# This happens if the user explicitly says 'no' this time.
Expand Down
5 changes: 1 addition & 4 deletions aider/tools/command_interactive.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,7 @@ async def _execute_command_interactive(coder, command_string):
coder.io.tool_output(" \n")
coder.io.tool_output(">>> Interactive command finished <<<")

if not coder.io.input_task or coder.io.input_task.done() or coder.io.input_task.cancelled():
coder.io.input_task = asyncio.create_task(coder.get_input())

await asyncio.sleep(0)
await coder.io.recreate_input()

# Format the output for the result message, include more content
output_content = combined_output or ""
Expand Down
Loading
Loading