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.14.dev"
__version__ = "0.88.15.dev"
safe_version = __version__

try:
Expand Down
17 changes: 13 additions & 4 deletions aider/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -414,7 +414,8 @@ async def cmd_lint(self, args="", fnames=None):
)

lint_coder.add_rel_fname(fname)
await lint_coder.run(errors)
await self.coder.io.recreate_input()
await lint_coder.run_one(errors, preproc=False)
lint_coder.abs_fnames = set()

if lint_coder and self.coder.repo.is_dirty() and self.coder.auto_commits:
Expand Down Expand Up @@ -536,7 +537,7 @@ def cmd_tokens(self, args):
tokens = self.coder.main_model.token_count_for_image(fname)
else:
# approximate
content = f"{relative_fname}\n{fence}\n" + content + "{fence}\n"
content = f"{relative_fname}\n{fence}\n" + content + f"{fence}\n"
tokens = self.coder.main_model.token_count(content)
file_res.append((tokens, f"{relative_fname}", "/drop to remove"))

Expand All @@ -556,7 +557,7 @@ def cmd_tokens(self, args):
content = self.io.read_text(fname)
if content is not None and not is_image_file(relative_fname):
# approximate
content = f"{relative_fname}\n{fence}\n" + content + "{fence}\n"
content = f"{relative_fname}\n{fence}\n" + content + f"{fence}\n"
tokens = self.coder.main_model.token_count(content)
file_res.append((tokens, f"{relative_fname} (read-only)", "/drop to remove"))

Expand Down Expand Up @@ -1215,9 +1216,17 @@ async def cmd_run(self, args, add_on_nonzero_exit=False):
finally:
self.cmd_running = False

def cmd_exit(self, args):
async def cmd_exit(self, args):
"Exit the application"
self.coder.event("exit", reason="/exit")

for server in self.coder.mcp_servers:
try:
await server.exit_stack.aclose()
except Exception:
pass

await asyncio.sleep(0)
sys.exit()

def cmd_quit(self, args):
Expand Down
20 changes: 19 additions & 1 deletion aider/io.py
Original file line number Diff line number Diff line change
Expand Up @@ -699,6 +699,8 @@ async def recreate_input(self, future=None):
if coder:
self.input_task = asyncio.create_task(coder.get_input())
await asyncio.sleep(0)
else:
self.input_task = asyncio.create_task(self.get_input(None, [], [], []))

async def get_input(
self,
Expand Down Expand Up @@ -1359,13 +1361,18 @@ def stream_output(self, text, final=False):
incomplete_line = ""
output = ""

lines = self.remove_consecutive_empty_strings(lines)
needs_new_line = False if len(lines) == 2 and lines[0] and not lines[-1] else True

if len(lines) > 1 or final:
# All lines except the last one are complete
complete_lines = lines[:-1] if not final else lines
incomplete_line = lines[-1] if not final else ""
last_index = len(complete_lines) - 1

for complete_line in complete_lines:
for index, complete_line in enumerate(complete_lines):
output += complete_line
output += "\n" if needs_new_line and index != last_index else ""
self._stream_line_count += 1

self._stream_buffer = incomplete_line
Expand All @@ -1380,6 +1387,17 @@ def stream_output(self, text, final=False):
self.console.print(Text.from_ansi(output) if self.has_ansi_codes(output) else output)
self.reset_streaming_response()

def remove_consecutive_empty_strings(self, string_list):
new_list = []
first_item = True

for item in string_list:
if first_item or item != "" or (new_list and new_list[-1] != ""):
first_item = False
new_list.append(item)

return new_list

def has_ansi_codes(self, s: str) -> bool:
"""Check if a string contains the ANSI escape character."""
return "\x1b" in s
Expand Down
56 changes: 28 additions & 28 deletions aider/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ async def setup_git(git_root, io):
)
return
elif cwd and await io.confirm_ask(
"No git repo found, create one to track aider's changes (recommended)?"
"No git repo found, create one to track aider's changes (recommended)?", acknowledge=True
):
git_root = str(cwd.resolve())
repo = await make_new_repo(git_root, io)
Expand Down Expand Up @@ -193,7 +193,8 @@ async def check_gitignore(git_root, io, ask=True):
if ask:
io.tool_output("You can skip this check with --no-gitignore")
if not await io.confirm_ask(
f"Add {', '.join(patterns_to_add)} to .gitignore (recommended)?"
f"Add {', '.join(patterns_to_add)} to .gitignore (recommended)?",
acknowledge=True,
):
return

Expand Down Expand Up @@ -717,28 +718,27 @@ def get_io(pretty):
posthog_host=args.analytics_posthog_host,
posthog_project_api_key=args.analytics_posthog_project_api_key,
)
if args.analytics is not False:
if analytics.need_to_ask(args.analytics):
io.tool_output(
"Aider respects your privacy and never collects your code, chat messages, keys or"
" personal info."
)
io.tool_output(f"For more info: {urls.analytics}")
disable = not await io.confirm_ask(
"Allow collection of anonymous analytics to help improve aider?"
)

analytics.asked_opt_in = True
if disable:
analytics.disable(permanently=True)
io.tool_output("Analytics have been permanently disabled.")

analytics.save_data()
io.tool_output()

# This is a no-op if the user has opted out
analytics.enable()

# if args.analytics is not False:
# if analytics.need_to_ask(args.analytics):
# io.tool_output(
# "Aider respects your privacy and never collects your code, chat messages, keys or"
# " personal info."
# )
# io.tool_output(f"For more info: {urls.analytics}")
# disable = not await io.confirm_ask(
# "Allow collection of anonymous analytics to help improve aider?"
# )
# analytics.asked_opt_in = True
# if disable:
# analytics.disable(permanently=True)
# io.tool_output("Analytics have been permanently disabled.")
# analytics.save_data()
# io.tool_output()
# # This is a no-op if the user has opted out
# analytics.enable()

analytics.disable(permanently=True)
analytics.event("launched")

if args.gui and not return_coder:
Expand Down Expand Up @@ -821,11 +821,6 @@ def get_io(pretty):
if args.check_update:
check_version(io, verbose=args.verbose)

if args.git:
git_root = await setup_git(git_root, io)
if args.gitignore:
await check_gitignore(git_root, io)

if args.verbose:
show = format_settings(parser, args)
io.tool_output(show)
Expand Down Expand Up @@ -1118,6 +1113,11 @@ def get_io(pretty):
analytics.event("exit", reason="Keyboard interrupt during model warnings")
return 1

if args.git:
git_root = await setup_git(git_root, io)
if args.gitignore:
await check_gitignore(git_root, io)

except UnknownEditFormat as err:
io.tool_error(str(err))
await io.offer_url(urls.edit_formats, "Open documentation about edit formats?")
Expand Down
1 change: 1 addition & 0 deletions aider/onboarding.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ async def offer_openrouter_oauth(io, analytics):
if await io.confirm_ask(
"Login to OpenRouter or create a free account?",
default="y",
acknowledge=True,
):
analytics.event("oauth_flow_initiated", provider="openrouter")
openrouter_key = start_openrouter_oauth_flow(io, analytics)
Expand Down
Loading