44import asyncio
55import argparse
66from dotenv import load_dotenv
7- from pydantic import ValidationError
87
98from schemas import IssuePayload
109from parser import extract_function_source , replace_function_source
@@ -21,15 +20,15 @@ def clean_agent_code(response_text: str, function_name: str) -> str:
2120 code = code [3 :].strip ()
2221 if code .endswith ("```" ):
2322 code = code [:- 3 ].strip ()
24-
23+
2524 def_prefix = f"def { function_name } "
2625 if def_prefix in code :
2726 idx = code .find (def_prefix )
2827 code = code [idx :]
29-
28+
3029 if code .endswith ("```" ):
3130 code = code [:- 3 ].strip ()
32-
31+
3332 return code
3433
3534async def run_pytest_suite (test_file : str ) -> tuple [int , str ]:
@@ -38,15 +37,15 @@ async def run_pytest_suite(test_file: str) -> tuple[int, str]:
3837 python_exe = os .path .join (".venv" , "Scripts" , "python.exe" )
3938 else :
4039 python_exe = os .path .join (".venv" , "bin" , "python" )
41-
40+
4241 if not os .path .exists (python_exe ):
4342 python_exe = sys .executable
4443
4544 cmd = [python_exe , "-m" , "pytest" , test_file ]
46-
45+
4746 env = os .environ .copy ()
4847 env ["PYTHONPATH" ] = os .getcwd ()
49-
48+
5049 process = await asyncio .create_subprocess_exec (
5150 * cmd ,
5251 stdout = asyncio .subprocess .PIPE ,
@@ -64,15 +63,15 @@ async def run_script_file(script_file: str) -> tuple[int, str]:
6463 python_exe = os .path .join (".venv" , "Scripts" , "python.exe" )
6564 else :
6665 python_exe = os .path .join (".venv" , "bin" , "python" )
67-
66+
6867 if not os .path .exists (python_exe ):
6968 python_exe = sys .executable
7069
7170 cmd = [python_exe , script_file ]
72-
71+
7372 env = os .environ .copy ()
7473 env ["PYTHONPATH" ] = os .getcwd ()
75-
74+
7675 process = await asyncio .create_subprocess_exec (
7776 * cmd ,
7877 stdout = asyncio .subprocess .PIPE ,
@@ -92,13 +91,13 @@ def parse_python_traceback(traceback_text: str) -> tuple[str, str, str]:
9291 lines = traceback_text .splitlines ()
9392 if not lines :
9493 raise ValueError ("Empty output, no traceback found." )
95-
94+
9695 error_message = lines [- 1 ].strip ()
9796 file_path = None
9897 function_name = None
99-
98+
10099 traceback_re = re .compile (r'^\s*File\s+"([^"]+)",\s+line\s+(\d+),\s+in\s+(\w+)' )
101-
100+
102101 for i in range (len (lines ) - 2 , - 1 , - 1 ):
103102 match = traceback_re .match (lines [i ])
104103 if match :
@@ -108,10 +107,10 @@ def parse_python_traceback(traceback_text: str) -> tuple[str, str, str]:
108107 file_path = match .group (1 ).replace ("\\ " , "/" )
109108 function_name = func
110109 break
111-
110+
112111 if not file_path or not function_name :
113112 raise ValueError ("Could not extract file path or function name from Python traceback." )
114-
113+
115114 return file_path , function_name , error_message
116115
117116def parse_pytest_failure (pytest_output : str ) -> tuple [str , str , str ]:
@@ -122,43 +121,43 @@ def parse_pytest_failure(pytest_output: str) -> tuple[str, str, str]:
122121 lines = pytest_output .splitlines ()
123122 if not lines :
124123 raise ValueError ("Empty output, no pytest logs found." )
125-
124+
126125 file_path = None
127126 function_name = None
128127 error_log = []
129-
128+
130129 file_line_err_re = re .compile (r"^([a-zA-Z0-9_\-\/\\\. ]+\.py):(\d+): (\w+)" )
131-
130+
132131 for i in range (len (lines ) - 1 , - 1 , - 1 ):
133132 line = lines [i ].strip ()
134133 match = file_line_err_re .match (line )
135134 if match :
136135 possible_file = match .group (1 ).replace ("\\ " , "/" )
137136 if "test_" in os .path .basename (possible_file ):
138137 continue
139-
138+
140139 file_path = possible_file
141-
140+
142141 for j in range (i , - 1 , - 1 ):
143142 if lines [j ].strip ().startswith ("E " ):
144143 error_log .append (lines [j ].strip ()[4 :])
145144 break
146145 if not error_log :
147146 error_log .append (line )
148-
147+
149148 func_def_re = re .compile (r"^\s*def\s+(\w+)\s*\(" )
150149 for j in range (i , - 1 , - 1 ):
151150 m = func_def_re .match (lines [j ])
152151 if m :
153152 function_name = m .group (1 )
154153 break
155-
154+
156155 if file_path and function_name :
157156 break
158-
157+
159158 if not file_path or not function_name :
160159 raise ValueError ("Could not auto-detect buggy file or function from pytest output." )
161-
160+
162161 return file_path , function_name , "\n " .join (error_log )
163162
164163async def heal_once (payload : IssuePayload ) -> None :
@@ -190,7 +189,7 @@ async def heal_once(payload: IssuePayload) -> None:
190189 raise ValueError (err_msg )
191190
192191 print (f"Original source extracted successfully:\n ---\n { func_source } \n ---" )
193-
192+
194193 prompt = f"""
195194Original function code:
196195```python
@@ -204,7 +203,7 @@ async def heal_once(payload: IssuePayload) -> None:
204203
205204Please correct the function to make the execution/tests pass. Remember, return ONLY the corrected function definition.
206205"""
207-
206+
208207 print ("Sending prompt to Gemini via google.antigravity.Agent..." )
209208 try :
210209 async with Agent (config ) as agent :
@@ -239,20 +238,20 @@ async def auto_heal_code(run_target: str, mode: str = "script", max_attempts: in
239238 print ("=" * 60 )
240239 print (f"Starting Auto-Heal Loop for target: '{ run_target } ' in mode: '{ mode } '" )
241240 print ("=" * 60 )
242-
241+
243242 for attempt in range (1 , max_attempts + 1 ):
244243 print (f"\n [Auto-Heal Attempt { attempt } /{ max_attempts } ] Running target..." )
245244 if mode == "pytest" :
246245 exit_code , output = await run_pytest_suite (run_target )
247246 else :
248247 exit_code , output = await run_script_file (run_target )
249-
248+
250249 if exit_code == 0 :
251250 print ("\n " + "*" * 60 )
252251 print ("SUCCESS: Target executed successfully with no errors!" )
253252 print ("*" * 60 )
254253 return True
255-
254+
256255 print ("Execution failed. Parsing traceback to auto-detect target..." )
257256 try :
258257 if mode == "pytest" :
@@ -263,8 +262,8 @@ async def auto_heal_code(run_target: str, mode: str = "script", max_attempts: in
263262 print (f"Failed to auto-detect bug: { e } " )
264263 print (f"Raw Target Output:\n { output } " )
265264 raise ValueError (f"Could not auto-detect buggy code: { e } " )
266-
267- print (f "Auto-Detected Bug Details:" )
265+
266+ print ("Auto-Detected Bug Details:" )
268267 print (f" File Path: { file_path } " )
269268 print (f" Function Name: { function_name } " )
270269 print (f" Error Message: { error_msg } " )
@@ -275,10 +274,10 @@ async def auto_heal_code(run_target: str, mode: str = "script", max_attempts: in
275274 )
276275 print (f"Triggering repair for function '{ function_name } '..." )
277276 await heal_once (payload )
278-
277+
279278 print ("Pausing for 8 seconds to stay under the API rate limit..." )
280279 await asyncio .sleep (8 )
281-
280+
282281 print ("\n " + "!" * 60 )
283282 err_msg = f"Could not heal all errors after { max_attempts } attempts. Last output:\n { output } "
284283 print (err_msg )
0 commit comments