# File 9: tools/code_tools.py with open(f"{base_dir}/tools/code_tools.py", "w") as f: f.write('''"""Code Development and Analysis Tools""" import os import re import json import subprocess import tempfile from typing import Dict, List, Optional from config import Config from utils import Logger, truncate class CodeTools: """Code linting, formatting, git operations, and project analysis""" def __init__(self): self.logger = Logger() def code_lint(self, path: str = ".", language: str = "python", fix: bool = False, rules: List[str] = None) -> Dict: """Lint code for errors and style issues""" try: full_path = os.path.join(Config.SANDBOX_PATH, path) if language == "python": tools_to_try = [ ("flake8", f"flake8 {'--fix' if fix else ''} {full_path}"), ("pylint", f"pylint {full_path}"), ] for tool_name, cmd in tools_to_try: result = subprocess.run( cmd, shell=True, capture_output=True, text=True, timeout=30 ) if result.returncode in [0, 1]: issues = [] for line in (result.stdout + result.stderr).split("\n"): if line.strip(): issues.append(line.strip()) return { "success": True, "tool": tool_name, "language": language, "issues": issues[:50], "total_issues": len(issues), "fixed": fix, "path": path } if os.path.isfile(full_path): with open(full_path, "r") as f: code = f.read() compile(code, full_path, "exec") return { "success": True, "tool": "builtin", "language": language, "issues": [], "total_issues": 0, "message": "Syntax OK" } elif language == "javascript": for tool, cmd in [("eslint", f"eslint {full_path}"), ("jshint", f"jshint {full_path}")]: result = subprocess.run( cmd, shell=True, capture_output=True, text=True, timeout=30 ) if result.returncode in [0, 1, 2]: return { "success": True, "tool": tool, "language": language, "output": (result.stdout + result.stderr)[:2000], "path": path } return { "success": True, "language": language, "message": "No linter available, basic syntax check passed", "path": path } except Exception as e: return {"success": False, "error": str(e)} def code_format(self, path: str = ".", language: str = "python", check_only: bool = False) -> Dict: """Format code automatically""" try: full_path = os.path.join(Config.SANDBOX_PATH, path) formatters = { "python": ("black", f"black {'--check' if check_only else ''} {full_path}"), "javascript": ("prettier", f"prettier {'--check' if check_only else '--write'} {full_path}"), "html": ("prettier", f"prettier --parser html {'--check' if check_only else '--write'} {full_path}"), "css": ("prettier", f"prettier --parser css {'--check' if check_only else '--write'} {full_path}"), } if language in formatters: tool, cmd = formatters[language] result = subprocess.run( cmd, shell=True, capture_output=True, text=True, timeout=30 ) return { "success": result.returncode in [0, 1], "tool": tool, "language": language, "formatted": not check_only and result.returncode == 0, "needs_formatting": result.returncode == 1, "output": (result.stdout + result.stderr)[:2000], "path": path } return {"success": False, "error": f"No formatter for {language}"} except Exception as e: return {"success": False, "error": str(e)} def git_command(self, action: str = "status", args: str = "", repo_path: str = ".") -> Dict: """Execute git commands""" try: full_repo = os.path.join(Config.SANDBOX_PATH, repo_path) os.makedirs(full_repo, exist_ok=True) if action == "init": cmd = f"git init {args}" elif action == "clone": cmd = f"git clone {args}" elif action == "commit": cmd = f'git commit {args}' else: cmd = f"git {action} {args}" result = subprocess.run( cmd, shell=True, capture_output=True, text=True, timeout=60, cwd=full_repo ) self.logger.log("git_command", {"action": action, "args": args, "repo": repo_path}) return { "success": result.returncode == 0, "action": action, "command": cmd, "stdout": result.stdout[:3000], "stderr": result.stderr[:1000], "repo": repo_path } except Exception as e: return {"success": False, "error": str(e)} def dependency_check(self, path: str = ".", manifest: str = "auto") -> Dict: """Check for outdated or vulnerable dependencies""" try: full_path = os.path.join(Config.SANDBOX_PATH, path) if manifest == "auto": for candidate in ["requirements.txt", "package.json", "pyproject.toml", "Pipfile"]: if os.path.exists(os.path.join(full_path, candidate)): manifest = candidate break if manifest == "requirements.txt": req_file = os.path.join(full_path, "requirements.txt") result = subprocess.run( f"pip list --outdated --format=json", shell=True, capture_output=True, text=True, timeout=30 ) outdated = [] try: packages = json.loads(result.stdout) with open(req_file, "r") as f: required = [line.split("==")[0].strip().lower() for line in f if line.strip() and not line.startswith("#")] for pkg in packages: if pkg["name"].lower() in required: outdated.append(pkg) except: pass return { "success": True, "manifest": manifest, "outdated": outdated, "total_outdated": len(outdated), "path": path } elif manifest == "package.json": pkg_file = os.path.join(full_path, "package.json") result = subprocess.run( "npm outdated --json", shell=True, capture_output=True, text=True, timeout=30, cwd=full_path ) outdated = {} try: outdated = json.loads(result.stdout) if result.stdout.strip() else {} except: pass return { "success": True, "manifest": manifest, "outdated": outdated, "total_outdated": len(outdated), "path": path } return { "success": True, "manifest": manifest, "message": "No manifest found or unsupported format", "path": path } except Exception as e: return {"success": False, "error": str(e)} def project_structure(self, path: str = ".", max_depth: int = 4, exclude: List[str] = None) -> Dict: """Analyze project structure""" try: full_path = os.path.join(Config.SANDBOX_PATH, path) if not exclude: exclude = ["node_modules", ".git", "__pycache__", ".venv", "venv", ".env", ".trash"] structure = [] file_count = 0 dir_count = 0 total_lines = 0 languages = {} for root, dirs, files in os.walk(full_path): depth = root[len(full_path):].count(os.sep) if depth > max_depth: del dirs[:] continue dirs[:] = [d for d in dirs if d not in exclude and not d.startswith(".")] rel_root = os.path.relpath(root, full_path) if rel_root == ".": rel_root = "" indent = " " * depth structure.append(f"{indent}[DIR] {os.path.basename(root) or path}") for file in sorted(files): if any(file.endswith(ext) for ext in [".pyc", ".pyo", ".class"]): continue file_path = os.path.join(root, file) file_indent = " " * (depth + 1) try: with open(file_path, "r", encoding="utf-8", errors="ignore") as f: lines = len(f.readlines()) total_lines += lines except: lines = 0 ext = os.path.splitext(file)[1] if ext: languages[ext] = languages.get(ext, 0) + 1 structure.append(f"{file_indent}{file} ({lines} lines)") file_count += 1 dir_count += 1 top_langs = sorted(languages.items(), key=lambda x: x[1], reverse=True)[:10] self.logger.log("project_structure", {"path": path, "files": file_count, "dirs": dir_count}) return { "success": True, "path": path, "structure": "\n".join(structure), "files": file_count, "directories": dir_count, "total_lines": total_lines, "top_languages": top_langs, "depth": max_depth } except Exception as e: return {"success": False, "error": str(e)} def code_search(self, path: str = ".", query: str = "", language: str = "", max_results: int = 50) -> Dict: """Search codebase with regex""" try: full_path = os.path.join(Config.SANDBOX_PATH, path) results = [] pattern = re.compile(query, re.IGNORECASE) for root, dirs, files in os.walk(full_path): dirs[:] = [d for d in dirs if not d.startswith(".") and d not in ["node_modules", "__pycache__", ".venv", "venv"]] for file in files: if language: ext_map = { "python": ".py", "javascript": ".js", "typescript": ".ts", "html": ".html", "css": ".css", "java": ".java", "cpp": ".cpp", "c": ".c", "go": ".go", "rust": ".rs" } if not file.endswith(ext_map.get(language, language)): continue file_path = os.path.join(root, file) rel_path = os.path.relpath(file_path, full_path) try: with open(file_path, "r", encoding="utf-8", errors="ignore") as f: lines = f.readlines() for i, line in enumerate(lines, 1): if pattern.search(line): results.append({ "file": rel_path, "line": i, "text": line.strip()[:150], "context": "".join(lines[max(0,i-2):min(len(lines),i+1)]).strip()[:300] }) if len(results) >= max_results: break except: continue if len(results) >= max_results: break return { "success": True, "query": query, "language": language, "results": results, "total": len(results), "path": path } except Exception as e: return {"success": False, "error": str(e)} ''') print("tools/code_tools.py done")