fix: Improve regex patterns for tool command parsing in AICodeAgentCog

This commit is contained in:
Slipstream 2025-05-31 16:03:32 -06:00
parent ea93b85fc5
commit 1d650ef9ae
Signed by: slipstream
GPG Key ID: 13E498CE010AC6FD

View File

@ -101,7 +101,11 @@ IMPORTANT: Do NOT wrap your tool calls in markdown code blocks (e.g., ```tool ..
**Workflow and Rules:**
- **Tool Preference:** For modifying existing files, ALWAYS prefer `ApplyDiff` if the changes are targeted. Use `WriteFile` for new files or if `ApplyDiff` is unsuitable or fails repeatedly.
- **Direct Operation:** You operate directly. No explicit user confirmation is needed for individual tool actions after the initial user prompt.
- **Programmatic Snapshots:** The system will automatically create a separate, programmatic Git snapshot of the project's state *before* you are allowed to make any file modifications using `WriteFile` or `ApplyDiff`. This is for safety and rollback, managed by the cog. You will be notified when a snapshot is created.
- **Programmatic Snapshots (System-Managed):**
- The system AUTOMATICALLY creates a Git snapshot of the project *before* executing `WriteFile` or `ApplyDiff` tools.
- You will be notified by a "ToolResponse: SystemNotification..." message when a snapshot has been successfully created, right before your file modification tool is about to be truly processed.
- You do NOT need to request or create snapshots yourself. Do NOT include snapshot steps in your `ExecuteCommand` calls for `git`.
- If the system fails to create a snapshot, it will inform you with a "ToolResponse: SystemError...". In such a case, your `WriteFile` or `ApplyDiff` operation will NOT proceed. You should then typically inform the user of this critical system failure. Do not repeatedly try the same file operation if snapshot creation consistently fails.
- **Git Workflow for Your Changes:** After you believe your coding task and all related file modifications are complete and correct, you MUST use the `ExecuteCommand` tool to perform the following Git operations in sequence:
1. `git add .` (to stage all your changes)
2. `git commit --author="AI Coding Agent Cog <me@slipstreamm.dev>" -m "AI Agent: <Your concise summary of changes>"` (You will generate the commit message part)
@ -371,15 +375,18 @@ class AICodeAgentCog(commands.Cog):
# --- TaskComplete ---
# Check for TaskComplete first as it's a terminal operation for the loop.
task_complete_match = re.search(r"^\s*TaskComplete:\s*message:\s*(.*)\s*$", ai_response_text, re.IGNORECASE | re.DOTALL)
task_complete_match = re.search(r"TaskComplete:\s*message:\s*(.*)", ai_response_text, re.IGNORECASE | re.DOTALL)
if task_complete_match:
tool_executed = True
# Ensure we capture the message correctly, even if it's multi-line, up to the end of the tool block or string.
# The (.*) should be greedy enough for simple cases.
completion_message = task_complete_match.group(1).strip()
return "TASK_COMPLETE", completion_message
# --- ReadFile ---
if not tool_executed:
read_file_match = re.search(r"^\s*ReadFile:\s*path:\s*(.+?)\s*$", ai_response_text, re.IGNORECASE | re.MULTILINE)
# Path can contain spaces, so .+? is good. Ensure it doesn't grab parts of other tools if text is messy.
read_file_match = re.search(r"ReadFile:\s*path:\s*(.+?)(?:\n|$)", ai_response_text, re.IGNORECASE | re.MULTILINE)
if read_file_match:
tool_executed = True
file_path = read_file_match.group(1).strip()
@ -388,7 +395,9 @@ class AICodeAgentCog(commands.Cog):
# --- WriteFile ---
if not tool_executed:
write_file_match = re.search(r"^\s*WriteFile:\s*path:\s*(.+?)\s*content:\s*\|?\s*(.*)\s*$", ai_response_text, re.IGNORECASE | re.DOTALL)
# Content can be multi-line and extensive. DOTALL is crucial.
# Capture path, then content separately.
write_file_match = re.search(r"WriteFile:\s*path:\s*(.+?)\s*content:\s*\|?\s*(.*)", ai_response_text, re.IGNORECASE | re.DOTALL)
if write_file_match:
tool_executed = True
file_path = write_file_match.group(1).strip()
@ -398,13 +407,16 @@ class AICodeAgentCog(commands.Cog):
if not snapshot_branch:
return "TOOL_OUTPUT", "ToolResponse: SystemError\n---\nFailed to create project snapshot. WriteFile operation aborted."
else:
await ctx.send(f"AICodeAgent: Created snapshot: {snapshot_branch} before writing to {file_path}")
# The notification about snapshot creation is now in the system prompt's description of the workflow.
# We can still send a message to Discord for owner visibility if desired.
await ctx.send(f"AICodeAgent: [Info] Created snapshot: {snapshot_branch} before writing to {file_path}")
tool_output = await self._execute_tool_write_file(file_path, content)
return "TOOL_OUTPUT", f"ToolResponse: WriteFile\nPath: {file_path}\n---\n{tool_output}"
# --- ApplyDiff ---
if not tool_executed:
apply_diff_match = re.search(r"^\s*ApplyDiff:\s*path:\s*(.+?)\s*diff_block:\s*\|?\s*(.*)\s*$", ai_response_text, re.IGNORECASE | re.DOTALL)
# Diff block can be multi-line.
apply_diff_match = re.search(r"ApplyDiff:\s*path:\s*(.+?)\s*diff_block:\s*\|?\s*(.*)", ai_response_text, re.IGNORECASE | re.DOTALL)
if apply_diff_match:
tool_executed = True
file_path = apply_diff_match.group(1).strip()
@ -414,13 +426,14 @@ class AICodeAgentCog(commands.Cog):
if not snapshot_branch:
return "TOOL_OUTPUT", "ToolResponse: SystemError\n---\nFailed to create project snapshot. ApplyDiff operation aborted."
else:
await ctx.send(f"AICodeAgent: Created snapshot: {snapshot_branch} before applying diff to {file_path}")
await ctx.send(f"AICodeAgent: [Info] Created snapshot: {snapshot_branch} before applying diff to {file_path}")
tool_output = await self._execute_tool_apply_diff(file_path, diff_block)
return "TOOL_OUTPUT", f"ToolResponse: ApplyDiff\nPath: {file_path}\n---\n{tool_output}"
# --- ExecuteCommand ---
if not tool_executed:
exec_command_match = re.search(r"^\s*ExecuteCommand:\s*command:\s*(.+?)\s*$", ai_response_text, re.IGNORECASE | re.DOTALL)
# Command can have spaces.
exec_command_match = re.search(r"ExecuteCommand:\s*command:\s*(.+?)(?:\n|$)", ai_response_text, re.IGNORECASE | re.DOTALL)
if exec_command_match:
tool_executed = True
command_str = exec_command_match.group(1).strip()
@ -429,18 +442,20 @@ class AICodeAgentCog(commands.Cog):
# --- ListFiles ---
if not tool_executed:
list_files_match = re.search(r"^\s*ListFiles:\s*path:\s*(.+?)(?:\s*recursive:\s*(true|false))?\s*$", ai_response_text, re.IGNORECASE | re.MULTILINE)
# Path and optional recursive flag.
list_files_match = re.search(r"ListFiles:\s*path:\s*(.+?)(?:\s*recursive:\s*(true|false))?(?:\n|$)", ai_response_text, re.IGNORECASE | re.MULTILINE)
if list_files_match:
tool_executed = True
file_path = list_files_match.group(1).strip()
recursive_str = list_files_match.group(2)
recursive_str = list_files_match.group(2) # This might be None
recursive = recursive_str.lower() == 'true' if recursive_str else False
tool_output = await self._execute_tool_list_files(file_path, recursive)
return "TOOL_OUTPUT", f"ToolResponse: ListFiles\nPath: {file_path}\nRecursive: {recursive}\n---\n{tool_output}"
# --- WebSearch ---
if not tool_executed:
web_search_match = re.search(r"^\s*WebSearch:\s*query:\s*(.+?)\s*$", ai_response_text, re.IGNORECASE | re.DOTALL)
# Query can have spaces.
web_search_match = re.search(r"WebSearch:\s*query:\s*(.+?)(?:\n|$)", ai_response_text, re.IGNORECASE | re.DOTALL)
if web_search_match:
tool_executed = True
query_str = web_search_match.group(1).strip()