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,446 @@
|
|
|
1
|
+
"""RoadmapCore - Lightweight Service Container and Coordinator.
|
|
2
|
+
|
|
3
|
+
RoadmapCore is a lightweight facade that provides a clean, domain-organized
|
|
4
|
+
API by coordinating across multiple specialized domain coordinators.
|
|
5
|
+
|
|
6
|
+
Instead of being a god object with 50+ methods, RoadmapCore now delegates
|
|
7
|
+
to focused domain coordinators:
|
|
8
|
+
- IssueCoordinator: Issue CRUD and queries
|
|
9
|
+
- MilestoneCoordinator: Milestone CRUD and consistency validation
|
|
10
|
+
- ProjectCoordinator: Project CRUD and management
|
|
11
|
+
- TeamCoordinator: Team member and user management
|
|
12
|
+
- GitCoordinator: Git integration and branch linking
|
|
13
|
+
- ValidationCoordinator: Validation and configuration
|
|
14
|
+
|
|
15
|
+
This design:
|
|
16
|
+
- Keeps RoadmapCore <200 LOC (down from 670 LOC)
|
|
17
|
+
- Makes each domain independently testable
|
|
18
|
+
- Provides a clear mental model for users
|
|
19
|
+
- Scales well for future features
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
from pathlib import Path
|
|
23
|
+
|
|
24
|
+
from roadmap.common.logging import get_logger
|
|
25
|
+
from roadmap.common.utils.path_utils import build_roadmap_paths
|
|
26
|
+
from roadmap.core.services import (
|
|
27
|
+
ConfigurationService,
|
|
28
|
+
GitHubIntegrationService,
|
|
29
|
+
IssueService,
|
|
30
|
+
MilestoneService,
|
|
31
|
+
ProjectService,
|
|
32
|
+
)
|
|
33
|
+
from roadmap.infrastructure.coordination.git_coordinator import GitCoordinator
|
|
34
|
+
from roadmap.infrastructure.coordination.initialization import InitializationManager
|
|
35
|
+
from roadmap.infrastructure.coordination.issue_coordinator import IssueCoordinator
|
|
36
|
+
from roadmap.infrastructure.coordination.issue_operations import IssueOperations
|
|
37
|
+
from roadmap.infrastructure.coordination.milestone_coordinator import (
|
|
38
|
+
MilestoneCoordinator,
|
|
39
|
+
)
|
|
40
|
+
from roadmap.infrastructure.coordination.milestone_operations import MilestoneOperations
|
|
41
|
+
from roadmap.infrastructure.coordination.project_coordinator import ProjectCoordinator
|
|
42
|
+
from roadmap.infrastructure.coordination.project_operations import ProjectOperations
|
|
43
|
+
from roadmap.infrastructure.coordination.team_coordinator import TeamCoordinator
|
|
44
|
+
from roadmap.infrastructure.coordination.user_operations import UserOperations
|
|
45
|
+
from roadmap.infrastructure.coordination.validation_coordinator import (
|
|
46
|
+
ValidationCoordinator,
|
|
47
|
+
)
|
|
48
|
+
from roadmap.infrastructure.coordination_gateway import CoordinationGateway
|
|
49
|
+
from roadmap.infrastructure.git.git_integration_ops import GitIntegrationOps
|
|
50
|
+
|
|
51
|
+
logger = get_logger(__name__)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class RoadmapCore:
|
|
55
|
+
"""Lightweight service container and coordinator of domain coordinators.
|
|
56
|
+
|
|
57
|
+
RoadmapCore provides a clean, organized API by delegating to focused
|
|
58
|
+
domain coordinators for each concern (issues, milestones, projects, etc).
|
|
59
|
+
|
|
60
|
+
Usage:
|
|
61
|
+
core = RoadmapCore()
|
|
62
|
+
core.initialize() # One-time setup
|
|
63
|
+
|
|
64
|
+
# Use domain-specific coordinators
|
|
65
|
+
issue = core.issues.create(title="...")
|
|
66
|
+
milestone = core.milestones.create(name="...")
|
|
67
|
+
members = core.team.get_members()
|
|
68
|
+
commits = core.git.get_commits_for_issue("issue-123")
|
|
69
|
+
"""
|
|
70
|
+
|
|
71
|
+
def __init__(
|
|
72
|
+
self, root_path: Path | None = None, roadmap_dir_name: str = ".roadmap"
|
|
73
|
+
):
|
|
74
|
+
"""Initialize RoadmapCore with service setup and coordinators.
|
|
75
|
+
|
|
76
|
+
Args:
|
|
77
|
+
root_path: Root path for the roadmap (defaults to cwd)
|
|
78
|
+
roadmap_dir_name: Name of the roadmap directory (defaults to .roadmap)
|
|
79
|
+
"""
|
|
80
|
+
self.root_path = root_path or Path.cwd()
|
|
81
|
+
self.roadmap_dir_name = roadmap_dir_name
|
|
82
|
+
|
|
83
|
+
# Build all standard roadmap paths
|
|
84
|
+
paths = build_roadmap_paths(self.root_path, roadmap_dir_name)
|
|
85
|
+
self.roadmap_dir = paths["roadmap_dir"]
|
|
86
|
+
self.issues_dir = paths["issues_dir"]
|
|
87
|
+
self.milestones_dir = paths["milestones_dir"]
|
|
88
|
+
self.projects_dir = paths["projects_dir"]
|
|
89
|
+
self.templates_dir = paths["templates_dir"]
|
|
90
|
+
self.artifacts_dir = paths["artifacts_dir"]
|
|
91
|
+
self.config_file = paths["config_file"]
|
|
92
|
+
self.db_dir = paths["db_dir"]
|
|
93
|
+
|
|
94
|
+
# Initialize core infrastructure
|
|
95
|
+
self._git = CoordinationGateway.get_git_integration()
|
|
96
|
+
self._git.root_path = self.root_path
|
|
97
|
+
|
|
98
|
+
# Initialize StateManager with correct project-local database path
|
|
99
|
+
db_path = self.db_dir / "state.db"
|
|
100
|
+
self.db = CoordinationGateway.get_state_manager(db_path=db_path)
|
|
101
|
+
|
|
102
|
+
# Initialize GitSyncMonitor with StateManager for database sync
|
|
103
|
+
self.git_sync_monitor = CoordinationGateway.get_git_sync_monitor()
|
|
104
|
+
self.git_sync_monitor.repo_path = self.root_path
|
|
105
|
+
self.git_sync_monitor.state_manager = self.db
|
|
106
|
+
|
|
107
|
+
# Initialize remote links from YAML (Phase 3)
|
|
108
|
+
try:
|
|
109
|
+
self.db.initialize_remote_links(self.root_path)
|
|
110
|
+
except Exception as e:
|
|
111
|
+
from roadmap.common.logging import get_logger
|
|
112
|
+
|
|
113
|
+
logger = get_logger(__name__)
|
|
114
|
+
logger.warning(
|
|
115
|
+
"failed_to_initialize_remote_links_in_core",
|
|
116
|
+
error=str(e),
|
|
117
|
+
severity="operational",
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
# Wire GitSyncMonitor into StateManager for transparent cache sync
|
|
121
|
+
self.db._git_sync_monitor = self.git_sync_monitor
|
|
122
|
+
|
|
123
|
+
self.github_service = GitHubIntegrationService(
|
|
124
|
+
root_path=self.root_path, config_file=self.config_file
|
|
125
|
+
)
|
|
126
|
+
self.config_service = ConfigurationService()
|
|
127
|
+
|
|
128
|
+
# Initialize repositories (abstraction layer)
|
|
129
|
+
issue_repository = CoordinationGateway.get_yaml_issue_repository(
|
|
130
|
+
db=self.db, issues_dir=self.issues_dir
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
from roadmap.adapters.persistence.yaml_repositories import (
|
|
134
|
+
YAMLMilestoneRepository,
|
|
135
|
+
YAMLProjectRepository,
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
milestone_repository = YAMLMilestoneRepository(self.db, self.milestones_dir)
|
|
139
|
+
project_repository = YAMLProjectRepository(self.db, self.projects_dir)
|
|
140
|
+
|
|
141
|
+
# Initialize services with repository injection
|
|
142
|
+
# (decoupled from implementation)
|
|
143
|
+
self.issue_service = IssueService(issue_repository)
|
|
144
|
+
self.milestone_service = MilestoneService(
|
|
145
|
+
milestone_repository,
|
|
146
|
+
issue_repository=issue_repository,
|
|
147
|
+
issues_dir=self.issues_dir,
|
|
148
|
+
milestones_dir=self.milestones_dir,
|
|
149
|
+
)
|
|
150
|
+
self.project_service = ProjectService(project_repository, self.milestones_dir)
|
|
151
|
+
|
|
152
|
+
# Keep reference to init manager for setup (needed before creating coordinators)
|
|
153
|
+
self._init_manager = InitializationManager(
|
|
154
|
+
self.root_path, self.roadmap_dir_name
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
# Initialize operations managers
|
|
158
|
+
issue_ops = IssueOperations(self.issue_service, self.issues_dir)
|
|
159
|
+
milestone_ops = MilestoneOperations(self.milestone_service)
|
|
160
|
+
project_ops = ProjectOperations(self.project_service)
|
|
161
|
+
|
|
162
|
+
# Determine appropriate assignee validator based on sync backend
|
|
163
|
+
assignee_validator = self._get_assignee_validator()
|
|
164
|
+
user_ops = UserOperations(
|
|
165
|
+
self.github_service, self.issue_service, assignee_validator
|
|
166
|
+
)
|
|
167
|
+
git_ops = GitIntegrationOps(self._git, self)
|
|
168
|
+
|
|
169
|
+
# Initialize domain coordinators (pass self for initialization check)
|
|
170
|
+
self.issues = IssueCoordinator(issue_ops, core=self)
|
|
171
|
+
self.milestones = MilestoneCoordinator(
|
|
172
|
+
milestone_ops, self.milestones_dir, core=self
|
|
173
|
+
)
|
|
174
|
+
self.projects = ProjectCoordinator(project_ops, core=self)
|
|
175
|
+
self.team = TeamCoordinator(user_ops, core=self)
|
|
176
|
+
self.git = GitCoordinator(git_ops, core=self)
|
|
177
|
+
self.validation = ValidationCoordinator(self.github_service, core=self)
|
|
178
|
+
|
|
179
|
+
def is_initialized(self) -> bool:
|
|
180
|
+
"""Check if roadmap is initialized in current directory."""
|
|
181
|
+
return self._init_manager.is_initialized()
|
|
182
|
+
|
|
183
|
+
def _check_initialized(self) -> None:
|
|
184
|
+
"""Check that roadmap is initialized and raise if not."""
|
|
185
|
+
if not self.is_initialized():
|
|
186
|
+
raise ValueError("Roadmap not initialized. Run 'roadmap init' first.")
|
|
187
|
+
|
|
188
|
+
def _get_assignee_validator(self):
|
|
189
|
+
"""Determine the appropriate assignee validator based on sync backend.
|
|
190
|
+
|
|
191
|
+
Returns the validator for the configured sync backend:
|
|
192
|
+
- "github" backend → GitHubAssigneeValidator (validates against collaborators)
|
|
193
|
+
- "git" or other backend → VanillaAssigneeValidator (accepts any assignee)
|
|
194
|
+
|
|
195
|
+
Defaults to vanilla/git validator if no backend is configured.
|
|
196
|
+
|
|
197
|
+
Returns:
|
|
198
|
+
An assignee validator instance (implements AssigneeValidator protocol)
|
|
199
|
+
"""
|
|
200
|
+
from roadmap.common.configuration import ConfigManager
|
|
201
|
+
from roadmap.infrastructure.validation.github_assignee_validator import (
|
|
202
|
+
GitHubAssigneeValidator,
|
|
203
|
+
)
|
|
204
|
+
from roadmap.infrastructure.validation.vanilla_assignee_validator import (
|
|
205
|
+
VanillaAssigneeValidator,
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
try:
|
|
209
|
+
config_manager = ConfigManager(self.config_file)
|
|
210
|
+
config = config_manager.load()
|
|
211
|
+
# Access dataclass attributes, not dictionary
|
|
212
|
+
sync_backend = config.github.sync_backend if config.github else "git"
|
|
213
|
+
except Exception:
|
|
214
|
+
# If we can't read config, default to vanilla/git validator
|
|
215
|
+
sync_backend = "git"
|
|
216
|
+
|
|
217
|
+
if str(sync_backend).lower() == "github":
|
|
218
|
+
return GitHubAssigneeValidator(self.github_service)
|
|
219
|
+
else:
|
|
220
|
+
# Default to vanilla validator for "git" backend or if not specified
|
|
221
|
+
return VanillaAssigneeValidator()
|
|
222
|
+
|
|
223
|
+
@classmethod
|
|
224
|
+
def find_existing_roadmap(
|
|
225
|
+
cls, root_path: Path | None = None
|
|
226
|
+
) -> "RoadmapCore | None":
|
|
227
|
+
"""Find an existing roadmap directory in the current path.
|
|
228
|
+
|
|
229
|
+
Searches for common roadmap directory names and returns a RoadmapCore
|
|
230
|
+
instance if found, or None if no roadmap is detected.
|
|
231
|
+
"""
|
|
232
|
+
manager = InitializationManager.find_existing_roadmap(root_path)
|
|
233
|
+
if manager:
|
|
234
|
+
return cls(
|
|
235
|
+
root_path=manager.root_path, roadmap_dir_name=manager.roadmap_dir_name
|
|
236
|
+
)
|
|
237
|
+
return None
|
|
238
|
+
|
|
239
|
+
def initialize(self) -> None:
|
|
240
|
+
"""Initialize a new roadmap in the current directory."""
|
|
241
|
+
self._init_manager.initialize()
|
|
242
|
+
|
|
243
|
+
def close(self) -> None:
|
|
244
|
+
"""Close any database resources held by this core."""
|
|
245
|
+
try:
|
|
246
|
+
self.db.close()
|
|
247
|
+
except Exception as e:
|
|
248
|
+
logger.warning(
|
|
249
|
+
"core_close_failed", error=str(e), error_type=type(e).__name__
|
|
250
|
+
)
|
|
251
|
+
|
|
252
|
+
def __enter__(self) -> "RoadmapCore":
|
|
253
|
+
"""Enter context manager and return self."""
|
|
254
|
+
return self
|
|
255
|
+
|
|
256
|
+
def __exit__(self, _exc_type, _exc, _tb) -> None:
|
|
257
|
+
"""Exit context manager and close resources."""
|
|
258
|
+
self.close()
|
|
259
|
+
|
|
260
|
+
def _update_gitignore(self) -> None:
|
|
261
|
+
"""Update .gitignore to exclude roadmap local data from version control."""
|
|
262
|
+
self._init_manager._update_gitignore()
|
|
263
|
+
|
|
264
|
+
def _sync_with_progress(self, message: str) -> dict | None:
|
|
265
|
+
"""Run database sync with progress display.
|
|
266
|
+
|
|
267
|
+
Args:
|
|
268
|
+
message: Progress message to display
|
|
269
|
+
|
|
270
|
+
Returns:
|
|
271
|
+
Sync result dictionary or None
|
|
272
|
+
"""
|
|
273
|
+
from rich.progress import Progress, SpinnerColumn, TextColumn
|
|
274
|
+
|
|
275
|
+
with Progress(
|
|
276
|
+
SpinnerColumn(),
|
|
277
|
+
TextColumn(f"[bold blue]{message}..."),
|
|
278
|
+
transient=True,
|
|
279
|
+
) as progress:
|
|
280
|
+
progress.add_task("sync", total=None)
|
|
281
|
+
return self.db.smart_sync()
|
|
282
|
+
|
|
283
|
+
def _sync_without_progress(self) -> dict | None:
|
|
284
|
+
"""Run database sync without progress display.
|
|
285
|
+
|
|
286
|
+
Returns:
|
|
287
|
+
Sync result dictionary or None
|
|
288
|
+
"""
|
|
289
|
+
return self.db.smart_sync()
|
|
290
|
+
|
|
291
|
+
def _display_sync_result(self, console, message: str, files_synced: int) -> None:
|
|
292
|
+
"""Display sync result message.
|
|
293
|
+
|
|
294
|
+
Args:
|
|
295
|
+
console: Rich console instance
|
|
296
|
+
message: Message to display
|
|
297
|
+
files_synced: Number of files synced
|
|
298
|
+
"""
|
|
299
|
+
console.print(f"✅ {message}: {files_synced} files synced")
|
|
300
|
+
|
|
301
|
+
def _handle_first_time_setup(
|
|
302
|
+
self, console, show_progress: bool, force_rebuild: bool
|
|
303
|
+
) -> None:
|
|
304
|
+
"""Handle first-time database initialization.
|
|
305
|
+
|
|
306
|
+
Args:
|
|
307
|
+
console: Rich console instance
|
|
308
|
+
show_progress: Whether to show progress
|
|
309
|
+
force_rebuild: Whether full rebuild was requested
|
|
310
|
+
"""
|
|
311
|
+
if show_progress:
|
|
312
|
+
sync_result = self._sync_with_progress(
|
|
313
|
+
"Initializing database from .roadmap/ files"
|
|
314
|
+
)
|
|
315
|
+
else:
|
|
316
|
+
sync_result = self._sync_without_progress()
|
|
317
|
+
|
|
318
|
+
if show_progress and sync_result:
|
|
319
|
+
files_synced = sync_result.get("files_synced", 0)
|
|
320
|
+
total_files = sync_result.get("total_files", 0)
|
|
321
|
+
console.print(
|
|
322
|
+
f"✅ Database initialized: {files_synced}/{total_files} files synced"
|
|
323
|
+
)
|
|
324
|
+
|
|
325
|
+
if not force_rebuild and self._git.is_git_repository():
|
|
326
|
+
self._ensure_git_hooks_installed(console, show_progress)
|
|
327
|
+
|
|
328
|
+
def _handle_incremental_sync(self, console, show_progress: bool) -> None:
|
|
329
|
+
"""Handle incremental database sync.
|
|
330
|
+
|
|
331
|
+
Args:
|
|
332
|
+
console: Rich console instance
|
|
333
|
+
show_progress: Whether to show progress
|
|
334
|
+
"""
|
|
335
|
+
if not self.db.has_file_changes():
|
|
336
|
+
return
|
|
337
|
+
|
|
338
|
+
if show_progress:
|
|
339
|
+
sync_result = self._sync_with_progress(
|
|
340
|
+
"Updating database with recent changes"
|
|
341
|
+
)
|
|
342
|
+
else:
|
|
343
|
+
sync_result = self._sync_without_progress()
|
|
344
|
+
|
|
345
|
+
if show_progress and sync_result:
|
|
346
|
+
files_synced = sync_result.get("files_synced", 0)
|
|
347
|
+
self._display_sync_result(console, "Database updated", files_synced)
|
|
348
|
+
|
|
349
|
+
def ensure_database_synced(
|
|
350
|
+
self, force_rebuild: bool = False, show_progress: bool = True
|
|
351
|
+
) -> None:
|
|
352
|
+
"""Ensure database is synced with .roadmap/ files.
|
|
353
|
+
|
|
354
|
+
This is called automatically on CLI startup to keep SQLite in sync with git files.
|
|
355
|
+
|
|
356
|
+
Args:
|
|
357
|
+
force_rebuild: Force a full rebuild even if database exists
|
|
358
|
+
show_progress: Show progress indicators during sync
|
|
359
|
+
"""
|
|
360
|
+
from rich.console import Console
|
|
361
|
+
|
|
362
|
+
console = Console()
|
|
363
|
+
|
|
364
|
+
if not self.is_initialized():
|
|
365
|
+
return
|
|
366
|
+
|
|
367
|
+
first_time_setup = not self.db.database_exists()
|
|
368
|
+
|
|
369
|
+
if first_time_setup or force_rebuild:
|
|
370
|
+
self._handle_first_time_setup(console, show_progress, first_time_setup)
|
|
371
|
+
else:
|
|
372
|
+
self._handle_incremental_sync(console, show_progress)
|
|
373
|
+
|
|
374
|
+
def _ensure_git_hooks_installed(self, console, show_progress: bool = True) -> None:
|
|
375
|
+
"""Ensure git hooks are installed for automatic sync."""
|
|
376
|
+
try:
|
|
377
|
+
hook_manager = CoordinationGateway.get_git_hook_manager(self)
|
|
378
|
+
|
|
379
|
+
if show_progress:
|
|
380
|
+
console.print("[dim]Installing git hooks for automatic sync...[/dim]")
|
|
381
|
+
|
|
382
|
+
success = hook_manager.install_hooks()
|
|
383
|
+
|
|
384
|
+
if show_progress:
|
|
385
|
+
if success:
|
|
386
|
+
console.print("✅ Git hooks installed successfully")
|
|
387
|
+
else:
|
|
388
|
+
console.print("[yellow]⚠️ Git hooks installation failed[/yellow]")
|
|
389
|
+
|
|
390
|
+
except Exception as e:
|
|
391
|
+
if show_progress:
|
|
392
|
+
console.print(f"[yellow]⚠️ Git hooks setup failed: {e}[/yellow]")
|
|
393
|
+
|
|
394
|
+
# ========== BACKWARD COMPATIBILITY WRAPPERS ==========
|
|
395
|
+
# These methods delegate to domain coordinators for backward compatibility
|
|
396
|
+
# New code should use: core.issues.method(), core.milestones.method(), etc.
|
|
397
|
+
|
|
398
|
+
def get_issues_by_milestone(self) -> dict[str, list]:
|
|
399
|
+
"""Get all issues grouped by milestone (backward compatibility wrapper)."""
|
|
400
|
+
return self.issues.get_grouped_by_milestone()
|
|
401
|
+
|
|
402
|
+
def get_next_milestone(self):
|
|
403
|
+
"""Get the next upcoming milestone by due date (backward compatibility wrapper)."""
|
|
404
|
+
return self.milestones._ops.get_next_milestone()
|
|
405
|
+
|
|
406
|
+
def move_issue_to_milestone(self, issue_id: str, milestone_name: str):
|
|
407
|
+
"""Move an issue to a specific milestone (backward compatibility wrapper)."""
|
|
408
|
+
return self.issues.move_to_milestone(issue_id, milestone_name)
|
|
409
|
+
|
|
410
|
+
def load_config(self):
|
|
411
|
+
"""Load roadmap configuration."""
|
|
412
|
+
if not self.is_initialized():
|
|
413
|
+
raise ValueError("Roadmap not initialized. Run 'roadmap init' first.")
|
|
414
|
+
|
|
415
|
+
import yaml
|
|
416
|
+
|
|
417
|
+
with open(self.config_file) as f:
|
|
418
|
+
loaded = yaml.safe_load(f)
|
|
419
|
+
config_data: dict = loaded if isinstance(loaded, dict) else {}
|
|
420
|
+
|
|
421
|
+
# Ensure all expected config sections exist
|
|
422
|
+
if "milestones" not in config_data:
|
|
423
|
+
config_data["milestones"] = {
|
|
424
|
+
"auto_sequence": True,
|
|
425
|
+
"version_format": "v{major}.{minor}.{patch}",
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
if "sync" not in config_data:
|
|
429
|
+
config_data["sync"] = {
|
|
430
|
+
"github_enabled": False,
|
|
431
|
+
"auto_sync": False,
|
|
432
|
+
"sync_interval_seconds": 300,
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
if "display" not in config_data:
|
|
436
|
+
config_data["display"] = {
|
|
437
|
+
"theme": "default",
|
|
438
|
+
"show_progress": True,
|
|
439
|
+
"compact_output": False,
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
class ConfigObject:
|
|
443
|
+
def __init__(self, data):
|
|
444
|
+
self.__dict__.update(data)
|
|
445
|
+
|
|
446
|
+
return ConfigObject(config_data)
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
"""Git Coordinator - Coordinates git-related operations.
|
|
2
|
+
|
|
3
|
+
Extracted from RoadmapCore to reduce god object complexity.
|
|
4
|
+
Provides a focused API for all git integration concerns.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from typing import TYPE_CHECKING, Any
|
|
10
|
+
|
|
11
|
+
from roadmap.core.domain import Issue
|
|
12
|
+
from roadmap.infrastructure.git.git_integration_ops import GitIntegrationOps
|
|
13
|
+
|
|
14
|
+
if TYPE_CHECKING:
|
|
15
|
+
from roadmap.infrastructure.coordination.core import RoadmapCore
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class GitCoordinator:
|
|
19
|
+
"""Coordinates all git-related operations."""
|
|
20
|
+
|
|
21
|
+
def __init__(self, git_ops: GitIntegrationOps, core: RoadmapCore | None = None):
|
|
22
|
+
"""Initialize coordinator with git operations manager.
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
git_ops: GitIntegrationOps instance
|
|
26
|
+
core: RoadmapCore instance for initialization checks
|
|
27
|
+
"""
|
|
28
|
+
self._ops: GitIntegrationOps = git_ops
|
|
29
|
+
self._git: Any = git_ops.git
|
|
30
|
+
self._core = core
|
|
31
|
+
|
|
32
|
+
def get_context(self) -> dict[str, Any]:
|
|
33
|
+
"""Get Git repository context information."""
|
|
34
|
+
return self._ops.get_git_context()
|
|
35
|
+
|
|
36
|
+
def get_current_user(self) -> str | None:
|
|
37
|
+
"""Get current user from Git configuration."""
|
|
38
|
+
return self._ops.get_current_user_from_git()
|
|
39
|
+
|
|
40
|
+
def create_issue_with_branch(self, title: str, **kwargs) -> Issue | None:
|
|
41
|
+
"""Create an issue and optionally create a Git branch for it."""
|
|
42
|
+
return self._ops.create_issue_with_git_branch(title, **kwargs)
|
|
43
|
+
|
|
44
|
+
def link_issue_to_branch(self, issue_id: str) -> bool:
|
|
45
|
+
"""Link an issue to the current Git branch."""
|
|
46
|
+
return self._ops.link_issue_to_current_branch(issue_id)
|
|
47
|
+
|
|
48
|
+
def get_commits_for_issue(self, issue_id: str, since: str | None = None) -> list:
|
|
49
|
+
"""Get Git commits that reference this issue."""
|
|
50
|
+
return self._ops.get_commits_for_issue(issue_id, since)
|
|
51
|
+
|
|
52
|
+
def update_issue_from_activity(self, issue_id: str) -> bool:
|
|
53
|
+
"""Update issue progress and status based on Git commit activity."""
|
|
54
|
+
return self._ops.update_issue_from_git_activity(issue_id)
|
|
55
|
+
|
|
56
|
+
def suggest_branch_name(self, issue_id: str) -> str | None:
|
|
57
|
+
"""Suggest a branch name for an issue."""
|
|
58
|
+
return self._ops.suggest_branch_name_for_issue(issue_id)
|
|
59
|
+
|
|
60
|
+
def get_branch_linked_issues(self) -> dict[str, list[str]]:
|
|
61
|
+
"""Get mapping of branches to their linked issue IDs."""
|
|
62
|
+
return self._ops.get_branch_linked_issues()
|
|
63
|
+
|
|
64
|
+
def is_git_repository(self) -> bool:
|
|
65
|
+
"""Check if current directory is a git repository."""
|
|
66
|
+
return self._ops.git.is_git_repository()
|
|
67
|
+
|
|
68
|
+
def create_branch_for_issue(self, issue: Issue, checkout: bool = True) -> bool:
|
|
69
|
+
"""Create a Git branch for an issue.
|
|
70
|
+
|
|
71
|
+
Args:
|
|
72
|
+
issue: The Issue object to create a branch for
|
|
73
|
+
checkout: If True, checkout the created branch (default: True)
|
|
74
|
+
|
|
75
|
+
Returns:
|
|
76
|
+
True if branch was created successfully, False otherwise
|
|
77
|
+
"""
|
|
78
|
+
return self._ops.git.create_branch_for_issue(issue, checkout=checkout)
|
|
79
|
+
|
|
80
|
+
@property
|
|
81
|
+
def repo_path(self):
|
|
82
|
+
"""Get the repository path (for backward compatibility)."""
|
|
83
|
+
return self._ops.git.repo_path
|
|
84
|
+
|
|
85
|
+
def get_repository_info(self) -> dict[str, Any]:
|
|
86
|
+
"""Get repository information.
|
|
87
|
+
|
|
88
|
+
Returns:
|
|
89
|
+
Dictionary with repository metadata
|
|
90
|
+
"""
|
|
91
|
+
return self._git.get_repository_info()
|
|
92
|
+
|
|
93
|
+
def get_current_branch(self) -> str | None:
|
|
94
|
+
"""Get the current Git branch name.
|
|
95
|
+
|
|
96
|
+
Returns:
|
|
97
|
+
Current branch name or None if not in a git repo
|
|
98
|
+
"""
|
|
99
|
+
branch = self._git.get_current_branch()
|
|
100
|
+
return branch.name if branch else None
|
|
101
|
+
|
|
102
|
+
def get_local_changes(self) -> list[str]:
|
|
103
|
+
"""Get list of issue IDs that have changed locally since last sync.
|
|
104
|
+
|
|
105
|
+
Compares current issue files in .roadmap/issues with the last committed
|
|
106
|
+
versions (git HEAD) to detect which issues were modified locally.
|
|
107
|
+
|
|
108
|
+
Returns:
|
|
109
|
+
List of issue IDs that have uncommitted changes
|
|
110
|
+
"""
|
|
111
|
+
try:
|
|
112
|
+
import subprocess
|
|
113
|
+
from pathlib import Path
|
|
114
|
+
|
|
115
|
+
issues_dir = self._core.issues_dir if self._core else None
|
|
116
|
+
if not issues_dir:
|
|
117
|
+
return []
|
|
118
|
+
|
|
119
|
+
# Get modified files relative to git HEAD in the issues directory
|
|
120
|
+
result = subprocess.run(
|
|
121
|
+
[
|
|
122
|
+
"git",
|
|
123
|
+
"diff",
|
|
124
|
+
"HEAD",
|
|
125
|
+
"--name-only",
|
|
126
|
+
"--",
|
|
127
|
+
str(issues_dir),
|
|
128
|
+
],
|
|
129
|
+
capture_output=True,
|
|
130
|
+
text=True,
|
|
131
|
+
cwd=self._git.repo_path,
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
if result.returncode != 0:
|
|
135
|
+
# If diff fails, return empty list (might not be in git repo)
|
|
136
|
+
return []
|
|
137
|
+
|
|
138
|
+
# Parse filenames to extract issue IDs
|
|
139
|
+
changed_issue_ids = []
|
|
140
|
+
for line in result.stdout.strip().split("\n"):
|
|
141
|
+
if not line:
|
|
142
|
+
continue
|
|
143
|
+
|
|
144
|
+
# Extract filename from path (e.g., ".roadmap/issues/ID.md" -> "ID")
|
|
145
|
+
filepath = Path(line)
|
|
146
|
+
filename = filepath.stem # Remove .md extension
|
|
147
|
+
|
|
148
|
+
# Validate it looks like an issue ID (uuid-like)
|
|
149
|
+
if filename and len(filename) > 4:
|
|
150
|
+
changed_issue_ids.append(filename)
|
|
151
|
+
|
|
152
|
+
return changed_issue_ids
|
|
153
|
+
|
|
154
|
+
except Exception as e:
|
|
155
|
+
# Log but don't crash on git errors
|
|
156
|
+
if self._core:
|
|
157
|
+
import structlog
|
|
158
|
+
|
|
159
|
+
structlog.get_logger().warning("get_local_changes_failed", error=str(e))
|
|
160
|
+
return []
|