feat: Implement comprehensive project management system with issue lifecycle support

- Add ProjectManager with milestone and label-based project organization
- Support project states (Todo, Active, Review, Done, Blocked) via labels
- Add priority management (Low, Medium, High, Critical) with label integration
- Implement milestone creation and management for project tracking
- Enhance IssueWriter with project management methods (assign_to_milestone, add/remove_labels)
- Add 8 new CLI commands for complete project management workflow
- Support automatic project management setup with ensure_project_labels()
- Enable issue state transitions with automatic closing for completed issues
- Integrate with existing Gitea API authentication and error handling patterns

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-09-24 23:51:29 +02:00
parent 72f341279a
commit 2b681b31c6
4 changed files with 586 additions and 1 deletions

View File

@@ -79,4 +79,60 @@ class IssueWriter:
def reopen_issue(self, issue_number: int) -> Dict[str, Any]:
"""Reopen a closed issue."""
return self.update_issue_state(issue_number, 'open')
return self.update_issue_state(issue_number, 'open')
def assign_to_milestone(self, issue_number: int, milestone_id: int) -> Dict[str, Any]:
"""Assign issue to a milestone (project)."""
return self.update_issue(issue_number, {'milestone': milestone_id})
def remove_from_milestone(self, issue_number: int) -> Dict[str, Any]:
"""Remove issue from its current milestone."""
return self.update_issue(issue_number, {'milestone': None})
def update_labels(self, issue_number: int, labels: list) -> Dict[str, Any]:
"""Update issue labels completely."""
return self.update_issue(issue_number, {'labels': labels})
def add_labels(self, issue_number: int, new_labels: list) -> Dict[str, Any]:
"""Add labels to issue (preserving existing labels)."""
# First get current labels
url = f"{self.config.issues_api_url}/{issue_number}"
curl_cmd = [
'curl', '-s', '-X', 'GET',
'-H', f'Authorization: token {self.auth_token}',
url
]
try:
result = subprocess.run(curl_cmd, stdout=PIPE, stderr=PIPE, universal_newlines=True, check=True)
issue_data = json.loads(result.stdout)
current_labels = [label['name'] for label in issue_data.get('labels', [])]
# Add new labels (avoid duplicates)
updated_labels = list(set(current_labels + new_labels))
return self.update_labels(issue_number, updated_labels)
except (subprocess.CalledProcessError, json.JSONDecodeError) as e:
raise IssueError(f"Failed to add labels to issue #{issue_number}: {e}")
def remove_labels(self, issue_number: int, labels_to_remove: list) -> Dict[str, Any]:
"""Remove specific labels from issue."""
# First get current labels
url = f"{self.config.issues_api_url}/{issue_number}"
curl_cmd = [
'curl', '-s', '-X', 'GET',
'-H', f'Authorization: token {self.auth_token}',
url
]
try:
result = subprocess.run(curl_cmd, stdout=PIPE, stderr=PIPE, universal_newlines=True, check=True)
issue_data = json.loads(result.stdout)
current_labels = [label['name'] for label in issue_data.get('labels', [])]
# Remove specified labels
updated_labels = [label for label in current_labels if label not in labels_to_remove]
return self.update_labels(issue_number, updated_labels)
except (subprocess.CalledProcessError, json.JSONDecodeError) as e:
raise IssueError(f"Failed to remove labels from issue #{issue_number}: {e}")