Skip to content

Commit 8ceb7b1

Browse files
committed
style: fix ruff formatting for v3 architect and self-healing tools
1 parent 8a53583 commit 8ceb7b1

2 files changed

Lines changed: 33 additions & 25 deletions

File tree

devguardian/tools/architect.py

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -11,54 +11,56 @@
1111
from pathlib import Path
1212
from devguardian.utils.file_reader import list_project_files
1313

14+
1415
def _extract_internal_imports(file_path: Path, project_root: Path) -> list[str]:
1516
"""Finds all internal imports (e.g., from devguardian.utils...) in a file."""
1617
try:
1718
content = file_path.read_text(encoding="utf-8", errors="replace")
1819
except:
1920
return []
20-
21+
2122
# Matches: from devguardian.utils... import ... OR import devguardian...
2223
patterns = [
2324
r"^\s*from\s+(devguardian[\w.]+)\s+import",
2425
r"^\s*import\s+(devguardian[\w.]+)",
2526
]
26-
27+
2728
internal_imports = []
2829
for pat in patterns:
2930
for match in re.findall(pat, content, re.MULTILINE):
3031
# Clean up: e.g. devguardian.utils.memory -> devguardian/utils/memory.py (abstractly)
3132
internal_imports.append(match)
32-
33+
3334
return list(set(internal_imports))
3435

36+
3537
def generate_architecture_map(project_path: str) -> str:
3638
"""
3739
Generates a Mermaid.js diagram representing the internal dependencies.
3840
"""
3941
root = Path(project_path)
4042
all_files = list_project_files(project_path, max_files=200)
41-
43+
4244
# Map nodes: file_path -> simple_name
4345
# Example: devguardian/tools/infra.py -> tools.infra
4446
nodes = {}
4547
edges = []
46-
48+
4749
for f in all_files:
4850
fp = Path(f)
4951
rel_path = fp.relative_to(root)
50-
52+
5153
# We only care about .py files for the dependency map
5254
if fp.suffix != ".py" or "__pycache__" in str(rel_path):
5355
continue
54-
56+
5557
# Clean name for node: devguardian/utils/security.py -> utils.security
5658
node_id = str(rel_path).replace(os.sep, ".").replace(".py", "")
5759
if node_id.startswith("devguardian."):
5860
node_id = node_id.replace("devguardian.", "", 1)
59-
61+
6062
nodes[node_id] = node_id
61-
63+
6264
# Find who this file imports
6365
imports = _extract_internal_imports(fp, root)
6466
for imp in imports:
@@ -68,7 +70,7 @@ def generate_architecture_map(project_path: str) -> str:
6870

6971
# Build Mermaid syntax
7072
mermaid = ["graph TD", " subgraph DevGuardian_Core"]
71-
73+
7274
# Deduplicate edges and filter to only existing internal nodes
7375
seen_edges = set()
7476
for source, target in edges:
@@ -77,23 +79,24 @@ def generate_architecture_map(project_path: str) -> str:
7779
if edge not in seen_edges:
7880
mermaid.append(edge)
7981
seen_edges.add(edge)
80-
82+
8183
mermaid.append(" end")
82-
84+
8385
# Add human-readable labels to make it look premium
8486
for nid in nodes:
8587
clean_id = nid.replace(".", "_")
86-
mermaid.append(f" {clean_id}[\"{nid}\"]")
88+
mermaid.append(f' {clean_id}["{nid}"]')
8789

8890
return "\n".join(mermaid)
8991

92+
9093
def generate_technical_docs(project_path: str) -> str:
9194
"""
9295
Generates a technical README snippet explaining the architecture.
9396
"""
9497
from devguardian.utils.gemini_client import ask_gemini
9598
from devguardian.utils.file_reader import build_project_context
96-
99+
97100
ctx = build_project_context(project_path)
98101
prompt = (
99102
f"{ctx}\n\n"
@@ -105,6 +108,6 @@ def generate_technical_docs(project_path: str) -> str:
105108
"4. The Data Flow (How information moves from tools to agents)\n"
106109
"\nFormat in professional markdown with icons."
107110
)
108-
111+
109112
system = "You are a lead software architect. Provide a high-density, professional architecture summary."
110113
return ask_gemini(prompt, system_instruction=system)

devguardian/tools/self_healing.py

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
from pathlib import Path
1515
from devguardian.agents.swarm import run_swarm
1616

17+
1718
def run_git_command(args: list[str], cwd: str) -> str:
1819
"""Helper to run git commands in a subprocess."""
1920
try:
@@ -29,13 +30,14 @@ def run_git_command(args: list[str], cwd: str) -> str:
2930
except Exception as e:
3031
return f"Git error: {e}"
3132

33+
3234
def detect_failure_context() -> dict:
3335
"""
3436
Tries to find which file recently failed by running ruff and pytest locally.
3537
In CI, it can also parse logs (if available).
3638
"""
3739
context = {"failed_files": [], "errors": ""}
38-
40+
3941
# Check linting first
4042
lint_res = subprocess.run(["uv", "run", "ruff", "check", "."], capture_output=True, text=True)
4143
if lint_res.returncode != 0:
@@ -53,43 +55,44 @@ def detect_failure_context() -> dict:
5355
# Extract failing test files from pytest output (simplified)
5456
test_files = re.findall(r"FAILED\s+([\w./\\]+)::", test_res.stdout)
5557
context["failed_files"].extend(test_files)
56-
58+
5759
return context
5860

61+
5962
async def repair_ci_failure(project_path: str):
6063
"""Entry point for the self-healing process."""
6164
print("🩹 Starting DevGuardian Self-Healing Process...")
62-
65+
6366
root = Path(project_path)
6467
failure = detect_failure_context()
65-
68+
6669
if not failure["failed_files"]:
6770
print("✅ No local failures detected. All checks passed!")
6871
return
69-
72+
7073
for target_file in set(failure["failed_files"]):
7174
target_abs = root / target_file
7275
if not target_abs.exists():
7376
continue
74-
77+
7578
print(f"🛠️ Attempting to repair: {target_file}")
76-
79+
7780
# Task for the swarm:
7881
task_desc = (
7982
f"Fix the following error in `{target_file}`:\n\n"
8083
f"```\n{failure['errors'][:2000]}\n```\n\n"
8184
"Ensure the file follows project style and passes architecture rules."
8285
)
83-
86+
8487
# Use our elite Swarm v3
8588
report = await run_swarm(task_desc, project_path)
86-
89+
8790
# Extract final code (best effort from the report markdown)
8891
if "## Final Code" in report:
8992
new_code = report.split("## Final Code")[1].split("## ")[0].strip()
9093
# Clean up potential markdown fences
9194
new_code = "\n".join([l for l in new_code.splitlines() if not l.strip().startswith("```")]).strip()
92-
95+
9396
# overwrite the file
9497
target_abs.write_text(new_code, encoding="utf-8")
9598
print(f"✅ Successfully updated {target_file}")
@@ -105,7 +108,9 @@ async def repair_ci_failure(project_path: str):
105108
else:
106109
print("💡 Local run complete. Review the changes in your working tree.")
107110

111+
108112
if __name__ == "__main__":
109113
import asyncio
110114
import re
115+
111116
asyncio.run(repair_ci_failure(os.getcwd()))

0 commit comments

Comments
 (0)