diff --git a/Makefile b/Makefile index 250d7b98..5649d5b6 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ # MarkiTect - Advanced Markdown Engine # Makefile for common development tasks -.PHONY: help setup install test build clean update status dev lint format check-deps venv-status update-digest add-diary-entry list-issues show-issue list-open-issues close-issue test-from-issue tdd-start tdd-add-test tdd-finish tdd-status test-status test-new test-coverage test-arch test-foundation test-infrastructure test-integration test-domain test-service test-application test-presentation test-quick test-layers test-random test-random-seed test-random-repeat test-install-randomly test-clean test-tdd test-changed test-module test-cache-clean test-efficient cli-help +.PHONY: help setup install test build clean update status dev lint format check-deps venv-status update-digest add-diary-entry list-issues show-issue list-open-issues close-issue close-issue-enhanced close-issues-batch test-from-issue tdd-start tdd-add-test tdd-finish tdd-status test-status test-new test-coverage test-arch test-foundation test-infrastructure test-integration test-domain test-service test-application test-presentation test-quick test-layers test-random test-random-seed test-random-repeat test-install-randomly test-clean test-tdd test-changed test-module test-cache-clean test-efficient cli-help # Default target help: @@ -66,7 +66,9 @@ help: @echo " list-issues - Show all gitea issues with status and priority" @echo " list-open-issues - Show only open issues (active backlog)" @echo " show-issue NUM=X - Show detailed view of specific issue" - @echo " close-issue NUM=X - Close an issue and mark as completed" + @echo " close-issue NUM=X [COMMENT='reason'] - Close an issue and mark as completed" + @echo " close-issue-enhanced NUM=X [WORK='description'] - Close issue with enhanced tddai/issue_closer.py" + @echo " close-issues-batch NUMS='X Y Z' [COMMENT='reason'] - Close multiple issues at once" @echo " issues-get - Export compact issue index to ISSUES.index" @echo " issues-csv - Export issues as CSV for spreadsheet processing" @echo " issues-json - Export issues as JSON for programmatic processing" @@ -343,11 +345,46 @@ close-issue: $(VENV)/bin/activate echo "❌ Please specify issue number: make close-issue NUM=5"; \ exit 1; \ fi - @echo "🔄 Closing issue #$(NUM)..." - @echo " Setting issue state to 'done'..." - @PYTHONPATH=. $(VENV_PYTHON) tddai_cli.py set-issue-state $(NUM) done + @if [ -n "$(COMMENT)" ]; then \ + echo "🔄 Closing issue #$(NUM) with comment..."; \ + PYTHONPATH=. $(VENV_PYTHON) tddai_cli.py close-issue $(NUM) --comment "$(COMMENT)"; \ + else \ + echo "🔄 Closing issue #$(NUM)..."; \ + PYTHONPATH=. $(VENV_PYTHON) tddai_cli.py close-issue $(NUM); \ + fi @echo "✅ Issue #$(NUM) closed successfully!" +# Close issue using dedicated issue_closer.py script (enhanced functionality) +close-issue-enhanced: $(VENV)/bin/activate + @if [ -z "$(NUM)" ]; then \ + echo "❌ Please specify issue number: make close-issue-enhanced NUM=5"; \ + exit 1; \ + fi + @if [ -n "$(WORK)" ]; then \ + echo "🔄 Closing issue #$(NUM) with completion message..."; \ + PYTHONPATH=. $(VENV_PYTHON) tddai/issue_closer.py $(NUM) --work-completed "$(WORK)"; \ + elif [ -n "$(COMMENT)" ]; then \ + echo "🔄 Closing issue #$(NUM) with comment..."; \ + PYTHONPATH=. $(VENV_PYTHON) tddai/issue_closer.py $(NUM) --comment "$(COMMENT)"; \ + else \ + echo "🔄 Closing issue #$(NUM)..."; \ + PYTHONPATH=. $(VENV_PYTHON) tddai/issue_closer.py $(NUM); \ + fi + +# Close multiple issues at once using issue_closer.py +close-issues-batch: $(VENV)/bin/activate + @if [ -z "$(NUMS)" ]; then \ + echo "❌ Please specify issue numbers: make close-issues-batch NUMS='42 43 44'"; \ + exit 1; \ + fi + @if [ -n "$(COMMENT)" ]; then \ + echo "🔄 Closing issues $(NUMS) with comment..."; \ + PYTHONPATH=. $(VENV_PYTHON) tddai/issue_closer.py $(NUMS) --comment "$(COMMENT)"; \ + else \ + echo "🔄 Closing issues $(NUMS)..."; \ + PYTHONPATH=. $(VENV_PYTHON) tddai/issue_closer.py $(NUMS); \ + fi + # Export compact issue index to ISSUES.index file (TSV format) issues-get: $(VENV)/bin/activate @echo "📋 Fetching issue index from gitea..." diff --git a/tddai/issue_closer.py b/tddai/issue_closer.py new file mode 100644 index 00000000..77de23dc --- /dev/null +++ b/tddai/issue_closer.py @@ -0,0 +1,180 @@ +#!/usr/bin/env python3 +""" +TDDAi Issue Closer + +A dedicated module for closing issues in the selected issue tracking backend. +Provides both programmatic API and CLI functionality for easy issue closure. + +This module integrates with the existing tddai framework and provides: +- Simple programmatic interface for closing issues +- Command-line utility for manual issue closure +- Integration with existing issue tracking backends +- Proper error handling and user feedback +""" + +import sys +import argparse +from pathlib import Path +from typing import Optional + +# Add project root to path for imports +project_root = Path(__file__).parent.parent +sys.path.insert(0, str(project_root)) + +from cli import CLIFramework +from services import IssueService +from tddai import TddaiError + + +class IssueCloser: + """Dedicated class for closing issues with the configured backend.""" + + def __init__(self): + """Initialize the issue closer with CLI framework.""" + self.cli = CLIFramework() + self.service = IssueService() + + def close_issue(self, issue_number: int, comment: str = "") -> bool: + """ + Close an issue with optional comment. + + Args: + issue_number: The issue number to close + comment: Optional closing comment + + Returns: + bool: True if issue was closed successfully, False otherwise + """ + try: + self.cli.close_issue(issue_number, comment) + return True + except TddaiError as e: + print(f"Error closing issue #{issue_number}: {e}") + return False + except Exception as e: + print(f"Unexpected error closing issue #{issue_number}: {e}") + return False + + def close_with_completion_message(self, issue_number: int, completed_work: str) -> bool: + """ + Close an issue with a standardized completion message. + + Args: + issue_number: The issue number to close + completed_work: Description of what was completed + + Returns: + bool: True if issue was closed successfully, False otherwise + """ + completion_comment = f"✅ Completed: {completed_work}" + return self.close_issue(issue_number, completion_comment) + + def close_multiple_issues(self, issue_numbers: list, comment: str = "") -> dict: + """ + Close multiple issues with the same comment. + + Args: + issue_numbers: List of issue numbers to close + comment: Comment to add to all issues + + Returns: + dict: Results with 'successful' and 'failed' lists + """ + results = {'successful': [], 'failed': []} + + for issue_num in issue_numbers: + if self.close_issue(issue_num, comment): + results['successful'].append(issue_num) + else: + results['failed'].append(issue_num) + + return results + + +def main(): + """Command-line interface for the issue closer.""" + parser = argparse.ArgumentParser( + description="TDDAi Issue Closer - Close issues in the configured tracking backend", + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=""" +Examples: + python3 issue_closer.py 42 # Close issue #42 + python3 issue_closer.py 42 -c "Fixed the bug" # Close with comment + python3 issue_closer.py 42 -w "All tests passing" # Close with completion message + python3 issue_closer.py 42 43 44 # Close multiple issues + python3 issue_closer.py 42 43 -c "Batch closure" # Close multiple with comment + +Integration with existing tddai CLI: + python3 tddai_cli.py close-issue 42 --comment "Done" # Alternative method + """ + ) + + parser.add_argument( + 'issue_numbers', + type=int, + nargs='+', + help='Issue number(s) to close' + ) + + parser.add_argument( + '-c', '--comment', + type=str, + default='', + help='Optional closing comment' + ) + + parser.add_argument( + '-w', '--work-completed', + type=str, + help='Description of completed work (creates standardized completion message)' + ) + + parser.add_argument( + '-v', '--verbose', + action='store_true', + help='Enable verbose output' + ) + + args = parser.parse_args() + + # Initialize issue closer + closer = IssueCloser() + + # Determine which closing method to use + if args.work_completed: + comment = f"✅ Completed: {args.work_completed}" + else: + comment = args.comment + + # Handle single or multiple issues + if len(args.issue_numbers) == 1: + issue_num = args.issue_numbers[0] + if args.verbose: + print(f"Closing issue #{issue_num}...") + if comment: + print(f"Comment: {comment}") + + success = closer.close_issue(issue_num, comment) + sys.exit(0 if success else 1) + + else: + # Multiple issues + if args.verbose: + print(f"Closing {len(args.issue_numbers)} issues...") + if comment: + print(f"Comment: {comment}") + + results = closer.close_multiple_issues(args.issue_numbers, comment) + + if results['successful']: + print(f"✅ Successfully closed: {results['successful']}") + + if results['failed']: + print(f"❌ Failed to close: {results['failed']}") + sys.exit(1) + + print(f"✅ All {len(args.issue_numbers)} issues closed successfully!") + + +if __name__ == '__main__': + main() \ No newline at end of file