mdan-cli 2.6.0 โ†’ 2.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,297 @@
1
+ """Build Flow - BUILD phase flow
2
+
3
+ Orchestrates the BUILD phase: implementation, refactoring, code review, testing, debugging.
4
+ """
5
+
6
+ from crewai.flow import Flow, listen, start
7
+ from typing import Dict, Any, Optional
8
+ import asyncio
9
+ import json
10
+ from pathlib import Path
11
+
12
+ from ..agents.dev_agent import DevAgent
13
+ from ..agents.security_agent import SecurityAgent
14
+
15
+ from ..tools.sql_tool import SQLTool
16
+ from ..tools.serper_tool import SerperTool
17
+ from ..tools.file_tool import FileTool
18
+
19
+
20
+ class BuildFlow(Flow):
21
+ """BUILD phase flow for implementation and development."""
22
+
23
+ def __init__(
24
+ self,
25
+ project_path: str,
26
+ llm=None,
27
+ sql_config: Optional[Dict[str, Any]] = None,
28
+ serper_api_key: Optional[str] = None,
29
+ ):
30
+ """Initialize Build Flow.
31
+
32
+ Args:
33
+ project_path: Path to the project directory
34
+ llm: Language model instance
35
+ sql_config: SQL database configuration
36
+ serper_api_key: Serper API key for web search
37
+ """
38
+ super().__init__()
39
+ self.project_path = Path(project_path)
40
+ self.llm = llm
41
+
42
+ # Initialize tools
43
+ self.sql_tool = SQLTool(**sql_config) if sql_config else None
44
+ self.serper_tool = (
45
+ SerperTool(api_key=serper_api_key) if serper_api_key else None
46
+ )
47
+ self.file_tool = FileTool(base_path=project_path)
48
+
49
+ # Initialize agents
50
+ self.dev_agent = DevAgent(
51
+ sql_tool=self.sql_tool,
52
+ serper_tool=self.serper_tool,
53
+ file_tool=self.file_tool,
54
+ llm=llm,
55
+ )
56
+
57
+ self.security_agent = SecurityAgent(
58
+ sql_tool=self.sql_tool,
59
+ serper_tool=self.serper_tool,
60
+ file_tool=self.file_tool,
61
+ llm=llm,
62
+ )
63
+
64
+ # Flow state
65
+ self.state = {
66
+ "current_step": None,
67
+ "project_context": {},
68
+ "step_results": {},
69
+ "errors": [],
70
+ }
71
+
72
+ @start
73
+ async def load_context(self, design_context: str):
74
+ """Load design context and architecture."""
75
+ self.state["current_step"] = "load_context"
76
+
77
+ print("๐Ÿ“‚ Loading design context...")
78
+
79
+ # Load project state if exists
80
+ state_file = self.project_path / "MDAN-STATE.json"
81
+ if state_file.exists():
82
+ with open(state_file, "r") as f:
83
+ self.state["project_context"] = json.load(f)
84
+ else:
85
+ self.state["project_context"] = {
86
+ "project_name": self.project_path.name,
87
+ "design_context": design_context,
88
+ }
89
+
90
+ self.state["step_results"]["load_context"] = {
91
+ "status": "completed",
92
+ "context": self.state["project_context"],
93
+ }
94
+
95
+ return self.state["project_context"]
96
+
97
+ @listen(load_context)
98
+ async def implement_features(self, context: Dict[str, Any]):
99
+ """Implement features based on design."""
100
+ self.state["current_step"] = "implement_features"
101
+
102
+ print("๐Ÿ’ป Implementing features...")
103
+
104
+ task = self.dev_agent.create_implementation_task(str(context))
105
+
106
+ try:
107
+ result = await asyncio.to_thread(task.execute)
108
+ self.state["step_results"]["implement_features"] = {
109
+ "status": "completed",
110
+ "result": result,
111
+ }
112
+ print("โœ… Features implemented successfully")
113
+ return result
114
+ except Exception as e:
115
+ self.state["errors"].append(f"Implementation error: {str(e)}")
116
+ print(f"โŒ Error implementing features: {str(e)}")
117
+ return None
118
+
119
+ @listen(implement_features)
120
+ async def security_review(self, implementation_result: Any):
121
+ """Conduct security review of implementation."""
122
+ self.state["current_step"] = "security_review"
123
+
124
+ print("๐Ÿ”’ Conducting security review...")
125
+
126
+ task = self.security_agent.create_security_review_task(
127
+ str(implementation_result)
128
+ )
129
+
130
+ try:
131
+ result = await asyncio.to_thread(task.execute)
132
+ self.state["step_results"]["security_review"] = {
133
+ "status": "completed",
134
+ "result": result,
135
+ }
136
+ print("โœ… Security review completed")
137
+ return result
138
+ except Exception as e:
139
+ self.state["errors"].append(f"Security review error: {str(e)}")
140
+ print(f"โŒ Error in security review: {str(e)}")
141
+ return None
142
+
143
+ @listen(security_review)
144
+ async def refactor_code(self, security_result: Any):
145
+ """Refactor code based on feedback."""
146
+ self.state["current_step"] = "refactor_code"
147
+
148
+ print("๐Ÿ”ง Refactoring code...")
149
+
150
+ task = self.dev_agent.create_refactoring_task(str(security_result))
151
+
152
+ try:
153
+ result = await asyncio.to_thread(task.execute)
154
+ self.state["step_results"]["refactor_code"] = {
155
+ "status": "completed",
156
+ "result": result,
157
+ }
158
+ print("โœ… Code refactored successfully")
159
+ return result
160
+ except Exception as e:
161
+ self.state["errors"].append(f"Refactoring error: {str(e)}")
162
+ print(f"โŒ Error refactoring code: {str(e)}")
163
+ return None
164
+
165
+ @listen(refactor_code)
166
+ async def code_review(self, refactor_result: Any):
167
+ """Conduct code review."""
168
+ self.state["current_step"] = "code_review"
169
+
170
+ print("๐Ÿ‘€ Conducting code review...")
171
+
172
+ task = self.dev_agent.create_code_review_task(str(refactor_result))
173
+
174
+ try:
175
+ result = await asyncio.to_thread(task.execute)
176
+ self.state["step_results"]["code_review"] = {
177
+ "status": "completed",
178
+ "result": result,
179
+ }
180
+ print("โœ… Code review completed")
181
+ return result
182
+ except Exception as e:
183
+ self.state["errors"].append(f"Code review error: {str(e)}")
184
+ print(f"โŒ Error in code review: {str(e)}")
185
+ return None
186
+
187
+ @listen(code_review)
188
+ async def write_tests(self, review_result: Any):
189
+ """Write tests for the implementation."""
190
+ self.state["current_step"] = "write_tests"
191
+
192
+ print("๐Ÿงช Writing tests...")
193
+
194
+ task = self.dev_agent.create_testing_task(str(review_result))
195
+
196
+ try:
197
+ result = await asyncio.to_thread(task.execute)
198
+ self.state["step_results"]["write_tests"] = {
199
+ "status": "completed",
200
+ "result": result,
201
+ }
202
+ print("โœ… Tests written successfully")
203
+ return result
204
+ except Exception as e:
205
+ self.state["errors"].append(f"Test writing error: {str(e)}")
206
+ print(f"โŒ Error writing tests: {str(e)}")
207
+ return None
208
+
209
+ @listen(write_tests)
210
+ async def debug_issues(self, test_result: Any):
211
+ """Debug any issues found."""
212
+ self.state["current_step"] = "debug_issues"
213
+
214
+ print("๐Ÿ› Debugging issues...")
215
+
216
+ task = self.dev_agent.create_debugging_task(str(test_result))
217
+
218
+ try:
219
+ result = await asyncio.to_thread(task.execute)
220
+ self.state["step_results"]["debug_issues"] = {
221
+ "status": "completed",
222
+ "result": result,
223
+ }
224
+ print("โœ… Debugging completed")
225
+ return result
226
+ except Exception as e:
227
+ self.state["errors"].append(f"Debugging error: {str(e)}")
228
+ print(f"โŒ Error debugging: {str(e)}")
229
+ return None
230
+
231
+ @listen(debug_issues)
232
+ async def finalize_build(self, debug_result: Any):
233
+ """Finalize BUILD phase."""
234
+ print("โœจ BUILD phase completed!")
235
+
236
+ # Update project state
237
+ self.state["project_context"]["phases_completed"] = self.state[
238
+ "project_context"
239
+ ].get("phases_completed", [])
240
+ if "BUILD" not in self.state["project_context"]["phases_completed"]:
241
+ self.state["project_context"]["phases_completed"].append("BUILD")
242
+ self.state["project_context"]["current_phase"] = "BUILD_COMPLETED"
243
+
244
+ # Save state
245
+ state_file = self.project_path / "MDAN-STATE.json"
246
+ with open(state_file, "w") as f:
247
+ json.dump(self.state["project_context"], f, indent=2)
248
+
249
+ print(f"โœ… Project state saved to {state_file}")
250
+
251
+ if self.state["errors"]:
252
+ print(f"\nโš ๏ธ Errors encountered: {len(self.state['errors'])}")
253
+ for error in self.state["errors"]:
254
+ print(f" - {error}")
255
+
256
+ return {
257
+ "status": "completed",
258
+ "steps": self.state["step_results"],
259
+ "errors": self.state["errors"],
260
+ }
261
+
262
+ def get_state(self) -> Dict[str, Any]:
263
+ """Get current flow state.
264
+
265
+ Returns:
266
+ Current flow state
267
+ """
268
+ return self.state
269
+
270
+ def save_context(self, filepath: str):
271
+ """Save flow context to file.
272
+
273
+ Args:
274
+ filepath: Path to save context
275
+ """
276
+ context = {"state": self.state, "project_path": str(self.project_path)}
277
+ with open(filepath, "w") as f:
278
+ json.dump(context, f, indent=2)
279
+
280
+ @classmethod
281
+ def load_context(cls, filepath: str, llm=None) -> "BuildFlow":
282
+ """Load flow context from file.
283
+
284
+ Args:
285
+ filepath: Path to load context from
286
+ llm: Language model instance
287
+
288
+ Returns:
289
+ BuildFlow instance with loaded context
290
+ """
291
+ with open(filepath, "r") as f:
292
+ context = json.load(f)
293
+
294
+ flow = cls(project_path=context["project_path"], llm=llm)
295
+ flow.state = context["state"]
296
+
297
+ return flow