Skip to content

Commit 785de23

Browse files
authored
Merge pull request #65 from ryanmac/refactor-setup-modular-structure
refactor: modularize setup.py for better maintainability
2 parents b561447 + 01ff51d commit 785de23

File tree

15 files changed

+2643
-2176
lines changed

15 files changed

+2643
-2176
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
"""
2+
Code Conductor Setup Package
3+
This package contains all the modular components for setting up Code Conductor
4+
"""
Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
"""
2+
Configuration Management Module
3+
Handles gathering and managing project configuration through interactive prompts
4+
"""
5+
6+
import sys
7+
from pathlib import Path
8+
from typing import Dict, Any, List, Optional
9+
10+
11+
class ConfigurationManager:
12+
"""Manages project configuration through interactive or automatic setup"""
13+
14+
def __init__(
15+
self, project_root: Path, auto_mode: bool = False, debug: bool = False
16+
):
17+
self.project_root = project_root
18+
self.auto_mode = auto_mode
19+
self.debug = debug
20+
self.config = {}
21+
22+
def gather_configuration(
23+
self, detected_stack: List[Dict[str, Any]]
24+
) -> Dict[str, Any]:
25+
"""Gather configuration through interactive prompts or auto-configuration"""
26+
if self.auto_mode:
27+
self._auto_configure(detected_stack)
28+
else:
29+
self._interactive_configure(detected_stack)
30+
return self.config
31+
32+
def _safe_input(self, prompt: str, default: Optional[str] = None) -> str:
33+
"""Safe input with error handling"""
34+
try:
35+
response = input(prompt).strip()
36+
return response or default
37+
except KeyboardInterrupt:
38+
print("\n\n❌ Setup cancelled by user.")
39+
sys.exit(1)
40+
except EOFError:
41+
return default
42+
except Exception as e:
43+
if self.debug:
44+
print(f"❌ Input error: {e}")
45+
return default
46+
47+
def _interactive_configure(self, detected_stack: List[Dict[str, Any]]):
48+
"""Interactive configuration prompts"""
49+
print("\n📝 Project Configuration")
50+
print("-" * 30)
51+
52+
# Project name
53+
default_name = self.project_root.name
54+
try:
55+
self.config["project_name"] = self._safe_input(
56+
f"Project name [{default_name}]: ", default_name
57+
)
58+
except Exception as e:
59+
print(f"❌ Error reading input: {e}")
60+
print("💡 Try running with --auto flag for automatic configuration")
61+
sys.exit(1)
62+
63+
# Documentation directory
64+
self.config["docs_directory"] = self._get_docs_directory()
65+
66+
# Role configuration
67+
self._configure_roles(detected_stack)
68+
69+
# Task management approach
70+
self._configure_task_management()
71+
72+
# Concurrent agents
73+
self._configure_concurrent_agents()
74+
75+
def _get_docs_directory(self) -> str:
76+
"""Determine documentation directory"""
77+
default_docs = "docs"
78+
if (self.project_root / "docs").exists():
79+
default_docs = "docs"
80+
elif (self.project_root / "documentation").exists():
81+
default_docs = "documentation"
82+
83+
return self._safe_input(
84+
f"Documentation directory [{default_docs}]: ", default_docs
85+
)
86+
87+
def _configure_roles(self, detected_stack: List[Dict[str, Any]]):
88+
"""Configure agent roles based on detected stack"""
89+
print("\n🎭 Agent Role Configuration")
90+
print("The hybrid model uses 'dev' as the default generalist role")
91+
print("with optional specialized roles for complex tasks.")
92+
93+
# Suggest roles based on detected stack
94+
suggested = set()
95+
for stack in detected_stack:
96+
suggested.update(stack.get("suggested_roles", []))
97+
98+
suggested_str = ", ".join(suggested) if suggested else "none detected"
99+
print(f"\nSuggested specialized roles: {suggested_str}")
100+
101+
print("\nCommon specialized roles:")
102+
print(" - devops: CI/CD, deployments, infrastructure")
103+
print(" - security: Audits, vulnerability scanning")
104+
print(" - ml-engineer: Machine learning tasks")
105+
print(" - ui-designer: Design system, components")
106+
107+
roles_input = self._safe_input(
108+
"\nEnter specialized roles (comma-separated, or press Enter for none): ",
109+
"",
110+
)
111+
112+
specialized_roles = []
113+
if roles_input:
114+
specialized_roles = [r.strip() for r in roles_input.split(",") if r.strip()]
115+
116+
self.config["roles"] = {"default": "dev", "specialized": specialized_roles}
117+
118+
def _configure_task_management(self):
119+
"""Configure task management approach"""
120+
print("\n📋 Task Management Configuration")
121+
print("1. GitHub Issues (recommended) - Use labels and automation")
122+
print("2. JSON files - Direct state management")
123+
print("3. Hybrid - Both approaches")
124+
125+
choice = self._safe_input("Select approach [1]: ", "1")
126+
task_approaches = {"1": "github-issues", "2": "json-files", "3": "hybrid"}
127+
self.config["task_management"] = task_approaches.get(choice, "github-issues")
128+
129+
def _configure_concurrent_agents(self):
130+
"""Configure maximum concurrent agents"""
131+
default_concurrent = "10"
132+
max_agents = self._safe_input(
133+
f"\nMaximum concurrent agents [{default_concurrent}]: ", default_concurrent
134+
)
135+
136+
try:
137+
self.config["max_concurrent_agents"] = int(max_agents)
138+
except ValueError:
139+
print(f"⚠️ Invalid number '{max_agents}', using default: 10")
140+
self.config["max_concurrent_agents"] = 10
141+
142+
def _auto_configure(self, detected_stack: List[Dict[str, Any]]):
143+
"""Auto-configuration mode with minimal prompts"""
144+
print("\n🤖 Auto-configuration mode enabled")
145+
print("-" * 30)
146+
147+
# Use sensible defaults
148+
self.config["project_name"] = self.project_root.name
149+
self.config["docs_directory"] = "docs"
150+
151+
# Detect roles based on enhanced stack detection
152+
suggested_roles = set()
153+
detected_stacks = []
154+
155+
for stack in detected_stack:
156+
suggested_roles.update(stack.get("suggested_roles", []))
157+
if "detected_subtypes" in stack:
158+
detected_stacks.append(
159+
f"{stack['tech']} ({', '.join(stack['detected_subtypes'])})"
160+
)
161+
else:
162+
detected_stacks.append(stack["tech"])
163+
164+
# Always include code-reviewer role for AI-powered PR reviews
165+
specialized_roles = ["code-reviewer"]
166+
167+
# Add roles based on detected stack
168+
specialized_roles.extend(list(suggested_roles))
169+
170+
# Additional heuristics
171+
if any("docker" in str(f).lower() for f in self.project_root.glob("*")):
172+
if "devops" not in specialized_roles:
173+
specialized_roles.append("devops")
174+
if any("security" in str(f).lower() for f in self.project_root.glob("*")):
175+
if "security" not in specialized_roles:
176+
specialized_roles.append("security")
177+
178+
# Remove duplicates while preserving order
179+
specialized_roles = list(dict.fromkeys(specialized_roles))
180+
181+
self.config["roles"] = {"default": "dev", "specialized": specialized_roles}
182+
self.config["detected_stacks"] = detected_stacks
183+
184+
# Smart task management detection
185+
if (self.project_root / ".github").exists():
186+
self.config["task_management"] = "github-issues"
187+
else:
188+
self.config["task_management"] = "hybrid"
189+
190+
# Conservative agent count
191+
self.config["max_concurrent_agents"] = 5
192+
193+
print(f"✓ Project: {self.config['project_name']}")
194+
if detected_stacks:
195+
print(f"✓ Detected stacks: {', '.join(detected_stacks)}")
196+
print(
197+
f"✓ Roles: dev + {len(specialized_roles)} specialized "
198+
f"({', '.join(specialized_roles)})"
199+
)
200+
print(f"✓ Task management: {self.config['task_management']}")
201+
print(f"✓ Max agents: {self.config['max_concurrent_agents']}")

0 commit comments

Comments
 (0)