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,432 @@
|
|
|
1
|
+
"""Roadmap exception hierarchy.
|
|
2
|
+
|
|
3
|
+
All exceptions include:
|
|
4
|
+
- domain_message: Technical message for logging
|
|
5
|
+
- user_message: User-friendly message for CLI output
|
|
6
|
+
- exit_code: CLI exit code (for Click commands)
|
|
7
|
+
|
|
8
|
+
This separation allows logging the full technical context while showing
|
|
9
|
+
users a clean, actionable message.
|
|
10
|
+
|
|
11
|
+
Example:
|
|
12
|
+
try:
|
|
13
|
+
service.create_issue(...)
|
|
14
|
+
except NotInitializedError as e:
|
|
15
|
+
logger.error(e.domain_message) # Technical details
|
|
16
|
+
click.echo(e.user_message, err=True) # What user sees
|
|
17
|
+
ctx.exit(e.exit_code)
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class RoadmapException(Exception):
|
|
22
|
+
"""Base exception for all roadmap errors.
|
|
23
|
+
|
|
24
|
+
Attributes:
|
|
25
|
+
domain_message: Technical message for logging/debugging
|
|
26
|
+
user_message: User-friendly message for CLI output
|
|
27
|
+
exit_code: Exit code for CLI commands
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
exit_code: int = 1
|
|
31
|
+
|
|
32
|
+
def __init__(
|
|
33
|
+
self,
|
|
34
|
+
domain_message: str,
|
|
35
|
+
user_message: str | None = None,
|
|
36
|
+
):
|
|
37
|
+
"""Initialize exception with messages.
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
domain_message: Technical message (what happened technically)
|
|
41
|
+
user_message: User-friendly message (what user should do)
|
|
42
|
+
Defaults to domain_message if not provided
|
|
43
|
+
"""
|
|
44
|
+
self.domain_message = domain_message
|
|
45
|
+
self.user_message = user_message or domain_message
|
|
46
|
+
super().__init__(domain_message)
|
|
47
|
+
|
|
48
|
+
def __str__(self) -> str:
|
|
49
|
+
"""Return user message for str()."""
|
|
50
|
+
return self.user_message
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
# ============================================================================
|
|
54
|
+
# Initialization & Setup Errors
|
|
55
|
+
# ============================================================================
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class NotInitializedError(RoadmapException):
|
|
59
|
+
"""Roadmap directory (.roadmap/) not initialized."""
|
|
60
|
+
|
|
61
|
+
exit_code = 1
|
|
62
|
+
|
|
63
|
+
def __init__(self):
|
|
64
|
+
"""Initialize NotInitializedError."""
|
|
65
|
+
super().__init__(
|
|
66
|
+
domain_message="Roadmap .roadmap/ directory not found",
|
|
67
|
+
user_message="Roadmap not initialized. Run 'roadmap init' first.",
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class AlreadyInitializedError(RoadmapException):
|
|
72
|
+
"""Roadmap already initialized in this directory."""
|
|
73
|
+
|
|
74
|
+
exit_code = 1
|
|
75
|
+
|
|
76
|
+
def __init__(self, directory: str):
|
|
77
|
+
"""Initialize AlreadyInitializedError.
|
|
78
|
+
|
|
79
|
+
Args:
|
|
80
|
+
directory: Path where roadmap is already initialized.
|
|
81
|
+
"""
|
|
82
|
+
super().__init__(
|
|
83
|
+
domain_message=f"Roadmap already initialized in {directory}",
|
|
84
|
+
user_message=f"Roadmap already initialized in {directory}/. "
|
|
85
|
+
"Use --force to reinitialize.",
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
# ============================================================================
|
|
90
|
+
# Entity Not Found Errors
|
|
91
|
+
# ============================================================================
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
class EntityNotFoundError(RoadmapException):
|
|
95
|
+
"""Entity (issue, milestone, project) not found."""
|
|
96
|
+
|
|
97
|
+
exit_code = 1
|
|
98
|
+
|
|
99
|
+
def __init__(self, entity_type: str, entity_id: str):
|
|
100
|
+
"""Initialize EntityNotFoundError.
|
|
101
|
+
|
|
102
|
+
Args:
|
|
103
|
+
entity_type: Type of entity (Issue, Milestone, Project).
|
|
104
|
+
entity_id: ID of the missing entity.
|
|
105
|
+
"""
|
|
106
|
+
super().__init__(
|
|
107
|
+
domain_message=f"{entity_type} {entity_id} not found in database",
|
|
108
|
+
user_message=f"{entity_type} not found: {entity_id}",
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
class IssueNotFoundError(EntityNotFoundError):
|
|
113
|
+
"""Specific: Issue not found."""
|
|
114
|
+
|
|
115
|
+
def __init__(self, issue_id: str):
|
|
116
|
+
"""Initialize IssueNotFoundError.
|
|
117
|
+
|
|
118
|
+
Args:
|
|
119
|
+
issue_id: ID of the missing issue.
|
|
120
|
+
"""
|
|
121
|
+
super().__init__("Issue", issue_id)
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
class MilestoneNotFoundError(EntityNotFoundError):
|
|
125
|
+
"""Specific: Milestone not found."""
|
|
126
|
+
|
|
127
|
+
def __init__(self, milestone_id: str):
|
|
128
|
+
"""Initialize MilestoneNotFoundError.
|
|
129
|
+
|
|
130
|
+
Args:
|
|
131
|
+
milestone_id: ID of the missing milestone.
|
|
132
|
+
"""
|
|
133
|
+
super().__init__("Milestone", milestone_id)
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
class ProjectNotFoundError(EntityNotFoundError):
|
|
137
|
+
"""Specific: Project not found."""
|
|
138
|
+
|
|
139
|
+
def __init__(self, project_id: str):
|
|
140
|
+
"""Initialize ProjectNotFoundError.
|
|
141
|
+
|
|
142
|
+
Args:
|
|
143
|
+
project_id: ID of the missing project.
|
|
144
|
+
"""
|
|
145
|
+
super().__init__("Project", project_id)
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
# ============================================================================
|
|
149
|
+
# Validation Errors
|
|
150
|
+
# ============================================================================
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
class ValidationError(RoadmapException):
|
|
154
|
+
"""Input validation failed."""
|
|
155
|
+
|
|
156
|
+
exit_code = 2
|
|
157
|
+
|
|
158
|
+
def __init__(self, domain_message: str, user_message: str | None = None):
|
|
159
|
+
"""Initialize ValidationError.
|
|
160
|
+
|
|
161
|
+
Args:
|
|
162
|
+
domain_message: Technical message describing validation failure.
|
|
163
|
+
user_message: User-friendly message. Defaults to domain_message.
|
|
164
|
+
"""
|
|
165
|
+
super().__init__(
|
|
166
|
+
domain_message=domain_message,
|
|
167
|
+
user_message=user_message or domain_message,
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
class InvalidStatusError(ValidationError):
|
|
172
|
+
"""Invalid status value."""
|
|
173
|
+
|
|
174
|
+
def __init__(self, status: str, valid_statuses: list[str]):
|
|
175
|
+
"""Initialize InvalidStatusError.
|
|
176
|
+
|
|
177
|
+
Args:
|
|
178
|
+
status: Invalid status value provided.
|
|
179
|
+
valid_statuses: List of valid status values.
|
|
180
|
+
"""
|
|
181
|
+
domain_message = f"Validation failed: status '{status}' is not valid"
|
|
182
|
+
user_message = (
|
|
183
|
+
f"Invalid status: '{status}'. Valid values: {', '.join(valid_statuses)}"
|
|
184
|
+
)
|
|
185
|
+
super().__init__(domain_message=domain_message, user_message=user_message)
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
class InvalidPriorityError(ValidationError):
|
|
189
|
+
"""Invalid priority value."""
|
|
190
|
+
|
|
191
|
+
def __init__(self, priority: str, valid_priorities: list[str]):
|
|
192
|
+
"""Initialize InvalidPriorityError.
|
|
193
|
+
|
|
194
|
+
Args:
|
|
195
|
+
priority: Invalid priority value provided.
|
|
196
|
+
valid_priorities: List of valid priority values.
|
|
197
|
+
"""
|
|
198
|
+
domain_message = f"Validation failed: priority '{priority}' is not valid"
|
|
199
|
+
user_message = f"Invalid priority: '{priority}'. Valid values: {', '.join(valid_priorities)}"
|
|
200
|
+
super().__init__(domain_message=domain_message, user_message=user_message)
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
# ============================================================================
|
|
204
|
+
# Operation Errors
|
|
205
|
+
# ============================================================================
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
class OperationError(RoadmapException):
|
|
209
|
+
"""Operation failed (creation, update, deletion, etc.)."""
|
|
210
|
+
|
|
211
|
+
exit_code = 1
|
|
212
|
+
|
|
213
|
+
def __init__(self, operation: str, reason: str):
|
|
214
|
+
"""Initialize OperationError.
|
|
215
|
+
|
|
216
|
+
Args:
|
|
217
|
+
operation: Description of the operation that failed.
|
|
218
|
+
reason: Reason why the operation failed.
|
|
219
|
+
"""
|
|
220
|
+
super().__init__(
|
|
221
|
+
domain_message=f"Failed to {operation}: {reason}",
|
|
222
|
+
user_message=f"Failed to {operation}: {reason}",
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
class CreateError(OperationError):
|
|
227
|
+
"""Failed to create entity."""
|
|
228
|
+
|
|
229
|
+
def __init__(self, entity_type: str, reason: str):
|
|
230
|
+
"""Initialize CreateError.
|
|
231
|
+
|
|
232
|
+
Args:
|
|
233
|
+
entity_type: Type of entity that failed to create.
|
|
234
|
+
reason: Reason for creation failure.
|
|
235
|
+
"""
|
|
236
|
+
super().__init__(f"create {entity_type}", reason)
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
class UpdateError(OperationError):
|
|
240
|
+
"""Failed to update entity."""
|
|
241
|
+
|
|
242
|
+
def __init__(self, entity_type: str, reason: str):
|
|
243
|
+
"""Initialize UpdateError.
|
|
244
|
+
|
|
245
|
+
Args:
|
|
246
|
+
entity_type: Type of entity that failed to update.
|
|
247
|
+
reason: Reason for update failure.
|
|
248
|
+
"""
|
|
249
|
+
super().__init__(f"update {entity_type}", reason)
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
class DeleteError(OperationError):
|
|
253
|
+
"""Failed to delete entity."""
|
|
254
|
+
|
|
255
|
+
def __init__(self, entity_type: str, reason: str):
|
|
256
|
+
"""Initialize DeleteError.
|
|
257
|
+
|
|
258
|
+
Args:
|
|
259
|
+
entity_type: Type of entity that failed to delete.
|
|
260
|
+
reason: Reason for deletion failure.
|
|
261
|
+
"""
|
|
262
|
+
super().__init__(f"delete {entity_type}", reason)
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
# ============================================================================
|
|
266
|
+
# Database/Persistence Errors
|
|
267
|
+
# ============================================================================
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
class PersistenceError(RoadmapException):
|
|
271
|
+
"""Database or file persistence operation failed."""
|
|
272
|
+
|
|
273
|
+
exit_code = 1
|
|
274
|
+
|
|
275
|
+
def __init__(self, operation: str, reason: str):
|
|
276
|
+
"""Initialize PersistenceError.
|
|
277
|
+
|
|
278
|
+
Args:
|
|
279
|
+
operation: Name of the persistence operation that failed.
|
|
280
|
+
reason: Reason for failure.
|
|
281
|
+
"""
|
|
282
|
+
super().__init__(
|
|
283
|
+
domain_message=f"Persistence operation '{operation}' failed: {reason}",
|
|
284
|
+
user_message="Storage operation failed. Check disk space and permissions.",
|
|
285
|
+
)
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
class DatabaseError(PersistenceError):
|
|
289
|
+
"""Database operation failed."""
|
|
290
|
+
|
|
291
|
+
def __init__(self, reason: str):
|
|
292
|
+
"""Initialize DatabaseError.
|
|
293
|
+
|
|
294
|
+
Args:
|
|
295
|
+
reason: Reason for database operation failure.
|
|
296
|
+
"""
|
|
297
|
+
super().__init__("database", reason)
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
class FileOperationError(PersistenceError):
|
|
301
|
+
"""File operation failed."""
|
|
302
|
+
|
|
303
|
+
def __init__(self, reason: str):
|
|
304
|
+
"""Initialize FileOperationError.
|
|
305
|
+
|
|
306
|
+
Args:
|
|
307
|
+
reason: Reason for file operation failure.
|
|
308
|
+
"""
|
|
309
|
+
super().__init__("file_operation", reason)
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
# ============================================================================
|
|
313
|
+
# Git/GitHub Errors
|
|
314
|
+
# ============================================================================
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
class GitError(RoadmapException):
|
|
318
|
+
"""Git operation failed."""
|
|
319
|
+
|
|
320
|
+
exit_code = 1
|
|
321
|
+
|
|
322
|
+
def __init__(self, operation: str, reason: str):
|
|
323
|
+
"""Initialize GitError.
|
|
324
|
+
|
|
325
|
+
Args:
|
|
326
|
+
operation: Name of the Git operation that failed.
|
|
327
|
+
reason: Reason for failure.
|
|
328
|
+
"""
|
|
329
|
+
super().__init__(
|
|
330
|
+
domain_message=f"Git operation '{operation}' failed: {reason}",
|
|
331
|
+
user_message=f"Git operation failed: {reason}",
|
|
332
|
+
)
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
class GitHubError(RoadmapException):
|
|
336
|
+
"""GitHub integration failed."""
|
|
337
|
+
|
|
338
|
+
exit_code = 1
|
|
339
|
+
|
|
340
|
+
def __init__(self, operation: str, reason: str):
|
|
341
|
+
"""Initialize GitHubError.
|
|
342
|
+
|
|
343
|
+
Args:
|
|
344
|
+
operation: Name of the GitHub operation that failed.
|
|
345
|
+
reason: Reason for failure.
|
|
346
|
+
"""
|
|
347
|
+
super().__init__(
|
|
348
|
+
domain_message=f"GitHub operation '{operation}' failed: {reason}",
|
|
349
|
+
user_message=f"GitHub operation failed: {reason}",
|
|
350
|
+
)
|
|
351
|
+
|
|
352
|
+
|
|
353
|
+
class NoGitRepositoryError(GitError):
|
|
354
|
+
"""Not in a Git repository."""
|
|
355
|
+
|
|
356
|
+
def __init__(self):
|
|
357
|
+
"""Initialize NoGitRepositoryError."""
|
|
358
|
+
super().__init__(
|
|
359
|
+
"repository_check",
|
|
360
|
+
"Not in a Git repository",
|
|
361
|
+
)
|
|
362
|
+
|
|
363
|
+
|
|
364
|
+
class GitConfigError(GitError):
|
|
365
|
+
"""Git configuration missing or invalid."""
|
|
366
|
+
|
|
367
|
+
def __init__(self, config_key: str):
|
|
368
|
+
"""Initialize GitConfigError.
|
|
369
|
+
|
|
370
|
+
Args:
|
|
371
|
+
config_key: Name of the missing Git configuration key.
|
|
372
|
+
"""
|
|
373
|
+
super().__init__(
|
|
374
|
+
"config_read",
|
|
375
|
+
f"Git config '{config_key}' not found. Run: git config user.name 'Your Name'",
|
|
376
|
+
)
|
|
377
|
+
|
|
378
|
+
|
|
379
|
+
# ============================================================================
|
|
380
|
+
# Configuration/Setup Errors
|
|
381
|
+
# ============================================================================
|
|
382
|
+
|
|
383
|
+
|
|
384
|
+
class ConfigurationError(RoadmapException):
|
|
385
|
+
"""Configuration is invalid or missing."""
|
|
386
|
+
|
|
387
|
+
exit_code = 1
|
|
388
|
+
|
|
389
|
+
def __init__(self, issue: str):
|
|
390
|
+
"""Initialize ConfigurationError.
|
|
391
|
+
|
|
392
|
+
Args:
|
|
393
|
+
issue: Description of the configuration problem.
|
|
394
|
+
"""
|
|
395
|
+
super().__init__(
|
|
396
|
+
domain_message=f"Configuration error: {issue}",
|
|
397
|
+
user_message=f"Configuration error: {issue}",
|
|
398
|
+
)
|
|
399
|
+
|
|
400
|
+
|
|
401
|
+
# ============================================================================
|
|
402
|
+
# Security Errors
|
|
403
|
+
# ============================================================================
|
|
404
|
+
|
|
405
|
+
|
|
406
|
+
class SecurityError(RoadmapException):
|
|
407
|
+
"""Security validation failed."""
|
|
408
|
+
|
|
409
|
+
exit_code = 1
|
|
410
|
+
|
|
411
|
+
def __init__(self, issue: str):
|
|
412
|
+
"""Initialize SecurityError.
|
|
413
|
+
|
|
414
|
+
Args:
|
|
415
|
+
issue: Description of the security check failure.
|
|
416
|
+
"""
|
|
417
|
+
super().__init__(
|
|
418
|
+
domain_message=f"Security check failed: {issue}",
|
|
419
|
+
user_message="Security validation failed. Check file permissions.",
|
|
420
|
+
)
|
|
421
|
+
|
|
422
|
+
|
|
423
|
+
class PathValidationError(SecurityError):
|
|
424
|
+
"""Path traversal or invalid path detected."""
|
|
425
|
+
|
|
426
|
+
def __init__(self, path: str):
|
|
427
|
+
"""Initialize PathValidationError.
|
|
428
|
+
|
|
429
|
+
Args:
|
|
430
|
+
path: The invalid path that failed validation.
|
|
431
|
+
"""
|
|
432
|
+
super().__init__(f"Invalid path: {path}")
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"""Formatters package - unified formatting utilities.
|
|
2
|
+
|
|
3
|
+
Organized into focused modules:
|
|
4
|
+
- text: Basic text, duration, status, operation formatting
|
|
5
|
+
- output: Generic TableData rendering (JSON, CSV, Markdown, plain-text)
|
|
6
|
+
- tables: Domain object to TableData conversion (Issue, Milestone, Project)
|
|
7
|
+
- export: Issue export with multiple format support
|
|
8
|
+
- kanban: Kanban board organization and layout
|
|
9
|
+
|
|
10
|
+
Use submodule imports for clarity:
|
|
11
|
+
from roadmap.common.formatters.text import format_table, format_header
|
|
12
|
+
from roadmap.common.formatters.output import OutputFormatter
|
|
13
|
+
from roadmap.common.formatters.tables import IssueTableFormatter
|
|
14
|
+
from roadmap.common.formatters.export import IssueExporter
|
|
15
|
+
from roadmap.common.formatters.kanban import KanbanOrganizer, KanbanLayout
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from .export import IssueExporter
|
|
19
|
+
from .kanban import KanbanLayout, KanbanOrganizer
|
|
20
|
+
from .tables import IssueTableFormatter, MilestoneTableFormatter, ProjectTableFormatter
|
|
21
|
+
|
|
22
|
+
__all__ = [
|
|
23
|
+
"IssueExporter",
|
|
24
|
+
"IssueTableFormatter",
|
|
25
|
+
"MilestoneTableFormatter",
|
|
26
|
+
"ProjectTableFormatter",
|
|
27
|
+
"KanbanOrganizer",
|
|
28
|
+
"KanbanLayout",
|
|
29
|
+
]
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"""Base table formatter for DRY consolidation.
|
|
2
|
+
|
|
3
|
+
This module provides abstract base class for all table formatters,
|
|
4
|
+
eliminating duplicate code across Issue, Project, and Milestone formatters.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from abc import ABC, abstractmethod
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
from roadmap.common.console import get_console
|
|
11
|
+
from roadmap.common.models import TableData
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class BaseTableFormatter[T](ABC):
|
|
15
|
+
"""Abstract base class for table formatters."""
|
|
16
|
+
|
|
17
|
+
@property
|
|
18
|
+
def console(self):
|
|
19
|
+
"""Get console instance dynamically for test compatibility."""
|
|
20
|
+
return get_console()
|
|
21
|
+
|
|
22
|
+
@abstractmethod
|
|
23
|
+
def create_table(self) -> Any:
|
|
24
|
+
"""Create and configure the rich Table for this formatter.
|
|
25
|
+
|
|
26
|
+
Returns:
|
|
27
|
+
Configured Rich Table with columns
|
|
28
|
+
"""
|
|
29
|
+
pass
|
|
30
|
+
|
|
31
|
+
@abstractmethod
|
|
32
|
+
def add_row(self, table: Any, item: T) -> None:
|
|
33
|
+
"""Add a single item row to the table.
|
|
34
|
+
|
|
35
|
+
Args:
|
|
36
|
+
table: The Rich Table to add row to
|
|
37
|
+
item: The domain object to format
|
|
38
|
+
"""
|
|
39
|
+
pass
|
|
40
|
+
|
|
41
|
+
@abstractmethod
|
|
42
|
+
def get_filter_description(self, items: list[T]) -> str:
|
|
43
|
+
"""Get human-readable description of filtered items.
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
items: List of items being displayed
|
|
47
|
+
|
|
48
|
+
Returns:
|
|
49
|
+
Description string (e.g., "2 open issue")
|
|
50
|
+
"""
|
|
51
|
+
pass
|
|
52
|
+
|
|
53
|
+
def display_items(self, items: list[T], filter_description: str = "all") -> None:
|
|
54
|
+
"""Display items in a formatted table.
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
items: List of items to display
|
|
58
|
+
filter_description: Human-readable filter description
|
|
59
|
+
"""
|
|
60
|
+
if not items:
|
|
61
|
+
self.console.print(
|
|
62
|
+
f"📋 No {filter_description} items found.", style="yellow"
|
|
63
|
+
)
|
|
64
|
+
self.console.print(
|
|
65
|
+
"Create one with: roadmap [command] create '[Name]'",
|
|
66
|
+
style="dim",
|
|
67
|
+
)
|
|
68
|
+
return
|
|
69
|
+
|
|
70
|
+
# Display header with filter info
|
|
71
|
+
filter_desc = self.get_filter_description(items)
|
|
72
|
+
self.console.print(f"📋 {filter_desc}", style="bold cyan")
|
|
73
|
+
self.console.print()
|
|
74
|
+
|
|
75
|
+
# Create and populate table
|
|
76
|
+
table = self.create_table()
|
|
77
|
+
for item in items:
|
|
78
|
+
self.add_row(table, item)
|
|
79
|
+
|
|
80
|
+
self.console.print(table)
|
|
81
|
+
|
|
82
|
+
@abstractmethod
|
|
83
|
+
def items_to_table_data(
|
|
84
|
+
self, items: list[T], title: str = "Items", description: str = ""
|
|
85
|
+
) -> TableData:
|
|
86
|
+
"""Convert items to TableData for structured output.
|
|
87
|
+
|
|
88
|
+
Args:
|
|
89
|
+
items: List of items to convert
|
|
90
|
+
title: Table title
|
|
91
|
+
description: Optional description
|
|
92
|
+
|
|
93
|
+
Returns:
|
|
94
|
+
TableData object for rendering in any format
|
|
95
|
+
"""
|
|
96
|
+
pass
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"""Issue export functionality."""
|
|
2
|
+
|
|
3
|
+
import csv
|
|
4
|
+
import json
|
|
5
|
+
from collections.abc import Sequence
|
|
6
|
+
from io import StringIO
|
|
7
|
+
|
|
8
|
+
from roadmap.common.formatters.output import OutputFormatter
|
|
9
|
+
from roadmap.common.formatters.tables import IssueTableFormatter
|
|
10
|
+
from roadmap.core.domain import Issue
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class IssueExporter:
|
|
14
|
+
"""Export issues to various formats.
|
|
15
|
+
|
|
16
|
+
Supports two export styles:
|
|
17
|
+
1. Unified export() - recommended for production use
|
|
18
|
+
2. Legacy to_json/to_csv/to_markdown - for backward compatibility
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
# Legacy CSV fields definition
|
|
22
|
+
CSV_FIELDS = [
|
|
23
|
+
"id",
|
|
24
|
+
"title",
|
|
25
|
+
"status",
|
|
26
|
+
"assignee",
|
|
27
|
+
"priority",
|
|
28
|
+
"estimated_hours",
|
|
29
|
+
"milestone",
|
|
30
|
+
"created",
|
|
31
|
+
"updated",
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
@staticmethod
|
|
35
|
+
def export(issues: Sequence[Issue], format_type: str, title: str = "Issues") -> str:
|
|
36
|
+
"""Export issues using the unified OutputFormatter.
|
|
37
|
+
|
|
38
|
+
Converts issues to TableData and uses OutputFormatter for consistent
|
|
39
|
+
rendering across all formats.
|
|
40
|
+
|
|
41
|
+
Args:
|
|
42
|
+
issues: List of Issue objects
|
|
43
|
+
format_type: One of 'json', 'csv', 'markdown'
|
|
44
|
+
title: Optional table title
|
|
45
|
+
|
|
46
|
+
Returns:
|
|
47
|
+
Formatted string ready for output
|
|
48
|
+
"""
|
|
49
|
+
table_data = IssueTableFormatter.issues_to_table_data(issues, title=title) # type: ignore
|
|
50
|
+
formatter = OutputFormatter(table_data)
|
|
51
|
+
|
|
52
|
+
if format_type == "json":
|
|
53
|
+
return formatter.to_json()
|
|
54
|
+
elif format_type == "csv":
|
|
55
|
+
return formatter.to_csv()
|
|
56
|
+
elif format_type == "markdown":
|
|
57
|
+
return formatter.to_markdown()
|
|
58
|
+
else:
|
|
59
|
+
raise ValueError(
|
|
60
|
+
f"Unknown format: {format_type}. Use 'json', 'csv', or 'markdown'."
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
@staticmethod
|
|
64
|
+
def to_json(issues: Sequence[Issue], serializer_func) -> str:
|
|
65
|
+
"""Legacy JSON export with custom serializer (for backward compatibility)."""
|
|
66
|
+
payload = [serializer_func(i) for i in issues]
|
|
67
|
+
return json.dumps(payload, indent=2)
|
|
68
|
+
|
|
69
|
+
@classmethod
|
|
70
|
+
def to_csv(cls, issues: Sequence[Issue], serializer_func) -> str:
|
|
71
|
+
"""Legacy CSV export with custom serializer (for backward compatibility)."""
|
|
72
|
+
buf = StringIO()
|
|
73
|
+
writer = csv.DictWriter(buf, fieldnames=cls.CSV_FIELDS)
|
|
74
|
+
writer.writeheader()
|
|
75
|
+
for issue in issues:
|
|
76
|
+
row = serializer_func(issue)
|
|
77
|
+
writer.writerow({f: row.get(f, "") for f in cls.CSV_FIELDS})
|
|
78
|
+
return buf.getvalue()
|
|
79
|
+
|
|
80
|
+
@staticmethod
|
|
81
|
+
def to_markdown(issues: Sequence[Issue]) -> str:
|
|
82
|
+
"""Legacy Markdown export (for backward compatibility)."""
|
|
83
|
+
lines = [
|
|
84
|
+
"| id | title | status | assignee | milestone | estimated |",
|
|
85
|
+
"|---|---|---:|---|---|---:|",
|
|
86
|
+
]
|
|
87
|
+
for i in issues:
|
|
88
|
+
est = (
|
|
89
|
+
i.estimated_time_display
|
|
90
|
+
if hasattr(i, "estimated_time_display")
|
|
91
|
+
else (i.estimated_hours or "")
|
|
92
|
+
)
|
|
93
|
+
status_val = i.status.value if hasattr(i.status, "value") else i.status
|
|
94
|
+
lines.append(
|
|
95
|
+
f"| {i.id} | {i.title} | {status_val} | {i.assignee or ''} | {i.milestone or ''} | {est} |"
|
|
96
|
+
)
|
|
97
|
+
return "\n".join(lines)
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"""Formatting helpers - wrapper to avoid circular dependencies.
|
|
2
|
+
|
|
3
|
+
This module re-exports OutputFormatHandler from cli_helpers to break
|
|
4
|
+
the circular dependency between formatting and utils modules.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import TYPE_CHECKING
|
|
8
|
+
|
|
9
|
+
# Import only for type checking to avoid circular imports
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
from roadmap.common.utils.cli_helpers import OutputFormatHandler, format_output
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def __getattr__(name: str): # noqa: ANN001, ANN201
|
|
15
|
+
"""Lazy load OutputFormatHandler and format_output from cli_helpers."""
|
|
16
|
+
if name == "OutputFormatHandler":
|
|
17
|
+
from roadmap.common.utils.cli_helpers import OutputFormatHandler
|
|
18
|
+
|
|
19
|
+
return OutputFormatHandler
|
|
20
|
+
elif name == "format_output":
|
|
21
|
+
from roadmap.common.utils.cli_helpers import format_output
|
|
22
|
+
|
|
23
|
+
return format_output
|
|
24
|
+
msg = f"module {__name__!r} has no attribute {name!r}"
|
|
25
|
+
raise AttributeError(msg)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
__all__ = ["format_output", "OutputFormatHandler"]
|