1111from pathlib import Path
1212from devguardian .utils .file_reader import list_project_files
1313
14+
1415def _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+
3537def 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+
9093def 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 "\n Format 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 )
0 commit comments