Files
markitect-main/tddai_cli.py
tegwick 960a7c4850 feat: Complete CLI consolidation - fix redundancy and missing interfaces
🎯 MAJOR CLI ARCHITECTURE CONSOLIDATION:

 Added Missing CLI Entry Points:
• tddai = "tddai_cli:main" - TDD workflow management
• issue = "cli.issue_cli:main" - Pure issue management
• All three CLIs now properly installed: markitect, tddai, issue

🧹 Eliminated Functionality Redundancy:
• Removed issue commands from markitect/cli.py (clean separation)
• MarkiTect now focuses purely on document processing
• TDD workflow in tddai CLI, issue management in issue CLI

🏗️ Clean Architecture Implementation:
• Created cli/issue_cli.py - Dedicated pure issue management
• Enhanced cli/commands/export.py with export_issues_csv/json
• Updated cli/core.py with proper export method delegation
• Fixed pyproject.toml to include all required packages

🧪 Comprehensive Testing:
• Added tests/test_cli_consolidation.py - Prevents CLI regression
• Tests ensure all CLIs are installed and functional
• Tests verify no functionality duplication
• Regression protection against missing CLI commands

📋 Clear Separation of Concerns:
• markitect CLI - Document processing, templates, performance
• tddai CLI - TDD workflow, workspace management, coverage
• issue CLI - Pure issue operations, project management, export

🔧 Package Configuration:
• Updated pyproject.toml to include cli*, tddai*, services*, etc.
• Added py-modules for tddai_cli standalone module
• Fixed import paths and dependencies

This consolidation resolves the major redundancy identified in issues
functionality and ensures proper CLI interfaces are available and tested.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-02 23:04:57 +02:00

342 lines
14 KiB
Python

#!/usr/bin/env python3
"""
CLI interface for tddai library.
This module now uses the separated architecture with services and presenters.
Business logic is handled by services, presentation by CLI framework.
"""
import sys
import argparse
from pathlib import Path
from typing import Optional, Any
# Add current directory to path so we can import modules
sys.path.insert(0, str(Path(__file__).parent))
from cli import CLIFramework
# Lazy initialization of CLI framework
_cli_framework: Optional[CLIFramework] = None
def _get_cli() -> CLIFramework:
"""Get CLI framework instance (lazy initialization)."""
global _cli_framework
if _cli_framework is None:
_cli_framework = CLIFramework()
return _cli_framework
def workspace_status() -> None:
"""Show current workspace status."""
_get_cli().workspace_status()
def start_issue(issue_number: int) -> None:
"""Start working on an issue."""
_get_cli().start_issue(issue_number)
def finish_issue() -> None:
"""Finish current issue workspace."""
_get_cli().finish_issue()
def add_test_guidance() -> None:
"""Show guidance for adding tests."""
_get_cli().add_test_guidance()
def list_issues() -> None:
"""List all issues."""
_get_cli().list_issues()
def list_open_issues() -> None:
"""List only open issues."""
_get_cli().list_open_issues()
def show_issue(issue_number: int) -> None:
"""Show detailed issue information."""
_get_cli().show_issue(issue_number)
def create_issue(title: str, body: str, issue_type: str = "enhancement") -> None:
"""Create a new issue."""
_get_cli().create_issue(title, body, issue_type)
def create_enhancement_issue(title: str, use_case: str, technical_requirements: str = "",
acceptance_criteria: str = "", dependencies: str = "",
priority: str = "Medium") -> None:
"""Create a structured enhancement issue."""
# Parse acceptance criteria if provided
criteria_list = []
if acceptance_criteria:
criteria_list = [line.strip() for line in acceptance_criteria.split('\n') if line.strip()]
# Parse dependencies if provided
deps_list = []
if dependencies:
deps_list = [line.strip() for line in dependencies.split('\n') if line.strip()]
_get_cli().create_enhancement_issue(
title=title,
use_case=use_case,
technical_requirements=technical_requirements,
acceptance_criteria=criteria_list,
dependencies=deps_list,
priority=priority
)
def create_from_template(template_file: str, **kwargs: Any) -> None:
"""Create issue from template file."""
_get_cli().create_from_template(template_file, **kwargs)
def close_issue(issue_number: int, comment: str = "") -> None:
"""Close an issue with optional comment."""
_get_cli().close_issue(issue_number, comment)
def analyze_coverage(issue_number: int) -> None:
"""Analyze test coverage for a specific issue."""
_get_cli().analyze_coverage(issue_number)
def setup_project_management() -> None:
"""Setup project management labels and milestones."""
_get_cli().setup_project_management()
def move_issue_to_state(issue_number: int, state: str) -> None:
"""Move issue to a specific project state."""
_get_cli().move_issue_to_state(issue_number, state)
def set_issue_priority(issue_number: int, priority: str) -> None:
"""Set issue priority."""
_get_cli().set_issue_priority(issue_number, priority)
def create_milestone(title: str, description: str = "") -> None:
"""Create a new milestone (project)."""
_get_cli().create_milestone(title, description)
def list_milestones() -> None:
"""List all milestones."""
_get_cli().list_milestones()
def assign_issue_to_milestone(issue_number: int, milestone_id: int) -> None:
"""Assign issue to a milestone."""
_get_cli().assign_issue_to_milestone(issue_number, milestone_id)
def project_overview() -> None:
"""Show project management overview."""
_get_cli().project_overview()
def issue_index(format_type: str = "tsv", sort_by: str = "number", filter_state: Optional[str] = None, filter_priority: Optional[str] = None, include_state: bool = False) -> None:
"""Output compact index of all issues for Unix processing."""
_get_cli().issue_index(
format_type=format_type,
sort_by=sort_by,
filter_state=filter_state,
filter_priority=filter_priority,
include_state=include_state
)
def show_config(show_sensitive: bool = False) -> None:
"""Display current configuration values."""
_get_cli().show_config(show_sensitive)
def validate_config(verbose: bool = False) -> None:
"""Validate current configuration and show any issues."""
_get_cli().validate_config(verbose)
def troubleshoot_config() -> None:
"""Run comprehensive configuration troubleshooting."""
_get_cli().troubleshoot_config()
def check_config_files() -> None:
"""Check for configuration files and their status."""
_get_cli().check_config_files()
def main() -> None:
"""Main CLI entry point."""
parser = argparse.ArgumentParser(description="tddai CLI tool")
subparsers = parser.add_subparsers(dest='command', help='Available commands')
# Workspace commands
subparsers.add_parser('workspace-status', help='Show workspace status')
start_parser = subparsers.add_parser('start-issue', help='Start working on issue')
start_parser.add_argument('issue_number', type=int, help='Issue number')
subparsers.add_parser('finish-issue', help='Finish current issue')
subparsers.add_parser('add-test', help='Show guidance for adding tests')
# Issue commands
subparsers.add_parser('list-issues', help='List all issues')
subparsers.add_parser('list-open-issues', help='List open issues')
index_parser = subparsers.add_parser('issue-index', help='Output compact issue index for Unix processing')
index_parser.add_argument('--format', choices=['tsv', 'csv', 'json', 'fields'], default='tsv',
help='Output format (default: tsv)')
index_parser.add_argument('--sort', choices=['number', 'title', 'priority', 'state', 'created', 'updated'],
default='number', help='Sort by field (default: number)')
index_parser.add_argument('--filter-state', choices=['open', 'closed'],
help='Filter by issue state')
index_parser.add_argument('--filter-priority', choices=['low', 'medium', 'high', 'critical', 'none'],
help='Filter by priority level')
index_parser.add_argument('--include-state', action='store_true',
help='Include state column in output')
show_parser = subparsers.add_parser('show-issue', help='Show issue details')
show_parser.add_argument('issue_number', type=int, help='Issue number')
coverage_parser = subparsers.add_parser('analyze-coverage', help='Analyze test coverage for issue')
coverage_parser.add_argument('issue_number', type=int, help='Issue number')
close_parser = subparsers.add_parser('close-issue', help='Close an issue')
close_parser.add_argument('issue_number', type=int, help='Issue number')
close_parser.add_argument('--comment', help='Optional closing comment', default='')
# Issue creation commands
create_parser = subparsers.add_parser('create-issue', help='Create a new issue')
create_parser.add_argument('title', help='Issue title')
create_parser.add_argument('body', help='Issue body/description')
create_parser.add_argument('--type', choices=['enhancement', 'bug'], default='enhancement', help='Issue type')
create_enh_parser = subparsers.add_parser('create-enhancement', help='Create a structured enhancement issue')
create_enh_parser.add_argument('title', help='Issue title')
create_enh_parser.add_argument('use_case', help='UseCase description')
create_enh_parser.add_argument('--technical', help='Technical requirements', default='')
create_enh_parser.add_argument('--criteria', help='Acceptance criteria (newline separated)', default='')
create_enh_parser.add_argument('--dependencies', help='Dependencies (newline separated)', default='')
create_enh_parser.add_argument('--priority', choices=['High', 'Medium', 'Low'], default='Medium', help='Priority level')
template_parser = subparsers.add_parser('create-from-template', help='Create issue from template')
template_parser.add_argument('template_file', help='Template file path')
template_parser.add_argument('--vars', help='Template variables in key=value format', nargs='*', default=[])
# Project management commands
subparsers.add_parser('setup-project-mgmt', help='Setup project management labels and milestones')
subparsers.add_parser('project-overview', help='Show project management overview')
state_parser = subparsers.add_parser('set-issue-state', help='Set issue project state')
state_parser.add_argument('issue_number', type=int, help='Issue number')
state_parser.add_argument('state', choices=['todo', 'active', 'review', 'done', 'blocked'], help='Project state')
priority_parser = subparsers.add_parser('set-issue-priority', help='Set issue priority')
priority_parser.add_argument('issue_number', type=int, help='Issue number')
priority_parser.add_argument('priority', choices=['low', 'medium', 'high', 'critical'], help='Priority level')
milestone_parser = subparsers.add_parser('create-milestone', help='Create a new milestone (project)')
milestone_parser.add_argument('title', help='Milestone title')
milestone_parser.add_argument('--description', help='Milestone description', default='')
subparsers.add_parser('list-milestones', help='List all milestones')
assign_parser = subparsers.add_parser('assign-to-milestone', help='Assign issue to milestone')
assign_parser.add_argument('issue_number', type=int, help='Issue number')
assign_parser.add_argument('milestone_id', type=int, help='Milestone ID')
# Configuration management commands
config_show_parser = subparsers.add_parser('config-show', help='Display current configuration values')
config_show_parser.add_argument('--show-sensitive', action='store_true', help='Show sensitive information like masked tokens')
config_validate_parser = subparsers.add_parser('config-validate', help='Validate current configuration')
config_validate_parser.add_argument('--verbose', '-v', action='store_true', help='Show detailed validation results')
subparsers.add_parser('config-troubleshoot', help='Run comprehensive configuration troubleshooting')
subparsers.add_parser('config-files', help='Check configuration files status')
args = parser.parse_args()
if not args.command:
parser.print_help()
return
try:
if args.command == 'workspace-status':
workspace_status()
elif args.command == 'start-issue':
start_issue(args.issue_number)
elif args.command == 'finish-issue':
finish_issue()
elif args.command == 'add-test':
add_test_guidance()
elif args.command == 'list-issues':
list_issues()
elif args.command == 'list-open-issues':
list_open_issues()
elif args.command == 'issue-index':
issue_index(
format_type=args.format,
sort_by=args.sort,
filter_state=args.filter_state,
filter_priority=args.filter_priority,
include_state=args.include_state
)
elif args.command == 'show-issue':
show_issue(args.issue_number)
elif args.command == 'analyze-coverage':
analyze_coverage(args.issue_number)
elif args.command == 'close-issue':
close_issue(args.issue_number, args.comment)
elif args.command == 'create-issue':
create_issue(args.title, args.body, args.type)
elif args.command == 'create-enhancement':
create_enhancement_issue(
args.title, args.use_case, args.technical,
args.criteria, args.dependencies, args.priority
)
elif args.command == 'create-from-template':
# Parse template variables
template_vars = {}
for var in args.vars:
if '=' in var:
key, value = var.split('=', 1)
template_vars[key] = value
create_from_template(args.template_file, **template_vars)
elif args.command == 'setup-project-mgmt':
setup_project_management()
elif args.command == 'project-overview':
project_overview()
elif args.command == 'set-issue-state':
move_issue_to_state(args.issue_number, args.state)
elif args.command == 'set-issue-priority':
set_issue_priority(args.issue_number, args.priority)
elif args.command == 'create-milestone':
create_milestone(args.title, args.description)
elif args.command == 'list-milestones':
list_milestones()
elif args.command == 'assign-to-milestone':
assign_issue_to_milestone(args.issue_number, args.milestone_id)
elif args.command == 'config-show':
show_config(args.show_sensitive)
elif args.command == 'config-validate':
validate_config(args.verbose)
elif args.command == 'config-troubleshoot':
troubleshoot_config()
elif args.command == 'config-files':
check_config_files()
except KeyboardInterrupt:
print("\n⚠️ Operation cancelled")
sys.exit(1)
if __name__ == '__main__':
main()