May / tools /code_tools.py
Mayank2027's picture
Create tools/code_tools.py
2bfb7e6 verified
Raw
History Blame Contribute Delete
14.6 kB
# 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")