77from pathlib import Path
88from typing import Dict , Any , List , Optional
99
10+ from .cache_manager import get_cache
11+ from .ui_manager import UIManager
12+
13+
14+ # Express configurations for common project types
15+ EXPRESS_CONFIGS = {
16+ "react-typescript" : {
17+ "patterns" : ["react" , "typescript" , "tsx" , "jsx" ],
18+ "roles" : {"default" : "dev" , "specialized" : ["frontend" , "code-reviewer" ]},
19+ "github_integration" : {"issue_to_task" : True , "pr_reviews" : True },
20+ "build_validation" : ["npm test" , "npm run build" ],
21+ "suggested_tasks" : [
22+ {
23+ "title" : "Set up component testing with React Testing Library" ,
24+ "labels" : ["conductor:task" , "testing" , "frontend" ],
25+ },
26+ {
27+ "title" : "Add Storybook for component development" ,
28+ "labels" : ["conductor:task" , "enhancement" , "frontend" ],
29+ },
30+ {
31+ "title" : "Configure ESLint and Prettier" ,
32+ "labels" : ["conductor:task" , "code-quality" , "dev-experience" ],
33+ },
34+ ],
35+ },
36+ "python-fastapi" : {
37+ "patterns" : ["fastapi" , "python" , "uvicorn" , "pydantic" ],
38+ "roles" : {"default" : "dev" , "specialized" : ["backend" , "code-reviewer" ]},
39+ "github_integration" : {"issue_to_task" : True , "pr_reviews" : True },
40+ "build_validation" : ["pytest" , "black --check ." ],
41+ "suggested_tasks" : [
42+ {
43+ "title" : "Add API documentation with OpenAPI" ,
44+ "labels" : ["conductor:task" , "documentation" , "backend" ],
45+ },
46+ {
47+ "title" : "Set up database migrations with Alembic" ,
48+ "labels" : ["conductor:task" , "database" , "backend" ],
49+ },
50+ {
51+ "title" : "Add integration tests for endpoints" ,
52+ "labels" : ["conductor:task" , "testing" , "backend" ],
53+ },
54+ ],
55+ },
56+ "nextjs-fullstack" : {
57+ "patterns" : ["next" , "react" , "vercel" ],
58+ "roles" : {
59+ "default" : "dev" ,
60+ "specialized" : ["frontend" , "backend" , "code-reviewer" ],
61+ },
62+ "github_integration" : {"issue_to_task" : True , "pr_reviews" : True },
63+ "build_validation" : ["npm test" , "npm run build" , "npm run lint" ],
64+ "suggested_tasks" : [
65+ {
66+ "title" : "Set up authentication with NextAuth.js" ,
67+ "labels" : ["conductor:task" , "auth" , "fullstack" ],
68+ },
69+ {
70+ "title" : "Configure Prisma for database access" ,
71+ "labels" : ["conductor:task" , "database" , "backend" ],
72+ },
73+ {
74+ "title" : "Add E2E tests with Playwright" ,
75+ "labels" : ["conductor:task" , "testing" , "e2e" ],
76+ },
77+ ],
78+ },
79+ "vue-javascript" : {
80+ "patterns" : ["vue" , "nuxt" , "vite" ],
81+ "roles" : {"default" : "dev" , "specialized" : ["frontend" , "code-reviewer" ]},
82+ "github_integration" : {"issue_to_task" : True , "pr_reviews" : True },
83+ "build_validation" : ["npm test" , "npm run build" ],
84+ "suggested_tasks" : [
85+ {
86+ "title" : "Set up Pinia for state management" ,
87+ "labels" : ["conductor:task" , "state-management" , "frontend" ],
88+ },
89+ {
90+ "title" : "Add component testing with Vitest" ,
91+ "labels" : ["conductor:task" , "testing" , "frontend" ],
92+ },
93+ {
94+ "title" : "Configure Vue Router for navigation" ,
95+ "labels" : ["conductor:task" , "routing" , "frontend" ],
96+ },
97+ ],
98+ },
99+ "python-django" : {
100+ "patterns" : ["django" , "python" , "wsgi" ],
101+ "roles" : {"default" : "dev" , "specialized" : ["backend" , "code-reviewer" ]},
102+ "github_integration" : {"issue_to_task" : True , "pr_reviews" : True },
103+ "build_validation" : ["python manage.py test" , "black --check ." ],
104+ "suggested_tasks" : [
105+ {
106+ "title" : "Set up Django REST framework" ,
107+ "labels" : ["conductor:task" , "api" , "backend" ],
108+ },
109+ {
110+ "title" : "Configure Celery for async tasks" ,
111+ "labels" : ["conductor:task" , "async" , "backend" ],
112+ },
113+ {
114+ "title" : "Add Django Debug Toolbar" ,
115+ "labels" : ["conductor:task" , "dev-experience" , "backend" ],
116+ },
117+ ],
118+ },
119+ }
120+
10121
11122class ConfigurationManager :
12123 """Manages project configuration through interactive or automatic setup"""
@@ -18,17 +129,141 @@ def __init__(
18129 self .auto_mode = auto_mode
19130 self .debug = debug
20131 self .config = {}
132+ self .cache = get_cache ()
21133
22134 def gather_configuration (
23- self , detected_stack : List [Dict [str , Any ]]
135+ self ,
136+ detected_stack : List [Dict [str , Any ]],
137+ enhanced_stack : Optional [Dict [str , Any ]] = None ,
138+ ui : Optional [UIManager ] = None ,
24139 ) -> Dict [str , Any ]:
25- """Gather configuration through interactive prompts or auto-configuration"""
140+ """Gather configuration with express-by-default approach"""
141+ # Try express config first if we have enhanced stack info
142+ if enhanced_stack and ui :
143+ express_config = self .get_express_config (enhanced_stack )
144+ if express_config :
145+ return self .apply_express_config (express_config , enhanced_stack , ui )
146+
147+ # Fall back to legacy modes
26148 if self .auto_mode :
27149 self ._auto_configure (detected_stack )
28150 else :
29151 self ._interactive_configure (detected_stack )
30152 return self .config
31153
154+ def get_express_config (
155+ self , stack_info : Dict [str , Any ]
156+ ) -> Optional [Dict [str , Any ]]:
157+ """Match detected stack to express config"""
158+ # Use the primary stack from summary if available
159+ if stack_info .get ("summary" , {}).get ("primary_stack" ):
160+ stack_name = stack_info ["summary" ]["primary_stack" ]
161+ if stack_name in EXPRESS_CONFIGS :
162+ return EXPRESS_CONFIGS [stack_name ]
163+
164+ # Otherwise try pattern matching
165+ detected_items = set ()
166+ detected_items .update (stack_info .get ("frameworks" , []))
167+ detected_items .update (stack_info .get ("summary" , {}).get ("languages" , []))
168+ detected_items .update (stack_info .get ("summary" , {}).get ("tools" , []))
169+
170+ # Add items from modern tools
171+ modern = stack_info .get ("modern_tools" , {})
172+ if modern .get ("framework" ):
173+ detected_items .add (modern ["framework" ])
174+ if modern .get ("build_tool" ):
175+ detected_items .add (modern ["build_tool" ])
176+
177+ # Find best match
178+ best_match = None
179+ best_score = 0
180+
181+ for stack_name , config in EXPRESS_CONFIGS .items ():
182+ score = len (detected_items .intersection (config ["patterns" ]))
183+ if score > best_score :
184+ best_match = stack_name
185+ best_score = score
186+
187+ return (
188+ EXPRESS_CONFIGS .get (best_match ) if best_match and best_score > 0 else None
189+ )
190+
191+ def apply_express_config (
192+ self , express_config : Dict [str , Any ], stack_info : Dict [str , Any ], ui : UIManager
193+ ) -> Dict [str , Any ]:
194+ """Apply express configuration without prompts"""
195+ primary_stack = stack_info .get ("summary" , {}).get ("primary_stack" , "project" )
196+ ui .console .print (
197+ f"\n Detected { primary_stack } - applying optimal configuration..."
198+ )
199+
200+ with ui .create_progress () as progress :
201+ task = progress .add_task ("Configuring" , total = 4 )
202+
203+ progress .update (task , advance = 1 , description = "Setting project defaults..." )
204+ self .config ["project_name" ] = self ._infer_project_name ()
205+ self .config ["docs_directory" ] = self ._infer_docs_directory ()
206+
207+ progress .update (task , advance = 1 , description = "Configuring agent roles..." )
208+ self .config ["roles" ] = express_config ["roles" ]
209+
210+ progress .update (task , advance = 1 , description = "Enabling integrations..." )
211+ self .config ["github_integration" ] = express_config ["github_integration" ]
212+ self .config ["task_management" ] = "github-issues"
213+ self .config ["max_concurrent_agents" ] = 5
214+
215+ progress .update (task , advance = 1 , description = "Preparing starter tasks..." )
216+ self .config ["suggested_tasks" ] = express_config ["suggested_tasks" ]
217+ self .config ["build_validation" ] = express_config .get ("build_validation" , [])
218+
219+ # Add metadata
220+ self .config ["setup_mode" ] = "express"
221+ self .config ["stack_info" ] = stack_info
222+ self .config ["stack_summary" ] = stack_info .get ("summary" , {}).get (
223+ "primary_stack" , "Unknown"
224+ )
225+ self .config ["task_count" ] = len (express_config ["suggested_tasks" ])
226+
227+ return self .config
228+
229+ def _infer_project_name (self ) -> str :
230+ """Infer project name from directory or package files"""
231+ # Try package.json first
232+ if (self .project_root / "package.json" ).exists ():
233+ try :
234+ import json
235+
236+ package = json .loads ((self .project_root / "package.json" ).read_text ())
237+ if package .get ("name" ):
238+ return package ["name" ]
239+ except Exception :
240+ pass
241+
242+ # Try pyproject.toml
243+ if (self .project_root / "pyproject.toml" ).exists ():
244+ try :
245+ content = (self .project_root / "pyproject.toml" ).read_text ()
246+ for line in content .split ("\n " ):
247+ if line .strip ().startswith ("name" ):
248+ name = line .split ("=" )[1 ].strip ().strip ("\" '" )
249+ if name :
250+ return name
251+ except Exception :
252+ pass
253+
254+ # Default to directory name
255+ return self .project_root .name
256+
257+ def _infer_docs_directory (self ) -> str :
258+ """Infer documentation directory"""
259+ if (self .project_root / "docs" ).exists ():
260+ return "docs"
261+ elif (self .project_root / "documentation" ).exists ():
262+ return "documentation"
263+ elif (self .project_root / "doc" ).exists ():
264+ return "doc"
265+ return "docs"
266+
32267 def _safe_input (self , prompt : str , default : Optional [str ] = None ) -> str :
33268 """Safe input with error handling"""
34269 try :
0 commit comments