markitect-main/tddai/issue_writer.py
tegwick 72f341279a feat: Implement comprehensive IssueCreator system and create CLI roadmap issues
IssueCreator Implementation:
- Add tddai/issue_creator.py with full POST API functionality for issue creation
- Support multiple creation methods: basic, enhancement, bug, template-based
- Include structured issue formatting with acceptance criteria and dependencies
- Template system with variable substitution for reusable issue creation

Authentication Fix:
- Fix critical authentication bug: use GITEA_API_TOKEN instead of GITEA_TOKEN
- Update both IssueCreator and IssueWriter for consistency
- Update all tests and documentation to reflect correct environment variable

Comprehensive Test Suite:
- Add 15 unit tests for IssueCreator (tests/test_issue_creator.py)
- Add 5 integration tests for full API lifecycle (tests/test_issue_integration.py)
- Create test_environment_variable_detection to prevent future auth issues
- Total 33 tests covering complete issue handling workflow

CLI Integration:
- Enhance tddai_cli.py with 3 new commands: create-issue, create-enhancement, create-from-template
- Add comprehensive argument parsing with optional fields and priority support
- Include user-friendly output with next step guidance
- Update package exports to include IssueCreator

CLI Roadmap Execution:
- Successfully create 8 CLI implementation issues (#12-#19) in Gitea
- Resolve mismatch between NEXT.md roadmap and actual Gitea issues
- Issues prioritized for core USPs: Database Query CLI and AST Query CLI
- Remove local MISSING_ISSUES.md file after successful creation

Framework Maturity:
- Complete CRUD operations for issue management (Create, Read, Update, Delete)
- Robust error handling and API integration patterns
- Full authentication and environment variable management
- Ready for production CLI implementation workflow

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-24 23:36:07 +02:00

82 lines
No EOL
3 KiB
Python

"""
Issue writing to Gitea API.
"""
import json
import os
import subprocess
from subprocess import PIPE
from typing import Dict, Any, Optional
from .config import get_config
from .exceptions import IssueError
class IssueWriter:
"""Writes issue updates to Gitea API."""
def __init__(self, config=None, auth_token=None):
self.config = config or get_config()
self.auth_token = auth_token or os.getenv('GITEA_API_TOKEN')
def update_issue(self, issue_number: int, update_data: Dict[str, Any]) -> Dict[str, Any]:
"""Update an issue via PATCH operation."""
if not self.auth_token:
raise IssueError("Authentication token required for issue updates")
url = f"{self.config.issues_api_url}/{issue_number}"
try:
# Prepare curl command with authentication
curl_cmd = [
'curl', '-s', '-X', 'PATCH',
'-H', 'Content-Type: application/json',
'-H', f'Authorization: token {self.auth_token}',
'-d', json.dumps(update_data),
url
]
result = subprocess.run(
curl_cmd,
stdout=PIPE,
stderr=PIPE,
universal_newlines=True,
check=True
)
if result.returncode != 0:
raise IssueError(f"Failed to update issue #{issue_number}: {result.stderr}")
response_data = json.loads(result.stdout)
if 'message' in response_data and 'number' not in response_data:
raise IssueError(f"Failed to update issue #{issue_number}: {response_data['message']}")
return response_data
except subprocess.CalledProcessError as e:
raise IssueError(f"Failed to update issue #{issue_number}: {e}")
except json.JSONDecodeError as e:
raise IssueError(f"Failed to parse response data: {e}")
def update_issue_title(self, issue_number: int, new_title: str) -> Dict[str, Any]:
"""Update only the title of an issue."""
return self.update_issue(issue_number, {'title': new_title})
def update_issue_body(self, issue_number: int, new_body: str) -> Dict[str, Any]:
"""Update only the body of an issue."""
return self.update_issue(issue_number, {'body': new_body})
def update_issue_state(self, issue_number: int, new_state: str) -> Dict[str, Any]:
"""Update only the state of an issue (open/closed)."""
if new_state not in ['open', 'closed']:
raise IssueError(f"Invalid state '{new_state}'. Must be 'open' or 'closed'")
return self.update_issue(issue_number, {'state': new_state})
def close_issue(self, issue_number: int) -> Dict[str, Any]:
"""Close an issue."""
return self.update_issue_state(issue_number, 'closed')
def reopen_issue(self, issue_number: int) -> Dict[str, Any]:
"""Reopen a closed issue."""
return self.update_issue_state(issue_number, 'open')