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,288 @@
|
|
|
1
|
+
"""Retry logic with exponential backoff for handling transient failures."""
|
|
2
|
+
|
|
3
|
+
import functools
|
|
4
|
+
import time
|
|
5
|
+
from collections.abc import Callable
|
|
6
|
+
from typing import Any, TypeVar
|
|
7
|
+
|
|
8
|
+
from ..logging import get_logger
|
|
9
|
+
|
|
10
|
+
logger = get_logger(__name__)
|
|
11
|
+
|
|
12
|
+
F = TypeVar("F", bound=Callable[..., Any])
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def retry(
|
|
16
|
+
max_attempts: int = 3,
|
|
17
|
+
delay: float = 1.0,
|
|
18
|
+
backoff: float = 2.0,
|
|
19
|
+
exceptions: tuple[type[Exception], ...] = (Exception,),
|
|
20
|
+
on_retry: Callable[[Exception, int], None] | None = None,
|
|
21
|
+
):
|
|
22
|
+
"""Retry decorator with exponential backoff.
|
|
23
|
+
|
|
24
|
+
This decorator automatically retries a function if it raises one of the
|
|
25
|
+
specified exceptions. The delay between retries increases exponentially
|
|
26
|
+
using the backoff factor.
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
max_attempts: Maximum number of attempts (including the first try)
|
|
30
|
+
delay: Initial delay in seconds between retries
|
|
31
|
+
backoff: Multiplier for delay after each retry (exponential backoff)
|
|
32
|
+
exceptions: Tuple of exception types to catch and retry on
|
|
33
|
+
on_retry: Optional callback function called before each retry.
|
|
34
|
+
Takes (exception, attempt_number) as arguments.
|
|
35
|
+
|
|
36
|
+
Example:
|
|
37
|
+
@retry(max_attempts=5, delay=1.0, backoff=2.0, exceptions=(ConnectionError, TimeoutError))
|
|
38
|
+
def fetch_data():
|
|
39
|
+
# ... make network request
|
|
40
|
+
pass
|
|
41
|
+
|
|
42
|
+
The retry delays will be: 1s, 2s, 4s, 8s
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
def decorator(func: F) -> F:
|
|
46
|
+
@functools.wraps(func)
|
|
47
|
+
def wrapper(*args: Any, **kwargs: Any) -> Any:
|
|
48
|
+
attempt = 0
|
|
49
|
+
current_delay = delay
|
|
50
|
+
last_exception: Exception | None = None
|
|
51
|
+
|
|
52
|
+
while attempt < max_attempts:
|
|
53
|
+
attempt += 1
|
|
54
|
+
|
|
55
|
+
try:
|
|
56
|
+
result = func(*args, **kwargs)
|
|
57
|
+
|
|
58
|
+
# Log success if we had previous failures
|
|
59
|
+
if attempt > 1:
|
|
60
|
+
logger.info(
|
|
61
|
+
"retry_succeeded",
|
|
62
|
+
function=func.__name__,
|
|
63
|
+
attempt=attempt,
|
|
64
|
+
max_attempts=max_attempts,
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
return result
|
|
68
|
+
|
|
69
|
+
except exceptions as e:
|
|
70
|
+
last_exception = e
|
|
71
|
+
|
|
72
|
+
# Don't retry if we've exhausted attempts
|
|
73
|
+
if attempt >= max_attempts:
|
|
74
|
+
logger.error(
|
|
75
|
+
"retry_exhausted",
|
|
76
|
+
function=func.__name__,
|
|
77
|
+
attempts=attempt,
|
|
78
|
+
error=str(e),
|
|
79
|
+
error_type=type(e).__name__,
|
|
80
|
+
)
|
|
81
|
+
raise
|
|
82
|
+
|
|
83
|
+
# Log retry attempt
|
|
84
|
+
logger.warning(
|
|
85
|
+
"retry_attempt",
|
|
86
|
+
function=func.__name__,
|
|
87
|
+
attempt=attempt,
|
|
88
|
+
max_attempts=max_attempts,
|
|
89
|
+
delay_seconds=current_delay,
|
|
90
|
+
error=str(e),
|
|
91
|
+
error_type=type(e).__name__,
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
# Call on_retry callback if provided
|
|
95
|
+
if on_retry:
|
|
96
|
+
try:
|
|
97
|
+
on_retry(e, attempt)
|
|
98
|
+
except Exception as callback_error:
|
|
99
|
+
logger.error(
|
|
100
|
+
"retry_callback_failed",
|
|
101
|
+
function=func.__name__,
|
|
102
|
+
error=str(callback_error),
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
# Wait before retrying
|
|
106
|
+
time.sleep(current_delay)
|
|
107
|
+
|
|
108
|
+
# Exponential backoff
|
|
109
|
+
current_delay *= backoff
|
|
110
|
+
|
|
111
|
+
# This should never be reached, but just in case
|
|
112
|
+
if last_exception:
|
|
113
|
+
raise last_exception
|
|
114
|
+
raise RuntimeError(f"Retry logic failed for {func.__name__}")
|
|
115
|
+
|
|
116
|
+
return wrapper # type: ignore
|
|
117
|
+
|
|
118
|
+
return decorator
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def async_retry(
|
|
122
|
+
max_attempts: int = 3,
|
|
123
|
+
delay: float = 1.0,
|
|
124
|
+
backoff: float = 2.0,
|
|
125
|
+
exceptions: tuple[type[Exception], ...] = (Exception,),
|
|
126
|
+
on_retry: Callable[[Exception, int], None] | None = None,
|
|
127
|
+
):
|
|
128
|
+
"""Async version of retry decorator with exponential backoff.
|
|
129
|
+
|
|
130
|
+
Args:
|
|
131
|
+
max_attempts: Maximum number of attempts (including the first try)
|
|
132
|
+
delay: Initial delay in seconds between retries
|
|
133
|
+
backoff: Multiplier for delay after each retry (exponential backoff)
|
|
134
|
+
exceptions: Tuple of exception types to catch and retry on
|
|
135
|
+
on_retry: Optional callback function called before each retry
|
|
136
|
+
|
|
137
|
+
Example:
|
|
138
|
+
@async_retry(max_attempts=5, delay=1.0, backoff=2.0)
|
|
139
|
+
async def fetch_data():
|
|
140
|
+
# ... make async network request
|
|
141
|
+
pass
|
|
142
|
+
"""
|
|
143
|
+
|
|
144
|
+
def decorator(func: F) -> F:
|
|
145
|
+
@functools.wraps(func)
|
|
146
|
+
async def wrapper(*args: Any, **kwargs: Any) -> Any:
|
|
147
|
+
import asyncio
|
|
148
|
+
|
|
149
|
+
attempt = 0
|
|
150
|
+
current_delay = delay
|
|
151
|
+
last_exception: Exception | None = None
|
|
152
|
+
|
|
153
|
+
while attempt < max_attempts:
|
|
154
|
+
attempt += 1
|
|
155
|
+
|
|
156
|
+
try:
|
|
157
|
+
result = await func(*args, **kwargs)
|
|
158
|
+
|
|
159
|
+
# Log success if we had previous failures
|
|
160
|
+
if attempt > 1:
|
|
161
|
+
logger.info(
|
|
162
|
+
"async_retry_succeeded",
|
|
163
|
+
function=func.__name__,
|
|
164
|
+
attempt=attempt,
|
|
165
|
+
max_attempts=max_attempts,
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
return result
|
|
169
|
+
|
|
170
|
+
except exceptions as e:
|
|
171
|
+
last_exception = e
|
|
172
|
+
|
|
173
|
+
# Don't retry if we've exhausted attempts
|
|
174
|
+
if attempt >= max_attempts:
|
|
175
|
+
logger.error(
|
|
176
|
+
"async_retry_exhausted",
|
|
177
|
+
function=func.__name__,
|
|
178
|
+
attempts=attempt,
|
|
179
|
+
error=str(e),
|
|
180
|
+
error_type=type(e).__name__,
|
|
181
|
+
)
|
|
182
|
+
raise
|
|
183
|
+
|
|
184
|
+
# Log retry attempt
|
|
185
|
+
logger.warning(
|
|
186
|
+
"async_retry_attempt",
|
|
187
|
+
function=func.__name__,
|
|
188
|
+
attempt=attempt,
|
|
189
|
+
max_attempts=max_attempts,
|
|
190
|
+
delay_seconds=current_delay,
|
|
191
|
+
error=str(e),
|
|
192
|
+
error_type=type(e).__name__,
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
# Call on_retry callback if provided
|
|
196
|
+
if on_retry:
|
|
197
|
+
try:
|
|
198
|
+
on_retry(e, attempt)
|
|
199
|
+
except Exception as callback_error:
|
|
200
|
+
logger.error(
|
|
201
|
+
"async_retry_callback_failed",
|
|
202
|
+
function=func.__name__,
|
|
203
|
+
error=str(callback_error),
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
# Wait before retrying (async sleep)
|
|
207
|
+
await asyncio.sleep(current_delay)
|
|
208
|
+
|
|
209
|
+
# Exponential backoff
|
|
210
|
+
current_delay *= backoff
|
|
211
|
+
|
|
212
|
+
# This should never be reached, but just in case
|
|
213
|
+
if last_exception:
|
|
214
|
+
raise last_exception
|
|
215
|
+
raise RuntimeError(f"Async retry logic failed for {func.__name__}")
|
|
216
|
+
|
|
217
|
+
return wrapper # type: ignore
|
|
218
|
+
|
|
219
|
+
return decorator
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
class RetryConfig:
|
|
223
|
+
"""Configuration for retry behavior.
|
|
224
|
+
|
|
225
|
+
This class provides a convenient way to share retry configuration
|
|
226
|
+
across multiple operations.
|
|
227
|
+
"""
|
|
228
|
+
|
|
229
|
+
def __init__(
|
|
230
|
+
self,
|
|
231
|
+
max_attempts: int = 3,
|
|
232
|
+
delay: float = 1.0,
|
|
233
|
+
backoff: float = 2.0,
|
|
234
|
+
exceptions: tuple[type[Exception], ...] = (Exception,),
|
|
235
|
+
):
|
|
236
|
+
"""Initialize retry configuration.
|
|
237
|
+
|
|
238
|
+
Args:
|
|
239
|
+
max_attempts: Maximum number of attempts
|
|
240
|
+
delay: Initial delay in seconds
|
|
241
|
+
backoff: Exponential backoff multiplier
|
|
242
|
+
exceptions: Exception types to retry on
|
|
243
|
+
"""
|
|
244
|
+
self.max_attempts = max_attempts
|
|
245
|
+
self.delay = delay
|
|
246
|
+
self.backoff = backoff
|
|
247
|
+
self.exceptions = exceptions
|
|
248
|
+
|
|
249
|
+
def __call__(self, func: F) -> F:
|
|
250
|
+
"""Use config as a decorator."""
|
|
251
|
+
return retry(
|
|
252
|
+
max_attempts=self.max_attempts,
|
|
253
|
+
delay=self.delay,
|
|
254
|
+
backoff=self.backoff,
|
|
255
|
+
exceptions=self.exceptions,
|
|
256
|
+
)(func)
|
|
257
|
+
|
|
258
|
+
def async_decorator(self, func: F) -> F:
|
|
259
|
+
"""Use config as an async decorator."""
|
|
260
|
+
return async_retry(
|
|
261
|
+
max_attempts=self.max_attempts,
|
|
262
|
+
delay=self.delay,
|
|
263
|
+
backoff=self.backoff,
|
|
264
|
+
exceptions=self.exceptions,
|
|
265
|
+
)(func)
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
# Predefined retry configurations for common scenarios
|
|
269
|
+
NETWORK_RETRY = RetryConfig(
|
|
270
|
+
max_attempts=5,
|
|
271
|
+
delay=1.0,
|
|
272
|
+
backoff=2.0,
|
|
273
|
+
exceptions=(ConnectionError, TimeoutError, OSError),
|
|
274
|
+
)
|
|
275
|
+
|
|
276
|
+
API_RETRY = RetryConfig(
|
|
277
|
+
max_attempts=3,
|
|
278
|
+
delay=0.5,
|
|
279
|
+
backoff=1.5,
|
|
280
|
+
exceptions=(ConnectionError, TimeoutError),
|
|
281
|
+
)
|
|
282
|
+
|
|
283
|
+
DATABASE_RETRY = RetryConfig(
|
|
284
|
+
max_attempts=3,
|
|
285
|
+
delay=0.1,
|
|
286
|
+
backoff=2.0,
|
|
287
|
+
exceptions=(Exception,), # Database-specific exceptions would go here
|
|
288
|
+
)
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
"""Centralized status styling for consistent UI across application.
|
|
2
|
+
|
|
3
|
+
This module consolidates all status-to-style mappings to eliminate DRY violations
|
|
4
|
+
and ensure consistent status rendering everywhere.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from enum import Enum
|
|
8
|
+
from typing import TYPE_CHECKING, Any
|
|
9
|
+
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
pass
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class StatusStyle(Enum):
|
|
15
|
+
"""Enum for common status values."""
|
|
16
|
+
|
|
17
|
+
TODO = "TODO"
|
|
18
|
+
IN_PROGRESS = "IN_PROGRESS"
|
|
19
|
+
BLOCKED = "BLOCKED"
|
|
20
|
+
REVIEW = "REVIEW"
|
|
21
|
+
CLOSED = "CLOSED"
|
|
22
|
+
OPEN = "OPEN"
|
|
23
|
+
COMPLETED = "COMPLETED"
|
|
24
|
+
ARCHIVED = "ARCHIVED"
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class StatusStyleManager:
|
|
28
|
+
"""Centralized manager for status styling across application."""
|
|
29
|
+
|
|
30
|
+
# Rich style mappings for status values
|
|
31
|
+
STATUS_STYLES: dict[str, str] = {
|
|
32
|
+
# Issue/Task statuses
|
|
33
|
+
"TODO": "white",
|
|
34
|
+
"IN_PROGRESS": "yellow",
|
|
35
|
+
"BLOCKED": "red",
|
|
36
|
+
"REVIEW": "blue",
|
|
37
|
+
"CLOSED": "green",
|
|
38
|
+
# Project/Milestone statuses
|
|
39
|
+
"OPEN": "cyan",
|
|
40
|
+
"COMPLETED": "green",
|
|
41
|
+
"ARCHIVED": "dim",
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
# Emoji mapping for status
|
|
45
|
+
STATUS_EMOJIS: dict[str, str] = {
|
|
46
|
+
"TODO": "📝",
|
|
47
|
+
"IN_PROGRESS": "🔄",
|
|
48
|
+
"BLOCKED": "🚫",
|
|
49
|
+
"REVIEW": "👀",
|
|
50
|
+
"CLOSED": "✅",
|
|
51
|
+
"OPEN": "📂",
|
|
52
|
+
"COMPLETED": "🎉",
|
|
53
|
+
"ARCHIVED": "📦",
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
@classmethod
|
|
57
|
+
def get_style(cls, status: str | Enum) -> str:
|
|
58
|
+
"""Get Rich style string for a status.
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
status: Status value (string or Enum)
|
|
62
|
+
|
|
63
|
+
Returns:
|
|
64
|
+
Rich style string (e.g., "bold red")
|
|
65
|
+
"""
|
|
66
|
+
status_str = status.value if isinstance(status, Enum) else str(status).upper()
|
|
67
|
+
return cls.STATUS_STYLES.get(status_str, "white")
|
|
68
|
+
|
|
69
|
+
@classmethod
|
|
70
|
+
def get_emoji(cls, status: str | Enum) -> str:
|
|
71
|
+
"""Get emoji for a status.
|
|
72
|
+
|
|
73
|
+
Args:
|
|
74
|
+
status: Status value (string or Enum)
|
|
75
|
+
|
|
76
|
+
Returns:
|
|
77
|
+
Emoji character
|
|
78
|
+
"""
|
|
79
|
+
status_str = status.value if isinstance(status, Enum) else str(status).upper()
|
|
80
|
+
return cls.STATUS_EMOJIS.get(status_str, "•")
|
|
81
|
+
|
|
82
|
+
@classmethod
|
|
83
|
+
def get_rich_text(cls, status: str | Enum, text: str | None = None) -> Any:
|
|
84
|
+
"""Create Rich Text object with proper style and emoji.
|
|
85
|
+
|
|
86
|
+
Args:
|
|
87
|
+
status: Status value
|
|
88
|
+
text: Optional custom text (defaults to status string)
|
|
89
|
+
|
|
90
|
+
Returns:
|
|
91
|
+
Rich Text object ready for rendering
|
|
92
|
+
"""
|
|
93
|
+
from rich.text import Text
|
|
94
|
+
|
|
95
|
+
display_text = text or str(status)
|
|
96
|
+
return Text(display_text, style=cls.get_style(status))
|
|
97
|
+
|
|
98
|
+
@classmethod
|
|
99
|
+
def all_styles(cls) -> dict[str, str]:
|
|
100
|
+
"""Get all available status styles.
|
|
101
|
+
|
|
102
|
+
Returns:
|
|
103
|
+
Dictionary mapping status -> style
|
|
104
|
+
"""
|
|
105
|
+
return cls.STATUS_STYLES.copy()
|
|
106
|
+
|
|
107
|
+
@classmethod
|
|
108
|
+
def all_emojis(cls) -> dict[str, str]:
|
|
109
|
+
"""Get all available status emojis.
|
|
110
|
+
|
|
111
|
+
Returns:
|
|
112
|
+
Dictionary mapping status -> emoji
|
|
113
|
+
"""
|
|
114
|
+
return cls.STATUS_EMOJIS.copy()
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"""Union-Find (Disjoint Set Union) data structure with path compression.
|
|
2
|
+
|
|
3
|
+
Provides O(α(n)) ≈ O(1) operations for union and find operations,
|
|
4
|
+
where α is the inverse Ackermann function (effectively constant).
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class UnionFind:
|
|
11
|
+
"""Efficient disjoint set data structure with path compression and union by rank."""
|
|
12
|
+
|
|
13
|
+
def __init__(self, items: list[Any]) -> None:
|
|
14
|
+
"""Initialize union-find with given items.
|
|
15
|
+
|
|
16
|
+
Args:
|
|
17
|
+
items: List of hashable items to track
|
|
18
|
+
"""
|
|
19
|
+
self.parent: dict[Any, Any] = {item: item for item in items}
|
|
20
|
+
self.rank: dict[Any, int] = dict.fromkeys(items, 0)
|
|
21
|
+
|
|
22
|
+
def find(self, item: Any) -> Any:
|
|
23
|
+
"""Find the canonical representative of item's set with path compression.
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
item: Item to find representative for
|
|
27
|
+
|
|
28
|
+
Returns:
|
|
29
|
+
Canonical representative of item's set
|
|
30
|
+
"""
|
|
31
|
+
if self.parent[item] != item:
|
|
32
|
+
# Path compression: point directly to root
|
|
33
|
+
self.parent[item] = self.find(self.parent[item])
|
|
34
|
+
return self.parent[item]
|
|
35
|
+
|
|
36
|
+
def union(self, item1: Any, item2: Any) -> bool:
|
|
37
|
+
"""Union the sets containing item1 and item2.
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
item1: First item
|
|
41
|
+
item2: Second item
|
|
42
|
+
|
|
43
|
+
Returns:
|
|
44
|
+
True if union occurred, False if already in same set
|
|
45
|
+
"""
|
|
46
|
+
root1 = self.find(item1)
|
|
47
|
+
root2 = self.find(item2)
|
|
48
|
+
|
|
49
|
+
if root1 == root2:
|
|
50
|
+
return False # Already in same set
|
|
51
|
+
|
|
52
|
+
# Union by rank: attach smaller tree under larger
|
|
53
|
+
if self.rank[root1] < self.rank[root2]:
|
|
54
|
+
self.parent[root1] = root2
|
|
55
|
+
elif self.rank[root1] > self.rank[root2]:
|
|
56
|
+
self.parent[root2] = root1
|
|
57
|
+
else:
|
|
58
|
+
# Same rank: pick one and increment its rank
|
|
59
|
+
self.parent[root2] = root1
|
|
60
|
+
self.rank[root1] += 1
|
|
61
|
+
|
|
62
|
+
return True
|
|
63
|
+
|
|
64
|
+
def get_canonical(self, item: Any) -> Any:
|
|
65
|
+
"""Get canonical representative (alias for find)."""
|
|
66
|
+
return self.find(item)
|
|
67
|
+
|
|
68
|
+
def get_representatives(self) -> set[Any]:
|
|
69
|
+
"""Get all canonical representatives (roots of each set).
|
|
70
|
+
|
|
71
|
+
Returns:
|
|
72
|
+
Set of canonical representatives
|
|
73
|
+
"""
|
|
74
|
+
return {self.find(item) for item in self.parent.keys()}
|
|
75
|
+
|
|
76
|
+
def get_groups(self) -> dict[Any, list[Any]]:
|
|
77
|
+
"""Get mapping of representative -> list of items in that group.
|
|
78
|
+
|
|
79
|
+
Returns:
|
|
80
|
+
Dict mapping canonical representative to list of items
|
|
81
|
+
"""
|
|
82
|
+
groups: dict[Any, list[Any]] = {}
|
|
83
|
+
for item in self.parent.keys():
|
|
84
|
+
rep = self.find(item)
|
|
85
|
+
if rep not in groups:
|
|
86
|
+
groups[rep] = []
|
|
87
|
+
groups[rep].append(item)
|
|
88
|
+
return groups
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"""Shared constants for update operations."""
|
|
2
|
+
|
|
3
|
+
# Fields that are displayed when showing update results
|
|
4
|
+
DISPLAYABLE_UPDATE_FIELDS = [
|
|
5
|
+
"title",
|
|
6
|
+
"priority",
|
|
7
|
+
"status",
|
|
8
|
+
"assignee",
|
|
9
|
+
"milestone",
|
|
10
|
+
"description",
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
__all__ = ["DISPLAYABLE_UPDATE_FIELDS"]
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"""Common utility functions for CLI operations, file handling, and data processing.
|
|
2
|
+
|
|
3
|
+
This package provides low-level utilities used across the application:
|
|
4
|
+
- CLI helpers: Output formatting, decorators for command functions
|
|
5
|
+
- File utilities: File operations and secure file handling
|
|
6
|
+
- Path utilities: Path checking and validation
|
|
7
|
+
- Status utilities: Status-related helpers
|
|
8
|
+
- Timezone utilities: Timezone-aware date/time operations
|
|
9
|
+
|
|
10
|
+
Note: These are utility functions with minimal dependencies - they should not
|
|
11
|
+
import from higher-level modules (services, domain, adapters) to avoid
|
|
12
|
+
layer violations and circular dependencies.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from .cli_helpers import OutputFormatHandler, format_output
|
|
16
|
+
from .file_utils import (
|
|
17
|
+
SecureFileManager,
|
|
18
|
+
backup_file,
|
|
19
|
+
cleanup_temp_files,
|
|
20
|
+
ensure_directory_exists,
|
|
21
|
+
file_exists_check,
|
|
22
|
+
get_file_size,
|
|
23
|
+
safe_read_file,
|
|
24
|
+
safe_write_file,
|
|
25
|
+
)
|
|
26
|
+
from .path_utils import build_roadmap_paths
|
|
27
|
+
from .status_utils import StatusSummary
|
|
28
|
+
from .timezone_utils import (
|
|
29
|
+
TimezoneManager,
|
|
30
|
+
ensure_timezone_aware,
|
|
31
|
+
format_datetime,
|
|
32
|
+
format_relative_time,
|
|
33
|
+
get_timezone_manager,
|
|
34
|
+
migrate_naive_datetime,
|
|
35
|
+
now_local,
|
|
36
|
+
now_utc,
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
__all__ = [
|
|
40
|
+
# CLI helpers
|
|
41
|
+
"OutputFormatHandler",
|
|
42
|
+
"format_output",
|
|
43
|
+
# File utilities
|
|
44
|
+
"SecureFileManager",
|
|
45
|
+
"backup_file",
|
|
46
|
+
"cleanup_temp_files",
|
|
47
|
+
"ensure_directory_exists",
|
|
48
|
+
"file_exists_check",
|
|
49
|
+
"get_file_size",
|
|
50
|
+
"safe_read_file",
|
|
51
|
+
"safe_write_file",
|
|
52
|
+
# Path utilities
|
|
53
|
+
"build_roadmap_paths",
|
|
54
|
+
# Status utilities
|
|
55
|
+
"StatusSummary",
|
|
56
|
+
# Timezone utilities
|
|
57
|
+
"TimezoneManager",
|
|
58
|
+
"ensure_timezone_aware",
|
|
59
|
+
"format_datetime",
|
|
60
|
+
"format_relative_time",
|
|
61
|
+
"get_timezone_manager",
|
|
62
|
+
"migrate_naive_datetime",
|
|
63
|
+
"now_local",
|
|
64
|
+
"now_utc",
|
|
65
|
+
]
|