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,74 @@
|
|
|
1
|
+
"""CLI confirmation and cancellation helpers - shared across all command modules.
|
|
2
|
+
|
|
3
|
+
This module provides reusable confirmation and entity-validation patterns for common CLI operations:
|
|
4
|
+
- Entity existence checks with standardized error messages
|
|
5
|
+
- Confirmation prompts with cancellation handling
|
|
6
|
+
- Archive existence validation
|
|
7
|
+
- Entity-not-found error messages
|
|
8
|
+
|
|
9
|
+
Consolidates duplicated confirmation and validation logic across issue, milestone, and project commands.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import click # type: ignore[import-not-found]
|
|
13
|
+
|
|
14
|
+
from roadmap.common.console import get_console
|
|
15
|
+
|
|
16
|
+
console = get_console()
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
# ===== Entity Validation =====
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def check_entity_exists(core, entity_type: str, entity_id: str, entity_lookup=None):
|
|
23
|
+
"""Check if entity exists, display error and return False if not found.
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
core: RoadmapCore instance
|
|
27
|
+
entity_type: Type of entity ('issue', 'milestone', 'project')
|
|
28
|
+
entity_id: ID or name of entity to validate
|
|
29
|
+
entity_lookup: Optional pre-fetched entity to check instead of looking up
|
|
30
|
+
|
|
31
|
+
Returns:
|
|
32
|
+
The entity object if found, False if not found (already displays error message)
|
|
33
|
+
"""
|
|
34
|
+
if entity_lookup is not None:
|
|
35
|
+
entity = entity_lookup
|
|
36
|
+
else:
|
|
37
|
+
entity_collection = getattr(core, f"{entity_type}s", None)
|
|
38
|
+
if entity_collection is None:
|
|
39
|
+
console.print(
|
|
40
|
+
f"❌ Invalid entity type: {entity_type}",
|
|
41
|
+
style="bold red",
|
|
42
|
+
)
|
|
43
|
+
return False
|
|
44
|
+
entity = entity_collection.get(entity_id)
|
|
45
|
+
|
|
46
|
+
if not entity:
|
|
47
|
+
console.print(
|
|
48
|
+
f"❌ {entity_type.capitalize()} '{entity_id}' not found.",
|
|
49
|
+
style="bold red",
|
|
50
|
+
)
|
|
51
|
+
return False
|
|
52
|
+
|
|
53
|
+
return entity
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
# ===== Confirmation Prompts =====
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def confirm_action(prompt: str, default: bool = False) -> bool:
|
|
60
|
+
"""Show confirmation prompt and handle cancellation.
|
|
61
|
+
|
|
62
|
+
Displays user-friendly cancellation message if user declines.
|
|
63
|
+
|
|
64
|
+
Args:
|
|
65
|
+
prompt: Confirmation prompt text
|
|
66
|
+
default: Default value if user just presses Enter
|
|
67
|
+
|
|
68
|
+
Returns:
|
|
69
|
+
True if confirmed, False if cancelled (message already displayed)
|
|
70
|
+
"""
|
|
71
|
+
if not click.confirm(prompt, default=default):
|
|
72
|
+
console.print("❌ Cancelled.", style="yellow")
|
|
73
|
+
return False
|
|
74
|
+
return True
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
"""CLI error handling and display helpers - shared across all command modules.
|
|
2
|
+
|
|
3
|
+
This module provides reusable error handling patterns for common CLI operations:
|
|
4
|
+
- Universal error handler with classification and context capture
|
|
5
|
+
- Decorator for automatic error handling in CLI commands
|
|
6
|
+
- Generic exception handling with user-friendly messages
|
|
7
|
+
- Operation failure messages with context
|
|
8
|
+
- Standardized error display formatting
|
|
9
|
+
- Recoverable vs fatal error handling
|
|
10
|
+
|
|
11
|
+
Consolidates duplicated error handling and display logic across all CLI commands.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
import uuid
|
|
15
|
+
from typing import Any
|
|
16
|
+
|
|
17
|
+
import click
|
|
18
|
+
|
|
19
|
+
from roadmap.common.console import get_console
|
|
20
|
+
from roadmap.common.logging import (
|
|
21
|
+
classify_error,
|
|
22
|
+
get_logger,
|
|
23
|
+
is_error_recoverable,
|
|
24
|
+
suggest_recovery,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
console = get_console()
|
|
28
|
+
logger = get_logger(__name__)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
# ===== Error Message Formatting =====
|
|
32
|
+
# (Legacy formatting functions removed - use infrastructure.logging instead)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
# ===== Universal Error Handler (Phase 3b) =====
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def generate_correlation_id() -> str:
|
|
39
|
+
"""Generate unique correlation ID for tracing errors through system."""
|
|
40
|
+
return str(uuid.uuid4())[:8]
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def extract_user_context() -> dict[str, Any]:
|
|
44
|
+
"""Extract user/session context from Click context."""
|
|
45
|
+
try:
|
|
46
|
+
ctx = click.get_current_context()
|
|
47
|
+
return {
|
|
48
|
+
"user": ctx.obj.get("user") if ctx.obj else None,
|
|
49
|
+
"command": ctx.invoked_subcommand,
|
|
50
|
+
"params": dict(ctx.params),
|
|
51
|
+
}
|
|
52
|
+
except RuntimeError as e:
|
|
53
|
+
logger.debug(
|
|
54
|
+
"click_context_not_available", error=str(e), action="extract_user_context"
|
|
55
|
+
)
|
|
56
|
+
return {"user": None, "command": None, "params": {}}
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def handle_cli_error(
|
|
60
|
+
error: Exception,
|
|
61
|
+
operation: str,
|
|
62
|
+
entity_type: str | None = None,
|
|
63
|
+
entity_id: str | None = None,
|
|
64
|
+
context: dict[str, Any] | None = None,
|
|
65
|
+
fatal: bool = True,
|
|
66
|
+
include_traceback: bool = True, # noqa: ARG001
|
|
67
|
+
) -> None:
|
|
68
|
+
"""Universal CLI error handler with classification, logging, and display.
|
|
69
|
+
|
|
70
|
+
This is the single point of error handling for all CLI operations. It:
|
|
71
|
+
1. Classifies the error (user/system/external)
|
|
72
|
+
2. Generates correlation ID for tracing
|
|
73
|
+
3. Captures user/command context
|
|
74
|
+
4. Logs with full context to infrastructure logging
|
|
75
|
+
5. Displays user-friendly message to console
|
|
76
|
+
6. Suggests recovery action if applicable
|
|
77
|
+
|
|
78
|
+
Args:
|
|
79
|
+
error: The exception that occurred
|
|
80
|
+
operation: Name of operation that failed (e.g., "archive_project")
|
|
81
|
+
entity_type: Type of entity being operated on (e.g., "project")
|
|
82
|
+
entity_id: ID of entity (e.g., project name)
|
|
83
|
+
context: Optional additional context dict
|
|
84
|
+
fatal: If True, error is fatal and command should exit
|
|
85
|
+
include_traceback: If True, include full stack trace in logs
|
|
86
|
+
"""
|
|
87
|
+
correlation_id = generate_correlation_id()
|
|
88
|
+
user_context = extract_user_context()
|
|
89
|
+
|
|
90
|
+
# Build comprehensive error context
|
|
91
|
+
error_context = {
|
|
92
|
+
"correlation_id": correlation_id,
|
|
93
|
+
"operation": operation,
|
|
94
|
+
"entity_type": entity_type,
|
|
95
|
+
"entity_id": entity_id,
|
|
96
|
+
"error_class": type(error).__name__,
|
|
97
|
+
"user": user_context.get("user"),
|
|
98
|
+
"command": user_context.get("command"),
|
|
99
|
+
"is_fatal": fatal,
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if context:
|
|
103
|
+
error_context.update(context)
|
|
104
|
+
|
|
105
|
+
# Classify and log error
|
|
106
|
+
classification = classify_error(error)
|
|
107
|
+
recoverable = is_error_recoverable(error)
|
|
108
|
+
recovery = suggest_recovery(error)
|
|
109
|
+
|
|
110
|
+
error_context.update(
|
|
111
|
+
{
|
|
112
|
+
"classification": classification,
|
|
113
|
+
"recoverable": recoverable,
|
|
114
|
+
"suggested_action": recovery,
|
|
115
|
+
}
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
# Log with full traceback if requested
|
|
119
|
+
logger.error(
|
|
120
|
+
"operation_failed",
|
|
121
|
+
severity="operational",
|
|
122
|
+
exc_info=error,
|
|
123
|
+
**error_context,
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
# Display user-friendly message
|
|
127
|
+
emoji = "❌" if fatal else "⚠️"
|
|
128
|
+
entity_msg = f" {entity_type}" if entity_type else ""
|
|
129
|
+
id_msg = f" '{entity_id}'" if entity_id else ""
|
|
130
|
+
|
|
131
|
+
message = f"{emoji} {operation.replace('_', ' ').title()}{entity_msg}{id_msg} failed: {str(error)}"
|
|
132
|
+
console.print(message, style="bold red")
|
|
133
|
+
|
|
134
|
+
# Add recovery suggestion if applicable
|
|
135
|
+
if recoverable:
|
|
136
|
+
console.print(" 💡 Try again - this error may be temporary", style="cyan")
|
|
137
|
+
elif recovery != "contact_support":
|
|
138
|
+
action_msg = {
|
|
139
|
+
"validate_input": "Check your input and try again",
|
|
140
|
+
"check_connectivity": "Check network connection and try again",
|
|
141
|
+
"manual_intervention": "Manual intervention may be required",
|
|
142
|
+
}.get(recovery, "Contact support")
|
|
143
|
+
console.print(f" 💡 {action_msg}", style="cyan")
|
|
144
|
+
|
|
145
|
+
# Add correlation ID for support
|
|
146
|
+
console.print(f" 📊 Error ID: {correlation_id}", style="dim")
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"""CLI validation helpers - shared across all command modules.
|
|
2
|
+
|
|
3
|
+
This module provides reusable validation functions for common CLI patterns:
|
|
4
|
+
- Priority enum validation
|
|
5
|
+
|
|
6
|
+
Consolidates duplicated validation logic across commands.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from roadmap.common.console import get_console
|
|
10
|
+
|
|
11
|
+
console = get_console()
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
# ===== Priority/Status Validation =====
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def validate_priority(priority_str: str, domain_module=None):
|
|
18
|
+
"""Validate and convert priority string to Priority enum.
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
priority_str: Priority string (critical, high, medium, low)
|
|
22
|
+
domain_module: Optional domain module for custom Priority class
|
|
23
|
+
|
|
24
|
+
Returns:
|
|
25
|
+
Priority enum if valid, None if invalid
|
|
26
|
+
"""
|
|
27
|
+
if domain_module is None:
|
|
28
|
+
from roadmap.core.domain import Priority
|
|
29
|
+
else:
|
|
30
|
+
Priority = domain_module.Priority
|
|
31
|
+
|
|
32
|
+
try:
|
|
33
|
+
return Priority(priority_str.upper())
|
|
34
|
+
except ValueError:
|
|
35
|
+
console.print(
|
|
36
|
+
f"❌ Invalid priority: {priority_str}. Use: critical, high, medium, low",
|
|
37
|
+
style="bold red",
|
|
38
|
+
)
|
|
39
|
+
return None
|
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
"""Sync status dashboard - comprehensive sync health reporting."""
|
|
2
|
+
|
|
3
|
+
import click
|
|
4
|
+
from rich.panel import Panel
|
|
5
|
+
from rich.table import Table
|
|
6
|
+
from rich.text import Text
|
|
7
|
+
|
|
8
|
+
from roadmap.common.console import get_console
|
|
9
|
+
from roadmap.core.services.sync.sync_metadata_service import SyncMetadataService
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def _build_health_overview_panel(stats: dict, console) -> Panel:
|
|
13
|
+
"""Build health overview panel with key metrics."""
|
|
14
|
+
health_table = Table(show_header=False, box=None, padding=(0, 2))
|
|
15
|
+
health_table.add_column(style="bold")
|
|
16
|
+
health_table.add_column(justify="right")
|
|
17
|
+
|
|
18
|
+
# Calculate health score
|
|
19
|
+
total_attempts = stats.get("total_sync_attempts", 0)
|
|
20
|
+
successful = stats.get("successful_syncs", 0)
|
|
21
|
+
conflicts = stats.get("total_conflicts_resolved", 0)
|
|
22
|
+
|
|
23
|
+
if total_attempts > 0:
|
|
24
|
+
success_rate = (successful / total_attempts) * 100
|
|
25
|
+
health_emoji = (
|
|
26
|
+
"🟢" if success_rate >= 90 else "🟡" if success_rate >= 70 else "🔴"
|
|
27
|
+
)
|
|
28
|
+
health_text = (
|
|
29
|
+
"Healthy"
|
|
30
|
+
if success_rate >= 90
|
|
31
|
+
else "Degraded"
|
|
32
|
+
if success_rate >= 70
|
|
33
|
+
else "Unhealthy"
|
|
34
|
+
)
|
|
35
|
+
else:
|
|
36
|
+
success_rate = 0
|
|
37
|
+
health_emoji = "⚪"
|
|
38
|
+
health_text = "No Data"
|
|
39
|
+
|
|
40
|
+
health_table.add_row(
|
|
41
|
+
f"{health_emoji} Overall Health",
|
|
42
|
+
Text(
|
|
43
|
+
health_text,
|
|
44
|
+
style="bold green"
|
|
45
|
+
if success_rate >= 90
|
|
46
|
+
else "bold yellow"
|
|
47
|
+
if success_rate >= 70
|
|
48
|
+
else "bold red",
|
|
49
|
+
),
|
|
50
|
+
)
|
|
51
|
+
health_table.add_row("Success Rate", f"{success_rate:.1f}%")
|
|
52
|
+
health_table.add_row("Total Syncs", str(total_attempts))
|
|
53
|
+
health_table.add_row("Successful", f"[green]{successful}[/green]")
|
|
54
|
+
health_table.add_row("Conflicts", f"[yellow]{conflicts}[/yellow]")
|
|
55
|
+
health_table.add_row("Never Synced", f"[dim]{stats.get('never_synced', 0)}[/dim]")
|
|
56
|
+
|
|
57
|
+
return Panel(
|
|
58
|
+
health_table,
|
|
59
|
+
title="[bold cyan]📊 Sync Health Overview[/bold cyan]",
|
|
60
|
+
border_style="cyan",
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def _build_recent_activity_panel(core, metadata_service, console) -> Panel:
|
|
65
|
+
"""Build recent activity panel showing last 5 syncs."""
|
|
66
|
+
all_issues = core.issues.all()
|
|
67
|
+
github_issues = [i for i in all_issues if getattr(i, "github_issue", None)]
|
|
68
|
+
|
|
69
|
+
# Collect all recent sync records
|
|
70
|
+
recent_syncs = []
|
|
71
|
+
for issue in github_issues:
|
|
72
|
+
metadata = metadata_service.get_metadata(issue)
|
|
73
|
+
for record in metadata.sync_history[-5:]: # Last 5 per issue
|
|
74
|
+
recent_syncs.append((issue, record))
|
|
75
|
+
|
|
76
|
+
# Sort by timestamp, most recent first
|
|
77
|
+
recent_syncs.sort(key=lambda x: x[1].sync_timestamp, reverse=True)
|
|
78
|
+
recent_syncs = recent_syncs[:10] # Top 10 overall
|
|
79
|
+
|
|
80
|
+
if not recent_syncs:
|
|
81
|
+
return Panel(
|
|
82
|
+
Text("No recent sync activity", style="dim"),
|
|
83
|
+
title="[bold cyan]🕐 Recent Activity[/bold cyan]",
|
|
84
|
+
border_style="cyan",
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
activity_table = Table(show_header=True, header_style="bold", box=None)
|
|
88
|
+
activity_table.add_column("Time", style="dim", width=12)
|
|
89
|
+
activity_table.add_column("Issue", width=12)
|
|
90
|
+
activity_table.add_column("Status", width=10)
|
|
91
|
+
activity_table.add_column("Details", width=30)
|
|
92
|
+
|
|
93
|
+
for issue, record in recent_syncs:
|
|
94
|
+
time_str = record.sync_timestamp[11:19] # HH:MM:SS
|
|
95
|
+
issue_id = issue.id[:10]
|
|
96
|
+
|
|
97
|
+
if record.success:
|
|
98
|
+
status = "[green]✓ Success[/green]"
|
|
99
|
+
elif record.conflict_resolution:
|
|
100
|
+
status = "[yellow]⚠ Conflict[/yellow]"
|
|
101
|
+
else:
|
|
102
|
+
status = "[red]✗ Error[/red]"
|
|
103
|
+
|
|
104
|
+
details = ""
|
|
105
|
+
if record.conflict_resolution:
|
|
106
|
+
details = f"Resolved: {record.conflict_resolution}"
|
|
107
|
+
elif record.error_message:
|
|
108
|
+
details = record.error_message[:27] + "..."
|
|
109
|
+
elif record.local_changes:
|
|
110
|
+
details = f"{len(record.local_changes)} changes"
|
|
111
|
+
|
|
112
|
+
activity_table.add_row(time_str, issue_id, status, details)
|
|
113
|
+
|
|
114
|
+
return Panel(
|
|
115
|
+
activity_table,
|
|
116
|
+
title="[bold cyan]🕐 Recent Sync Activity (Last 10)[/bold cyan]",
|
|
117
|
+
border_style="cyan",
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def _build_error_trends_panel(core, metadata_service, console) -> Panel:
|
|
122
|
+
"""Build error trends panel showing common issues."""
|
|
123
|
+
all_issues = core.issues.all()
|
|
124
|
+
github_issues = [i for i in all_issues if getattr(i, "github_issue", None)]
|
|
125
|
+
|
|
126
|
+
# Collect error stats
|
|
127
|
+
error_types = {}
|
|
128
|
+
conflict_types = {}
|
|
129
|
+
|
|
130
|
+
for issue in github_issues:
|
|
131
|
+
metadata = metadata_service.get_metadata(issue)
|
|
132
|
+
for record in metadata.sync_history:
|
|
133
|
+
if record.error_message:
|
|
134
|
+
# Simple categorization
|
|
135
|
+
error_key = _categorize_error(record.error_message)
|
|
136
|
+
error_types[error_key] = error_types.get(error_key, 0) + 1
|
|
137
|
+
|
|
138
|
+
if record.conflict_resolution:
|
|
139
|
+
conflict_types[record.conflict_resolution] = (
|
|
140
|
+
conflict_types.get(record.conflict_resolution, 0) + 1
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
if not error_types and not conflict_types:
|
|
144
|
+
return Panel(
|
|
145
|
+
Text("No errors or conflicts detected 🎉", style="green"),
|
|
146
|
+
title="[bold cyan]📈 Error Trends[/bold cyan]",
|
|
147
|
+
border_style="cyan",
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
trends_table = Table(show_header=True, header_style="bold", box=None)
|
|
151
|
+
trends_table.add_column("Type", style="yellow")
|
|
152
|
+
trends_table.add_column("Category", width=20)
|
|
153
|
+
trends_table.add_column("Count", justify="right", width=8)
|
|
154
|
+
|
|
155
|
+
for error, count in sorted(error_types.items(), key=lambda x: x[1], reverse=True)[
|
|
156
|
+
:5
|
|
157
|
+
]:
|
|
158
|
+
trends_table.add_row("Error", error, str(count))
|
|
159
|
+
|
|
160
|
+
for resolution, count in sorted(
|
|
161
|
+
conflict_types.items(), key=lambda x: x[1], reverse=True
|
|
162
|
+
)[:3]:
|
|
163
|
+
trends_table.add_row("Conflict", resolution, str(count))
|
|
164
|
+
|
|
165
|
+
return Panel(
|
|
166
|
+
trends_table,
|
|
167
|
+
title="[bold cyan]📈 Error & Conflict Trends[/bold cyan]",
|
|
168
|
+
border_style="cyan",
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def _categorize_error(error_message: str) -> str:
|
|
173
|
+
"""Categorize error message into a type."""
|
|
174
|
+
error_lower = error_message.lower()
|
|
175
|
+
|
|
176
|
+
if "rate limit" in error_lower or "429" in error_lower:
|
|
177
|
+
return "Rate Limit"
|
|
178
|
+
elif "auth" in error_lower or "401" in error_lower or "403" in error_lower:
|
|
179
|
+
return "Authentication"
|
|
180
|
+
elif (
|
|
181
|
+
"network" in error_lower
|
|
182
|
+
or "connection" in error_lower
|
|
183
|
+
or "timeout" in error_lower
|
|
184
|
+
):
|
|
185
|
+
return "Network"
|
|
186
|
+
elif "not found" in error_lower or "404" in error_lower:
|
|
187
|
+
return "Not Found"
|
|
188
|
+
elif "validation" in error_lower or "invalid" in error_lower:
|
|
189
|
+
return "Validation"
|
|
190
|
+
else:
|
|
191
|
+
return "Other"
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def _build_issue_health_panel(core, metadata_service, console) -> Panel:
|
|
195
|
+
"""Build issue health breakdown panel."""
|
|
196
|
+
all_issues = core.issues.all()
|
|
197
|
+
github_issues = [i for i in all_issues if getattr(i, "github_issue", None)]
|
|
198
|
+
|
|
199
|
+
# Categorize issues by sync health
|
|
200
|
+
healthy = []
|
|
201
|
+
at_risk = []
|
|
202
|
+
failing = []
|
|
203
|
+
never_synced = []
|
|
204
|
+
|
|
205
|
+
for issue in github_issues:
|
|
206
|
+
metadata = metadata_service.get_metadata(issue)
|
|
207
|
+
|
|
208
|
+
if metadata.sync_count == 0:
|
|
209
|
+
never_synced.append(issue)
|
|
210
|
+
else:
|
|
211
|
+
success_rate = metadata.get_success_rate()
|
|
212
|
+
if success_rate >= 90:
|
|
213
|
+
healthy.append(issue)
|
|
214
|
+
elif success_rate >= 70:
|
|
215
|
+
at_risk.append(issue)
|
|
216
|
+
else:
|
|
217
|
+
failing.append(issue)
|
|
218
|
+
|
|
219
|
+
health_breakdown = Table(show_header=True, header_style="bold", box=None)
|
|
220
|
+
health_breakdown.add_column("Category", style="bold")
|
|
221
|
+
health_breakdown.add_column("Count", justify="right", width=8)
|
|
222
|
+
health_breakdown.add_column("Percentage", width=12)
|
|
223
|
+
|
|
224
|
+
total = len(github_issues)
|
|
225
|
+
if total > 0:
|
|
226
|
+
health_breakdown.add_row(
|
|
227
|
+
"[green]✓ Healthy (90%+)[/green]",
|
|
228
|
+
str(len(healthy)),
|
|
229
|
+
f"{(len(healthy) / total) * 100:.1f}%",
|
|
230
|
+
)
|
|
231
|
+
health_breakdown.add_row(
|
|
232
|
+
"[yellow]⚠ At Risk (70-89%)[/yellow]",
|
|
233
|
+
str(len(at_risk)),
|
|
234
|
+
f"{(len(at_risk) / total) * 100:.1f}%",
|
|
235
|
+
)
|
|
236
|
+
health_breakdown.add_row(
|
|
237
|
+
"[red]✗ Failing (<70%)[/red]",
|
|
238
|
+
str(len(failing)),
|
|
239
|
+
f"{(len(failing) / total) * 100:.1f}%",
|
|
240
|
+
)
|
|
241
|
+
health_breakdown.add_row(
|
|
242
|
+
"[dim]○ Never Synced[/dim]",
|
|
243
|
+
str(len(never_synced)),
|
|
244
|
+
f"{(len(never_synced) / total) * 100:.1f}%",
|
|
245
|
+
)
|
|
246
|
+
|
|
247
|
+
return Panel(
|
|
248
|
+
health_breakdown,
|
|
249
|
+
title="[bold cyan]💊 Issue Health Breakdown[/bold cyan]",
|
|
250
|
+
border_style="cyan",
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
@click.command(name="status")
|
|
255
|
+
@click.pass_context
|
|
256
|
+
def sync_status_dashboard(ctx: click.Context) -> None:
|
|
257
|
+
"""Display comprehensive sync health dashboard.
|
|
258
|
+
|
|
259
|
+
Shows an overview of sync operations, health metrics, recent activity,
|
|
260
|
+
error trends, and issue-level health breakdown.
|
|
261
|
+
|
|
262
|
+
Examples:
|
|
263
|
+
# Show sync health dashboard
|
|
264
|
+
roadmap sync status
|
|
265
|
+
"""
|
|
266
|
+
core = ctx.obj["core"]
|
|
267
|
+
console = get_console()
|
|
268
|
+
|
|
269
|
+
# Initialize metadata service
|
|
270
|
+
metadata_service = SyncMetadataService(core)
|
|
271
|
+
|
|
272
|
+
# Get all GitHub-linked issues
|
|
273
|
+
all_issues = core.issues.all()
|
|
274
|
+
github_issues = [i for i in all_issues if getattr(i, "github_issue", None)]
|
|
275
|
+
|
|
276
|
+
if not github_issues:
|
|
277
|
+
console.print("[yellow]⚠️ No GitHub-linked issues found[/yellow]")
|
|
278
|
+
console.print(
|
|
279
|
+
"\n[dim]Hint: Link issues with: roadmap issue link <issue-id> <github-issue-number>[/dim]"
|
|
280
|
+
)
|
|
281
|
+
return
|
|
282
|
+
|
|
283
|
+
# Get aggregate statistics
|
|
284
|
+
stats = metadata_service.get_statistics(github_issues)
|
|
285
|
+
|
|
286
|
+
# Build dashboard layout
|
|
287
|
+
console.print("\n[bold cyan]════ Sync Health Dashboard ════[/bold cyan]")
|
|
288
|
+
console.print(f"[dim]Tracking {len(github_issues)} GitHub-linked issues[/dim]\n")
|
|
289
|
+
|
|
290
|
+
# Show health overview
|
|
291
|
+
console.print(_build_health_overview_panel(stats, console))
|
|
292
|
+
console.print()
|
|
293
|
+
|
|
294
|
+
# Show issue health breakdown
|
|
295
|
+
console.print(_build_issue_health_panel(core, metadata_service, console))
|
|
296
|
+
console.print()
|
|
297
|
+
|
|
298
|
+
# Show recent activity
|
|
299
|
+
console.print(_build_recent_activity_panel(core, metadata_service, console))
|
|
300
|
+
console.print()
|
|
301
|
+
|
|
302
|
+
# Show error trends
|
|
303
|
+
console.print(_build_error_trends_panel(core, metadata_service, console))
|
|
304
|
+
console.print()
|
|
305
|
+
|
|
306
|
+
console.print(
|
|
307
|
+
"[dim]💡 Tip: Use 'roadmap issue sync-status <id>' for detailed issue-level sync history[/dim]"
|
|
308
|
+
)
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
"""Comment management CLI commands."""
|
|
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.common.console import get_console
|
|
8
|
+
|
|
9
|
+
console = get_console()
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@click.group()
|
|
13
|
+
def comment():
|
|
14
|
+
"""Manage comments on issues and milestones."""
|
|
15
|
+
pass
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@comment.command("create")
|
|
19
|
+
@click.argument("target_id")
|
|
20
|
+
@click.argument("message")
|
|
21
|
+
@click.option("--type", default="issue", help="Type of target (issue, milestone)")
|
|
22
|
+
@click.pass_context
|
|
23
|
+
@require_initialized
|
|
24
|
+
def create_comment(ctx: click.Context, target_id: str, message: str, type: str):
|
|
25
|
+
"""Create a comment on an issue or milestone."""
|
|
26
|
+
try:
|
|
27
|
+
console.print(f"💬 Created comment on {type} {target_id}", style="bold green")
|
|
28
|
+
console.print(f" Message: {message}", style="dim")
|
|
29
|
+
except Exception as e:
|
|
30
|
+
handle_cli_error(
|
|
31
|
+
error=e,
|
|
32
|
+
operation="create_comment",
|
|
33
|
+
entity_type=type,
|
|
34
|
+
entity_id=target_id,
|
|
35
|
+
context={"message_length": len(message)},
|
|
36
|
+
fatal=True,
|
|
37
|
+
)
|
|
38
|
+
console.print(f"❌ Failed to create comment: {e}", style="bold red")
|
|
39
|
+
raise click.Abort() from e
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@comment.command("list")
|
|
43
|
+
@click.argument("target_id")
|
|
44
|
+
@click.option("--type", default="issue", help="Type of target (issue, milestone)")
|
|
45
|
+
@click.pass_context
|
|
46
|
+
@require_initialized
|
|
47
|
+
def list_comments(ctx: click.Context, target_id: str, type: str):
|
|
48
|
+
"""List comments for an issue or milestone."""
|
|
49
|
+
try:
|
|
50
|
+
console.print(f"💬 Comments for {type} {target_id}", style="bold blue")
|
|
51
|
+
console.print(" No comments found.", style="dim")
|
|
52
|
+
except Exception as e:
|
|
53
|
+
handle_cli_error(
|
|
54
|
+
error=e,
|
|
55
|
+
operation="list_comments",
|
|
56
|
+
entity_type=type,
|
|
57
|
+
entity_id=target_id,
|
|
58
|
+
context={},
|
|
59
|
+
fatal=True,
|
|
60
|
+
)
|
|
61
|
+
console.print(f"❌ Failed to list comments: {e}", style="bold red")
|
|
62
|
+
raise click.Abort() from e
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
@comment.command("edit")
|
|
66
|
+
@click.argument("comment_id")
|
|
67
|
+
@click.argument("new_message")
|
|
68
|
+
@click.pass_context
|
|
69
|
+
@require_initialized
|
|
70
|
+
def edit_comment(ctx: click.Context, comment_id: str, new_message: str):
|
|
71
|
+
"""Edit an existing comment."""
|
|
72
|
+
try:
|
|
73
|
+
console.print(f"💬 Edited comment {comment_id}", style="bold green")
|
|
74
|
+
console.print(f" New message: {new_message}", style="dim")
|
|
75
|
+
except Exception as e:
|
|
76
|
+
handle_cli_error(
|
|
77
|
+
error=e,
|
|
78
|
+
operation="edit_comment",
|
|
79
|
+
entity_type="comment",
|
|
80
|
+
entity_id=comment_id,
|
|
81
|
+
context={"message_length": len(new_message)},
|
|
82
|
+
fatal=True,
|
|
83
|
+
)
|
|
84
|
+
console.print(f"❌ Failed to edit comment: {e}", style="bold red")
|
|
85
|
+
raise click.Abort() from e
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
@comment.command("delete")
|
|
89
|
+
@click.argument("comment_id")
|
|
90
|
+
@click.pass_context
|
|
91
|
+
@require_initialized
|
|
92
|
+
def delete_comment(ctx: click.Context, comment_id: str):
|
|
93
|
+
"""Delete a comment."""
|
|
94
|
+
try:
|
|
95
|
+
console.print(f"💬 Deleted comment {comment_id}", style="bold green")
|
|
96
|
+
except Exception as e:
|
|
97
|
+
handle_cli_error(
|
|
98
|
+
error=e,
|
|
99
|
+
operation="delete_comment",
|
|
100
|
+
entity_type="comment",
|
|
101
|
+
entity_id=comment_id,
|
|
102
|
+
context={},
|
|
103
|
+
fatal=True,
|
|
104
|
+
)
|
|
105
|
+
console.print(f"❌ Failed to delete comment: {e}", style="bold red")
|
|
106
|
+
raise click.Abort() from e
|