roadmap-cli 0.1.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- roadmap/__init__.py +27 -0
- roadmap/adapters/__init__.py +1 -0
- roadmap/adapters/base_paginated_adapter.py +117 -0
- roadmap/adapters/cli/__init__.py +249 -0
- roadmap/adapters/cli/analysis/__init__.py +5 -0
- roadmap/adapters/cli/analysis/commands.py +370 -0
- roadmap/adapters/cli/analysis/presenter.py +125 -0
- roadmap/adapters/cli/archive_operations.py +86 -0
- roadmap/adapters/cli/cli_command_helpers.py +109 -0
- roadmap/adapters/cli/cli_confirmations.py +74 -0
- roadmap/adapters/cli/cli_error_handlers.py +146 -0
- roadmap/adapters/cli/cli_validators.py +39 -0
- roadmap/adapters/cli/commands/sync_status.py +308 -0
- roadmap/adapters/cli/comment/__init__.py +5 -0
- roadmap/adapters/cli/comment/commands.py +106 -0
- roadmap/adapters/cli/config/__init__.py +5 -0
- roadmap/adapters/cli/config/commands.py +198 -0
- roadmap/adapters/cli/console_exports.py +10 -0
- roadmap/adapters/cli/core.py +26 -0
- roadmap/adapters/cli/crud/__init__.py +47 -0
- roadmap/adapters/cli/crud/base_archive.py +208 -0
- roadmap/adapters/cli/crud/base_create.py +154 -0
- roadmap/adapters/cli/crud/base_delete.py +155 -0
- roadmap/adapters/cli/crud/base_restore.py +283 -0
- roadmap/adapters/cli/crud/base_update.py +193 -0
- roadmap/adapters/cli/crud/crud_helpers.py +206 -0
- roadmap/adapters/cli/crud/crud_utils.py +152 -0
- roadmap/adapters/cli/crud/entity_builders.py +374 -0
- roadmap/adapters/cli/data/__init__.py +8 -0
- roadmap/adapters/cli/data/commands.py +149 -0
- roadmap/adapters/cli/decorators.py +208 -0
- roadmap/adapters/cli/dtos/__init__.py +155 -0
- roadmap/adapters/cli/exception_handler.py +105 -0
- roadmap/adapters/cli/git/__init__.py +8 -0
- roadmap/adapters/cli/git/commands.py +475 -0
- roadmap/adapters/cli/git/handlers/__init__.py +1 -0
- roadmap/adapters/cli/git/handlers/git_authentication_handler.py +124 -0
- roadmap/adapters/cli/git/handlers/git_branch_handler.py +238 -0
- roadmap/adapters/cli/git/handlers/git_connectivity_handler.py +122 -0
- roadmap/adapters/cli/git/handlers/git_hooks_handler.py +105 -0
- roadmap/adapters/cli/git/hooks_config.py +327 -0
- roadmap/adapters/cli/git/status_display.py +158 -0
- roadmap/adapters/cli/health/__init__.py +8 -0
- roadmap/adapters/cli/health/db_integrity.py +204 -0
- roadmap/adapters/cli/health/enhancer.py +227 -0
- roadmap/adapters/cli/health/fixer.py +295 -0
- roadmap/adapters/cli/health/fixers/__init__.py +10 -0
- roadmap/adapters/cli/health/fixers/corrupted_comments_fixer.py +231 -0
- roadmap/adapters/cli/health/fixers/data_integrity_fixer.py +137 -0
- roadmap/adapters/cli/health/fixers/duplicate_issues_fixer.py +138 -0
- roadmap/adapters/cli/health/fixers/folder_structure_fixer.py +114 -0
- roadmap/adapters/cli/health/fixers/label_normalization_fixer.py +162 -0
- roadmap/adapters/cli/health/fixers/milestone_name_normalization_fixer.py +181 -0
- roadmap/adapters/cli/health/fixers/milestone_naming_compliance_fixer.py +244 -0
- roadmap/adapters/cli/health/fixers/milestone_parse_error_fixer.py +162 -0
- roadmap/adapters/cli/health/fixers/milestone_validation_fixer.py +211 -0
- roadmap/adapters/cli/health/fixers/old_backups_fixer.py +135 -0
- roadmap/adapters/cli/health/fixers/orphaned_issues_fixer.py +252 -0
- roadmap/adapters/cli/health/formatter.py +250 -0
- roadmap/adapters/cli/health/formatters.py +427 -0
- roadmap/adapters/cli/health/scan.py +293 -0
- roadmap/adapters/cli/init/__init__.py +5 -0
- roadmap/adapters/cli/init/commands.py +587 -0
- roadmap/adapters/cli/init.py +9 -0
- roadmap/adapters/cli/issues/__init__.py +70 -0
- roadmap/adapters/cli/issues/archive.py +137 -0
- roadmap/adapters/cli/issues/archive_class.py +253 -0
- roadmap/adapters/cli/issues/block.py +39 -0
- roadmap/adapters/cli/issues/close.py +216 -0
- roadmap/adapters/cli/issues/comment.py +178 -0
- roadmap/adapters/cli/issues/create.py +162 -0
- roadmap/adapters/cli/issues/delete.py +31 -0
- roadmap/adapters/cli/issues/deps.py +161 -0
- roadmap/adapters/cli/issues/issue_status_helpers.py +111 -0
- roadmap/adapters/cli/issues/link.py +308 -0
- roadmap/adapters/cli/issues/list.py +402 -0
- roadmap/adapters/cli/issues/lookup.py +96 -0
- roadmap/adapters/cli/issues/progress.py +102 -0
- roadmap/adapters/cli/issues/restore.py +96 -0
- roadmap/adapters/cli/issues/restore_class.py +57 -0
- roadmap/adapters/cli/issues/start.py +165 -0
- roadmap/adapters/cli/issues/sync_status.py +374 -0
- roadmap/adapters/cli/issues/unblock.py +43 -0
- roadmap/adapters/cli/issues/unlink.py +104 -0
- roadmap/adapters/cli/issues/update.py +120 -0
- roadmap/adapters/cli/issues/view.py +36 -0
- roadmap/adapters/cli/layout.py +220 -0
- roadmap/adapters/cli/mappers.py +200 -0
- roadmap/adapters/cli/milestones/__init__.py +52 -0
- roadmap/adapters/cli/milestones/archive.py +125 -0
- roadmap/adapters/cli/milestones/archive_class.py +236 -0
- roadmap/adapters/cli/milestones/assign.py +62 -0
- roadmap/adapters/cli/milestones/close.py +177 -0
- roadmap/adapters/cli/milestones/create.py +131 -0
- roadmap/adapters/cli/milestones/delete.py +27 -0
- roadmap/adapters/cli/milestones/kanban.py +123 -0
- roadmap/adapters/cli/milestones/list.py +87 -0
- roadmap/adapters/cli/milestones/recalculate.py +79 -0
- roadmap/adapters/cli/milestones/restore.py +87 -0
- roadmap/adapters/cli/milestones/restore_class.py +99 -0
- roadmap/adapters/cli/milestones/update.py +61 -0
- roadmap/adapters/cli/milestones/view.py +142 -0
- roadmap/adapters/cli/output_manager.py +319 -0
- roadmap/adapters/cli/presentation/base_presenter.py +121 -0
- roadmap/adapters/cli/presentation/cleanup_presenter.py +235 -0
- roadmap/adapters/cli/presentation/core_initialization_presenter.py +277 -0
- roadmap/adapters/cli/presentation/crud_presenter.py +134 -0
- roadmap/adapters/cli/presentation/daily_summary_presenter.py +266 -0
- roadmap/adapters/cli/presentation/issue_presenter.py +258 -0
- roadmap/adapters/cli/presentation/milestone_list_presenter.py +135 -0
- roadmap/adapters/cli/presentation/milestone_presenter.py +297 -0
- roadmap/adapters/cli/presentation/project_presenter.py +233 -0
- roadmap/adapters/cli/presentation/project_status_presenter.py +186 -0
- roadmap/adapters/cli/presentation/table_builders.py +95 -0
- roadmap/adapters/cli/projects/__init__.py +42 -0
- roadmap/adapters/cli/projects/archive.py +127 -0
- roadmap/adapters/cli/projects/archive_class.py +96 -0
- roadmap/adapters/cli/projects/close.py +94 -0
- roadmap/adapters/cli/projects/create.py +50 -0
- roadmap/adapters/cli/projects/delete.py +27 -0
- roadmap/adapters/cli/projects/list.py +227 -0
- roadmap/adapters/cli/projects/restore.py +87 -0
- roadmap/adapters/cli/projects/restore_class.py +56 -0
- roadmap/adapters/cli/projects/update.py +57 -0
- roadmap/adapters/cli/projects/view.py +157 -0
- roadmap/adapters/cli/services/__init__.py +1 -0
- roadmap/adapters/cli/services/daily_summary_service.py +313 -0
- roadmap/adapters/cli/services/export_manager.py +268 -0
- roadmap/adapters/cli/services/milestone_list_service.py +266 -0
- roadmap/adapters/cli/services/project_initialization_service.py +29 -0
- roadmap/adapters/cli/services/project_status_service.py +462 -0
- roadmap/adapters/cli/services/sync_service.py +62 -0
- roadmap/adapters/cli/status.py +408 -0
- roadmap/adapters/cli/styling.py +21 -0
- roadmap/adapters/cli/sync.py +1117 -0
- roadmap/adapters/cli/sync_context.py +432 -0
- roadmap/adapters/cli/sync_handlers/__init__.py +77 -0
- roadmap/adapters/cli/sync_handlers/apply_ops.py +522 -0
- roadmap/adapters/cli/sync_handlers/baseline_ops.py +366 -0
- roadmap/adapters/cli/sync_handlers/conflict_ops.py +161 -0
- roadmap/adapters/cli/sync_handlers/dry_run_display.py +237 -0
- roadmap/adapters/cli/sync_handlers/interactive_resolver.py +396 -0
- roadmap/adapters/cli/sync_handlers/progress_tracker.py +128 -0
- roadmap/adapters/cli/sync_metrics_command.py +190 -0
- roadmap/adapters/cli/sync_presenter.py +58 -0
- roadmap/adapters/cli/sync_validation.py +295 -0
- roadmap/adapters/cli/today.py +52 -0
- roadmap/adapters/cli/utils/click_options.py +38 -0
- roadmap/adapters/git/__init__.py +1 -0
- roadmap/adapters/git/git.py +208 -0
- roadmap/adapters/git/git_branch_manager.py +400 -0
- roadmap/adapters/git/git_command_executor.py +56 -0
- roadmap/adapters/git/git_commit_analyzer.py +334 -0
- roadmap/adapters/git/git_hooks.py +22 -0
- roadmap/adapters/git/git_hooks_manager.py +455 -0
- roadmap/adapters/git/git_repository_info.py +65 -0
- roadmap/adapters/git/hook_installer.py +106 -0
- roadmap/adapters/git/hook_registry.py +97 -0
- roadmap/adapters/git/hook_script_generator.py +58 -0
- roadmap/adapters/git/sync_monitor.py +409 -0
- roadmap/adapters/git/workflow_automation.py +286 -0
- roadmap/adapters/github/__init__.py +1 -0
- roadmap/adapters/github/github.py +360 -0
- roadmap/adapters/github/handlers/__init__.py +17 -0
- roadmap/adapters/github/handlers/base.py +337 -0
- roadmap/adapters/github/handlers/collaborators.py +165 -0
- roadmap/adapters/github/handlers/comments.py +118 -0
- roadmap/adapters/github/handlers/issues.py +150 -0
- roadmap/adapters/github/handlers/labels.py +164 -0
- roadmap/adapters/github/handlers/milestones.py +104 -0
- roadmap/adapters/persistence/__init__.py +35 -0
- roadmap/adapters/persistence/conflict_resolver.py +190 -0
- roadmap/adapters/persistence/database_manager.py +492 -0
- roadmap/adapters/persistence/entity_sync_coordinators.py +672 -0
- roadmap/adapters/persistence/file_locking.py +348 -0
- roadmap/adapters/persistence/file_parser.py +92 -0
- roadmap/adapters/persistence/file_synchronizer.py +107 -0
- roadmap/adapters/persistence/focused_managers.py +140 -0
- roadmap/adapters/persistence/git_history.py +330 -0
- roadmap/adapters/persistence/parser/__init__.py +20 -0
- roadmap/adapters/persistence/parser/frontmatter.py +152 -0
- roadmap/adapters/persistence/parser/issue.py +245 -0
- roadmap/adapters/persistence/parser/milestone.py +146 -0
- roadmap/adapters/persistence/parser/project.py +106 -0
- roadmap/adapters/persistence/persistence.py +288 -0
- roadmap/adapters/persistence/repositories/__init__.py +15 -0
- roadmap/adapters/persistence/repositories/issue_repository.py +223 -0
- roadmap/adapters/persistence/repositories/milestone_repository.py +140 -0
- roadmap/adapters/persistence/repositories/project_repository.py +168 -0
- roadmap/adapters/persistence/repositories/remote_link_repository.py +355 -0
- roadmap/adapters/persistence/repositories/sync_state_repository.py +66 -0
- roadmap/adapters/persistence/storage/__init__.py +36 -0
- roadmap/adapters/persistence/storage/conflicts.py +101 -0
- roadmap/adapters/persistence/storage/connection_manager.py +79 -0
- roadmap/adapters/persistence/storage/issue_storage.py +89 -0
- roadmap/adapters/persistence/storage/milestone_storage.py +80 -0
- roadmap/adapters/persistence/storage/project_storage.py +102 -0
- roadmap/adapters/persistence/storage/queries.py +227 -0
- roadmap/adapters/persistence/storage/state_manager.py +652 -0
- roadmap/adapters/persistence/storage/sync_state_storage.py +207 -0
- roadmap/adapters/persistence/sync_metrics_repository.py +342 -0
- roadmap/adapters/persistence/sync_orchestrator.py +293 -0
- roadmap/adapters/persistence/sync_state_tracker.py +86 -0
- roadmap/adapters/persistence/yaml_repositories.py +711 -0
- roadmap/adapters/sync/__init__.py +21 -0
- roadmap/adapters/sync/backend_factory.py +108 -0
- roadmap/adapters/sync/backends/__init__.py +15 -0
- roadmap/adapters/sync/backends/converters.py +209 -0
- roadmap/adapters/sync/backends/github_backend_helpers.py +380 -0
- roadmap/adapters/sync/backends/github_client.py +99 -0
- roadmap/adapters/sync/backends/github_sync_backend.py +792 -0
- roadmap/adapters/sync/backends/github_sync_ops.py +908 -0
- roadmap/adapters/sync/backends/services/__init__.py +1 -0
- roadmap/adapters/sync/backends/services/github_authentication_service.py +105 -0
- roadmap/adapters/sync/backends/services/github_issue_delete_service.py +423 -0
- roadmap/adapters/sync/backends/services/github_issue_dependency_service.py +63 -0
- roadmap/adapters/sync/backends/services/github_issue_fetch_service.py +158 -0
- roadmap/adapters/sync/backends/services/github_label_sync_service.py +126 -0
- roadmap/adapters/sync/backends/services/github_local_issue_sync_service.py +229 -0
- roadmap/adapters/sync/backends/services/github_milestone_fetch_service.py +204 -0
- roadmap/adapters/sync/backends/vanilla_git_sync_backend.py +198 -0
- roadmap/adapters/sync/services/__init__.py +11 -0
- roadmap/adapters/sync/services/baseline_state_handler.py +83 -0
- roadmap/adapters/sync/services/conflict_converter.py +85 -0
- roadmap/adapters/sync/services/issue_persistence_service.py +263 -0
- roadmap/adapters/sync/services/issue_state_service.py +243 -0
- roadmap/adapters/sync/services/local_change_filter.py +129 -0
- roadmap/adapters/sync/services/pull_result_processor.py +71 -0
- roadmap/adapters/sync/services/remote_issue_creation_service.py +112 -0
- roadmap/adapters/sync/services/sync_analysis_service.py +250 -0
- roadmap/adapters/sync/services/sync_authentication_service.py +57 -0
- roadmap/adapters/sync/services/sync_data_fetch_service.py +212 -0
- roadmap/adapters/sync/services/sync_linking_service.py +329 -0
- roadmap/adapters/sync/services/sync_report_service.py +62 -0
- roadmap/adapters/sync/services/sync_state_update_service.py +51 -0
- roadmap/adapters/sync/sync_cache_orchestrator.py +454 -0
- roadmap/adapters/sync/sync_merge_engine.py +416 -0
- roadmap/adapters/sync/sync_merge_orchestrator.py +1313 -0
- roadmap/adapters/sync/sync_retrieval_orchestrator.py +810 -0
- roadmap/application/services/__init__.py +1 -0
- roadmap/application/services/deduplicate_service.py +517 -0
- roadmap/application/use_cases/__init__.py +1 -0
- roadmap/common/__init__.py +308 -0
- roadmap/common/cache.py +209 -0
- roadmap/common/cli_errors.py +147 -0
- roadmap/common/configuration/__init__.py +33 -0
- roadmap/common/configuration/config_loader.py +264 -0
- roadmap/common/configuration/config_manager.py +164 -0
- roadmap/common/configuration/config_schema.py +145 -0
- roadmap/common/configuration/github/__init__.py +9 -0
- roadmap/common/configuration/github/config_manager.py +154 -0
- roadmap/common/configuration/github/token_resolver.py +84 -0
- roadmap/common/console.py +184 -0
- roadmap/common/constants.py +134 -0
- roadmap/common/datetime_parser.py +319 -0
- roadmap/common/error_formatter.py +84 -0
- roadmap/common/errors/__init__.py +143 -0
- roadmap/common/errors/error_base.py +73 -0
- roadmap/common/errors/error_file.py +82 -0
- roadmap/common/errors/error_git.py +67 -0
- roadmap/common/errors/error_handler.py +166 -0
- roadmap/common/errors/error_network.py +90 -0
- roadmap/common/errors/error_security.py +78 -0
- roadmap/common/errors/error_standards.py +561 -0
- roadmap/common/errors/error_validation.py +111 -0
- roadmap/common/errors/exceptions.py +432 -0
- roadmap/common/formatters/__init__.py +29 -0
- roadmap/common/formatters/base_table_formatter.py +96 -0
- roadmap/common/formatters/export/__init__.py +7 -0
- roadmap/common/formatters/export/issue_exporter.py +97 -0
- roadmap/common/formatters/helpers.py +28 -0
- roadmap/common/formatters/kanban/__init__.py +9 -0
- roadmap/common/formatters/kanban/layout.py +48 -0
- roadmap/common/formatters/kanban/organizer.py +81 -0
- roadmap/common/formatters/output/__init__.py +15 -0
- roadmap/common/formatters/tables/__init__.py +11 -0
- roadmap/common/formatters/tables/column_factory.py +312 -0
- roadmap/common/formatters/tables/issue_table.py +276 -0
- roadmap/common/formatters/tables/milestone_table.py +247 -0
- roadmap/common/formatters/tables/project_table.py +195 -0
- roadmap/common/formatters/text/__init__.py +57 -0
- roadmap/common/formatters/text/basic.py +217 -0
- roadmap/common/formatters/text/display.py +42 -0
- roadmap/common/formatters/text/duration.py +36 -0
- roadmap/common/formatters/text/operations.py +325 -0
- roadmap/common/formatters/text/status_badges.py +57 -0
- roadmap/common/initialization/github/__init__.py +11 -0
- roadmap/common/initialization/github/setup_service.py +238 -0
- roadmap/common/initialization/github/setup_validator.py +78 -0
- roadmap/common/logging/__init__.py +113 -0
- roadmap/common/logging/audit_logging.py +5 -0
- roadmap/common/logging/correlation.py +45 -0
- roadmap/common/logging/decorators.py +267 -0
- roadmap/common/logging/error_logging.py +217 -0
- roadmap/common/logging/formatters.py +124 -0
- roadmap/common/logging/loggers.py +68 -0
- roadmap/common/logging/performance_tracking.py +267 -0
- roadmap/common/logging/utils.py +335 -0
- roadmap/common/models/__init__.py +44 -0
- roadmap/common/models/cli_models.py +118 -0
- roadmap/common/models/config_models.py +199 -0
- roadmap/common/models/output_models.py +381 -0
- roadmap/common/observability/__init__.py +28 -0
- roadmap/common/observability/instrumentation.py +103 -0
- roadmap/common/observability/observability.py +76 -0
- roadmap/common/observability/otel_init.py +109 -0
- roadmap/common/output_formatter.py +393 -0
- roadmap/common/progress.py +379 -0
- roadmap/common/result.py +396 -0
- roadmap/common/security/__init__.py +48 -0
- roadmap/common/security/exceptions.py +13 -0
- roadmap/common/security/export_cleanup.py +79 -0
- roadmap/common/security/file_operations.py +104 -0
- roadmap/common/security/filename_sanitization.py +54 -0
- roadmap/common/security/logging.py +27 -0
- roadmap/common/security/path_validation.py +137 -0
- roadmap/common/security/temp_files.py +41 -0
- roadmap/common/services/__init__.py +65 -0
- roadmap/common/services/decorators.py +183 -0
- roadmap/common/services/logging_utils.py +208 -0
- roadmap/common/services/metrics.py +161 -0
- roadmap/common/services/performance.py +219 -0
- roadmap/common/services/profiling.py +288 -0
- roadmap/common/services/retry.py +288 -0
- roadmap/common/status_style_manager.py +114 -0
- roadmap/common/union_find.py +88 -0
- roadmap/common/update_constants.py +13 -0
- roadmap/common/utils/__init__.py +65 -0
- roadmap/common/utils/cli_helpers.py +381 -0
- roadmap/common/utils/file_utils.py +440 -0
- roadmap/common/utils/path_utils.py +38 -0
- roadmap/common/utils/status_utils.py +130 -0
- roadmap/common/utils/timezone_utils.py +480 -0
- roadmap/common/validation/__init__.py +37 -0
- roadmap/common/validation/field_validator.py +104 -0
- roadmap/common/validation/result.py +37 -0
- roadmap/common/validation/roadmap_validator.py +310 -0
- roadmap/common/validation/schema_validator.py +36 -0
- roadmap/common/validation/validators.py +50 -0
- roadmap/core/__init__.py +1 -0
- roadmap/core/domain/__init__.py +30 -0
- roadmap/core/domain/comment.py +24 -0
- roadmap/core/domain/health.py +15 -0
- roadmap/core/domain/issue.py +274 -0
- roadmap/core/domain/milestone.py +173 -0
- roadmap/core/domain/ports/__init__.py +1 -0
- roadmap/core/domain/ports/baseline_repository.py +24 -0
- roadmap/core/domain/ports/issue_repository.py +34 -0
- roadmap/core/domain/ports/remote_backend_port.py +31 -0
- roadmap/core/domain/project.py +130 -0
- roadmap/core/interfaces/__init__.py +109 -0
- roadmap/core/interfaces/assignee_validator.py +40 -0
- roadmap/core/interfaces/backend_factory.py +175 -0
- roadmap/core/interfaces/github.py +102 -0
- roadmap/core/interfaces/parsers.py +140 -0
- roadmap/core/interfaces/persistence.py +45 -0
- roadmap/core/interfaces/repositories.py +174 -0
- roadmap/core/interfaces/state_managers.py +111 -0
- roadmap/core/interfaces/state_storage.py +64 -0
- roadmap/core/interfaces/sync_backend.py +275 -0
- roadmap/core/interfaces/sync_services.py +119 -0
- roadmap/core/interfaces/sync_validators.py +180 -0
- roadmap/core/models/__init__.py +31 -0
- roadmap/core/models/sync_models.py +162 -0
- roadmap/core/models.py +115 -0
- roadmap/core/observability/__init__.py +17 -0
- roadmap/core/observability/sync_metrics.py +699 -0
- roadmap/core/repositories.py +200 -0
- roadmap/core/services/__init__.py +313 -0
- roadmap/core/services/baseline/__init__.py +1 -0
- roadmap/core/services/baseline/baseline_builder_progress.py +274 -0
- roadmap/core/services/baseline/baseline_retriever.py +63 -0
- roadmap/core/services/baseline/baseline_selector.py +248 -0
- roadmap/core/services/baseline/baseline_state_retriever.py +284 -0
- roadmap/core/services/baseline/optimized_baseline_builder.py +425 -0
- roadmap/core/services/comment/__init__.py +1 -0
- roadmap/core/services/comment/comment_service.py +277 -0
- roadmap/core/services/git/__init__.py +1 -0
- roadmap/core/services/git/git_hook_auto_sync_service.py +403 -0
- roadmap/core/services/github/__init__.py +1 -0
- roadmap/core/services/github/github_change_detector.py +162 -0
- roadmap/core/services/github/github_config_validator.py +148 -0
- roadmap/core/services/github/github_conflict_detector.py +179 -0
- roadmap/core/services/github/github_entity_classifier.py +51 -0
- roadmap/core/services/github/github_integration_service.py +439 -0
- roadmap/core/services/github/github_issue_client.py +346 -0
- roadmap/core/services/health/__init__.py +1 -0
- roadmap/core/services/health/backup_cleanup_service.py +202 -0
- roadmap/core/services/health/data_integrity_validator_service.py +90 -0
- roadmap/core/services/health/entity_health_scanner.py +460 -0
- roadmap/core/services/health/file_repair_service.py +193 -0
- roadmap/core/services/health/health_check_service.py +166 -0
- roadmap/core/services/health/health_models.py +80 -0
- roadmap/core/services/health/infrastructure_validator_service.py +328 -0
- roadmap/core/services/health/issue_health_scanner.py +263 -0
- roadmap/core/services/helpers/__init__.py +18 -0
- roadmap/core/services/helpers/status_change_helpers.py +88 -0
- roadmap/core/services/initialization/__init__.py +19 -0
- roadmap/core/services/initialization/utils.py +104 -0
- roadmap/core/services/initialization/validator.py +78 -0
- roadmap/core/services/initialization/workflow.py +182 -0
- roadmap/core/services/initialization_service.py +111 -0
- roadmap/core/services/issue/__init__.py +1 -0
- roadmap/core/services/issue/assignee_validation_service.py +312 -0
- roadmap/core/services/issue/issue_creation_service.py +309 -0
- roadmap/core/services/issue/issue_filter_service.py +302 -0
- roadmap/core/services/issue/issue_matching_service.py +127 -0
- roadmap/core/services/issue/issue_service.py +765 -0
- roadmap/core/services/issue/issue_update_service.py +144 -0
- roadmap/core/services/issue/start_issue_service.py +174 -0
- roadmap/core/services/issue_helpers/__init__.py +18 -0
- roadmap/core/services/issue_helpers/issue_filters.py +296 -0
- roadmap/core/services/milestone_service.py +438 -0
- roadmap/core/services/project/__init__.py +1 -0
- roadmap/core/services/project/project_service.py +434 -0
- roadmap/core/services/project/project_status_service.py +125 -0
- roadmap/core/services/project_init/__init__.py +22 -0
- roadmap/core/services/project_init/context_detection.py +146 -0
- roadmap/core/services/project_init/creation.py +76 -0
- roadmap/core/services/project_init/detection.py +51 -0
- roadmap/core/services/project_init/template.py +228 -0
- roadmap/core/services/status_change_service.py +88 -0
- roadmap/core/services/sync/__init__.py +1 -0
- roadmap/core/services/sync/batch_processor.py +290 -0
- roadmap/core/services/sync/conflict_resolver.py +154 -0
- roadmap/core/services/sync/dependency_resolver.py +367 -0
- roadmap/core/services/sync/duplicate_detector.py +771 -0
- roadmap/core/services/sync/duplicate_resolver.py +315 -0
- roadmap/core/services/sync/error_classification.py +470 -0
- roadmap/core/services/sync/performance.py +263 -0
- roadmap/core/services/sync/sync_change_computer.py +211 -0
- roadmap/core/services/sync/sync_checkpoint.py +348 -0
- roadmap/core/services/sync/sync_conflict_detector.py +147 -0
- roadmap/core/services/sync/sync_conflict_resolver.py +448 -0
- roadmap/core/services/sync/sync_errors.py +383 -0
- roadmap/core/services/sync/sync_key_normalizer.py +222 -0
- roadmap/core/services/sync/sync_metadata_service.py +361 -0
- roadmap/core/services/sync/sync_plan.py +171 -0
- roadmap/core/services/sync/sync_plan_executor.py +447 -0
- roadmap/core/services/sync/sync_report.py +296 -0
- roadmap/core/services/sync/sync_state.py +173 -0
- roadmap/core/services/sync/sync_state_comparator.py +690 -0
- roadmap/core/services/sync/sync_state_manager.py +431 -0
- roadmap/core/services/sync/sync_state_normalizer.py +98 -0
- roadmap/core/services/sync/sync_three_way.py +78 -0
- roadmap/core/services/sync/three_way_merger.py +178 -0
- roadmap/core/services/utils/__init__.py +1 -0
- roadmap/core/services/utils/configuration_service.py +113 -0
- roadmap/core/services/utils/critical_path_calculator.py +418 -0
- roadmap/core/services/utils/dependency_analyzer.py +360 -0
- roadmap/core/services/utils/field_conflict_detector.py +126 -0
- roadmap/core/services/utils/remote_fetcher.py +202 -0
- roadmap/core/services/utils/remote_state_normalizer.py +110 -0
- roadmap/core/services/utils/retry_policy.py +51 -0
- roadmap/core/services/validator_base.py +130 -0
- roadmap/core/services/validators/__init__.py +28 -0
- roadmap/core/services/validators/_utils.py +20 -0
- roadmap/core/services/validators/archivable_issues_validator.py +71 -0
- roadmap/core/services/validators/archivable_milestones_validator.py +69 -0
- roadmap/core/services/validators/backup_validator.py +89 -0
- roadmap/core/services/validators/data_integrity_validator.py +84 -0
- roadmap/core/services/validators/duplicate_issues_validator.py +75 -0
- roadmap/core/services/validators/duplicate_milestones_validator.py +141 -0
- roadmap/core/services/validators/folder_structure_validator.py +165 -0
- roadmap/core/services/validators/health_status_utils.py +28 -0
- roadmap/core/services/validators/milestone_naming_validator.py +146 -0
- roadmap/core/services/validators/missing_headlines_validator.py +97 -0
- roadmap/core/services/validators/orphaned_issues_validator.py +142 -0
- roadmap/core/services/validators/orphaned_milestones_validator.py +85 -0
- roadmap/core/utils/git_remote_parser.py +83 -0
- roadmap/core/utils/github_urls.py +66 -0
- roadmap/domain/validation.py +30 -0
- roadmap/infrastructure/__init__.py +55 -0
- roadmap/infrastructure/coordination/__init__.py +8 -0
- roadmap/infrastructure/coordination/coordinator_params.py +82 -0
- roadmap/infrastructure/coordination/core.py +446 -0
- roadmap/infrastructure/coordination/git_coordinator.py +160 -0
- roadmap/infrastructure/coordination/initialization.py +348 -0
- roadmap/infrastructure/coordination/issue_coordinator.py +154 -0
- roadmap/infrastructure/coordination/issue_operations.py +456 -0
- roadmap/infrastructure/coordination/milestone_coordinator.py +135 -0
- roadmap/infrastructure/coordination/milestone_operations.py +201 -0
- roadmap/infrastructure/coordination/project_coordinator.py +113 -0
- roadmap/infrastructure/coordination/project_operations.py +144 -0
- roadmap/infrastructure/coordination/team_coordinator.py +107 -0
- roadmap/infrastructure/coordination/user_operations.py +150 -0
- roadmap/infrastructure/coordination/validation_coordinator.py +249 -0
- roadmap/infrastructure/coordination_gateway.py +152 -0
- roadmap/infrastructure/git/__init__.py +6 -0
- roadmap/infrastructure/git/git_integration_ops.py +234 -0
- roadmap/infrastructure/git_gateway.py +35 -0
- roadmap/infrastructure/github_gateway.py +52 -0
- roadmap/infrastructure/maintenance/__init__.py +5 -0
- roadmap/infrastructure/maintenance/cleanup.py +483 -0
- roadmap/infrastructure/observability/__init__.py +7 -0
- roadmap/infrastructure/observability/health.py +196 -0
- roadmap/infrastructure/observability/health_formatter.py +73 -0
- roadmap/infrastructure/observability/specialized_health_checkers.py +188 -0
- roadmap/infrastructure/persistence_gateway.py +164 -0
- roadmap/infrastructure/security/__init__.py +1 -0
- roadmap/infrastructure/security/credentials.py +489 -0
- roadmap/infrastructure/sync_gateway.py +94 -0
- roadmap/infrastructure/validation/__init__.py +7 -0
- roadmap/infrastructure/validation/file_enumeration.py +219 -0
- roadmap/infrastructure/validation/github_assignee_validator.py +47 -0
- roadmap/infrastructure/validation/github_validator.py +101 -0
- roadmap/infrastructure/validation/milestone_consistency_validator.py +96 -0
- roadmap/infrastructure/validation/vanilla_assignee_validator.py +37 -0
- roadmap/infrastructure/validation_gateway.py +96 -0
- roadmap/presentation/formatters/sync_metrics_formatter.py +355 -0
- roadmap/settings.py +345 -0
- roadmap/templates/github_workflows/ci-cd-roadmap.yml +241 -0
- roadmap/templates/github_workflows/issue-lifecycle.yml +236 -0
- roadmap/templates/github_workflows/roadmap-integration.yml +137 -0
- roadmap/templates/github_workflows/roadmap-starter.yml +59 -0
- roadmap/version.py +381 -0
- roadmap_cli-0.1.1.dist-info/METADATA +448 -0
- roadmap_cli-0.1.1.dist-info/RECORD +521 -0
- roadmap_cli-0.1.1.dist-info/WHEEL +4 -0
- roadmap_cli-0.1.1.dist-info/entry_points.txt +2 -0
- roadmap_cli-0.1.1.dist-info/licenses/LICENSE.md +21 -0
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
"""CLI presenter for Project DTOs."""
|
|
2
|
+
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
|
|
5
|
+
from rich.markdown import Markdown
|
|
6
|
+
from rich.panel import Panel
|
|
7
|
+
from rich.text import Text
|
|
8
|
+
|
|
9
|
+
from roadmap.adapters.cli.dtos import ProjectDTO
|
|
10
|
+
from roadmap.adapters.cli.presentation.base_presenter import BasePresenter
|
|
11
|
+
from roadmap.adapters.cli.presentation.table_builders import (
|
|
12
|
+
create_list_table,
|
|
13
|
+
create_metadata_table,
|
|
14
|
+
)
|
|
15
|
+
from roadmap.adapters.cli.styling import STATUS_COLORS
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class ProjectPresenter(BasePresenter):
|
|
19
|
+
"""Presenter for formatting and displaying Project DTOs."""
|
|
20
|
+
|
|
21
|
+
def render(
|
|
22
|
+
self,
|
|
23
|
+
project_dto: ProjectDTO,
|
|
24
|
+
milestones: list | None = None,
|
|
25
|
+
milestone_progress: dict | None = None,
|
|
26
|
+
description_content: str | None = None,
|
|
27
|
+
comments_text: str | None = None,
|
|
28
|
+
effort_data: dict | None = None,
|
|
29
|
+
) -> None:
|
|
30
|
+
"""Render a project DTO in detailed view.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
project_dto: The ProjectDTO to render
|
|
34
|
+
milestones: Optional list of milestone objects to display
|
|
35
|
+
milestone_progress: Optional progress information dict
|
|
36
|
+
description_content: Optional content with description
|
|
37
|
+
comments_text: Optional formatted comments text
|
|
38
|
+
effort_data: Optional dict with 'estimated' and 'actual' hour counts
|
|
39
|
+
"""
|
|
40
|
+
# Display header
|
|
41
|
+
header = self._build_project_header(project_dto)
|
|
42
|
+
self._get_console().print(Panel(header, border_style="cyan"))
|
|
43
|
+
|
|
44
|
+
# Display metadata
|
|
45
|
+
metadata = self._build_metadata_table(project_dto)
|
|
46
|
+
self._get_console().print(
|
|
47
|
+
Panel(metadata, title="š Details", border_style="blue")
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
# Display effort if provided
|
|
51
|
+
if effort_data:
|
|
52
|
+
effort = self._build_effort_table(effort_data)
|
|
53
|
+
if effort:
|
|
54
|
+
self._get_console().print(
|
|
55
|
+
Panel(effort, title="ā±ļø Effort", border_style="yellow")
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
# Display milestones if provided
|
|
59
|
+
if milestones:
|
|
60
|
+
self._display_milestones_panel(milestones, milestone_progress)
|
|
61
|
+
|
|
62
|
+
# Display description if provided
|
|
63
|
+
if description_content:
|
|
64
|
+
md = Markdown(description_content)
|
|
65
|
+
self._get_console().print(
|
|
66
|
+
Panel(md, title="š Description", border_style="white")
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
# Display comments if provided
|
|
70
|
+
if comments_text:
|
|
71
|
+
self._get_console().print(
|
|
72
|
+
Panel(
|
|
73
|
+
comments_text,
|
|
74
|
+
title="š¬ Comments",
|
|
75
|
+
border_style="cyan",
|
|
76
|
+
)
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
# Display metadata footer
|
|
80
|
+
metadata_footer = (
|
|
81
|
+
f"Created: {project_dto.created} ⢠Updated: {project_dto.updated}"
|
|
82
|
+
)
|
|
83
|
+
self._get_console().print(f"\n[dim]{metadata_footer}[/dim]")
|
|
84
|
+
|
|
85
|
+
def _build_project_header(self, project_dto: ProjectDTO) -> Text:
|
|
86
|
+
"""Build header text with project name and status.
|
|
87
|
+
|
|
88
|
+
Args:
|
|
89
|
+
project_dto: The ProjectDTO to format
|
|
90
|
+
|
|
91
|
+
Returns:
|
|
92
|
+
Rich Text object with formatted header
|
|
93
|
+
"""
|
|
94
|
+
status_colors = {
|
|
95
|
+
"planning": "blue",
|
|
96
|
+
"active": "green",
|
|
97
|
+
"on-hold": "yellow",
|
|
98
|
+
"completed": "bold green",
|
|
99
|
+
"cancelled": "red",
|
|
100
|
+
}
|
|
101
|
+
status_color = status_colors.get(project_dto.status, "white")
|
|
102
|
+
|
|
103
|
+
header = Text()
|
|
104
|
+
header.append(project_dto.name, style="bold white")
|
|
105
|
+
header.append("\n")
|
|
106
|
+
header.append(f"[{project_dto.status.upper()}]", style=f"bold {status_color}")
|
|
107
|
+
|
|
108
|
+
return header
|
|
109
|
+
|
|
110
|
+
def _build_metadata_table(self, project_dto: ProjectDTO):
|
|
111
|
+
"""Build metadata table for project DTO.
|
|
112
|
+
|
|
113
|
+
Args:
|
|
114
|
+
project_dto: The ProjectDTO to format
|
|
115
|
+
|
|
116
|
+
Returns:
|
|
117
|
+
Rich Table with metadata rows
|
|
118
|
+
"""
|
|
119
|
+
metadata = create_metadata_table()
|
|
120
|
+
|
|
121
|
+
metadata.add_row("Status", project_dto.status.upper())
|
|
122
|
+
metadata.add_row("Owner", project_dto.owner or "Unassigned")
|
|
123
|
+
|
|
124
|
+
if project_dto.headline:
|
|
125
|
+
metadata.add_row("Headline", project_dto.headline)
|
|
126
|
+
|
|
127
|
+
if isinstance(project_dto.target_end_date, datetime):
|
|
128
|
+
metadata.add_row(
|
|
129
|
+
"Target End Date", project_dto.target_end_date.strftime("%Y-%m-%d")
|
|
130
|
+
)
|
|
131
|
+
else:
|
|
132
|
+
metadata.add_row("Target End Date", "Not set")
|
|
133
|
+
|
|
134
|
+
if isinstance(project_dto.actual_end_date, datetime):
|
|
135
|
+
metadata.add_row(
|
|
136
|
+
"Actual End Date", project_dto.actual_end_date.strftime("%Y-%m-%d")
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
metadata.add_row("Milestones", str(project_dto.milestone_count))
|
|
140
|
+
metadata.add_row("Issues", str(project_dto.issue_count))
|
|
141
|
+
|
|
142
|
+
if isinstance(project_dto.created, datetime):
|
|
143
|
+
metadata.add_row("Created", project_dto.created.strftime("%Y-%m-%d %H:%M"))
|
|
144
|
+
|
|
145
|
+
if isinstance(project_dto.updated, datetime):
|
|
146
|
+
metadata.add_row("Updated", project_dto.updated.strftime("%Y-%m-%d %H:%M"))
|
|
147
|
+
|
|
148
|
+
return metadata
|
|
149
|
+
|
|
150
|
+
def _build_effort_table(self, effort_data: dict):
|
|
151
|
+
"""Build effort/hours table if data exists.
|
|
152
|
+
|
|
153
|
+
Args:
|
|
154
|
+
effort_data: Dict with optional 'estimated' and 'actual' keys
|
|
155
|
+
|
|
156
|
+
Returns:
|
|
157
|
+
Rich Table with effort data or None if no data
|
|
158
|
+
"""
|
|
159
|
+
if not (effort_data.get("estimated") or effort_data.get("actual")):
|
|
160
|
+
return None
|
|
161
|
+
|
|
162
|
+
effort = create_metadata_table()
|
|
163
|
+
|
|
164
|
+
if effort_data.get("estimated"):
|
|
165
|
+
estimated = effort_data["estimated"]
|
|
166
|
+
if estimated < 8:
|
|
167
|
+
estimated_display = f"{estimated:.1f}h"
|
|
168
|
+
else:
|
|
169
|
+
days = estimated / 8
|
|
170
|
+
estimated_display = f"{days:.1f}d ({estimated:.1f}h)"
|
|
171
|
+
effort.add_row("Estimated", estimated_display)
|
|
172
|
+
|
|
173
|
+
if effort_data.get("actual"):
|
|
174
|
+
actual = effort_data["actual"]
|
|
175
|
+
if actual < 8:
|
|
176
|
+
actual_display = f"{actual:.1f}h"
|
|
177
|
+
else:
|
|
178
|
+
days = actual / 8
|
|
179
|
+
actual_display = f"{days:.1f}d ({actual:.1f}h)"
|
|
180
|
+
effort.add_row("Actual", actual_display)
|
|
181
|
+
|
|
182
|
+
return effort
|
|
183
|
+
|
|
184
|
+
def _display_milestones_panel(
|
|
185
|
+
self, milestones: list, _progress: dict | None = None
|
|
186
|
+
) -> None:
|
|
187
|
+
"""Display milestones in a table panel.
|
|
188
|
+
|
|
189
|
+
Args:
|
|
190
|
+
milestones: List of milestone objects
|
|
191
|
+
_progress: Optional progress information dict (currently unused)
|
|
192
|
+
"""
|
|
193
|
+
if not milestones:
|
|
194
|
+
self._get_console().print(
|
|
195
|
+
Panel(
|
|
196
|
+
"[dim]No milestones assigned to this project[/dim]",
|
|
197
|
+
title="šÆ Milestones",
|
|
198
|
+
border_style="magenta",
|
|
199
|
+
)
|
|
200
|
+
)
|
|
201
|
+
return
|
|
202
|
+
|
|
203
|
+
columns = [
|
|
204
|
+
("Name", "white", 25),
|
|
205
|
+
("Status", None, 11),
|
|
206
|
+
("Due Date", None, 11),
|
|
207
|
+
("Progress", None, 10),
|
|
208
|
+
]
|
|
209
|
+
milestones_table = create_list_table(columns)
|
|
210
|
+
|
|
211
|
+
for milestone in milestones[:10]:
|
|
212
|
+
status_color = STATUS_COLORS.get(milestone.status.value, "white")
|
|
213
|
+
due_date_str = (
|
|
214
|
+
milestone.due_date.strftime("%Y-%m-%d")
|
|
215
|
+
if hasattr(milestone.due_date, "strftime")
|
|
216
|
+
else "Not set"
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
milestones_table.add_row(
|
|
220
|
+
milestone.name,
|
|
221
|
+
f"[{status_color}]{milestone.status.value}[/{status_color}]",
|
|
222
|
+
due_date_str,
|
|
223
|
+
f"{milestone.progress_percentage}%",
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
title = (
|
|
227
|
+
f"šÆ Milestones (Showing 10 of {len(milestones)})"
|
|
228
|
+
if len(milestones) > 10
|
|
229
|
+
else "šÆ Milestones"
|
|
230
|
+
)
|
|
231
|
+
self._get_console().print(
|
|
232
|
+
Panel(milestones_table, title=title, border_style="magenta")
|
|
233
|
+
)
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
"""Presentation layer for project status display logic.
|
|
2
|
+
|
|
3
|
+
Handles all user-facing output:
|
|
4
|
+
- Displaying milestone progress
|
|
5
|
+
- Rendering issue status tables
|
|
6
|
+
- Showing roadmap summaries
|
|
7
|
+
- Formatting status information
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from rich.progress import BarColumn, Progress, TextColumn
|
|
11
|
+
from rich.table import Table
|
|
12
|
+
from rich.text import Text
|
|
13
|
+
|
|
14
|
+
from roadmap.common.console import get_console
|
|
15
|
+
from roadmap.core.domain import Status
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class MilestoneProgressPresenter:
|
|
19
|
+
"""Presenter for milestone progress display."""
|
|
20
|
+
|
|
21
|
+
@staticmethod
|
|
22
|
+
def show_milestone_header() -> None:
|
|
23
|
+
"""Display milestone section header."""
|
|
24
|
+
get_console().print("\nšÆ Milestones:", style="bold cyan")
|
|
25
|
+
|
|
26
|
+
@staticmethod
|
|
27
|
+
def show_milestone_progress(milestone_name: str, progress: dict) -> None:
|
|
28
|
+
"""Display progress for a single milestone.
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
milestone_name: Name of the milestone
|
|
32
|
+
progress: Progress dictionary with 'total', 'completed', 'percentage'
|
|
33
|
+
"""
|
|
34
|
+
console = get_console()
|
|
35
|
+
console.print(f"\n {milestone_name}")
|
|
36
|
+
|
|
37
|
+
if progress.get("total", 0) > 0:
|
|
38
|
+
with Progress(
|
|
39
|
+
TextColumn("[progress.description]{task.description}"),
|
|
40
|
+
BarColumn(),
|
|
41
|
+
TextColumn("[progress.percentage]{task.percentage:>3.0f}%"),
|
|
42
|
+
console=console,
|
|
43
|
+
transient=True,
|
|
44
|
+
) as progress_bar:
|
|
45
|
+
completed = progress.get("completed", 0)
|
|
46
|
+
total = progress.get("total", 0)
|
|
47
|
+
progress_bar.add_task(
|
|
48
|
+
f" Progress ({completed}/{total})",
|
|
49
|
+
total=total,
|
|
50
|
+
completed=completed,
|
|
51
|
+
)
|
|
52
|
+
else:
|
|
53
|
+
console.print(" No issues assigned", style="dim")
|
|
54
|
+
|
|
55
|
+
@staticmethod
|
|
56
|
+
def show_all_milestones(milestones: list, milestone_progress: dict) -> None:
|
|
57
|
+
"""Display all milestones with progress.
|
|
58
|
+
|
|
59
|
+
Args:
|
|
60
|
+
milestones: List of milestone objects
|
|
61
|
+
milestone_progress: Dictionary mapping milestone name to progress
|
|
62
|
+
"""
|
|
63
|
+
MilestoneProgressPresenter.show_milestone_header()
|
|
64
|
+
for milestone in milestones:
|
|
65
|
+
progress = milestone_progress.get(milestone.name, {})
|
|
66
|
+
MilestoneProgressPresenter.show_milestone_progress(milestone.name, progress)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class IssueStatusPresenter:
|
|
70
|
+
"""Presenter for issue status display."""
|
|
71
|
+
|
|
72
|
+
@staticmethod
|
|
73
|
+
def get_status_style(status: Status) -> str:
|
|
74
|
+
"""Get display style for a status.
|
|
75
|
+
|
|
76
|
+
Args:
|
|
77
|
+
status: Status enum value
|
|
78
|
+
|
|
79
|
+
Returns:
|
|
80
|
+
Rich style string
|
|
81
|
+
"""
|
|
82
|
+
styles = {
|
|
83
|
+
Status.TODO: "white",
|
|
84
|
+
Status.IN_PROGRESS: "yellow",
|
|
85
|
+
Status.BLOCKED: "red",
|
|
86
|
+
Status.REVIEW: "blue",
|
|
87
|
+
Status.CLOSED: "green",
|
|
88
|
+
}
|
|
89
|
+
return styles.get(status, "white")
|
|
90
|
+
|
|
91
|
+
@staticmethod
|
|
92
|
+
def show_issue_status_header() -> None:
|
|
93
|
+
"""Display issue status section header."""
|
|
94
|
+
get_console().print("\nš Issues by Status:", style="bold cyan")
|
|
95
|
+
|
|
96
|
+
@staticmethod
|
|
97
|
+
def show_issue_status_table(issue_counts: dict) -> None:
|
|
98
|
+
"""Display table of issue counts by status.
|
|
99
|
+
|
|
100
|
+
Args:
|
|
101
|
+
issue_counts: Dictionary mapping Status enum to count
|
|
102
|
+
"""
|
|
103
|
+
console = get_console()
|
|
104
|
+
if not issue_counts or not any(issue_counts.values()):
|
|
105
|
+
console.print(" No issues found", style="dim")
|
|
106
|
+
return
|
|
107
|
+
|
|
108
|
+
status_table = Table(show_header=False, box=None)
|
|
109
|
+
status_table.add_column("Status", style="white", width=15)
|
|
110
|
+
status_table.add_column("Count", style="cyan", width=10)
|
|
111
|
+
|
|
112
|
+
for status in Status:
|
|
113
|
+
count = issue_counts.get(status, 0)
|
|
114
|
+
if count > 0: # Only show statuses that have issues
|
|
115
|
+
style = IssueStatusPresenter.get_status_style(status)
|
|
116
|
+
status_table.add_row(
|
|
117
|
+
Text(f" {status.value}", style=style),
|
|
118
|
+
str(count),
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
console.print(status_table)
|
|
122
|
+
|
|
123
|
+
@staticmethod
|
|
124
|
+
def show_all_issue_statuses(issue_counts: dict) -> None:
|
|
125
|
+
"""Display all issue statuses with counts.
|
|
126
|
+
|
|
127
|
+
Args:
|
|
128
|
+
issue_counts: Dictionary mapping Status enum to count
|
|
129
|
+
"""
|
|
130
|
+
IssueStatusPresenter.show_issue_status_header()
|
|
131
|
+
IssueStatusPresenter.show_issue_status_table(issue_counts)
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
class RoadmapStatusPresenter:
|
|
135
|
+
"""Presenter for overall roadmap status."""
|
|
136
|
+
|
|
137
|
+
@staticmethod
|
|
138
|
+
def show_empty_state() -> None:
|
|
139
|
+
"""Display message when no issues or milestones exist."""
|
|
140
|
+
console = get_console()
|
|
141
|
+
console.print("\nš No issues or milestones found.", style="yellow")
|
|
142
|
+
console.print("Get started with:")
|
|
143
|
+
console.print(" roadmap issue create 'My first issue'")
|
|
144
|
+
console.print(" roadmap milestone create 'My first milestone'")
|
|
145
|
+
|
|
146
|
+
@staticmethod
|
|
147
|
+
def show_status_header() -> None:
|
|
148
|
+
"""Display main status header."""
|
|
149
|
+
get_console().print("š Roadmap Status", style="bold blue")
|
|
150
|
+
|
|
151
|
+
@staticmethod
|
|
152
|
+
def show_roadmap_summary(summary: dict) -> None:
|
|
153
|
+
"""Display comprehensive roadmap summary.
|
|
154
|
+
|
|
155
|
+
Args:
|
|
156
|
+
summary: Dictionary with roadmap summary data
|
|
157
|
+
"""
|
|
158
|
+
console = get_console()
|
|
159
|
+
console.print("\nš Summary:", style="bold cyan")
|
|
160
|
+
console.print(
|
|
161
|
+
f" Total Issues: {summary.get('total_issues', 0)}", style="white"
|
|
162
|
+
)
|
|
163
|
+
console.print(
|
|
164
|
+
f" Active Issues: {summary.get('active_issues', 0)}", style="yellow"
|
|
165
|
+
)
|
|
166
|
+
blocked = summary.get("blocked_issues", 0)
|
|
167
|
+
if blocked > 0:
|
|
168
|
+
console.print(f" Blocked Issues: {blocked}", style="red")
|
|
169
|
+
console.print(
|
|
170
|
+
f" Total Milestones: {summary.get('total_milestones', 0)}", style="white"
|
|
171
|
+
)
|
|
172
|
+
completed = summary.get("completed_milestones", 0)
|
|
173
|
+
total = summary.get("total_milestones", 0)
|
|
174
|
+
if total > 0:
|
|
175
|
+
console.print(f" Completed Milestones: {completed}/{total}", style="green")
|
|
176
|
+
|
|
177
|
+
@staticmethod
|
|
178
|
+
def show_error(error_message: str) -> None:
|
|
179
|
+
"""Display error message.
|
|
180
|
+
|
|
181
|
+
Args:
|
|
182
|
+
error_message: Error message to display
|
|
183
|
+
"""
|
|
184
|
+
get_console().print(
|
|
185
|
+
f"ā Failed to show status: {error_message}", style="bold red"
|
|
186
|
+
)
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"""Table and panel builders for consistent display formatting.
|
|
2
|
+
|
|
3
|
+
This module provides reusable Rich table and panel construction patterns for entity display:
|
|
4
|
+
- Two-column metadata tables (Key-Value format)
|
|
5
|
+
- Status and progress tables
|
|
6
|
+
- Panel creation with standard borders and styles
|
|
7
|
+
- Custom table styling
|
|
8
|
+
|
|
9
|
+
Consolidates duplicated table construction logic across view.py files.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from typing import Any
|
|
13
|
+
|
|
14
|
+
from rich.panel import Panel # type: ignore[import-not-found]
|
|
15
|
+
from rich.table import Table # type: ignore[import-not-found]
|
|
16
|
+
|
|
17
|
+
# ===== Generic Table Builders =====
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def create_metadata_table() -> Table:
|
|
21
|
+
"""Create standard 2-column metadata table (Key-Value format).
|
|
22
|
+
|
|
23
|
+
Returns:
|
|
24
|
+
Rich Table configured with standard metadata layout
|
|
25
|
+
"""
|
|
26
|
+
table = Table(show_header=False, box=None, padding=(0, 2))
|
|
27
|
+
table.add_column("Key", style="dim")
|
|
28
|
+
table.add_column("Value")
|
|
29
|
+
return table
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def create_list_table(
|
|
33
|
+
columns: list[tuple[str, str | None, int | None]] | None = None,
|
|
34
|
+
) -> Table:
|
|
35
|
+
"""Create table for list display with custom columns.
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
columns: List of (name, style, width) tuples for columns.
|
|
39
|
+
If None, creates basic 2-column table.
|
|
40
|
+
|
|
41
|
+
Returns:
|
|
42
|
+
Rich Table configured for list display
|
|
43
|
+
"""
|
|
44
|
+
table = Table(show_header=True, box=None, padding=(0, 1))
|
|
45
|
+
|
|
46
|
+
if columns is None:
|
|
47
|
+
table.add_column("ID", style="cyan", width=9)
|
|
48
|
+
table.add_column("Title", style="white")
|
|
49
|
+
else:
|
|
50
|
+
for col_name, col_style, col_width in columns:
|
|
51
|
+
kwargs: dict[str, Any] = {}
|
|
52
|
+
if col_style:
|
|
53
|
+
kwargs["style"] = col_style
|
|
54
|
+
if col_width:
|
|
55
|
+
kwargs["width"] = col_width
|
|
56
|
+
table.add_column(col_name, **kwargs)
|
|
57
|
+
|
|
58
|
+
return table
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
# ===== Progress Display =====
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
# ===== Panel Creation =====
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def create_panel(
|
|
68
|
+
content: Any,
|
|
69
|
+
title: str | None = None,
|
|
70
|
+
emoji: str | None = None,
|
|
71
|
+
border_style: str = "white",
|
|
72
|
+
) -> Panel:
|
|
73
|
+
"""Create standard panel with configurable title and border.
|
|
74
|
+
|
|
75
|
+
Args:
|
|
76
|
+
content: Content to display in panel (Table, Text, Markdown, etc.)
|
|
77
|
+
title: Optional panel title
|
|
78
|
+
emoji: Optional emoji to prepend to title
|
|
79
|
+
border_style: Border color (default: white)
|
|
80
|
+
|
|
81
|
+
Returns:
|
|
82
|
+
Rich Panel
|
|
83
|
+
"""
|
|
84
|
+
full_title = None
|
|
85
|
+
if title:
|
|
86
|
+
full_title = f"{emoji} {title}" if emoji else title
|
|
87
|
+
|
|
88
|
+
return Panel(
|
|
89
|
+
content,
|
|
90
|
+
title=full_title,
|
|
91
|
+
border_style=border_style,
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
# ===== Text Builders =====
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"""Project commands module.
|
|
2
|
+
|
|
3
|
+
Provides all project management commands:
|
|
4
|
+
- create: Create a new project
|
|
5
|
+
- list: List all projects with optional filtering
|
|
6
|
+
- delete: Delete a project
|
|
7
|
+
- view: View detailed information about a project
|
|
8
|
+
- update: Update project properties
|
|
9
|
+
- close: Close a completed project
|
|
10
|
+
- archive: Archive a completed project
|
|
11
|
+
- restore: Restore an archived project
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
import click
|
|
15
|
+
|
|
16
|
+
from roadmap.adapters.cli.projects.archive import archive_project
|
|
17
|
+
from roadmap.adapters.cli.projects.close import close_project
|
|
18
|
+
from roadmap.adapters.cli.projects.create import create_project
|
|
19
|
+
from roadmap.adapters.cli.projects.delete import delete_project
|
|
20
|
+
from roadmap.adapters.cli.projects.list import list_projects
|
|
21
|
+
from roadmap.adapters.cli.projects.restore import restore_project
|
|
22
|
+
from roadmap.adapters.cli.projects.update import update_project
|
|
23
|
+
from roadmap.adapters.cli.projects.view import view_project
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@click.group()
|
|
27
|
+
def project():
|
|
28
|
+
"""Manage projects (top-level planning documents)."""
|
|
29
|
+
pass
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
# Register all commands
|
|
33
|
+
project.add_command(create_project, name="create")
|
|
34
|
+
project.add_command(list_projects, name="list")
|
|
35
|
+
project.add_command(update_project, name="update")
|
|
36
|
+
project.add_command(delete_project, name="delete")
|
|
37
|
+
project.add_command(view_project, name="view")
|
|
38
|
+
project.add_command(close_project, name="close")
|
|
39
|
+
project.add_command(archive_project, name="archive")
|
|
40
|
+
project.add_command(restore_project, name="restore")
|
|
41
|
+
|
|
42
|
+
__all__ = ["project"]
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
"""Archive project command - move completed projects to archive."""
|
|
2
|
+
|
|
3
|
+
import click
|
|
4
|
+
|
|
5
|
+
from roadmap.adapters.cli.cli_command_helpers import require_initialized
|
|
6
|
+
from roadmap.adapters.cli.projects.archive_class import ProjectArchive
|
|
7
|
+
from roadmap.common.console import get_console
|
|
8
|
+
from roadmap.common.logging import (
|
|
9
|
+
log_command,
|
|
10
|
+
verbose_output,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@click.command()
|
|
15
|
+
@click.argument("project_name", required=False)
|
|
16
|
+
@click.option(
|
|
17
|
+
"--all-closed",
|
|
18
|
+
is_flag=True,
|
|
19
|
+
help="Archive all closed projects",
|
|
20
|
+
)
|
|
21
|
+
@click.option(
|
|
22
|
+
"--list",
|
|
23
|
+
"list_archived",
|
|
24
|
+
is_flag=True,
|
|
25
|
+
help="List archived projects",
|
|
26
|
+
)
|
|
27
|
+
@click.option(
|
|
28
|
+
"--dry-run",
|
|
29
|
+
is_flag=True,
|
|
30
|
+
help="Preview what would be archived without actually doing it",
|
|
31
|
+
)
|
|
32
|
+
@click.option(
|
|
33
|
+
"--force",
|
|
34
|
+
is_flag=True,
|
|
35
|
+
help="Skip confirmation prompt",
|
|
36
|
+
)
|
|
37
|
+
@click.option(
|
|
38
|
+
"--verbose",
|
|
39
|
+
"-v",
|
|
40
|
+
is_flag=True,
|
|
41
|
+
help="Show detailed debug information",
|
|
42
|
+
)
|
|
43
|
+
@click.pass_context
|
|
44
|
+
@verbose_output
|
|
45
|
+
@log_command("project_archive", entity_type="project", track_duration=True)
|
|
46
|
+
@require_initialized
|
|
47
|
+
def archive_project(
|
|
48
|
+
ctx: click.Context,
|
|
49
|
+
project_name: str | None,
|
|
50
|
+
all_closed: bool,
|
|
51
|
+
list_archived: bool,
|
|
52
|
+
dry_run: bool,
|
|
53
|
+
force: bool,
|
|
54
|
+
verbose: bool, # noqa: F841
|
|
55
|
+
):
|
|
56
|
+
"""Archive a project by moving it to .roadmap/archive/projects/.
|
|
57
|
+
|
|
58
|
+
This is a non-destructive operation that preserves project data while
|
|
59
|
+
cleaning up the active workspace.
|
|
60
|
+
|
|
61
|
+
Examples:
|
|
62
|
+
roadmap project archive "Project Name"
|
|
63
|
+
roadmap project archive --all-closed
|
|
64
|
+
roadmap project archive --list
|
|
65
|
+
"""
|
|
66
|
+
core = ctx.obj["core"]
|
|
67
|
+
console = get_console()
|
|
68
|
+
|
|
69
|
+
archive = ProjectArchive(core, console)
|
|
70
|
+
|
|
71
|
+
if list_archived:
|
|
72
|
+
from pathlib import Path
|
|
73
|
+
|
|
74
|
+
from roadmap.adapters.cli.crud.crud_helpers import (
|
|
75
|
+
EntityType,
|
|
76
|
+
collect_archive_files,
|
|
77
|
+
)
|
|
78
|
+
from roadmap.adapters.persistence.parser import ProjectParser
|
|
79
|
+
|
|
80
|
+
roadmap_dir = Path.cwd() / ".roadmap"
|
|
81
|
+
archive_dir = roadmap_dir / "archive" / "projects"
|
|
82
|
+
|
|
83
|
+
if not archive_dir.exists():
|
|
84
|
+
console.print("š No archived projects.", style="yellow")
|
|
85
|
+
return
|
|
86
|
+
|
|
87
|
+
archived_files = collect_archive_files(archive_dir, EntityType.PROJECT)
|
|
88
|
+
if not archived_files:
|
|
89
|
+
console.print("š No archived projects.", style="yellow")
|
|
90
|
+
return
|
|
91
|
+
|
|
92
|
+
console.print("\nš¦ Archived Projects:\n", style="bold blue")
|
|
93
|
+
for file_path in sorted(archived_files):
|
|
94
|
+
try:
|
|
95
|
+
project = ProjectParser.parse_project_file(file_path)
|
|
96
|
+
console.print(f" ⢠{project.name} ({project.status})", style="cyan")
|
|
97
|
+
except Exception:
|
|
98
|
+
console.print(
|
|
99
|
+
f" ⢠{file_path.name} (error reading file)", style="yellow"
|
|
100
|
+
)
|
|
101
|
+
return
|
|
102
|
+
|
|
103
|
+
# Validate arguments
|
|
104
|
+
if not project_name and not all_closed:
|
|
105
|
+
console.print(
|
|
106
|
+
"ā Error: Specify a project name or use --all-closed",
|
|
107
|
+
style="bold red",
|
|
108
|
+
)
|
|
109
|
+
ctx.exit(1)
|
|
110
|
+
|
|
111
|
+
if project_name and all_closed:
|
|
112
|
+
console.print(
|
|
113
|
+
"ā Error: Specify only one of: project name or --all-closed",
|
|
114
|
+
style="bold red",
|
|
115
|
+
)
|
|
116
|
+
ctx.exit(1)
|
|
117
|
+
|
|
118
|
+
try:
|
|
119
|
+
archive.execute(
|
|
120
|
+
entity_id=project_name,
|
|
121
|
+
all_closed=all_closed,
|
|
122
|
+
dry_run=dry_run,
|
|
123
|
+
force=force,
|
|
124
|
+
)
|
|
125
|
+
except Exception as e:
|
|
126
|
+
console.print(f"ā Archive operation failed: {str(e)}", style="bold red")
|
|
127
|
+
ctx.exit(1)
|