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>
69 lines
2.6 KiB
Python
69 lines
2.6 KiB
Python
"""
|
|
Issue service - business logic for issue operations.
|
|
"""
|
|
|
|
from typing import List, Dict, Any, Optional
|
|
from datetime import datetime
|
|
|
|
from tddai import IssueFetcher, TddaiError
|
|
from tddai.issue_creator import IssueCreator
|
|
from gitea.models import Issue, Priority
|
|
|
|
|
|
class IssueService:
|
|
"""Service for issue operations."""
|
|
|
|
def __init__(self):
|
|
self.issue_fetcher = IssueFetcher()
|
|
self.issue_creator = IssueCreator()
|
|
|
|
def get_issue(self, issue_number: int) -> Issue:
|
|
"""Get a specific issue by number."""
|
|
return self.issue_fetcher.fetch_issue(issue_number)
|
|
|
|
def list_issues(self, state: str = "all") -> List[Issue]:
|
|
"""List issues with optional state filter."""
|
|
return self.issue_fetcher.fetch_issues(state)
|
|
|
|
def list_open_issues(self) -> List[Issue]:
|
|
"""List only open issues."""
|
|
return self.issue_fetcher.fetch_open_issues()
|
|
|
|
def create_issue(self, title: str, body: str, **kwargs) -> Dict[str, Any]:
|
|
"""Create a new issue."""
|
|
return self.issue_creator.create_issue(title, body, **kwargs)
|
|
|
|
def create_enhancement_issue(self, title: str, use_case: str,
|
|
technical_requirements: str = "",
|
|
acceptance_criteria: List[str] = None,
|
|
dependencies: List[str] = None,
|
|
priority: str = "Medium") -> Dict[str, Any]:
|
|
"""Create a structured enhancement issue."""
|
|
return self.issue_creator.create_enhancement_issue(
|
|
title, use_case, technical_requirements,
|
|
acceptance_criteria, dependencies, priority
|
|
)
|
|
|
|
def create_from_template(self, template_file: str, **kwargs) -> Dict[str, Any]:
|
|
"""Create issue from template file."""
|
|
return self.issue_creator.create_from_template(template_file, **kwargs)
|
|
|
|
def get_issue_summary(self, issue_number: int) -> Dict[str, Any]:
|
|
"""Get issue summary for display purposes."""
|
|
issue = self.get_issue(issue_number)
|
|
|
|
return {
|
|
'number': issue.number,
|
|
'title': issue.title,
|
|
'body': issue.body,
|
|
'state': issue.state,
|
|
'priority': issue.priority,
|
|
'status': issue.status,
|
|
'created_at': issue.created_at,
|
|
'updated_at': issue.updated_at,
|
|
'html_url': issue.html_url,
|
|
'assignee': issue.assignee.login if issue.assignee else None,
|
|
'labels': [label.name for label in issue.labels],
|
|
'has_milestone': issue.milestone is not None,
|
|
'milestone_title': issue.milestone.title if issue.milestone else None
|
|
} |