import ast import os from typing import Dict, List, Any, Optional, Callable class AutonomousCodeBuilder: """Manages file generation, incremental patching, syntax validation, and self-healing repair loops.""" def __init__(self, workspace_root: str = "."): self.workspace_root = workspace_root def generate_project(self, files: Dict[str, str]) -> List[str]: """Scaffolds directory structures and writes initial project file assets.""" created_files = [] for relative_path, content in files.items(): full_path = os.path.join(self.workspace_root, relative_path) os.makedirs(os.path.dirname(full_path), exist_ok=True) with open(full_path, "w", encoding="utf-8") as f: f.write(content) created_files.append(relative_path) return created_files def apply_patch(self, relative_path: str, search_text: str, replace_text: str) -> bool: """Applies targeted incremental search-and-replace patches to workspace files.""" full_path = os.path.join(self.workspace_root, relative_path) if not os.path.exists(full_path): return False with open(full_path, "r", encoding="utf-8") as f: content = f.read() if search_text not in content: return False new_content = content.replace(search_text, replace_text, 1) with open(full_path, "w", encoding="utf-8") as f: f.write(new_content) return True def validate_syntax(self, relative_path: str) -> Optional[str]: """Validates Python AST structure and returns syntax error message if invalid.""" full_path = os.path.join(self.workspace_root, relative_path) if not os.path.exists(full_path): return f"File not found: {relative_path}" with open(full_path, "r", encoding="utf-8") as f: content = f.read() try: ast.parse(content) return None except SyntaxError as e: return f"SyntaxError on line {e.lineno}: {e.msg}" def auto_repair( self, relative_path: str, repair_fn: Callable[[str, str], Optional[str]], max_attempts: int = 3 ) -> bool: """Executes automated repair loop until syntax validation passes or max_attempts is reached.""" error_msg = self.validate_syntax(relative_path) attempts = 0 while error_msg and attempts < max_attempts: attempts += 1 repaired_code = repair_fn(relative_path, error_msg) if repaired_code is not None: full_path = os.path.join(self.workspace_root, relative_path) with open(full_path, "w", encoding="utf-8") as f: f.write(repaired_code) error_msg = self.validate_syntax(relative_path) return error_msg is None