Skip to content

Commit 90e7e05

Browse files
authored
Merge pull request #114 from dwash96/v0.88.12
V0.88.12
2 parents 9f8defe + 66c850a commit 90e7e05

19 files changed

Lines changed: 527 additions & 324 deletions

aider/args.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -794,6 +794,18 @@ def get_parser(default_config_files, git_root):
794794
help="Preserve the existing .aider.todo.txt file on startup (default: False)",
795795
default=False,
796796
)
797+
group.add_argument(
798+
"--auto-save",
799+
action=argparse.BooleanOptionalAction,
800+
default=False,
801+
help="Enable/disable automatic saving of sessions as 'auto-save' (default: False)",
802+
)
803+
group.add_argument(
804+
"--auto-load",
805+
action=argparse.BooleanOptionalAction,
806+
default=False,
807+
help="Enable/disable automatic loading of 'auto-save' session on startup (default: False)",
808+
)
797809
group.add_argument(
798810
"--disable-playwright",
799811
action="store_true",

aider/coders/agent_coder.py

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ def __init__(self, *args, **kwargs):
140140
def _build_tool_registry(self):
141141
"""
142142
Build a registry of available tools with their normalized names and process_response functions.
143-
Handles agent configuration with whitelist/blacklist functionality.
143+
Handles agent configuration with includelist/excludelist functionality.
144144
145145
Returns:
146146
dict: Mapping of normalized tool names to tool modules
@@ -182,10 +182,14 @@ def _build_tool_registry(self):
182182

183183
# Process agent configuration if provided
184184
agent_config = self._get_agent_config()
185-
tools_whitelist = agent_config.get("tools_whitelist", [])
186-
tools_blacklist = agent_config.get("tools_blacklist", [])
185+
tools_includelist = agent_config.get(
186+
"tools_includelist", agent_config.get("tools_whitelist", [])
187+
)
188+
tools_excludelist = agent_config.get(
189+
"tools_excludelist", agent_config.get("tools_blacklist", [])
190+
)
187191

188-
# Always include essential tools regardless of whitelist/blacklist
192+
# Always include essential tools regardless of includelist/excludelist
189193
essential_tools = {"makeeditable", "replacetext", "view", "finished"}
190194
for module in tool_modules:
191195
if hasattr(module, "NORM_NAME") and hasattr(module, "process_response"):
@@ -194,16 +198,16 @@ def _build_tool_registry(self):
194198
# Check if tool should be included based on configuration
195199
should_include = True
196200

197-
# If whitelist is specified, only include tools in whitelist
198-
if tools_whitelist:
199-
should_include = tool_name in tools_whitelist
201+
# If includelist is specified, only include tools in includelist
202+
if tools_includelist:
203+
should_include = tool_name in tools_includelist
200204

201205
# Always include essential tools
202206
if tool_name in essential_tools:
203207
should_include = True
204208

205-
# Exclude tools in blacklist (unless they're essential)
206-
if tool_name in tools_blacklist and tool_name not in essential_tools:
209+
# Exclude tools in excludelist (unless they're essential)
210+
if tool_name in tools_excludelist and tool_name not in essential_tools:
207211
should_include = False
208212

209213
if should_include:
@@ -236,10 +240,10 @@ def _get_agent_config(self):
236240
# Set defaults for missing values
237241
if "large_file_token_threshold" not in config:
238242
config["large_file_token_threshold"] = 25000
239-
if "tools_whitelist" not in config:
240-
config["tools_whitelist"] = []
241-
if "tools_blacklist" not in config:
242-
config["tools_blacklist"] = []
243+
if "tools_includelist" not in config:
244+
config["tools_includelist"] = []
245+
if "tools_excludelist" not in config:
246+
config["tools_excludelist"] = []
243247

244248
# Apply configuration to instance
245249
self.large_file_token_threshold = config["large_file_token_threshold"]
@@ -255,12 +259,6 @@ def get_local_tool_schemas(self):
255259
if hasattr(tool_module, "schema"):
256260
schemas.append(tool_module.schema)
257261

258-
# Add git schemas from the tool registry
259-
git_tools = [git_diff, git_log, git_show, git_status]
260-
for git_tool in git_tools:
261-
if hasattr(git_tool, "schema"):
262-
schemas.append(git_tool.schema)
263-
264262
return schemas
265263

266264
async def initialize_mcp_tools(self):
@@ -935,6 +933,7 @@ async def process_tool_calls(self, tool_call_response):
935933
"""
936934
Track tool usage before calling the base implementation.
937935
"""
936+
self.auto_save_session()
938937

939938
if self.partial_response_tool_calls:
940939
for tool_call in self.partial_response_tool_calls:
@@ -976,6 +975,7 @@ async def reply_completed(self):
976975
) = await self._process_tool_commands(content)
977976

978977
if self.agent_finished:
978+
self.tool_usage_history = []
979979
return True
980980

981981
# Since we are no longer suppressing, the partial_response_content IS the final content.

aider/coders/base_coder.py

Lines changed: 45 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
from aider.repo import ANY_GIT_ERROR, GitRepo
6060
from aider.repomap import RepoMap
6161
from aider.run_cmd import run_cmd
62+
from aider.sessions import SessionManager
6263
from aider.utils import format_tokens, is_image_file
6364

6465
from ..dump import dump # noqa: F401
@@ -1121,6 +1122,8 @@ async def _run_linear(self, with_message=None, preproc=True):
11211122
self.keyboard_interrupt()
11221123
except (asyncio.CancelledError, IndexError):
11231124
pass
1125+
1126+
self.auto_save_session()
11241127
except EOFError:
11251128
return
11261129
finally:
@@ -1272,6 +1275,8 @@ async def _run_patched(self, with_message=None, preproc=True):
12721275
self.io.stop_spinner()
12731276

12741277
self.keyboard_interrupt()
1278+
1279+
self.auto_save_session()
12751280
except EOFError:
12761281
return
12771282
finally:
@@ -2240,12 +2245,20 @@ def _print_tool_call_info(self, server_tool_calls):
22402245

22412246
for server, tool_calls in server_tool_calls.items():
22422247
for tool_call in tool_calls:
2243-
self.io.tool_output(f"Tool Call: {tool_call.function.name}")
2248+
color_start = "[blue]" if self.pretty else ""
2249+
color_end = "[/blue]" if self.pretty else ""
22442250

2251+
self.io.tool_output(
2252+
f"{color_start}Tool Call:{color_end} {server.name}{tool_call.function.name}"
2253+
)
22452254
# Parse and format arguments as headers with values
22462255
if tool_call.function.arguments:
22472256
# Only do JSON unwrapping for tools containing "replace" in their name
2248-
if "replace" in tool_call.function.name.lower():
2257+
if (
2258+
"replace" in tool_call.function.name.lower()
2259+
or "insert" in tool_call.function.name.lower()
2260+
or "update" in tool_call.function.name.lower()
2261+
):
22492262
try:
22502263
args_dict = json.loads(tool_call.function.arguments)
22512264
first_key = True
@@ -2258,7 +2271,7 @@ def _print_tool_call_info(self, server_tool_calls):
22582271
if first_key:
22592272
self.io.tool_output("\n")
22602273
first_key = False
2261-
self.io.tool_output(f"{key}:")
2274+
self.io.tool_output(f"{color_start}{key}:{color_end}")
22622275
# Split the value by newlines and output each line separately
22632276
if isinstance(value, str):
22642277
for line in value.split("\n"):
@@ -2269,13 +2282,11 @@ def _print_tool_call_info(self, server_tool_calls):
22692282
except json.JSONDecodeError:
22702283
# If JSON parsing fails, show raw arguments
22712284
raw_args = tool_call.function.arguments
2272-
self.io.tool_output(f"Arguments: {raw_args}")
2285+
self.io.tool_output(f"{color_start}Arguments:{color_end} {raw_args}")
22732286
else:
22742287
# For non-replace tools, show raw arguments
22752288
raw_args = tool_call.function.arguments
2276-
self.io.tool_output(f"Arguments: {raw_args}")
2277-
2278-
self.io.tool_output(f"MCP Server: {server.name}")
2289+
self.io.tool_output(f"{color_start}Arguments:{color_end} {raw_args}")
22792290

22802291
if self.verbose:
22812292
self.io.tool_output(f"Tool ID: {tool_call.id}")
@@ -2295,7 +2306,15 @@ def _gather_server_tool_calls(self, tool_calls):
22952306
return None
22962307

22972308
server_tool_calls = {}
2309+
tool_id_set = set()
2310+
22982311
for tool_call in tool_calls:
2312+
# LLM APIs sometimes return duplicates and that's annoying part 3
2313+
if tool_call.get("id") in tool_id_set:
2314+
continue
2315+
2316+
tool_id_set.add(tool_call.get("id"))
2317+
22992318
# Check if this tool_call matches any MCP tool
23002319
for server_name, server_tools in self.mcp_tools:
23012320
for tool in server_tools:
@@ -2343,8 +2362,16 @@ async def _exec_server_tools(server, tool_calls_list):
23432362
try:
23442363
# Connect to the server once
23452364
session = await server.connect()
2365+
tool_id_set = set()
2366+
23462367
# Execute all tool calls for this server
23472368
for tool_call in tool_calls_list:
2369+
# LLM APIs sometimes return duplicates and that's annoying part 4
2370+
if tool_call.id in tool_id_set:
2371+
continue
2372+
2373+
tool_id_set.add(tool_call.id)
2374+
23482375
try:
23492376
# Arguments can be a stream of JSON objects.
23502377
# We need to parse them and run a tool call for each.
@@ -3491,6 +3518,17 @@ def apply_edits(self, edits):
34913518
def apply_edits_dry_run(self, edits):
34923519
return edits
34933520

3521+
def auto_save_session(self):
3522+
"""Automatically save the current session as 'auto-save'."""
3523+
if not getattr(self.args, "auto_save", False):
3524+
return
3525+
try:
3526+
session_manager = SessionManager(self, self.io)
3527+
session_manager.save_session("auto-save", False)
3528+
except Exception:
3529+
# Don't show errors for auto-save to avoid interrupting the user experience
3530+
pass
3531+
34943532
async def run_shell_commands(self):
34953533
if not self.suggest_shell_commands:
34963534
return ""

0 commit comments

Comments
 (0)