737 lines
22 KiB
Python
737 lines
22 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Suggest architectural improvements for Bubble Tea applications.
|
|
Analyzes complexity and recommends patterns like model trees, composable views, etc.
|
|
"""
|
|
|
|
import os
|
|
import re
|
|
import json
|
|
from pathlib import Path
|
|
from typing import Dict, List, Any, Tuple, Optional
|
|
|
|
|
|
def suggest_architecture(code_path: str, complexity_level: str = "auto") -> Dict[str, Any]:
|
|
"""
|
|
Analyze code and suggest architectural improvements.
|
|
|
|
Args:
|
|
code_path: Path to Go file or directory
|
|
complexity_level: "auto" (detect), "simple", "medium", "complex"
|
|
|
|
Returns:
|
|
Dictionary containing:
|
|
- current_pattern: Detected architectural pattern
|
|
- complexity_score: 0-100 (higher = more complex)
|
|
- recommended_pattern: Suggested pattern for improvement
|
|
- refactoring_steps: List of steps to implement
|
|
- code_templates: Example code for new pattern
|
|
- validation: Validation report
|
|
"""
|
|
path = Path(code_path)
|
|
|
|
if not path.exists():
|
|
return {
|
|
"error": f"Path not found: {code_path}",
|
|
"validation": {"status": "error", "summary": "Invalid path"}
|
|
}
|
|
|
|
# Collect all .go files
|
|
go_files = []
|
|
if path.is_file():
|
|
if path.suffix == '.go':
|
|
go_files = [path]
|
|
else:
|
|
go_files = list(path.glob('**/*.go'))
|
|
|
|
if not go_files:
|
|
return {
|
|
"error": "No .go files found",
|
|
"validation": {"status": "error", "summary": "No Go files"}
|
|
}
|
|
|
|
# Read all code
|
|
all_content = ""
|
|
for go_file in go_files:
|
|
try:
|
|
all_content += go_file.read_text() + "\n"
|
|
except Exception:
|
|
pass
|
|
|
|
# Analyze current architecture
|
|
current_pattern = _detect_current_pattern(all_content)
|
|
complexity_score = _calculate_complexity(all_content, go_files)
|
|
|
|
# Auto-detect complexity level if needed
|
|
if complexity_level == "auto":
|
|
if complexity_score < 30:
|
|
complexity_level = "simple"
|
|
elif complexity_score < 70:
|
|
complexity_level = "medium"
|
|
else:
|
|
complexity_level = "complex"
|
|
|
|
# Generate recommendations
|
|
recommended_pattern = _recommend_pattern(current_pattern, complexity_score, complexity_level)
|
|
refactoring_steps = _generate_refactoring_steps(current_pattern, recommended_pattern, all_content)
|
|
code_templates = _generate_code_templates(recommended_pattern, all_content)
|
|
|
|
# Summary
|
|
if recommended_pattern == current_pattern:
|
|
summary = f"✅ Current architecture ({current_pattern}) is appropriate for complexity level"
|
|
else:
|
|
summary = f"💡 Recommend refactoring from {current_pattern} to {recommended_pattern}"
|
|
|
|
# Validation
|
|
validation = {
|
|
"status": "pass" if recommended_pattern == current_pattern else "info",
|
|
"summary": summary,
|
|
"checks": {
|
|
"complexity_analyzed": complexity_score >= 0,
|
|
"pattern_detected": current_pattern != "unknown",
|
|
"has_recommendations": len(refactoring_steps) > 0,
|
|
"has_templates": len(code_templates) > 0
|
|
}
|
|
}
|
|
|
|
return {
|
|
"current_pattern": current_pattern,
|
|
"complexity_score": complexity_score,
|
|
"complexity_level": complexity_level,
|
|
"recommended_pattern": recommended_pattern,
|
|
"refactoring_steps": refactoring_steps,
|
|
"code_templates": code_templates,
|
|
"summary": summary,
|
|
"analysis": {
|
|
"files_analyzed": len(go_files),
|
|
"model_count": _count_models(all_content),
|
|
"view_functions": _count_view_functions(all_content),
|
|
"state_fields": _count_state_fields(all_content)
|
|
},
|
|
"validation": validation
|
|
}
|
|
|
|
|
|
def _detect_current_pattern(content: str) -> str:
|
|
"""Detect the current architectural pattern."""
|
|
|
|
# Check for various patterns
|
|
patterns_detected = []
|
|
|
|
# Pattern 1: Flat Model (single model struct, no child models)
|
|
has_model = bool(re.search(r'type\s+\w*[Mm]odel\s+struct', content))
|
|
has_child_models = bool(re.search(r'\w+Model\s+\w+Model', content))
|
|
|
|
if has_model and not has_child_models:
|
|
patterns_detected.append("flat_model")
|
|
|
|
# Pattern 2: Model Tree (parent model with child models)
|
|
if has_child_models:
|
|
patterns_detected.append("model_tree")
|
|
|
|
# Pattern 3: Multi-view (multiple view rendering based on state)
|
|
has_view_switcher = bool(re.search(r'switch\s+m\.\w*(view|mode|screen|state)', content, re.IGNORECASE))
|
|
if has_view_switcher:
|
|
patterns_detected.append("multi_view")
|
|
|
|
# Pattern 4: Component-based (using Bubble Tea components like list, viewport, etc.)
|
|
bubbletea_components = [
|
|
'list.Model',
|
|
'viewport.Model',
|
|
'textinput.Model',
|
|
'textarea.Model',
|
|
'table.Model',
|
|
'progress.Model',
|
|
'spinner.Model'
|
|
]
|
|
component_count = sum(1 for comp in bubbletea_components if comp in content)
|
|
|
|
if component_count >= 3:
|
|
patterns_detected.append("component_based")
|
|
elif component_count >= 1:
|
|
patterns_detected.append("uses_components")
|
|
|
|
# Pattern 5: State Machine (explicit state enums/constants)
|
|
has_state_enum = bool(re.search(r'type\s+\w*State\s+(int|string)', content))
|
|
has_iota_states = bool(re.search(r'const\s+\(\s*\w+State\s+\w*State\s+=\s+iota', content))
|
|
|
|
if has_state_enum or has_iota_states:
|
|
patterns_detected.append("state_machine")
|
|
|
|
# Pattern 6: Event-driven (heavy use of custom messages)
|
|
custom_msg_count = len(re.findall(r'type\s+\w+Msg\s+struct', content))
|
|
if custom_msg_count >= 5:
|
|
patterns_detected.append("event_driven")
|
|
|
|
# Return the most dominant pattern
|
|
if "model_tree" in patterns_detected:
|
|
return "model_tree"
|
|
elif "state_machine" in patterns_detected and "multi_view" in patterns_detected:
|
|
return "state_machine_multi_view"
|
|
elif "component_based" in patterns_detected:
|
|
return "component_based"
|
|
elif "multi_view" in patterns_detected:
|
|
return "multi_view"
|
|
elif "flat_model" in patterns_detected:
|
|
return "flat_model"
|
|
elif has_model:
|
|
return "basic_model"
|
|
else:
|
|
return "unknown"
|
|
|
|
|
|
def _calculate_complexity(content: str, files: List[Path]) -> int:
|
|
"""Calculate complexity score (0-100)."""
|
|
|
|
score = 0
|
|
|
|
# Factor 1: Number of files (10 points max)
|
|
file_count = len(files)
|
|
score += min(10, file_count * 2)
|
|
|
|
# Factor 2: Model field count (20 points max)
|
|
model_match = re.search(r'type\s+(\w*[Mm]odel)\s+struct\s*\{([^}]+)\}', content, re.DOTALL)
|
|
if model_match:
|
|
model_body = model_match.group(2)
|
|
field_count = len([line for line in model_body.split('\n')
|
|
if line.strip() and not line.strip().startswith('//')])
|
|
score += min(20, field_count)
|
|
|
|
# Factor 3: Number of Update() branches (20 points max)
|
|
update_match = re.search(r'func\s+\([^)]+\)\s+Update\s*\([^)]+\)\s*\([^)]+\)\s*\{(.+?)^func\s',
|
|
content, re.DOTALL | re.MULTILINE)
|
|
if update_match:
|
|
update_body = update_match.group(1)
|
|
case_count = len(re.findall(r'case\s+', update_body))
|
|
score += min(20, case_count * 2)
|
|
|
|
# Factor 4: View() complexity (15 points max)
|
|
view_match = re.search(r'func\s+\([^)]+\)\s+View\s*\(\s*\)\s+string\s*\{(.+?)^func\s',
|
|
content, re.DOTALL | re.MULTILINE)
|
|
if view_match:
|
|
view_body = view_match.group(1)
|
|
view_lines = len(view_body.split('\n'))
|
|
score += min(15, view_lines // 2)
|
|
|
|
# Factor 5: Custom message types (10 points max)
|
|
custom_msg_count = len(re.findall(r'type\s+\w+Msg\s+struct', content))
|
|
score += min(10, custom_msg_count * 2)
|
|
|
|
# Factor 6: Number of views/screens (15 points max)
|
|
view_count = len(re.findall(r'func\s+\([^)]+\)\s+render\w+', content, re.IGNORECASE))
|
|
score += min(15, view_count * 3)
|
|
|
|
# Factor 7: Use of channels/goroutines (10 points max)
|
|
has_channels = len(re.findall(r'make\s*\(\s*chan\s+', content))
|
|
has_goroutines = len(re.findall(r'\bgo\s+func', content))
|
|
score += min(10, (has_channels + has_goroutines) * 2)
|
|
|
|
return min(100, score)
|
|
|
|
|
|
def _recommend_pattern(current: str, complexity: int, level: str) -> str:
|
|
"""Recommend architectural pattern based on current state and complexity."""
|
|
|
|
# Simple apps (< 30 complexity)
|
|
if complexity < 30:
|
|
if current in ["unknown", "basic_model"]:
|
|
return "flat_model" # Simple flat model is fine
|
|
return current # Keep current pattern
|
|
|
|
# Medium complexity (30-70)
|
|
elif complexity < 70:
|
|
if current == "flat_model":
|
|
return "multi_view" # Evolve to multi-view
|
|
elif current == "basic_model":
|
|
return "component_based" # Start using components
|
|
return current
|
|
|
|
# High complexity (70+)
|
|
else:
|
|
if current in ["flat_model", "multi_view"]:
|
|
return "model_tree" # Need hierarchy
|
|
elif current == "component_based":
|
|
return "model_tree_with_components" # Combine patterns
|
|
return current
|
|
|
|
|
|
def _count_models(content: str) -> int:
|
|
"""Count model structs."""
|
|
return len(re.findall(r'type\s+\w*[Mm]odel\s+struct', content))
|
|
|
|
|
|
def _count_view_functions(content: str) -> int:
|
|
"""Count view rendering functions."""
|
|
return len(re.findall(r'func\s+\([^)]+\)\s+(View|render\w+)', content, re.IGNORECASE))
|
|
|
|
|
|
def _count_state_fields(content: str) -> int:
|
|
"""Count state fields in model."""
|
|
model_match = re.search(r'type\s+(\w*[Mm]odel)\s+struct\s*\{([^}]+)\}', content, re.DOTALL)
|
|
if not model_match:
|
|
return 0
|
|
|
|
model_body = model_match.group(2)
|
|
return len([line for line in model_body.split('\n')
|
|
if line.strip() and not line.strip().startswith('//')])
|
|
|
|
|
|
def _generate_refactoring_steps(current: str, recommended: str, content: str) -> List[str]:
|
|
"""Generate step-by-step refactoring guide."""
|
|
|
|
if current == recommended:
|
|
return ["No refactoring needed - current architecture is appropriate"]
|
|
|
|
steps = []
|
|
|
|
# Flat Model → Multi-view
|
|
if current == "flat_model" and recommended == "multi_view":
|
|
steps = [
|
|
"1. Add view state enum to model",
|
|
"2. Create separate render functions for each view",
|
|
"3. Add view switching logic in Update()",
|
|
"4. Implement switch statement in View() to route to render functions",
|
|
"5. Add keyboard shortcuts for view navigation"
|
|
]
|
|
|
|
# Flat Model → Model Tree
|
|
elif current == "flat_model" and recommended == "model_tree":
|
|
steps = [
|
|
"1. Identify logical groupings of fields in current model",
|
|
"2. Create child model structs for each grouping",
|
|
"3. Add Init() methods to child models",
|
|
"4. Create parent model with child model fields",
|
|
"5. Implement message routing in parent's Update()",
|
|
"6. Delegate rendering to child models in View()",
|
|
"7. Test each child model independently"
|
|
]
|
|
|
|
# Multi-view → Model Tree
|
|
elif current == "multi_view" and recommended == "model_tree":
|
|
steps = [
|
|
"1. Convert each view into a separate child model",
|
|
"2. Extract view-specific state into child models",
|
|
"3. Create parent router model with activeView field",
|
|
"4. Implement message routing based on activeView",
|
|
"5. Move view rendering logic into child models",
|
|
"6. Add inter-model communication via custom messages"
|
|
]
|
|
|
|
# Component-based → Model Tree with Components
|
|
elif current == "component_based" and recommended == "model_tree_with_components":
|
|
steps = [
|
|
"1. Group related components into logical views",
|
|
"2. Create view models that own related components",
|
|
"3. Create parent model to manage view models",
|
|
"4. Implement message routing to active view",
|
|
"5. Keep component updates within their view models",
|
|
"6. Compose final view from view model renders"
|
|
]
|
|
|
|
# Basic Model → Component-based
|
|
elif current == "basic_model" and recommended == "component_based":
|
|
steps = [
|
|
"1. Identify UI patterns that match Bubble Tea components",
|
|
"2. Replace custom text input with textinput.Model",
|
|
"3. Replace custom list with list.Model",
|
|
"4. Replace custom scrolling with viewport.Model",
|
|
"5. Update Init() to initialize components",
|
|
"6. Route messages to components in Update()",
|
|
"7. Compose View() using component.View() calls"
|
|
]
|
|
|
|
# Generic fallback
|
|
else:
|
|
steps = [
|
|
f"1. Analyze current {current} pattern",
|
|
f"2. Study {recommended} pattern examples",
|
|
"3. Plan gradual migration strategy",
|
|
"4. Implement incrementally with tests",
|
|
"5. Validate each step before proceeding"
|
|
]
|
|
|
|
return steps
|
|
|
|
|
|
def _generate_code_templates(pattern: str, existing_code: str) -> Dict[str, str]:
|
|
"""Generate code templates for recommended pattern."""
|
|
|
|
templates = {}
|
|
|
|
if pattern == "model_tree":
|
|
templates["parent_model"] = '''// Parent model manages child models
|
|
type appModel struct {
|
|
activeView int
|
|
|
|
// Child models
|
|
listView listViewModel
|
|
detailView detailViewModel
|
|
searchView searchViewModel
|
|
}
|
|
|
|
func (m appModel) Init() tea.Cmd {
|
|
return tea.Batch(
|
|
m.listView.Init(),
|
|
m.detailView.Init(),
|
|
m.searchView.Init(),
|
|
)
|
|
}
|
|
|
|
func (m appModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|
var cmd tea.Cmd
|
|
|
|
// Global navigation
|
|
if key, ok := msg.(tea.KeyMsg); ok {
|
|
switch key.String() {
|
|
case "1":
|
|
m.activeView = 0
|
|
return m, nil
|
|
case "2":
|
|
m.activeView = 1
|
|
return m, nil
|
|
case "3":
|
|
m.activeView = 2
|
|
return m, nil
|
|
}
|
|
}
|
|
|
|
// Route to active child
|
|
switch m.activeView {
|
|
case 0:
|
|
m.listView, cmd = m.listView.Update(msg)
|
|
case 1:
|
|
m.detailView, cmd = m.detailView.Update(msg)
|
|
case 2:
|
|
m.searchView, cmd = m.searchView.Update(msg)
|
|
}
|
|
|
|
return m, cmd
|
|
}
|
|
|
|
func (m appModel) View() string {
|
|
switch m.activeView {
|
|
case 0:
|
|
return m.listView.View()
|
|
case 1:
|
|
return m.detailView.View()
|
|
case 2:
|
|
return m.searchView.View()
|
|
}
|
|
return ""
|
|
}'''
|
|
|
|
templates["child_model"] = '''// Child model handles its own state and rendering
|
|
type listViewModel struct {
|
|
items []string
|
|
cursor int
|
|
selected map[int]bool
|
|
}
|
|
|
|
func (m listViewModel) Init() tea.Cmd {
|
|
return nil
|
|
}
|
|
|
|
func (m listViewModel) Update(msg tea.Msg) (listViewModel, tea.Cmd) {
|
|
switch msg := msg.(type) {
|
|
case tea.KeyMsg:
|
|
switch msg.String() {
|
|
case "up", "k":
|
|
if m.cursor > 0 {
|
|
m.cursor--
|
|
}
|
|
case "down", "j":
|
|
if m.cursor < len(m.items)-1 {
|
|
m.cursor++
|
|
}
|
|
case " ":
|
|
m.selected[m.cursor] = !m.selected[m.cursor]
|
|
}
|
|
}
|
|
return m, nil
|
|
}
|
|
|
|
func (m listViewModel) View() string {
|
|
s := "Select items:\\n\\n"
|
|
for i, item := range m.items {
|
|
cursor := " "
|
|
if m.cursor == i {
|
|
cursor = ">"
|
|
}
|
|
checked := " "
|
|
if m.selected[i] {
|
|
checked = "x"
|
|
}
|
|
s += fmt.Sprintf("%s [%s] %s\\n", cursor, checked, item)
|
|
}
|
|
return s
|
|
}'''
|
|
|
|
templates["message_passing"] = '''// Custom message for inter-model communication
|
|
type itemSelectedMsg struct {
|
|
itemID string
|
|
}
|
|
|
|
// In listViewModel:
|
|
func (m listViewModel) Update(msg tea.Msg) (listViewModel, tea.Cmd) {
|
|
switch msg := msg.(type) {
|
|
case tea.KeyMsg:
|
|
if msg.String() == "enter" {
|
|
// Send message to parent (who routes to detail view)
|
|
return m, func() tea.Msg {
|
|
return itemSelectedMsg{itemID: m.items[m.cursor]}
|
|
}
|
|
}
|
|
}
|
|
return m, nil
|
|
}
|
|
|
|
// In appModel:
|
|
func (m appModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|
switch msg := msg.(type) {
|
|
case itemSelectedMsg:
|
|
// List selected item, switch to detail view
|
|
m.detailView.LoadItem(msg.itemID)
|
|
m.activeView = 1 // Switch to detail
|
|
return m, nil
|
|
}
|
|
|
|
// Route to children...
|
|
return m, nil
|
|
}'''
|
|
|
|
elif pattern == "multi_view":
|
|
templates["view_state"] = '''type viewState int
|
|
|
|
const (
|
|
listView viewState = iota
|
|
detailView
|
|
searchView
|
|
)
|
|
|
|
type model struct {
|
|
currentView viewState
|
|
|
|
// View-specific state
|
|
listItems []string
|
|
listCursor int
|
|
detailItem string
|
|
searchQuery string
|
|
}'''
|
|
|
|
templates["view_switching"] = '''func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|
switch msg := msg.(type) {
|
|
case tea.KeyMsg:
|
|
// Global navigation
|
|
switch msg.String() {
|
|
case "1":
|
|
m.currentView = listView
|
|
return m, nil
|
|
case "2":
|
|
m.currentView = detailView
|
|
return m, nil
|
|
case "3":
|
|
m.currentView = searchView
|
|
return m, nil
|
|
}
|
|
|
|
// View-specific handling
|
|
switch m.currentView {
|
|
case listView:
|
|
return m.updateListView(msg)
|
|
case detailView:
|
|
return m.updateDetailView(msg)
|
|
case searchView:
|
|
return m.updateSearchView(msg)
|
|
}
|
|
}
|
|
return m, nil
|
|
}
|
|
|
|
func (m model) View() string {
|
|
switch m.currentView {
|
|
case listView:
|
|
return m.renderListView()
|
|
case detailView:
|
|
return m.renderDetailView()
|
|
case searchView:
|
|
return m.renderSearchView()
|
|
}
|
|
return ""
|
|
}'''
|
|
|
|
elif pattern == "component_based":
|
|
templates["using_components"] = '''import (
|
|
"github.com/charmbracelet/bubbles/list"
|
|
"github.com/charmbracelet/bubbles/textinput"
|
|
"github.com/charmbracelet/bubbles/viewport"
|
|
tea "github.com/charmbracelet/bubbletea"
|
|
)
|
|
|
|
type model struct {
|
|
list list.Model
|
|
search textinput.Model
|
|
viewer viewport.Model
|
|
activeComponent int
|
|
}
|
|
|
|
func initialModel() model {
|
|
// Initialize components
|
|
items := []list.Item{
|
|
item{title: "Item 1", desc: "Description"},
|
|
item{title: "Item 2", desc: "Description"},
|
|
}
|
|
|
|
l := list.New(items, list.NewDefaultDelegate(), 20, 10)
|
|
l.Title = "Items"
|
|
|
|
ti := textinput.New()
|
|
ti.Placeholder = "Search..."
|
|
ti.Focus()
|
|
|
|
vp := viewport.New(80, 20)
|
|
|
|
return model{
|
|
list: l,
|
|
search: ti,
|
|
viewer: vp,
|
|
activeComponent: 0,
|
|
}
|
|
}
|
|
|
|
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|
var cmd tea.Cmd
|
|
|
|
// Route to active component
|
|
switch m.activeComponent {
|
|
case 0:
|
|
m.list, cmd = m.list.Update(msg)
|
|
case 1:
|
|
m.search, cmd = m.search.Update(msg)
|
|
case 2:
|
|
m.viewer, cmd = m.viewer.Update(msg)
|
|
}
|
|
|
|
return m, cmd
|
|
}
|
|
|
|
func (m model) View() string {
|
|
return lipgloss.JoinVertical(
|
|
lipgloss.Left,
|
|
m.search.View(),
|
|
m.list.View(),
|
|
m.viewer.View(),
|
|
)
|
|
}'''
|
|
|
|
elif pattern == "state_machine_multi_view":
|
|
templates["state_machine"] = '''type appState int
|
|
|
|
const (
|
|
loadingState appState = iota
|
|
listState
|
|
detailState
|
|
errorState
|
|
)
|
|
|
|
type model struct {
|
|
state appState
|
|
prevState appState
|
|
|
|
// State data
|
|
items []string
|
|
selected string
|
|
error error
|
|
}
|
|
|
|
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|
switch msg := msg.(type) {
|
|
case itemsLoadedMsg:
|
|
m.items = msg.items
|
|
m.state = listState
|
|
return m, nil
|
|
|
|
case itemSelectedMsg:
|
|
m.selected = msg.item
|
|
m.state = detailState
|
|
return m, loadItemDetails
|
|
|
|
case errorMsg:
|
|
m.prevState = m.state
|
|
m.state = errorState
|
|
m.error = msg.err
|
|
return m, nil
|
|
|
|
case tea.KeyMsg:
|
|
if msg.String() == "esc" && m.state == errorState {
|
|
m.state = m.prevState // Return to previous state
|
|
return m, nil
|
|
}
|
|
}
|
|
|
|
// State-specific update
|
|
switch m.state {
|
|
case listState:
|
|
return m.updateList(msg)
|
|
case detailState:
|
|
return m.updateDetail(msg)
|
|
}
|
|
|
|
return m, nil
|
|
}
|
|
|
|
func (m model) View() string {
|
|
switch m.state {
|
|
case loadingState:
|
|
return "Loading..."
|
|
case listState:
|
|
return m.renderList()
|
|
case detailState:
|
|
return m.renderDetail()
|
|
case errorState:
|
|
return fmt.Sprintf("Error: %v\\nPress ESC to continue", m.error)
|
|
}
|
|
return ""
|
|
}'''
|
|
|
|
return templates
|
|
|
|
|
|
def validate_architecture_suggestion(result: Dict[str, Any]) -> Dict[str, Any]:
|
|
"""Validate architecture suggestion result."""
|
|
if 'error' in result:
|
|
return {"status": "error", "summary": result['error']}
|
|
|
|
validation = result.get('validation', {})
|
|
status = validation.get('status', 'unknown')
|
|
summary = validation.get('summary', 'Architecture analysis complete')
|
|
|
|
checks = [
|
|
(result.get('current_pattern') is not None, "Pattern detected"),
|
|
(result.get('complexity_score') is not None, "Complexity calculated"),
|
|
(result.get('recommended_pattern') is not None, "Recommendation generated"),
|
|
(len(result.get('refactoring_steps', [])) > 0, "Has refactoring steps"),
|
|
]
|
|
|
|
all_pass = all(check[0] for check in checks)
|
|
|
|
return {
|
|
"status": status,
|
|
"summary": summary,
|
|
"checks": {check[1]: check[0] for check in checks},
|
|
"valid": all_pass
|
|
}
|
|
|
|
|
|
if __name__ == "__main__":
|
|
import sys
|
|
|
|
if len(sys.argv) < 2:
|
|
print("Usage: suggest_architecture.py <code_path> [complexity_level]")
|
|
sys.exit(1)
|
|
|
|
code_path = sys.argv[1]
|
|
complexity_level = sys.argv[2] if len(sys.argv) > 2 else "auto"
|
|
|
|
result = suggest_architecture(code_path, complexity_level)
|
|
print(json.dumps(result, indent=2))
|