🎯 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>
342 lines
14 KiB
Python
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() |