Complete architectural separation of concerns implementing clean layered design: • Services Layer: Pure business logic isolated from presentation - WorkspaceService: TDD workspace operations - IssueService: Issue management and creation - ProjectService: Project management and milestones - ExportService: Unix-friendly data export • CLI Layer: Clean presentation with command/presenter separation - Commands delegate to services for all business operations - Presenters handle formatted output and error messaging - Framework provides unified interface • Benefits: - Eliminates mixed concerns in 943-line CLI monolith - Enables easier testing and maintenance - Preserves all existing functionality and Unix pipeline compatibility - Provides foundation for future CLI development Resolves issue #20: CLI separation from core logic 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
88 lines
3.8 KiB
Python
88 lines
3.8 KiB
Python
"""
|
|
Project management CLI commands.
|
|
"""
|
|
|
|
from tddai import TddaiError
|
|
from services import ProjectService
|
|
from cli.presenters import OutputFormatter, ProjectView
|
|
|
|
|
|
class ProjectCommands:
|
|
"""Commands for project management operations."""
|
|
|
|
def __init__(self):
|
|
self.service = ProjectService()
|
|
|
|
def setup_project_management(self) -> None:
|
|
"""Setup project management labels and milestones."""
|
|
try:
|
|
OutputFormatter.info("Setting up project management system...")
|
|
self.service.setup_project_management()
|
|
ProjectView.show_setup_success()
|
|
except TddaiError as e:
|
|
OutputFormatter.exit_with_error(f"Error setting up project management: {e}")
|
|
|
|
def move_issue_to_state(self, issue_number: int, state: str) -> None:
|
|
"""Move issue to a specific project state."""
|
|
try:
|
|
OutputFormatter.info(f"Moving issue #{issue_number} to {state} state...")
|
|
result = self.service.set_issue_state(issue_number, state)
|
|
|
|
# If moving to done, also close the issue
|
|
if state == 'done':
|
|
self.service.move_issue_to_done(issue_number)
|
|
OutputFormatter.success(f"Issue #{issue_number} moved to {state} and closed")
|
|
else:
|
|
OutputFormatter.success(f"Issue #{issue_number} moved to {state}")
|
|
|
|
except TddaiError as e:
|
|
OutputFormatter.exit_with_error(f"Error moving issue to {state}: {e}")
|
|
|
|
def set_issue_priority(self, issue_number: int, priority: str) -> None:
|
|
"""Set issue priority."""
|
|
try:
|
|
OutputFormatter.info(f"Setting issue #{issue_number} priority to {priority}...")
|
|
result = self.service.set_issue_priority(issue_number, priority)
|
|
OutputFormatter.success(f"Issue #{issue_number} priority set to {priority}")
|
|
except TddaiError as e:
|
|
OutputFormatter.exit_with_error(f"Error setting issue priority: {e}")
|
|
|
|
def create_milestone(self, title: str, description: str = "") -> None:
|
|
"""Create a new milestone (project)."""
|
|
try:
|
|
OutputFormatter.info(f"Creating milestone: {title}")
|
|
milestone = self.service.create_milestone(title, description)
|
|
|
|
OutputFormatter.success("Milestone created successfully!")
|
|
OutputFormatter.key_value("ID", milestone.id)
|
|
OutputFormatter.key_value("Title", milestone.title)
|
|
OutputFormatter.key_value("Description", milestone.description)
|
|
OutputFormatter.key_value("State", milestone.state)
|
|
|
|
except TddaiError as e:
|
|
OutputFormatter.exit_with_error(f"Error creating milestone: {e}")
|
|
|
|
def list_milestones(self) -> None:
|
|
"""List all milestones."""
|
|
try:
|
|
milestones = self.service.list_milestones("all")
|
|
ProjectView.show_milestone_list(milestones)
|
|
except TddaiError as e:
|
|
OutputFormatter.exit_with_error(f"Error listing milestones: {e}")
|
|
|
|
def assign_issue_to_milestone(self, issue_number: int, milestone_id: int) -> None:
|
|
"""Assign issue to a milestone."""
|
|
try:
|
|
OutputFormatter.info(f"Assigning issue #{issue_number} to milestone #{milestone_id}...")
|
|
result = self.service.assign_issue_to_milestone(issue_number, milestone_id)
|
|
OutputFormatter.success(f"Issue #{issue_number} assigned to milestone #{milestone_id}")
|
|
except TddaiError as e:
|
|
OutputFormatter.exit_with_error(f"Error assigning issue to milestone: {e}")
|
|
|
|
def project_overview(self) -> None:
|
|
"""Show project management overview."""
|
|
try:
|
|
overview = self.service.get_project_overview()
|
|
ProjectView.show_overview(overview)
|
|
except TddaiError as e:
|
|
OutputFormatter.exit_with_error(f"Error getting project overview: {e}") |