Spaces:
Sleeping
Sleeping
Upload folder using huggingface_hub
Browse files- LICENCE +21 -0
- __pycache__/config.cpython-311.pyc +0 -0
- agents/__pycache__/agents.cpython-311.pyc +0 -0
- agents/__pycache__/code_overview_agents.cpython-311.pyc +0 -0
- agents/__pycache__/code_structure_agents.cpython-311.pyc +0 -0
- agents/__pycache__/code_visualization_agents.cpython-311.pyc +0 -0
- agents/__pycache__/directory_manager.cpython-311.pyc +0 -0
- agents/__pycache__/manager_agent.cpython-311.pyc +0 -0
- agents/code_overview_agents.py +108 -0
- agents/code_structure_agents.py +111 -0
- agents/code_visualization_agents.py +32 -0
- agents/directory_agents.py +94 -0
- agents/manager_agent.py +40 -0
- app.py +121 -0
- config.py +99 -0
- requirements.txt +11 -3
- run.py +2 -0
- ui/__pycache__/analysis_page.cpython-311.pyc +0 -0
- ui/__pycache__/code_container.cpython-311.pyc +0 -0
- ui/__pycache__/introductory_page.cpython-311.pyc +0 -0
- ui/analysis_page.py +57 -0
- ui/introductory_page.py +121 -0
- ui/styles.css +146 -0
- utils/__init__.py +0 -0
- utils/__pycache__/__init__.cpython-311.pyc +0 -0
- utils/__pycache__/utils.cpython-311.pyc +0 -0
- utils/utils.py +218 -0
LICENCE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
MIT License
|
| 2 |
+
|
| 3 |
+
Copyright (c) 2025 Eslam Tarek
|
| 4 |
+
|
| 5 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
| 6 |
+
of this software and associated documentation files (the "Software"), to deal
|
| 7 |
+
in the Software without restriction, including without limitation the rights
|
| 8 |
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
| 9 |
+
copies of the Software, and to permit persons to whom the Software is
|
| 10 |
+
furnished to do so, subject to the following conditions:
|
| 11 |
+
|
| 12 |
+
The above copyright notice and this permission notice shall be included in all
|
| 13 |
+
copies or substantial portions of the Software.
|
| 14 |
+
|
| 15 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
| 16 |
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
| 17 |
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
| 18 |
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
| 19 |
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
| 20 |
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
| 21 |
+
SOFTWARE.
|
__pycache__/config.cpython-311.pyc
ADDED
|
Binary file (3.41 kB). View file
|
|
|
agents/__pycache__/agents.cpython-311.pyc
ADDED
|
Binary file (850 Bytes). View file
|
|
|
agents/__pycache__/code_overview_agents.cpython-311.pyc
ADDED
|
Binary file (5.04 kB). View file
|
|
|
agents/__pycache__/code_structure_agents.cpython-311.pyc
ADDED
|
Binary file (6.13 kB). View file
|
|
|
agents/__pycache__/code_visualization_agents.cpython-311.pyc
ADDED
|
Binary file (1.93 kB). View file
|
|
|
agents/__pycache__/directory_manager.cpython-311.pyc
ADDED
|
Binary file (2.87 kB). View file
|
|
|
agents/__pycache__/manager_agent.cpython-311.pyc
ADDED
|
Binary file (3.59 kB). View file
|
|
|
agents/code_overview_agents.py
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
from autogen import ConversableAgent, UserProxyAgent, AssistantAgent
|
| 5 |
+
from typing import Dict, Any
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
class CodeSummarizerAgent:
|
| 9 |
+
name: str = 'CodeSummarizerAgent'
|
| 10 |
+
system_message: str = """
|
| 11 |
+
|
| 12 |
+
You are an expert software engineer and technical writer.
|
| 13 |
+
|
| 14 |
+
Your job is to analyze the code provided to you and produce:
|
| 15 |
+
1. A high-level summary of what the code is doing.
|
| 16 |
+
2. An explanation of any major classes, functions, and logic flow.
|
| 17 |
+
3. The purpose of the code (what task it's solving).
|
| 18 |
+
4. Any helpful notes about the code structure or style, if relevant.
|
| 19 |
+
|
| 20 |
+
Your goal is to make the code understandable to a junior developer or a non-expert.
|
| 21 |
+
Keep the explanation concise, clear, and helpful.
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
"""
|
| 25 |
+
code_execution_config: bool = False
|
| 26 |
+
human_input_mode: str = "NEVER"
|
| 27 |
+
|
| 28 |
+
def to_dict(self):
|
| 29 |
+
return {
|
| 30 |
+
"name": self.name,
|
| 31 |
+
"system_message": self.system_message,
|
| 32 |
+
"code_execution_config": self.code_execution_config,
|
| 33 |
+
"human_input_mode": self.human_input_mode
|
| 34 |
+
}
|
| 35 |
+
|
| 36 |
+
def make_agent(self, llm_config:Dict[str, Any]) -> ConversableAgent:
|
| 37 |
+
return ConversableAgent(**self.to_dict(), llm_config=llm_config)
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
|
| 41 |
+
|
| 42 |
+
class CodeSmellDetectorAgent:
|
| 43 |
+
name: str = 'CodeSmellDetectorAgent'
|
| 44 |
+
system_message: str = """
|
| 45 |
+
You are an expert software engineer and code quality reviewer.
|
| 46 |
+
|
| 47 |
+
Your task is to detect code smells in the provided source code.
|
| 48 |
+
You should:
|
| 49 |
+
1. Identify common code smells such as:
|
| 50 |
+
- Long methods/functions
|
| 51 |
+
- God objects (classes doing too much)
|
| 52 |
+
- Deep nesting
|
| 53 |
+
- Duplicate code
|
| 54 |
+
- Poor naming conventions
|
| 55 |
+
- Too many parameters
|
| 56 |
+
- Magic numbers or hardcoded values
|
| 57 |
+
- Violations of the Single Responsibility Principle
|
| 58 |
+
2. Explain why each code smell is problematic.
|
| 59 |
+
3. Suggest specific refactoring strategies to improve the code quality.
|
| 60 |
+
4. Optionally, evaluate how severe the smell is (low, medium, high impact).
|
| 61 |
+
|
| 62 |
+
Present your findings clearly and helpfully as if you're mentoring a junior developer.
|
| 63 |
+
Do not rewrite the code unless asked to. Focus on code critique and reasoning.
|
| 64 |
+
"""
|
| 65 |
+
code_execution_config: bool = False
|
| 66 |
+
human_input_mode: str = "NEVER"
|
| 67 |
+
|
| 68 |
+
def to_dict(self):
|
| 69 |
+
return {
|
| 70 |
+
"name": self.name,
|
| 71 |
+
"system_message": self.system_message,
|
| 72 |
+
"code_execution_config": self.code_execution_config,
|
| 73 |
+
"human_input_mode": self.human_input_mode
|
| 74 |
+
}
|
| 75 |
+
|
| 76 |
+
def make_agent(self, llm_config: Dict[str, Any]) -> ConversableAgent:
|
| 77 |
+
return ConversableAgent(**self.to_dict(), llm_config=llm_config)
|
| 78 |
+
|
| 79 |
+
|
| 80 |
+
|
| 81 |
+
|
| 82 |
+
class ComplexityAnalyzerAgent:
|
| 83 |
+
name: str = "ComplexityAnalyzerAgent"
|
| 84 |
+
system_message: str = """
|
| 85 |
+
You are an expert software performance engineer and static code analyzer.
|
| 86 |
+
|
| 87 |
+
Your job is to analyze the complexity of the given code file. Focus on:
|
| 88 |
+
1. Cyclomatic complexity of each function.
|
| 89 |
+
2. Deeply nested conditionals or loops.
|
| 90 |
+
3. Long functions or classes that might be doing too much.
|
| 91 |
+
4. Recommendations for breaking down complex parts.
|
| 92 |
+
|
| 93 |
+
Provide a complexity score or risk level for each main function and class.
|
| 94 |
+
Your output should be formatted and readable for engineers reviewing the code quality.
|
| 95 |
+
"""
|
| 96 |
+
code_execution_config: bool = False
|
| 97 |
+
human_input_mode: str = "NEVER"
|
| 98 |
+
|
| 99 |
+
def to_dict(self):
|
| 100 |
+
return {
|
| 101 |
+
"name": self.name,
|
| 102 |
+
"system_message": self.system_message,
|
| 103 |
+
"code_execution_config": self.code_execution_config,
|
| 104 |
+
"human_input_mode": self.human_input_mode
|
| 105 |
+
}
|
| 106 |
+
|
| 107 |
+
def make_agent(self, llm_config: Dict[str, Any]) -> ConversableAgent :
|
| 108 |
+
return ConversableAgent(**self.to_dict(), llm_config=llm_config)
|
agents/code_structure_agents.py
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
from typing import Dict, Any
|
| 3 |
+
from autogen import ConversableAgent
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
class DesignPatternDetectorAgent:
|
| 10 |
+
name: str = 'DesignPatternDetectorAgent'
|
| 11 |
+
system_message: str = """
|
| 12 |
+
You are an expert software architect and design pattern specialist.
|
| 13 |
+
|
| 14 |
+
Your job is to analyze the code provided to you and determine whether any known software design patterns are applied.
|
| 15 |
+
You should:
|
| 16 |
+
|
| 17 |
+
1. Identify common design patterns used in the code (e.g., Singleton, Factory, Observer, Strategy, Adapter, etc.).
|
| 18 |
+
2. Explain where and how each pattern is used (e.g., "This class behaves like a Singleton because...").
|
| 19 |
+
3. Evaluate whether the pattern is applied correctly and effectively.
|
| 20 |
+
4. Suggest a design pattern if none are found but the situation could benefit from one.
|
| 21 |
+
5. Be specific and concise. Include references to classes/functions when possible.
|
| 22 |
+
|
| 23 |
+
Your goal is to help developers understand the architectural design decisions in the codebase and encourage good pattern usage.
|
| 24 |
+
"""
|
| 25 |
+
code_execution_config: bool = False
|
| 26 |
+
human_input_mode: str = "NEVER"
|
| 27 |
+
|
| 28 |
+
def to_dict(self):
|
| 29 |
+
return {
|
| 30 |
+
"name": self.name,
|
| 31 |
+
"system_message": self.system_message,
|
| 32 |
+
"code_execution_config": self.code_execution_config,
|
| 33 |
+
"human_input_mode": self.human_input_mode
|
| 34 |
+
}
|
| 35 |
+
|
| 36 |
+
def make_agent(self, llm_config: Dict[str, Any]) -> ConversableAgent:
|
| 37 |
+
return ConversableAgent(**self.to_dict(), llm_config=llm_config)
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
|
| 41 |
+
class DesignPatternRecommenderAgent:
|
| 42 |
+
name: str = 'DesignPatternRecommenderAgent'
|
| 43 |
+
system_message: str = """
|
| 44 |
+
You are a senior software architect and design pattern expert.
|
| 45 |
+
|
| 46 |
+
Your task is to analyze the provided source code and recommend suitable design patterns that could be applied to improve its structure, flexibility, and maintainability.
|
| 47 |
+
|
| 48 |
+
You should:
|
| 49 |
+
1. Detect parts of the code that would benefit from design patterns (e.g., duplicated logic, tight coupling, hard-coded logic, lack of abstraction).
|
| 50 |
+
2. Recommend one or more appropriate design patterns (e.g., Strategy, Factory, Adapter, Singleton, Observer, etc.).
|
| 51 |
+
3. Explain **why** each recommended pattern fits the situation, including the benefits it provides.
|
| 52 |
+
4. Mention **how** the developer can refactor or restructure the code to apply the suggested pattern (briefly).
|
| 53 |
+
5. Keep your explanation clear and actionable for an intermediate-level developer.
|
| 54 |
+
|
| 55 |
+
Your goal is to guide developers toward more scalable and clean software design using well-known object-oriented design patterns.
|
| 56 |
+
"""
|
| 57 |
+
code_execution_config: bool = False
|
| 58 |
+
human_input_mode: str = "NEVER"
|
| 59 |
+
|
| 60 |
+
def to_dict(self):
|
| 61 |
+
return {
|
| 62 |
+
"name": self.name,
|
| 63 |
+
"system_message": self.system_message,
|
| 64 |
+
"code_execution_config": self.code_execution_config,
|
| 65 |
+
"human_input_mode": self.human_input_mode
|
| 66 |
+
}
|
| 67 |
+
|
| 68 |
+
def make_agent(self, llm_config: Dict[str, Any]) -> ConversableAgent:
|
| 69 |
+
return ConversableAgent(**self.to_dict(), llm_config=llm_config)
|
| 70 |
+
|
| 71 |
+
|
| 72 |
+
|
| 73 |
+
|
| 74 |
+
class SOLIDPrinciplesManagerAgent:
|
| 75 |
+
name: str = 'SOLIDPrinciplesManagerAgent'
|
| 76 |
+
system_message: str = """
|
| 77 |
+
You are a senior software architect and expert in object-oriented design and clean code principles.
|
| 78 |
+
|
| 79 |
+
Your job is to analyze the provided code and manage its compliance with the SOLID principles. The SOLID principles are:
|
| 80 |
+
|
| 81 |
+
1. **Single Responsibility Principle (SRP)** – each class should have one responsibility.
|
| 82 |
+
2. **Open/Closed Principle (OCP)** – software entities should be open for extension but closed for modification.
|
| 83 |
+
3. **Liskov Substitution Principle (LSP)** – subclasses should be replaceable for their base classes without altering functionality.
|
| 84 |
+
4. **Interface Segregation Principle (ISP)** – interfaces should be specific to client needs.
|
| 85 |
+
5. **Dependency Inversion Principle (DIP)** – high-level modules should not depend on low-level modules; both should depend on abstractions.
|
| 86 |
+
|
| 87 |
+
You should:
|
| 88 |
+
1. Identify whether each principle is being followed or violated.
|
| 89 |
+
2. Provide clear feedback on each principle's presence or absence.
|
| 90 |
+
3. Recommend specific improvements where the code violates any SOLID principle.
|
| 91 |
+
4. Explain why the principle matters and how to refactor or redesign the code accordingly.
|
| 92 |
+
5. Be concise, clear, and educational.
|
| 93 |
+
|
| 94 |
+
The goal is to help developers build maintainable, scalable, and clean object-oriented software.
|
| 95 |
+
"""
|
| 96 |
+
code_execution_config: bool = False
|
| 97 |
+
human_input_mode: str = "NEVER"
|
| 98 |
+
|
| 99 |
+
def to_dict(self):
|
| 100 |
+
return {
|
| 101 |
+
"name": self.name,
|
| 102 |
+
"system_message": self.system_message,
|
| 103 |
+
"code_execution_config": self.code_execution_config,
|
| 104 |
+
"human_input_mode": self.human_input_mode
|
| 105 |
+
}
|
| 106 |
+
|
| 107 |
+
def make_agent(self, llm_config: Dict[str, Any]) -> ConversableAgent:
|
| 108 |
+
return ConversableAgent(**self.to_dict(), llm_config=llm_config)
|
| 109 |
+
|
| 110 |
+
|
| 111 |
+
|
agents/code_visualization_agents.py
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
|
| 3 |
+
from autogen import ConversableAgent
|
| 4 |
+
from typing import Dict, Any
|
| 5 |
+
|
| 6 |
+
class MermaideDiagramAgent:
|
| 7 |
+
name: str = "MermaideDiagramAgent"
|
| 8 |
+
system_message: str = """
|
| 9 |
+
You are an expert software architect.
|
| 10 |
+
|
| 11 |
+
Your task is to read a given code file and generate a visual architecture diagram using Mermaid.js syntax.
|
| 12 |
+
Focus on:
|
| 13 |
+
1. High-level modules and packages.
|
| 14 |
+
2. Class and function relationships (inheritance, calls).
|
| 15 |
+
3. Imports and module-level structure.
|
| 16 |
+
|
| 17 |
+
Output should be concise, visual, and suitable for developers to understand overall code architecture quickly.
|
| 18 |
+
Only output the Mermaid.js code block, nothing else.
|
| 19 |
+
"""
|
| 20 |
+
code_execution_config: bool = False
|
| 21 |
+
human_input_mode: str = "NEVER"
|
| 22 |
+
|
| 23 |
+
def to_dict(self):
|
| 24 |
+
return {
|
| 25 |
+
"name": self.name,
|
| 26 |
+
"system_message": self.system_message,
|
| 27 |
+
"code_execution_config": self.code_execution_config,
|
| 28 |
+
"human_input_mode": self.human_input_mode
|
| 29 |
+
}
|
| 30 |
+
|
| 31 |
+
def make_agent(self, llm_config: Dict[str, Any]) -> ConversableAgent:
|
| 32 |
+
return ConversableAgent(**self.to_dict(), llm_config=llm_config)
|
agents/directory_agents.py
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from typing import Dict, Any
|
| 2 |
+
from autogen import AssistantAgent
|
| 3 |
+
|
| 4 |
+
from services.utils import get_directory_tree
|
| 5 |
+
|
| 6 |
+
class DirectoryManagerAgent:
|
| 7 |
+
"""
|
| 8 |
+
An assistant agent designed to inspect and analyze project directory structures.
|
| 9 |
+
|
| 10 |
+
Your job is to:
|
| 11 |
+
1. Fetch and present the directory tree of a given path.
|
| 12 |
+
2. Identify the structure of codebases.
|
| 13 |
+
3. Suggest which folders might contain main modules, tests, configs, documentation, etc.
|
| 14 |
+
4. Help other agents decide what to analyze next based on the file layout.
|
| 15 |
+
|
| 16 |
+
at the end of your response, show used python tools from my enviroment "functions"
|
| 17 |
+
"""
|
| 18 |
+
|
| 19 |
+
name: str = 'DirectoryManagerAgent'
|
| 20 |
+
system_message: str = """
|
| 21 |
+
|
| 22 |
+
An assistant agent designed to inspect and analyze project directory structures.
|
| 23 |
+
|
| 24 |
+
Your job is to:
|
| 25 |
+
1. Fetch and present the directory tree of a given path.
|
| 26 |
+
2. Identify the structure of codebases.
|
| 27 |
+
3. Suggest which folders might contain main modules, tests, configs, documentation, etc.
|
| 28 |
+
4. Help other agents decide what to analyze next based on the file layout.
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
"""
|
| 32 |
+
human_input_mode: str = "NEVER"
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
def to_dict(self) -> Dict[str, Any]:
|
| 36 |
+
"""Convert agent configuration to a dictionary"""
|
| 37 |
+
return {
|
| 38 |
+
"name": self.name,
|
| 39 |
+
"system_message": self.system_message,
|
| 40 |
+
"human_input_mode": self.human_input_mode,
|
| 41 |
+
}
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
def _store_function_tools(self) -> None:
|
| 45 |
+
"""Set the list of function tools for the agent"""
|
| 46 |
+
self.tools = [
|
| 47 |
+
{
|
| 48 |
+
"type": "function",
|
| 49 |
+
"function": {
|
| 50 |
+
"name": "get_directory_tree",
|
| 51 |
+
"description": "Get directory tree structure starting from specified path, excluding certain folders",
|
| 52 |
+
"parameters": {
|
| 53 |
+
"type": "object",
|
| 54 |
+
"properties": {
|
| 55 |
+
"path": {
|
| 56 |
+
"type": "string",
|
| 57 |
+
"description": "Directory path to start from (e.g., '.' for current directory)"
|
| 58 |
+
},
|
| 59 |
+
"indent": {
|
| 60 |
+
"type": "string",
|
| 61 |
+
"description": "Indentation string for formatting",
|
| 62 |
+
"default": ""
|
| 63 |
+
},
|
| 64 |
+
"exclude_folder": {
|
| 65 |
+
"type": "string",
|
| 66 |
+
"description": "Folder name to exclude from the tree",
|
| 67 |
+
"default": "__pycache__"
|
| 68 |
+
}
|
| 69 |
+
},
|
| 70 |
+
"required": ["path"]
|
| 71 |
+
}
|
| 72 |
+
}
|
| 73 |
+
}
|
| 74 |
+
]
|
| 75 |
+
|
| 76 |
+
|
| 77 |
+
def _add_agents_tools_to_config(self, llm_config:Dict[str, Any]) -> None:
|
| 78 |
+
"""Add the tools to the llm_config"""
|
| 79 |
+
llm_config["tools"] = self.tools
|
| 80 |
+
return llm_config
|
| 81 |
+
|
| 82 |
+
def make_agent(self, llm_config: Dict[str, Any]) -> AssistantAgent:
|
| 83 |
+
"""Create an instance of the agent with the given configuration"""
|
| 84 |
+
self._store_function_tools()
|
| 85 |
+
self.llm_config = self._add_agents_tools_to_config(llm_config)
|
| 86 |
+
|
| 87 |
+
return AssistantAgent(
|
| 88 |
+
**self.to_dict(),
|
| 89 |
+
llm_config=self.llm_config,
|
| 90 |
+
code_execution_config={"work_dir": ".", 'use_docker': False},
|
| 91 |
+
function_map={
|
| 92 |
+
"get_directory_tree": get_directory_tree
|
| 93 |
+
}
|
| 94 |
+
)
|
agents/manager_agent.py
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from typing import Dict, Any
|
| 2 |
+
from autogen import ConversableAgent, UserProxyAgent
|
| 3 |
+
|
| 4 |
+
class ManagerAgent:
|
| 5 |
+
name: str = 'ManagerAgent'
|
| 6 |
+
system_message: str = """
|
| 7 |
+
You are a Manager Agent responsible for overseeing and orchestrating a team of specialized code analysis agents. Your team includes:
|
| 8 |
+
|
| 9 |
+
- **CodeSummarizerAgent:** Provides a high-level summary of the code including an explanation of major classes, functions, and overall logic.
|
| 10 |
+
- **CodeSmellDetectorAgent:** Detects potential code smells (e.g., long methods, God objects, deep nesting, etc.), explains why they are problematic, and suggests refactoring strategies.
|
| 11 |
+
- **DesignPatternDetectorAgent:** Identifies commonly used design patterns within the code and explains how they are applied.
|
| 12 |
+
- **DesignPatternRecommenderAgent:** Evaluates the code and recommends suitable design patterns to improve structure, flexibility, and maintainability.
|
| 13 |
+
- **SOLIDPrinciplesManagerAgent:** Analyzes code compliance with the SOLID principles and provides actionable feedback for enhancing code quality.
|
| 14 |
+
- **DirectoryManagerAgent:** manages code directory structure and helps in navigating the codebase.
|
| 15 |
+
|
| 16 |
+
Your responsibilities are:
|
| 17 |
+
1. **Assess Incoming Requests:** Analyze the provided code or query and decide which specialized agent(s) should handle each aspect of the analysis.
|
| 18 |
+
2. **Delegate Tasks:** Route parts of the code analysis to the appropriate agent based on the needs of the request.
|
| 19 |
+
3. **Aggregate Responses:** Collect and synthesize the outputs from the various agents into a cohesive, clear, and structured report.
|
| 20 |
+
4. **Ensure Clarity and Consistency:** Present the final analysis in a manner that is both understandable to junior developers and valuable to experienced engineers.
|
| 21 |
+
5. **Maintain Best Practices:** Ensure that all insights and recommendations adhere to best practices in software engineering, architecture, and clean code principles.
|
| 22 |
+
|
| 23 |
+
Your goal is to streamline the code review process by ensuring that each specialized agent contributes effectively, and by delivering a final, comprehensive analysis that covers high-level summaries, code smells, design patterns, and SOLID principles. Work as the central coordinator to enhance the clarity and quality of the overall feedback provided to the developers.
|
| 24 |
+
"""
|
| 25 |
+
code_execution_config: bool = False
|
| 26 |
+
human_input_mode: str = "NEVER"
|
| 27 |
+
|
| 28 |
+
def to_dict(self) -> Dict[str, Any]:
|
| 29 |
+
return {
|
| 30 |
+
"name": self.name,
|
| 31 |
+
"system_message": self.system_message,
|
| 32 |
+
"code_execution_config": self.code_execution_config,
|
| 33 |
+
"human_input_mode": self.human_input_mode,
|
| 34 |
+
}
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
def make_agent(self, llm_config: Dict[str, Any]) -> UserProxyAgent:
|
| 40 |
+
return UserProxyAgent(**self.to_dict(), llm_config=llm_config)
|
app.py
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from typing import List
|
| 2 |
+
import streamlit as st
|
| 3 |
+
from tkinter import Tk, filedialog
|
| 4 |
+
from streamlit.components.v1 import html
|
| 5 |
+
from autogen import ConversableAgent
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
from ui.introductory_page import IntroductoryPage
|
| 9 |
+
from ui.analysis_page import NavigationPanel, CodePreview, ResultsDisplay
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
from agents.code_overview_agents import CodeSummarizerAgent, CodeSmellDetectorAgent
|
| 13 |
+
from agents.code_structure_agents import DesignPatternDetectorAgent, DesignPatternRecommenderAgent, SOLIDPrinciplesManagerAgent
|
| 14 |
+
from agents.code_visualization_agents import MermaideDiagramAgent
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
from config import DefaultSettings
|
| 18 |
+
from utils.utils import load_config, load_css
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
# Configuration and Agent Initialization
|
| 25 |
+
llm_config = load_config(DefaultSettings().default_model_type)
|
| 26 |
+
agents = [
|
| 27 |
+
CodeSummarizerAgent().make_agent(llm_config),
|
| 28 |
+
CodeSmellDetectorAgent().make_agent(llm_config),
|
| 29 |
+
DesignPatternDetectorAgent().make_agent(llm_config),
|
| 30 |
+
DesignPatternRecommenderAgent().make_agent(llm_config),
|
| 31 |
+
SOLIDPrinciplesManagerAgent().make_agent(llm_config),
|
| 32 |
+
MermaideDiagramAgent().make_agent(llm_config)
|
| 33 |
+
]
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
class AnalysisManager:
|
| 38 |
+
"""Manages and provides access to analysis results."""
|
| 39 |
+
|
| 40 |
+
def __init__(self, results):
|
| 41 |
+
load_css("./ui/styles.css")
|
| 42 |
+
self.results = results
|
| 43 |
+
|
| 44 |
+
def get_file_data(self, file_path):
|
| 45 |
+
return self.results.get(file_path, {})
|
| 46 |
+
|
| 47 |
+
def list_files(self):
|
| 48 |
+
return list(self.results.keys())
|
| 49 |
+
|
| 50 |
+
|
| 51 |
+
|
| 52 |
+
class CodeAnalysisApp:
|
| 53 |
+
"""Main application orchestrator."""
|
| 54 |
+
|
| 55 |
+
|
| 56 |
+
def __init__(self, agents: List[ConversableAgent]) -> None:
|
| 57 |
+
self.agents = agents
|
| 58 |
+
|
| 59 |
+
def run(self):
|
| 60 |
+
"""Main application entry point."""
|
| 61 |
+
st.set_page_config(
|
| 62 |
+
layout="wide",
|
| 63 |
+
initial_sidebar_state="expanded",
|
| 64 |
+
page_icon="🔍"
|
| 65 |
+
)
|
| 66 |
+
|
| 67 |
+
if not self._check_analysis_state():
|
| 68 |
+
IntroductoryPage(self.agents).render()
|
| 69 |
+
return
|
| 70 |
+
|
| 71 |
+
self._setup_interface()
|
| 72 |
+
self._render_main_view()
|
| 73 |
+
self._add_reset_button()
|
| 74 |
+
|
| 75 |
+
def _check_analysis_state(self):
|
| 76 |
+
"""Validate analysis completion status."""
|
| 77 |
+
if 'analysis_done' not in st.session_state:
|
| 78 |
+
st.session_state.analysis_done = False
|
| 79 |
+
return st.session_state.get('results') and st.session_state.analysis_done
|
| 80 |
+
|
| 81 |
+
def _setup_interface(self):
|
| 82 |
+
"""Initialize application components."""
|
| 83 |
+
load_css("./ui/styles.css")
|
| 84 |
+
self.analysis = AnalysisManager(st.session_state.results)
|
| 85 |
+
self.nav = NavigationPanel(self.analysis.list_files())
|
| 86 |
+
|
| 87 |
+
def _render_main_view(self):
|
| 88 |
+
"""Display main two-column interface."""
|
| 89 |
+
selected_file = self.nav.render()
|
| 90 |
+
file_data = self.analysis.get_file_data(selected_file)
|
| 91 |
+
|
| 92 |
+
col1, col2 = st.columns([0.5, 0.5])
|
| 93 |
+
with col1:
|
| 94 |
+
CodePreview(
|
| 95 |
+
file_path=selected_file,
|
| 96 |
+
language=file_data['language']
|
| 97 |
+
).render()
|
| 98 |
+
|
| 99 |
+
with col2:
|
| 100 |
+
ResultsDisplay(
|
| 101 |
+
file_data['analysis_results']
|
| 102 |
+
).render()
|
| 103 |
+
|
| 104 |
+
def _add_reset_button(self):
|
| 105 |
+
"""Add sidebar reset functionality."""
|
| 106 |
+
st.sidebar.button(
|
| 107 |
+
"New Analysis",
|
| 108 |
+
on_click=self._reset_state
|
| 109 |
+
)
|
| 110 |
+
|
| 111 |
+
def _reset_state(self):
|
| 112 |
+
"""Clear analysis-related session state."""
|
| 113 |
+
state_keys = ['results', 'analysis_done', 'selected_dir', 'prev_dir']
|
| 114 |
+
for key in state_keys:
|
| 115 |
+
if key in st.session_state:
|
| 116 |
+
del st.session_state[key]
|
| 117 |
+
st.rerun()
|
| 118 |
+
|
| 119 |
+
|
| 120 |
+
if __name__ == "__main__":
|
| 121 |
+
CodeAnalysisApp(agents).run()
|
config.py
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from dataclasses import dataclass
|
| 2 |
+
from pickle import DICT
|
| 3 |
+
from autogen import ConversableAgent, UserProxyAgent, AssistantAgent
|
| 4 |
+
from autogen.tools import tool
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
from typing import Dict, Any, List
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
# ----------------------------------------------------------------
|
| 14 |
+
# General Config
|
| 15 |
+
# ----------------------------------------------------------------
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
extension_to_language = {
|
| 19 |
+
'.py': 'Python',
|
| 20 |
+
'.js': 'JavaScript',
|
| 21 |
+
'.java': 'Java',
|
| 22 |
+
'.cpp': 'C++',
|
| 23 |
+
'.cxx': 'C++',
|
| 24 |
+
'.cc': 'C++',
|
| 25 |
+
'.c': 'C',
|
| 26 |
+
'.cs': 'C#',
|
| 27 |
+
'.rb': 'Ruby',
|
| 28 |
+
'.php': 'PHP',
|
| 29 |
+
'.html': 'HTML',
|
| 30 |
+
'.htm': 'HTML',
|
| 31 |
+
'.css': 'CSS',
|
| 32 |
+
'.ts': 'TypeScript',
|
| 33 |
+
'.go': 'Go',
|
| 34 |
+
'.rs': 'Rust',
|
| 35 |
+
'.swift': 'Swift',
|
| 36 |
+
'.kt': 'Kotlin',
|
| 37 |
+
'.m': 'Objective-C',
|
| 38 |
+
'.sh': 'Shell',
|
| 39 |
+
'.pl': 'Perl',
|
| 40 |
+
'.r': 'R',
|
| 41 |
+
'.lua': 'Lua',
|
| 42 |
+
'.scala': 'Scala',
|
| 43 |
+
'.sql': 'SQL',
|
| 44 |
+
'.dart': 'Dart',
|
| 45 |
+
'.jl': 'Julia',
|
| 46 |
+
'.json': 'JSON',
|
| 47 |
+
'.xml': 'XML',
|
| 48 |
+
'.yml': 'YAML',
|
| 49 |
+
'.yaml': 'YAML',
|
| 50 |
+
}
|
| 51 |
+
|
| 52 |
+
|
| 53 |
+
|
| 54 |
+
@dataclass
|
| 55 |
+
class DefaultSettings:
|
| 56 |
+
default_model_type: str = "google"
|
| 57 |
+
default_display_language: str = "python"
|
| 58 |
+
|
| 59 |
+
|
| 60 |
+
|
| 61 |
+
|
| 62 |
+
|
| 63 |
+
@dataclass
|
| 64 |
+
class BaseLLMConfig:
|
| 65 |
+
model_name: str
|
| 66 |
+
api_key: str
|
| 67 |
+
api_type: str
|
| 68 |
+
temperature: float
|
| 69 |
+
max_tokens: int
|
| 70 |
+
|
| 71 |
+
def get_llm_config(self):
|
| 72 |
+
return {'config_list': [
|
| 73 |
+
{'model': self.model_name,
|
| 74 |
+
'api_key': self.api_key,
|
| 75 |
+
'api_type': self.api_type}
|
| 76 |
+
]}
|
| 77 |
+
|
| 78 |
+
|
| 79 |
+
@dataclass
|
| 80 |
+
class GeminiLLMConfig(BaseLLMConfig):
|
| 81 |
+
model_name: str = "gemini-2.0-flash"
|
| 82 |
+
api_key: str = "AIzaSyCYI-tm7QjAfYKKlb2wfdgnQ1aTmqh91GY"
|
| 83 |
+
api_type: str = "google"
|
| 84 |
+
temperature: float = 0.1
|
| 85 |
+
max_tokens: int = 1024
|
| 86 |
+
|
| 87 |
+
|
| 88 |
+
|
| 89 |
+
@dataclass
|
| 90 |
+
class OpenAILLMConfig(BaseLLMConfig):
|
| 91 |
+
model_name: str = "chatgpt-4"
|
| 92 |
+
api_key: str = "api-key"
|
| 93 |
+
api_type: str = "openai"
|
| 94 |
+
temperature: float = 0.1
|
| 95 |
+
max_tokens: int = 1024
|
| 96 |
+
|
| 97 |
+
|
| 98 |
+
|
| 99 |
+
|
requirements.txt
CHANGED
|
@@ -1,3 +1,11 @@
|
|
| 1 |
-
|
| 2 |
-
|
| 3 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
streamlit>=1.29.0
|
| 2 |
+
autogen>=0.8.5
|
| 3 |
+
tkinter>=8.6.0
|
| 4 |
+
python-dotenv>=1.0.0
|
| 5 |
+
pyyaml>=6.0.1
|
| 6 |
+
typing-extensions>=4.8.0
|
| 7 |
+
pytest>=7.4.3
|
| 8 |
+
pytest-cov>=4.1.0
|
| 9 |
+
black>=23.11.0
|
| 10 |
+
isort>=5.12.0
|
| 11 |
+
mypy>=1.7.0
|
run.py
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import subprocess
|
| 2 |
+
subprocess.run(["streamlit", "run", "app.py"])
|
ui/__pycache__/analysis_page.cpython-311.pyc
ADDED
|
Binary file (3.88 kB). View file
|
|
|
ui/__pycache__/code_container.cpython-311.pyc
ADDED
|
Binary file (5.13 kB). View file
|
|
|
ui/__pycache__/introductory_page.cpython-311.pyc
ADDED
|
Binary file (6.05 kB). View file
|
|
|
ui/analysis_page.py
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import streamlit as st
|
| 3 |
+
|
| 4 |
+
from utils.utils import load_css
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
class NavigationPanel:
|
| 10 |
+
"""Renders the sidebar file navigation."""
|
| 11 |
+
|
| 12 |
+
def __init__(self, files):
|
| 13 |
+
self.files = files
|
| 14 |
+
|
| 15 |
+
def render(self):
|
| 16 |
+
load_css("./ui/styles.css")
|
| 17 |
+
with st.sidebar:
|
| 18 |
+
st.title("Code Analyis")
|
| 19 |
+
|
| 20 |
+
selected = st.selectbox(
|
| 21 |
+
"Select File",
|
| 22 |
+
self.files,
|
| 23 |
+
format_func=os.path.basename
|
| 24 |
+
)
|
| 25 |
+
|
| 26 |
+
return selected
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
class CodePreview:
|
| 30 |
+
"""Displays file contents with syntax highlighting."""
|
| 31 |
+
|
| 32 |
+
def __init__(self, file_path, language):
|
| 33 |
+
load_css("./ui/styles.css")
|
| 34 |
+
self.file_path = file_path
|
| 35 |
+
self.language = language.lower()
|
| 36 |
+
|
| 37 |
+
def render(self):
|
| 38 |
+
st.header("Code Preview")
|
| 39 |
+
try:
|
| 40 |
+
with open(self.file_path, 'r') as f:
|
| 41 |
+
st.code(f.read(), language=self.language)
|
| 42 |
+
except FileNotFoundError:
|
| 43 |
+
st.error("File not found")
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
class ResultsDisplay:
|
| 47 |
+
"""Presents analysis results in expandable sections."""
|
| 48 |
+
|
| 49 |
+
def __init__(self, results):
|
| 50 |
+
load_css("./ui/styles.css")
|
| 51 |
+
self.results = results
|
| 52 |
+
|
| 53 |
+
def render(self):
|
| 54 |
+
st.header("Analysis")
|
| 55 |
+
for agent, findings in self.results.items():
|
| 56 |
+
with st.expander(f"{agent}", expanded=False):
|
| 57 |
+
st.write(findings)
|
ui/introductory_page.py
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import streamlit as st
|
| 3 |
+
from tkinter import Tk, filedialog
|
| 4 |
+
import time
|
| 5 |
+
|
| 6 |
+
from utils.utils import analyze_directory_with_agents, load_css
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
class IntroductoryPage:
|
| 15 |
+
"""Handles directory selection and initial analysis."""
|
| 16 |
+
|
| 17 |
+
def __init__(self, agents):
|
| 18 |
+
self.agents = agents
|
| 19 |
+
self._init_session_state()
|
| 20 |
+
|
| 21 |
+
def _init_session_state(self):
|
| 22 |
+
required_state = {
|
| 23 |
+
'analysis_done': False,
|
| 24 |
+
'selected_dir': None,
|
| 25 |
+
'prev_dir': None,
|
| 26 |
+
'results': None
|
| 27 |
+
}
|
| 28 |
+
for key, val in required_state.items():
|
| 29 |
+
if key not in st.session_state:
|
| 30 |
+
st.session_state[key] = val
|
| 31 |
+
|
| 32 |
+
@staticmethod
|
| 33 |
+
def _select_directory():
|
| 34 |
+
"""Open system directory picker."""
|
| 35 |
+
root = Tk()
|
| 36 |
+
root.withdraw()
|
| 37 |
+
root.wm_attributes('-topmost', 1)
|
| 38 |
+
directory = filedialog.askdirectory(parent=root)
|
| 39 |
+
root.destroy()
|
| 40 |
+
return directory
|
| 41 |
+
|
| 42 |
+
def _process_directory(self):
|
| 43 |
+
"""Handle directory selection and analysis."""
|
| 44 |
+
directory = self._select_directory()
|
| 45 |
+
|
| 46 |
+
if not directory:
|
| 47 |
+
st.warning("No directory selected")
|
| 48 |
+
return
|
| 49 |
+
|
| 50 |
+
if not os.path.isdir(directory):
|
| 51 |
+
st.error("Invalid directory")
|
| 52 |
+
return
|
| 53 |
+
|
| 54 |
+
st.session_state.selected_dir = directory
|
| 55 |
+
self._analyze_directory(directory)
|
| 56 |
+
|
| 57 |
+
|
| 58 |
+
def _progress_bar_callback_func(
|
| 59 |
+
self,
|
| 60 |
+
current_idx: int,
|
| 61 |
+
file_path: str,
|
| 62 |
+
progress_value: int
|
| 63 |
+
) -> None:
|
| 64 |
+
"""
|
| 65 |
+
Callback function for `analyze_directory_with_agents`.
|
| 66 |
+
Updates the progress bar and status text with the current file being processed.
|
| 67 |
+
"""
|
| 68 |
+
if current_idx == 0:
|
| 69 |
+
self.status_text.markdown("🔍 Analyzing code structure...")
|
| 70 |
+
|
| 71 |
+
|
| 72 |
+
|
| 73 |
+
|
| 74 |
+
progress_value = progress_value if progress_value < 100 else 100
|
| 75 |
+
self.progress_bar.progress(current_idx * progress_value)
|
| 76 |
+
self.status_text.markdown(f"Processing {file_path}")
|
| 77 |
+
|
| 78 |
+
|
| 79 |
+
|
| 80 |
+
|
| 81 |
+
|
| 82 |
+
|
| 83 |
+
|
| 84 |
+
def _analyze_directory(self, directory):
|
| 85 |
+
"""Run analysis on selected directory with progress bar."""
|
| 86 |
+
self.progress_bar = st.progress(0)
|
| 87 |
+
self.status_text = st.empty()
|
| 88 |
+
|
| 89 |
+
try:
|
| 90 |
+
|
| 91 |
+
st.session_state.results = analyze_directory_with_agents(
|
| 92 |
+
directory, self.agents, verbose=True, callback_func = self._progress_bar_callback_func
|
| 93 |
+
)
|
| 94 |
+
|
| 95 |
+
st.session_state.analysis_done = True
|
| 96 |
+
st.rerun()
|
| 97 |
+
|
| 98 |
+
except Exception as e:
|
| 99 |
+
self.status_text.empty()
|
| 100 |
+
st.error(f"Analysis failed: {str(e)}")
|
| 101 |
+
st.session_state.analysis_done = False
|
| 102 |
+
st.session_state.selected_dir = None
|
| 103 |
+
|
| 104 |
+
def render(self):
|
| 105 |
+
"""Main entry point for introductory page."""
|
| 106 |
+
self._handle_state()
|
| 107 |
+
|
| 108 |
+
if not st.session_state.analysis_done:
|
| 109 |
+
self._render_selector()
|
| 110 |
+
|
| 111 |
+
def _render_selector(self):
|
| 112 |
+
"""Render file selection interface."""
|
| 113 |
+
load_css("./ui/styles.css")
|
| 114 |
+
if st.button("Select Directory", key="start_button"):
|
| 115 |
+
self._process_directory()
|
| 116 |
+
|
| 117 |
+
def _handle_state(self):
|
| 118 |
+
"""Manage session state transitions."""
|
| 119 |
+
if st.session_state.selected_dir != st.session_state.prev_dir:
|
| 120 |
+
st.session_state.analysis_done = False
|
| 121 |
+
st.session_state.prev_dir = st.session_state.selected_dir
|
ui/styles.css
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/* Hide Streamlit default UI elements */
|
| 2 |
+
#MainMenu, header, footer {
|
| 3 |
+
visibility: hidden;
|
| 4 |
+
}
|
| 5 |
+
|
| 6 |
+
/* Full-screen center layout */
|
| 7 |
+
.stApp {
|
| 8 |
+
display: flex;
|
| 9 |
+
justify-content: center;
|
| 10 |
+
align-items: center;
|
| 11 |
+
min-height: 100vh;
|
| 12 |
+
margin: 0;
|
| 13 |
+
padding: 0;
|
| 14 |
+
}
|
| 15 |
+
|
| 16 |
+
/* Global dark theme base */
|
| 17 |
+
body {
|
| 18 |
+
background-color: #343541; /* ChatGPT dark gray */
|
| 19 |
+
color: #ececf1; /* Light neutral for text */
|
| 20 |
+
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
| 21 |
+
margin: 0;
|
| 22 |
+
padding: 0;
|
| 23 |
+
}
|
| 24 |
+
|
| 25 |
+
/* Container centering */
|
| 26 |
+
.centered-container {
|
| 27 |
+
display: flex;
|
| 28 |
+
align-items: center;
|
| 29 |
+
justify-content: center;
|
| 30 |
+
height: 100vh;
|
| 31 |
+
width: 100vw;
|
| 32 |
+
}
|
| 33 |
+
|
| 34 |
+
/* ChatGPT-style button */
|
| 35 |
+
.stButton > button {
|
| 36 |
+
background-color: #444654 !important;
|
| 37 |
+
color: #ececf1 !important;
|
| 38 |
+
border: 1px solid #5c5f72 !important;
|
| 39 |
+
border-radius: 999px !important;
|
| 40 |
+
padding: 0.5rem 1.25rem !important;
|
| 41 |
+
font-weight: 500;
|
| 42 |
+
transition: background-color 0.2s ease, transform 0.1s ease;
|
| 43 |
+
position: relative;
|
| 44 |
+
}
|
| 45 |
+
|
| 46 |
+
.stButton > button:hover {
|
| 47 |
+
background-color: #565869 !important;
|
| 48 |
+
transform: scale(1.03);
|
| 49 |
+
}
|
| 50 |
+
|
| 51 |
+
/* Sidebar styling */
|
| 52 |
+
[data-testid="stSidebar"] {
|
| 53 |
+
background-color: #202123;
|
| 54 |
+
color: #ececf1;
|
| 55 |
+
border-right: 1px solid #2d2f36;
|
| 56 |
+
min-width: 140px;
|
| 57 |
+
max-width: 250px;
|
| 58 |
+
transition: all 0.3s ease;
|
| 59 |
+
}
|
| 60 |
+
|
| 61 |
+
[data-testid="stSidebar"][aria-expanded="false"] {
|
| 62 |
+
margin-left: -250px;
|
| 63 |
+
}
|
| 64 |
+
|
| 65 |
+
[data-testid="stSidebar"] h1,
|
| 66 |
+
[data-testid="stSidebar"] h2,
|
| 67 |
+
[data-testid="stSidebar"] h3 {
|
| 68 |
+
color: #ececf1;
|
| 69 |
+
}
|
| 70 |
+
|
| 71 |
+
/* Markdown and text elements */
|
| 72 |
+
.stMarkdown, .stCaption, .stHeader {
|
| 73 |
+
color: #ececf1;
|
| 74 |
+
}
|
| 75 |
+
|
| 76 |
+
/* Dropdown styling */
|
| 77 |
+
select {
|
| 78 |
+
background-color: #3e3f4b;
|
| 79 |
+
color: #ececf1;
|
| 80 |
+
border: 1px solid #5c5f72;
|
| 81 |
+
border-radius: 6px;
|
| 82 |
+
padding: 6px 10px;
|
| 83 |
+
}
|
| 84 |
+
|
| 85 |
+
/* Selectbox refinements */
|
| 86 |
+
.stSelectbox {
|
| 87 |
+
cursor: pointer !important;
|
| 88 |
+
}
|
| 89 |
+
.stSelectbox input {
|
| 90 |
+
cursor: pointer !important;
|
| 91 |
+
caret-color: transparent !important;
|
| 92 |
+
}
|
| 93 |
+
.stSelectbox div[data-baseweb="select"] {
|
| 94 |
+
cursor: pointer !important;
|
| 95 |
+
}
|
| 96 |
+
.stSelectbox [role="option"] {
|
| 97 |
+
cursor: pointer !important;
|
| 98 |
+
}
|
| 99 |
+
.stSelectbox ::selection {
|
| 100 |
+
background: transparent !important;
|
| 101 |
+
}
|
| 102 |
+
|
| 103 |
+
/* General container */
|
| 104 |
+
.block-container {
|
| 105 |
+
padding: 15px !important;
|
| 106 |
+
margin: 15px !important;
|
| 107 |
+
max-width: 100% !important;
|
| 108 |
+
}
|
| 109 |
+
|
| 110 |
+
/* Progress bar */
|
| 111 |
+
.stProgress > div > div > div {
|
| 112 |
+
background-color: #10a37f !important; /* ChatGPT green */
|
| 113 |
+
}
|
| 114 |
+
.stProgress > div > div {
|
| 115 |
+
background-color: #3e3f4b !important;
|
| 116 |
+
height: 10px !important;
|
| 117 |
+
border-radius: 5px;
|
| 118 |
+
}
|
| 119 |
+
|
| 120 |
+
/* Loading or status text */
|
| 121 |
+
.st-emotion-cache-1q7spjk {
|
| 122 |
+
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
| 123 |
+
color: #ececf1 !important;
|
| 124 |
+
font-size: 1.1rem;
|
| 125 |
+
margin-bottom: 15px;
|
| 126 |
+
}
|
| 127 |
+
|
| 128 |
+
/* Optional animation (retained from your original) */
|
| 129 |
+
.rotate {
|
| 130 |
+
display: inline-block;
|
| 131 |
+
color: #10a37f;
|
| 132 |
+
animation: rotation 2s infinite linear;
|
| 133 |
+
}
|
| 134 |
+
@keyframes rotation {
|
| 135 |
+
from { transform: rotate(0deg); }
|
| 136 |
+
to { transform: rotate(359deg); }
|
| 137 |
+
}
|
| 138 |
+
|
| 139 |
+
/* Centered button containers */
|
| 140 |
+
.centered-button-container,
|
| 141 |
+
.button-container {
|
| 142 |
+
display: flex;
|
| 143 |
+
justify-content: center;
|
| 144 |
+
align-items: center;
|
| 145 |
+
text-align: center;
|
| 146 |
+
}
|
utils/__init__.py
ADDED
|
File without changes
|
utils/__pycache__/__init__.cpython-311.pyc
ADDED
|
Binary file (145 Bytes). View file
|
|
|
utils/__pycache__/utils.cpython-311.pyc
ADDED
|
Binary file (9.82 kB). View file
|
|
|
utils/utils.py
ADDED
|
@@ -0,0 +1,218 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from autogen import ConversableAgent
|
| 2 |
+
|
| 3 |
+
import os
|
| 4 |
+
from typing import List, Dict, Optional, Callable
|
| 5 |
+
import time
|
| 6 |
+
import streamlit as st
|
| 7 |
+
|
| 8 |
+
from config import DefaultSettings, GeminiLLMConfig, OpenAILLMConfig, extension_to_language
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
def load_css(file_path):
|
| 13 |
+
"""Load and inject CSS styles from file."""
|
| 14 |
+
try:
|
| 15 |
+
with open(file_path, "r") as f:
|
| 16 |
+
css = f.read()
|
| 17 |
+
st.markdown(f"<style>{css}</style>", unsafe_allow_html=True)
|
| 18 |
+
except Exception as e:
|
| 19 |
+
st.error(f"Failed to load CSS: {str(e)}")
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
def load_config(
|
| 24 |
+
model_type: str) -> dict:
|
| 25 |
+
"""
|
| 26 |
+
Load the configuration for a given model type.
|
| 27 |
+
|
| 28 |
+
Args:
|
| 29 |
+
- model_type (str): The type of model to load the config for. Supported options are 'google' and 'openai'.
|
| 30 |
+
|
| 31 |
+
Returns:
|
| 32 |
+
- dict: A dictionary containing the configuration for the specified model type.
|
| 33 |
+
"""
|
| 34 |
+
if model_type == 'google':
|
| 35 |
+
return GeminiLLMConfig().get_llm_config()
|
| 36 |
+
elif model_type == 'openai':
|
| 37 |
+
return OpenAILLMConfig().get_llm_config()
|
| 38 |
+
else:
|
| 39 |
+
raise ValueError(f"Unsupported model type: {model_type}")
|
| 40 |
+
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
def get_directory_tree(
|
| 47 |
+
path: str,
|
| 48 |
+
indent: str = "",
|
| 49 |
+
exclude_folder: str = "__pycache__"
|
| 50 |
+
) -> str:
|
| 51 |
+
"""
|
| 52 |
+
Returns a tree-like string representation of the directory at 'path', skipping excluded folders.
|
| 53 |
+
"""
|
| 54 |
+
result = ""
|
| 55 |
+
for item in os.listdir(path):
|
| 56 |
+
if item == exclude_folder:
|
| 57 |
+
continue
|
| 58 |
+
full_path = os.path.join(path, item)
|
| 59 |
+
if os.path.isdir(full_path):
|
| 60 |
+
result += f"{indent}📁 {item}/\n"
|
| 61 |
+
result += get_directory_tree(full_path, indent + " ", exclude_folder)
|
| 62 |
+
else:
|
| 63 |
+
result += f"{indent}📄 {item}\n"
|
| 64 |
+
return result
|
| 65 |
+
|
| 66 |
+
|
| 67 |
+
|
| 68 |
+
|
| 69 |
+
def detect_language_by_extension(file_path: str) -> str:
|
| 70 |
+
"""
|
| 71 |
+
Detect the language of a file by its extension.
|
| 72 |
+
|
| 73 |
+
Args:
|
| 74 |
+
file_path (str): The path to the file to detect.
|
| 75 |
+
|
| 76 |
+
Returns:
|
| 77 |
+
str: The detected language.
|
| 78 |
+
"""
|
| 79 |
+
_, ext = os.path.splitext(file_path.lower())
|
| 80 |
+
return extension_to_language.get(ext, 'Unknown')
|
| 81 |
+
|
| 82 |
+
|
| 83 |
+
|
| 84 |
+
|
| 85 |
+
|
| 86 |
+
def get_supported_code_files(directory_path: str) -> List[str]:
|
| 87 |
+
"""
|
| 88 |
+
Scans a directory for files with supported programming language extensions.
|
| 89 |
+
|
| 90 |
+
Args:
|
| 91 |
+
directory_path (str): Path to the directory to scan
|
| 92 |
+
|
| 93 |
+
Returns:
|
| 94 |
+
List[str]: List of absolute paths to supported code files
|
| 95 |
+
"""
|
| 96 |
+
code_files = []
|
| 97 |
+
for root, _, files in os.walk(directory_path):
|
| 98 |
+
for file in files:
|
| 99 |
+
_, ext = os.path.splitext(file)
|
| 100 |
+
if ext in extension_to_language:
|
| 101 |
+
full_path = os.path.join(root, file)
|
| 102 |
+
code_files.append(full_path)
|
| 103 |
+
return code_files
|
| 104 |
+
|
| 105 |
+
def analyze_code_with_agents(
|
| 106 |
+
source_code: str,
|
| 107 |
+
analysis_agents: List[ConversableAgent]
|
| 108 |
+
) -> Dict[str, str]:
|
| 109 |
+
"""
|
| 110 |
+
Analyzes source code using multiple code analysis agents.
|
| 111 |
+
|
| 112 |
+
Args:
|
| 113 |
+
source_code (str): The code to analyze
|
| 114 |
+
analysis_agents (List[ConversableAgent]): List of agents to perform analysis
|
| 115 |
+
|
| 116 |
+
Returns:
|
| 117 |
+
Dict[str, str]: A dictionary mapping each agent's name to its analysis result
|
| 118 |
+
"""
|
| 119 |
+
results = {}
|
| 120 |
+
for agent in analysis_agents:
|
| 121 |
+
agent_response = agent.generate_reply(messages=[{'role': 'user', 'content': source_code}])
|
| 122 |
+
results[agent.name] = agent_response['content']
|
| 123 |
+
return results
|
| 124 |
+
|
| 125 |
+
def analyze_code_file(
|
| 126 |
+
file_path: str,
|
| 127 |
+
analysis_agents: List[ConversableAgent]
|
| 128 |
+
) -> Optional[Dict[str, str]]:
|
| 129 |
+
"""
|
| 130 |
+
Analyzes a single code file using multiple analysis agents.
|
| 131 |
+
|
| 132 |
+
Args:
|
| 133 |
+
file_path (str): Path to the code file to analyze
|
| 134 |
+
analysis_agents (List[ConversableAgent]): List of agents to perform analysis
|
| 135 |
+
|
| 136 |
+
Returns:
|
| 137 |
+
Optional[Dict[str, str]]: Analysis results if successful, None if an error occurred
|
| 138 |
+
"""
|
| 139 |
+
try:
|
| 140 |
+
with open(file_path, 'r', encoding='utf-8') as file:
|
| 141 |
+
code = file.read()
|
| 142 |
+
|
| 143 |
+
analysis_results = analyze_code_with_agents(code, analysis_agents)
|
| 144 |
+
return analysis_results
|
| 145 |
+
|
| 146 |
+
except UnicodeDecodeError:
|
| 147 |
+
print(f"Skipping binary file {file_path}")
|
| 148 |
+
return None
|
| 149 |
+
except PermissionError:
|
| 150 |
+
print(f"Permission denied for file {file_path}")
|
| 151 |
+
return None
|
| 152 |
+
except Exception as e:
|
| 153 |
+
print(f"Error processing file {file_path}: {str(e)}")
|
| 154 |
+
return None
|
| 155 |
+
|
| 156 |
+
def analyze_directory_with_agents(
|
| 157 |
+
directory_path: str,
|
| 158 |
+
analysis_agents: List[ConversableAgent],
|
| 159 |
+
time_between_analysis: int = 10,
|
| 160 |
+
verbose: bool = False,
|
| 161 |
+
callback_func: Callable[[int], None] = None
|
| 162 |
+
) -> Dict[str, Dict[str, str]]:
|
| 163 |
+
"""
|
| 164 |
+
Analyzes all code files in a directory using multiple analysis agents.
|
| 165 |
+
|
| 166 |
+
Args:
|
| 167 |
+
directory_path (str): Path to the directory to analyze
|
| 168 |
+
analysis_agents (List[ConversableAgent]): List of agents to perform analysis
|
| 169 |
+
verbose (bool): Whether to print verbose output (default: False)
|
| 170 |
+
|
| 171 |
+
Returns:
|
| 172 |
+
Dict[str, Dict[str, str]]: A nested dictionary where:
|
| 173 |
+
- Outer keys are file paths
|
| 174 |
+
- Inner keys are agent names
|
| 175 |
+
- Values are agent analysis results
|
| 176 |
+
"""
|
| 177 |
+
if verbose:
|
| 178 |
+
print(f"Started Analyzing using these agents {[agent.name for agent in analysis_agents]} [+]")
|
| 179 |
+
|
| 180 |
+
analysis_results = {}
|
| 181 |
+
|
| 182 |
+
# Get all supported code files in the directory
|
| 183 |
+
code_files = get_supported_code_files(directory_path)
|
| 184 |
+
|
| 185 |
+
# Progress Value
|
| 186 |
+
progress_value = int( 100 / (len(code_files) + 1))
|
| 187 |
+
|
| 188 |
+
# Process each code file
|
| 189 |
+
for idx, file_path in enumerate(code_files):
|
| 190 |
+
if callback_func:
|
| 191 |
+
callback_func(idx, file_path, progress_value)
|
| 192 |
+
|
| 193 |
+
try:
|
| 194 |
+
agents_names = [agent.name for agent in analysis_agents]
|
| 195 |
+
file_language = detect_language_by_extension(file_path)
|
| 196 |
+
file_analysis = analyze_code_file(file_path, analysis_agents)
|
| 197 |
+
|
| 198 |
+
if file_analysis is not None:
|
| 199 |
+
analysis_results[file_path] = {
|
| 200 |
+
"language": file_language,
|
| 201 |
+
"analysis_results": file_analysis
|
| 202 |
+
}
|
| 203 |
+
|
| 204 |
+
if verbose:
|
| 205 |
+
print(f"Analyzed {file_path} Successfully using agents {agents_names} [+]")
|
| 206 |
+
|
| 207 |
+
except Exception as e:
|
| 208 |
+
print(f"Error processing file {file_path}: {str(e)}")
|
| 209 |
+
continue
|
| 210 |
+
|
| 211 |
+
|
| 212 |
+
|
| 213 |
+
time.sleep(time_between_analysis)
|
| 214 |
+
|
| 215 |
+
if verbose:
|
| 216 |
+
print(f"Analyzed {len(code_files)} files in {directory_path}")
|
| 217 |
+
|
| 218 |
+
return analysis_results
|