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,123 @@
|
|
|
1
|
+
"""Milestone kanban board command."""
|
|
2
|
+
|
|
3
|
+
import click
|
|
4
|
+
|
|
5
|
+
from roadmap.adapters.cli.cli_command_helpers import require_initialized
|
|
6
|
+
from roadmap.common.console import get_console
|
|
7
|
+
from roadmap.common.formatters import KanbanLayout, KanbanOrganizer
|
|
8
|
+
|
|
9
|
+
console = get_console()
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@click.command("kanban")
|
|
13
|
+
@click.argument("milestone_name")
|
|
14
|
+
@click.option("--compact", is_flag=True, help="Compact view with less spacing")
|
|
15
|
+
@click.option("--no-color", is_flag=True, help="Disable color coding")
|
|
16
|
+
@click.pass_context
|
|
17
|
+
@require_initialized
|
|
18
|
+
def milestone_kanban(
|
|
19
|
+
ctx: click.Context, milestone_name: str, compact: bool, no_color: bool
|
|
20
|
+
):
|
|
21
|
+
"""Display milestone issues in a kanban board layout."""
|
|
22
|
+
core = ctx.obj["core"]
|
|
23
|
+
|
|
24
|
+
try:
|
|
25
|
+
# Get milestone
|
|
26
|
+
milestone = core.milestones.get(milestone_name)
|
|
27
|
+
if not milestone:
|
|
28
|
+
console.print(
|
|
29
|
+
f"❌ Milestone '{milestone_name}' not found", style="bold red"
|
|
30
|
+
)
|
|
31
|
+
return
|
|
32
|
+
|
|
33
|
+
# Get all issues for this milestone
|
|
34
|
+
all_issues = core.issues.list()
|
|
35
|
+
milestone_issues = [
|
|
36
|
+
issue for issue in all_issues if issue.milestone == milestone_name
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
if not milestone_issues:
|
|
40
|
+
console.print(
|
|
41
|
+
f"📋 No issues found for milestone '{milestone_name}'", style="yellow"
|
|
42
|
+
)
|
|
43
|
+
console.print(
|
|
44
|
+
"Create issues with: roadmap issue create 'Issue title' --milestone <milestone>",
|
|
45
|
+
style="dim",
|
|
46
|
+
)
|
|
47
|
+
return
|
|
48
|
+
|
|
49
|
+
# Organize into categories
|
|
50
|
+
categories = KanbanOrganizer.categorize_issues(milestone_issues)
|
|
51
|
+
columns = KanbanOrganizer.create_column_definitions(categories, no_color)
|
|
52
|
+
|
|
53
|
+
# Display kanban board
|
|
54
|
+
_display_header(milestone, milestone_issues)
|
|
55
|
+
_display_board(
|
|
56
|
+
columns,
|
|
57
|
+
compact,
|
|
58
|
+
col_width=KanbanLayout.calculate_column_width(len(columns)),
|
|
59
|
+
)
|
|
60
|
+
_display_summary(categories)
|
|
61
|
+
|
|
62
|
+
except Exception as e:
|
|
63
|
+
console.print(f"❌ Failed to display kanban board: {e}", style="bold red")
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _display_header(milestone, milestone_issues):
|
|
67
|
+
"""Display board header with milestone info."""
|
|
68
|
+
console.print(f"\n🎯 Kanban Board: {milestone.name}", style="bold blue")
|
|
69
|
+
console.print(
|
|
70
|
+
f"📅 Due: {milestone.due_date.strftime('%Y-%m-%d') if milestone.due_date else 'No due date'}"
|
|
71
|
+
)
|
|
72
|
+
done_count = sum(1 for i in milestone_issues if i.status.value == "closed")
|
|
73
|
+
console.print(
|
|
74
|
+
f"📊 Progress: {done_count}/{len(milestone_issues)} issues completed\n"
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def _display_board(columns, compact: bool, col_width: int):
|
|
79
|
+
"""Display the kanban board columns."""
|
|
80
|
+
# Print column headers
|
|
81
|
+
header_line = ""
|
|
82
|
+
separator_line = ""
|
|
83
|
+
for title, _issues, _style in columns:
|
|
84
|
+
header_line += f"{title:<{col_width}}"
|
|
85
|
+
separator_line += "─" * col_width
|
|
86
|
+
|
|
87
|
+
console.print(header_line, style="bold")
|
|
88
|
+
console.print(separator_line, style="dim")
|
|
89
|
+
|
|
90
|
+
# Print rows
|
|
91
|
+
max_issues = max(len(issues) for _, issues, _ in columns) if columns else 0
|
|
92
|
+
for row in range(max_issues):
|
|
93
|
+
row_line = ""
|
|
94
|
+
for _title, issues, _style in columns:
|
|
95
|
+
if row < len(issues):
|
|
96
|
+
issue = issues[row]
|
|
97
|
+
# Format issue card
|
|
98
|
+
title_space = col_width - 12 # Space for ID and padding
|
|
99
|
+
display_title = issue.title
|
|
100
|
+
if len(display_title) > title_space:
|
|
101
|
+
display_title = display_title[: title_space - 3] + "..."
|
|
102
|
+
card_text = f"#{issue.id[:8]} {display_title}"
|
|
103
|
+
row_line += f"{card_text:<{col_width}}"
|
|
104
|
+
else:
|
|
105
|
+
row_line += " " * col_width
|
|
106
|
+
|
|
107
|
+
console.print(row_line)
|
|
108
|
+
|
|
109
|
+
if not compact:
|
|
110
|
+
# Add spacing between cards
|
|
111
|
+
if row < max_issues - 1:
|
|
112
|
+
console.print("")
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def _display_summary(categories: dict):
|
|
116
|
+
"""Display summary statistics."""
|
|
117
|
+
console.print("\n📈 Summary:")
|
|
118
|
+
console.print(
|
|
119
|
+
f" Overdue: {len(categories['overdue'])} | Blocked: {len(categories['blocked'])} | In Progress: {len(categories['in_progress'])}"
|
|
120
|
+
)
|
|
121
|
+
console.print(
|
|
122
|
+
f" Not Started: {len(categories['not_started'])} | Done: {len(categories['closed'])}"
|
|
123
|
+
)
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"""List milestones command."""
|
|
2
|
+
|
|
3
|
+
import click
|
|
4
|
+
|
|
5
|
+
from roadmap.adapters.cli.cli_command_helpers import require_initialized
|
|
6
|
+
from roadmap.adapters.cli.cli_error_handlers import handle_cli_error
|
|
7
|
+
from roadmap.adapters.cli.decorators import with_output_support
|
|
8
|
+
from roadmap.adapters.cli.services.milestone_list_service import MilestoneListService
|
|
9
|
+
from roadmap.common.console import get_console
|
|
10
|
+
from roadmap.common.formatters import MilestoneTableFormatter
|
|
11
|
+
from roadmap.common.logging import verbose_output
|
|
12
|
+
from roadmap.common.models import ColumnType
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _get_console():
|
|
16
|
+
"""Get console instance at runtime to respect Click's test environment."""
|
|
17
|
+
return get_console()
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@click.command("list")
|
|
21
|
+
@click.option("--overdue", is_flag=True, help="Show only overdue milestones")
|
|
22
|
+
@click.pass_context
|
|
23
|
+
@with_output_support(
|
|
24
|
+
available_columns=[
|
|
25
|
+
"name",
|
|
26
|
+
"description",
|
|
27
|
+
"status",
|
|
28
|
+
"due_date",
|
|
29
|
+
"progress",
|
|
30
|
+
"estimate",
|
|
31
|
+
],
|
|
32
|
+
column_types={
|
|
33
|
+
"name": ColumnType.STRING,
|
|
34
|
+
"description": ColumnType.STRING,
|
|
35
|
+
"status": ColumnType.ENUM,
|
|
36
|
+
"due_date": ColumnType.DATE,
|
|
37
|
+
"progress": ColumnType.STRING,
|
|
38
|
+
"estimate": ColumnType.STRING,
|
|
39
|
+
},
|
|
40
|
+
)
|
|
41
|
+
@require_initialized
|
|
42
|
+
@verbose_output
|
|
43
|
+
def list_milestones(ctx: click.Context, overdue: bool):
|
|
44
|
+
"""List all milestones.
|
|
45
|
+
|
|
46
|
+
Supports output formatting with --format, --columns, --sort-by, --filter flags.
|
|
47
|
+
"""
|
|
48
|
+
core = ctx.obj["core"]
|
|
49
|
+
|
|
50
|
+
try:
|
|
51
|
+
# Use MilestoneListService to get and filter milestones
|
|
52
|
+
service = MilestoneListService(core)
|
|
53
|
+
milestones_data = service.get_milestones_list_data(overdue_only=overdue)
|
|
54
|
+
|
|
55
|
+
# Extract milestone list from service response
|
|
56
|
+
milestone_list = milestones_data.get("milestones", [])
|
|
57
|
+
|
|
58
|
+
# Handle empty result
|
|
59
|
+
if not milestone_list:
|
|
60
|
+
_get_console().print("📋 No milestones found.", style="yellow")
|
|
61
|
+
_get_console().print(
|
|
62
|
+
"Create one with: roadmap milestone create 'Milestone name' --due-date YYYY-MM-DD",
|
|
63
|
+
style="dim",
|
|
64
|
+
)
|
|
65
|
+
return
|
|
66
|
+
|
|
67
|
+
# Convert to TableData for structured output
|
|
68
|
+
description = "overdue" if overdue else "all"
|
|
69
|
+
table_data = MilestoneTableFormatter.milestones_to_table_data(
|
|
70
|
+
milestone_list,
|
|
71
|
+
title="Milestones",
|
|
72
|
+
description=description,
|
|
73
|
+
estimates=milestones_data.get("estimates", {}),
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
return table_data
|
|
77
|
+
|
|
78
|
+
except Exception as e:
|
|
79
|
+
handle_cli_error(
|
|
80
|
+
error=e,
|
|
81
|
+
operation="list_milestones",
|
|
82
|
+
entity_type="milestone",
|
|
83
|
+
entity_id="all",
|
|
84
|
+
context={"overdue": overdue},
|
|
85
|
+
fatal=True,
|
|
86
|
+
)
|
|
87
|
+
_get_console().print(f"❌ Error listing milestones: {str(e)}", style="bold red")
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"""Recalculate milestone progress command."""
|
|
2
|
+
|
|
3
|
+
import click
|
|
4
|
+
|
|
5
|
+
from roadmap.common.console import get_console
|
|
6
|
+
|
|
7
|
+
console = get_console()
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@click.command("recalculate")
|
|
11
|
+
@click.argument("milestone_name", required=False)
|
|
12
|
+
@click.option(
|
|
13
|
+
"--method",
|
|
14
|
+
type=click.Choice(["effort_weighted", "count_based"]),
|
|
15
|
+
default="effort_weighted",
|
|
16
|
+
help="Calculation method",
|
|
17
|
+
)
|
|
18
|
+
@click.pass_context
|
|
19
|
+
def recalculate_milestone_progress(
|
|
20
|
+
ctx: click.Context, milestone_name: str, method: str
|
|
21
|
+
):
|
|
22
|
+
"""Recalculate progress for a milestone or all milestones."""
|
|
23
|
+
core = ctx.obj["core"]
|
|
24
|
+
|
|
25
|
+
try:
|
|
26
|
+
# Import the progress engine
|
|
27
|
+
from roadmap.common.progress import ProgressCalculationEngine
|
|
28
|
+
|
|
29
|
+
# Load all data
|
|
30
|
+
all_issues = core.issues.list()
|
|
31
|
+
all_milestones = core.milestones.list()
|
|
32
|
+
|
|
33
|
+
# Create progress engine
|
|
34
|
+
engine = ProgressCalculationEngine(method=method)
|
|
35
|
+
|
|
36
|
+
if milestone_name:
|
|
37
|
+
# Recalculate specific milestone
|
|
38
|
+
milestone = core.milestones.get(milestone_name)
|
|
39
|
+
if not milestone:
|
|
40
|
+
console.print(
|
|
41
|
+
f"❌ Milestone '{milestone_name}' not found", style="bold red"
|
|
42
|
+
)
|
|
43
|
+
return
|
|
44
|
+
|
|
45
|
+
old_progress = milestone.calculated_progress
|
|
46
|
+
updated = engine.update_milestone_progress(milestone, all_issues)
|
|
47
|
+
|
|
48
|
+
if updated:
|
|
49
|
+
# Save the updated milestone (needs to be implemented properly)
|
|
50
|
+
console.print(
|
|
51
|
+
f"✅ Updated milestone '{milestone_name}':", style="bold green"
|
|
52
|
+
)
|
|
53
|
+
console.print(
|
|
54
|
+
f" Progress: {old_progress or 0:.1f}% → {milestone.calculated_progress:.1f}%"
|
|
55
|
+
)
|
|
56
|
+
console.print(f" Status: {milestone.status}")
|
|
57
|
+
console.print(f" Method: {method}")
|
|
58
|
+
else:
|
|
59
|
+
console.print(
|
|
60
|
+
f"ℹ️ Milestone '{milestone_name}' progress unchanged", style="blue"
|
|
61
|
+
)
|
|
62
|
+
console.print(
|
|
63
|
+
f" Current progress: {milestone.calculated_progress or 0:.1f}%"
|
|
64
|
+
)
|
|
65
|
+
else:
|
|
66
|
+
# Recalculate all milestones
|
|
67
|
+
updated_count = 0
|
|
68
|
+
for milestone in all_milestones:
|
|
69
|
+
if engine.update_milestone_progress(milestone, all_issues):
|
|
70
|
+
updated_count += 1
|
|
71
|
+
|
|
72
|
+
console.print("✅ Recalculation complete:", style="bold green")
|
|
73
|
+
console.print(
|
|
74
|
+
f" {updated_count}/{len(all_milestones)} milestones updated"
|
|
75
|
+
)
|
|
76
|
+
console.print(f" Method: {method}")
|
|
77
|
+
|
|
78
|
+
except Exception as e:
|
|
79
|
+
console.print(f"❌ Failed to recalculate progress: {e}", style="bold red")
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"""Restore milestone command - move archived milestones back to active."""
|
|
2
|
+
|
|
3
|
+
import click
|
|
4
|
+
|
|
5
|
+
from roadmap.adapters.cli.cli_command_helpers import require_initialized
|
|
6
|
+
from roadmap.adapters.cli.milestones.restore_class import MilestoneRestore
|
|
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("milestone_name", required=False)
|
|
16
|
+
@click.option(
|
|
17
|
+
"--all",
|
|
18
|
+
is_flag=True,
|
|
19
|
+
help="Restore all archived milestones",
|
|
20
|
+
)
|
|
21
|
+
@click.option(
|
|
22
|
+
"--dry-run",
|
|
23
|
+
is_flag=True,
|
|
24
|
+
help="Preview what would be restored without actually doing it",
|
|
25
|
+
)
|
|
26
|
+
@click.option(
|
|
27
|
+
"--force",
|
|
28
|
+
is_flag=True,
|
|
29
|
+
help="Skip confirmation prompt",
|
|
30
|
+
)
|
|
31
|
+
@click.option(
|
|
32
|
+
"--verbose",
|
|
33
|
+
"-v",
|
|
34
|
+
is_flag=True,
|
|
35
|
+
help="Show detailed debug information",
|
|
36
|
+
)
|
|
37
|
+
@click.pass_context
|
|
38
|
+
@verbose_output
|
|
39
|
+
@log_command("milestone_restore", entity_type="milestone", track_duration=True)
|
|
40
|
+
@require_initialized
|
|
41
|
+
def restore_milestone(
|
|
42
|
+
ctx: click.Context,
|
|
43
|
+
milestone_name: str | None,
|
|
44
|
+
all: bool,
|
|
45
|
+
dry_run: bool,
|
|
46
|
+
force: bool,
|
|
47
|
+
verbose: bool, # noqa: F841
|
|
48
|
+
):
|
|
49
|
+
"""Restore an archived milestone back to active milestones.
|
|
50
|
+
|
|
51
|
+
This moves a milestone from .roadmap/archive/milestones/ back to
|
|
52
|
+
.roadmap/milestones/, making it active again.
|
|
53
|
+
|
|
54
|
+
Examples:
|
|
55
|
+
roadmap milestone restore "v1.0"
|
|
56
|
+
roadmap milestone restore --all
|
|
57
|
+
roadmap milestone restore "v1.0" --dry-run
|
|
58
|
+
"""
|
|
59
|
+
core = ctx.obj["core"]
|
|
60
|
+
console = get_console()
|
|
61
|
+
|
|
62
|
+
restore = MilestoneRestore(core, console)
|
|
63
|
+
|
|
64
|
+
if not milestone_name and not all:
|
|
65
|
+
console.print(
|
|
66
|
+
"❌ Error: Specify a milestone name or use --all",
|
|
67
|
+
style="bold red",
|
|
68
|
+
)
|
|
69
|
+
ctx.exit(1)
|
|
70
|
+
|
|
71
|
+
if milestone_name and all:
|
|
72
|
+
console.print(
|
|
73
|
+
"❌ Error: Cannot specify milestone name with --all",
|
|
74
|
+
style="bold red",
|
|
75
|
+
)
|
|
76
|
+
ctx.exit(1)
|
|
77
|
+
|
|
78
|
+
try:
|
|
79
|
+
restore.execute(
|
|
80
|
+
entity_id=milestone_name,
|
|
81
|
+
all=all,
|
|
82
|
+
dry_run=dry_run,
|
|
83
|
+
force=force,
|
|
84
|
+
)
|
|
85
|
+
except Exception as e:
|
|
86
|
+
console.print(f"❌ Restore operation failed: {str(e)}", style="bold red")
|
|
87
|
+
ctx.exit(1)
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"""MilestoneRestore command implementation using base class."""
|
|
2
|
+
|
|
3
|
+
import shutil
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
from roadmap.adapters.cli.crud import BaseRestore, EntityType
|
|
7
|
+
from roadmap.adapters.persistence.parser import MilestoneParser
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class MilestoneRestore(BaseRestore):
|
|
11
|
+
"""Restore milestone command implementation."""
|
|
12
|
+
|
|
13
|
+
entity_type = EntityType.MILESTONE
|
|
14
|
+
|
|
15
|
+
def get_archived_files_to_restore(
|
|
16
|
+
self,
|
|
17
|
+
entity_id: str | None = None,
|
|
18
|
+
**filters, # noqa: ARG001
|
|
19
|
+
) -> list[Path]:
|
|
20
|
+
"""Get archived milestone files to restore.
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
entity_id: Specific milestone name to restore
|
|
24
|
+
**filters: Additional filter criteria
|
|
25
|
+
|
|
26
|
+
Returns:
|
|
27
|
+
List of archived file paths
|
|
28
|
+
"""
|
|
29
|
+
from roadmap.adapters.cli.crud.crud_helpers import get_archive_dir
|
|
30
|
+
|
|
31
|
+
archive_dir = get_archive_dir(self.entity_type)
|
|
32
|
+
|
|
33
|
+
if not archive_dir.exists():
|
|
34
|
+
return []
|
|
35
|
+
|
|
36
|
+
if entity_id:
|
|
37
|
+
# Find specific milestone file by name
|
|
38
|
+
for md_file in archive_dir.glob("*.md"):
|
|
39
|
+
try:
|
|
40
|
+
milestone = MilestoneParser.parse_milestone_file(md_file)
|
|
41
|
+
if milestone.name == entity_id:
|
|
42
|
+
return [md_file]
|
|
43
|
+
except Exception as e:
|
|
44
|
+
from roadmap.common.logging import get_logger
|
|
45
|
+
|
|
46
|
+
logger = get_logger(__name__)
|
|
47
|
+
logger.debug(
|
|
48
|
+
"milestone_parse_failed",
|
|
49
|
+
file=str(md_file),
|
|
50
|
+
error=str(e),
|
|
51
|
+
action="find_restore_files",
|
|
52
|
+
)
|
|
53
|
+
continue
|
|
54
|
+
return []
|
|
55
|
+
else:
|
|
56
|
+
# Return all archived milestones
|
|
57
|
+
return list(archive_dir.glob("*.md"))
|
|
58
|
+
|
|
59
|
+
def post_restore_hook(self, restored_files: list[Path], **kwargs) -> None:
|
|
60
|
+
"""Handle post-restore state updates.
|
|
61
|
+
|
|
62
|
+
Args:
|
|
63
|
+
restored_files: Files that were restored
|
|
64
|
+
**kwargs: Additional arguments
|
|
65
|
+
"""
|
|
66
|
+
# Move associated issues folder back from archive
|
|
67
|
+
roadmap_dir = Path.cwd() / ".roadmap"
|
|
68
|
+
|
|
69
|
+
for file_path in restored_files:
|
|
70
|
+
try:
|
|
71
|
+
milestone = MilestoneParser.parse_milestone_file(file_path)
|
|
72
|
+
milestone_name = milestone.name
|
|
73
|
+
|
|
74
|
+
archive_issues_dir = roadmap_dir / "archive" / "issues" / milestone_name
|
|
75
|
+
if archive_issues_dir.exists():
|
|
76
|
+
active_issues_dir = roadmap_dir / "issues" / milestone_name
|
|
77
|
+
active_issues_dir.parent.mkdir(parents=True, exist_ok=True)
|
|
78
|
+
|
|
79
|
+
try:
|
|
80
|
+
shutil.move(str(archive_issues_dir), str(active_issues_dir))
|
|
81
|
+
except Exception as e:
|
|
82
|
+
self.console.print(
|
|
83
|
+
f"⚠️ Warning: Failed to restore issues for milestone {milestone_name}: {e}",
|
|
84
|
+
style="yellow",
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
# Update database
|
|
88
|
+
try:
|
|
89
|
+
self.core.db.mark_milestone_archived(milestone_name, archived=False)
|
|
90
|
+
except Exception as e:
|
|
91
|
+
self.console.print(
|
|
92
|
+
f"⚠️ Warning: Failed to update restoration status for {milestone_name}: {e}",
|
|
93
|
+
style="yellow",
|
|
94
|
+
)
|
|
95
|
+
except Exception as e:
|
|
96
|
+
self.console.print(
|
|
97
|
+
f"⚠️ Failed to process milestone file {file_path.name}: {e}",
|
|
98
|
+
style="yellow",
|
|
99
|
+
)
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"""Update milestone command."""
|
|
2
|
+
|
|
3
|
+
import click
|
|
4
|
+
|
|
5
|
+
from roadmap.adapters.cli.cli_command_helpers import require_initialized
|
|
6
|
+
from roadmap.adapters.cli.crud import BaseUpdate, EntityType
|
|
7
|
+
from roadmap.adapters.cli.crud.entity_builders import MilestoneBuilder
|
|
8
|
+
from roadmap.common.logging import log_command
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class MilestoneUpdate(BaseUpdate):
|
|
12
|
+
"""Update milestone command implementation."""
|
|
13
|
+
|
|
14
|
+
entity_type = EntityType.MILESTONE
|
|
15
|
+
|
|
16
|
+
def build_update_dict(self, entity_id: str, **kwargs) -> dict:
|
|
17
|
+
"""Build update dictionary for milestone."""
|
|
18
|
+
return MilestoneBuilder.build_update_dict(
|
|
19
|
+
name=kwargs.get("name"),
|
|
20
|
+
description=kwargs.get("description"),
|
|
21
|
+
due_date=kwargs.get("due_date"),
|
|
22
|
+
status=kwargs.get("status"),
|
|
23
|
+
project_id=kwargs.get("project"),
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@click.command("update")
|
|
28
|
+
@click.argument("milestone_id")
|
|
29
|
+
@click.option("--name", help="Update milestone name")
|
|
30
|
+
@click.option("--description", "-d", help="Update milestone description")
|
|
31
|
+
@click.option("--due-date", help="Update due date (YYYY-MM-DD format)")
|
|
32
|
+
@click.option(
|
|
33
|
+
"--status",
|
|
34
|
+
type=click.Choice(["open", "closed"]),
|
|
35
|
+
help="Set milestone status (open|closed)",
|
|
36
|
+
)
|
|
37
|
+
@click.option("--project", "-p", default=None, help="Project ID to assign milestone to")
|
|
38
|
+
@click.pass_context
|
|
39
|
+
@require_initialized
|
|
40
|
+
@log_command("milestone_update", entity_type="milestone", track_duration=True)
|
|
41
|
+
def update_milestone(
|
|
42
|
+
ctx: click.Context,
|
|
43
|
+
milestone_id: str,
|
|
44
|
+
name: str,
|
|
45
|
+
description: str,
|
|
46
|
+
due_date: str,
|
|
47
|
+
status: str,
|
|
48
|
+
project: str,
|
|
49
|
+
):
|
|
50
|
+
"""Update an existing milestone."""
|
|
51
|
+
core = ctx.obj["core"]
|
|
52
|
+
updater = MilestoneUpdate(core)
|
|
53
|
+
|
|
54
|
+
updater.execute(
|
|
55
|
+
entity_id=milestone_id,
|
|
56
|
+
name=name,
|
|
57
|
+
description=description,
|
|
58
|
+
due_date=due_date,
|
|
59
|
+
status=status,
|
|
60
|
+
project=project,
|
|
61
|
+
)
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
"""View milestone command."""
|
|
2
|
+
|
|
3
|
+
import click
|
|
4
|
+
|
|
5
|
+
from roadmap.adapters.cli.cli_command_helpers import require_initialized
|
|
6
|
+
from roadmap.adapters.cli.mappers import MilestoneMapper
|
|
7
|
+
from roadmap.adapters.cli.presentation.milestone_presenter import MilestonePresenter
|
|
8
|
+
from roadmap.common.console import get_console
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def _filter_milestone_issues(issues, status_filters, priority_filters, only_open):
|
|
12
|
+
"""Filter milestone issues by status, priority, and open status."""
|
|
13
|
+
if not (status_filters or priority_filters or only_open):
|
|
14
|
+
return issues
|
|
15
|
+
|
|
16
|
+
status_lower = tuple(s.lower() for s in status_filters) if status_filters else ()
|
|
17
|
+
priority_lower = (
|
|
18
|
+
tuple(p.lower() for p in priority_filters) if priority_filters else ()
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
def matches_filters(issue):
|
|
22
|
+
if status_lower and issue.status.value not in status_lower:
|
|
23
|
+
return False
|
|
24
|
+
if priority_lower and issue.priority.value not in priority_lower:
|
|
25
|
+
return False
|
|
26
|
+
if only_open and issue.status.value == "closed":
|
|
27
|
+
return False
|
|
28
|
+
return True
|
|
29
|
+
|
|
30
|
+
return [issue for issue in issues if matches_filters(issue)]
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@click.command("view")
|
|
34
|
+
@click.argument("milestone_name")
|
|
35
|
+
@click.option(
|
|
36
|
+
"--status",
|
|
37
|
+
type=click.Choice(
|
|
38
|
+
["todo", "in-progress", "blocked", "review", "closed"], case_sensitive=False
|
|
39
|
+
),
|
|
40
|
+
multiple=True,
|
|
41
|
+
help="Filter issues by status (can specify multiple times)",
|
|
42
|
+
)
|
|
43
|
+
@click.option(
|
|
44
|
+
"--priority",
|
|
45
|
+
type=click.Choice(["critical", "high", "medium", "low"], case_sensitive=False),
|
|
46
|
+
multiple=True,
|
|
47
|
+
help="Filter issues by priority (can specify multiple times)",
|
|
48
|
+
)
|
|
49
|
+
@click.option(
|
|
50
|
+
"--only-open",
|
|
51
|
+
is_flag=True,
|
|
52
|
+
help="Show only open issues (excludes closed)",
|
|
53
|
+
)
|
|
54
|
+
@click.pass_context
|
|
55
|
+
@require_initialized
|
|
56
|
+
def view_milestone(
|
|
57
|
+
ctx: click.Context,
|
|
58
|
+
milestone_name: str,
|
|
59
|
+
status: tuple,
|
|
60
|
+
priority: tuple,
|
|
61
|
+
only_open: bool,
|
|
62
|
+
):
|
|
63
|
+
"""Display detailed information about a specific milestone.
|
|
64
|
+
|
|
65
|
+
Shows complete milestone details including progress, statistics,
|
|
66
|
+
issues breakdown, description, and goals in a formatted view.
|
|
67
|
+
|
|
68
|
+
Use --status and --priority to filter which issues are displayed.
|
|
69
|
+
|
|
70
|
+
Examples:
|
|
71
|
+
roadmap milestone view v.0.5.0
|
|
72
|
+
roadmap milestone view v.0.5.0 --status todo --status in-progress
|
|
73
|
+
roadmap milestone view v.0.5.0 --priority high --priority critical
|
|
74
|
+
roadmap milestone view v.0.5.0 --only-open
|
|
75
|
+
"""
|
|
76
|
+
core = ctx.obj["core"]
|
|
77
|
+
|
|
78
|
+
milestone = core.milestones.get(milestone_name)
|
|
79
|
+
if not milestone:
|
|
80
|
+
get_console().print(
|
|
81
|
+
f"❌ Milestone '{milestone_name}' not found.", style="bold red"
|
|
82
|
+
)
|
|
83
|
+
get_console().print(
|
|
84
|
+
"\n💡 Tip: Use 'roadmap milestone list' to see all available milestones.",
|
|
85
|
+
style="dim",
|
|
86
|
+
)
|
|
87
|
+
ctx.exit(1)
|
|
88
|
+
|
|
89
|
+
# Convert milestone to DTO
|
|
90
|
+
milestone_dto = MilestoneMapper.domain_to_dto(milestone)
|
|
91
|
+
|
|
92
|
+
# Get milestone issues and filter
|
|
93
|
+
all_issues = core.issues.list()
|
|
94
|
+
milestone_issues = milestone.get_issues(all_issues)
|
|
95
|
+
milestone_issues = _filter_milestone_issues(
|
|
96
|
+
milestone_issues, status, priority, only_open
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
# Get progress data
|
|
100
|
+
progress_data = core.milestones.get_progress(milestone_name)
|
|
101
|
+
|
|
102
|
+
# Extract description and goals
|
|
103
|
+
description_content = milestone.content if milestone.content else None
|
|
104
|
+
|
|
105
|
+
# Prepare comments if any
|
|
106
|
+
comments_text = None
|
|
107
|
+
if milestone.comments:
|
|
108
|
+
from roadmap.core.services.comment.comment_service import CommentService
|
|
109
|
+
|
|
110
|
+
threads = CommentService.build_comment_threads(milestone.comments)
|
|
111
|
+
comment_text = ""
|
|
112
|
+
|
|
113
|
+
top_level_ids = [k for k in threads.keys() if k is not None]
|
|
114
|
+
for top_level_id in sorted(top_level_ids):
|
|
115
|
+
for comment in threads.get(top_level_id, []):
|
|
116
|
+
comment_text += (
|
|
117
|
+
CommentService.format_comment_for_display(comment, indent=0) + "\n"
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
if comment.id in threads:
|
|
121
|
+
for reply in threads[comment.id]:
|
|
122
|
+
comment_text += (
|
|
123
|
+
CommentService.format_comment_for_display(reply, indent=1)
|
|
124
|
+
+ "\n"
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
comment_text += "\n"
|
|
128
|
+
|
|
129
|
+
comments_text = comment_text.rstrip()
|
|
130
|
+
|
|
131
|
+
# Render using presenter
|
|
132
|
+
presenter = MilestonePresenter()
|
|
133
|
+
presenter.render(
|
|
134
|
+
milestone_dto,
|
|
135
|
+
issues=milestone_issues if milestone_issues else None,
|
|
136
|
+
progress_data=progress_data,
|
|
137
|
+
description_content=description_content,
|
|
138
|
+
comments_text=comments_text,
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
# Display file info
|
|
142
|
+
get_console().print(f"[dim]File: .roadmap/milestones/{milestone.name}.md[/dim]")
|