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,327 @@
|
|
|
1
|
+
"""Git hooks configuration command - Set up auto-sync behavior."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
import click
|
|
6
|
+
|
|
7
|
+
from roadmap.adapters.cli.cli_command_helpers import require_initialized
|
|
8
|
+
from roadmap.common.console import get_console
|
|
9
|
+
from roadmap.core.services.git.git_hook_auto_sync_service import (
|
|
10
|
+
GitHookAutoSyncConfig,
|
|
11
|
+
GitHookAutoSyncService,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
console = get_console()
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _any_sync_flag_set(
|
|
18
|
+
enable_auto_sync: bool,
|
|
19
|
+
disable_auto_sync: bool,
|
|
20
|
+
sync_on_commit: bool,
|
|
21
|
+
no_sync_on_commit: bool,
|
|
22
|
+
sync_on_checkout: bool,
|
|
23
|
+
no_sync_on_checkout: bool,
|
|
24
|
+
sync_on_merge: bool,
|
|
25
|
+
no_sync_on_merge: bool,
|
|
26
|
+
confirm: bool | None,
|
|
27
|
+
force_local: bool,
|
|
28
|
+
force_github: bool,
|
|
29
|
+
) -> bool:
|
|
30
|
+
"""Return True if any config-changing flag was explicitly provided."""
|
|
31
|
+
return any(
|
|
32
|
+
[
|
|
33
|
+
enable_auto_sync,
|
|
34
|
+
disable_auto_sync,
|
|
35
|
+
sync_on_commit,
|
|
36
|
+
no_sync_on_commit,
|
|
37
|
+
sync_on_checkout,
|
|
38
|
+
no_sync_on_checkout,
|
|
39
|
+
sync_on_merge,
|
|
40
|
+
no_sync_on_merge,
|
|
41
|
+
confirm is not None,
|
|
42
|
+
force_local,
|
|
43
|
+
force_github,
|
|
44
|
+
]
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _build_new_sync_config(
|
|
49
|
+
current_config: "GitHookAutoSyncConfig",
|
|
50
|
+
enable_auto_sync: bool,
|
|
51
|
+
disable_auto_sync: bool,
|
|
52
|
+
sync_on_commit: bool,
|
|
53
|
+
no_sync_on_commit: bool,
|
|
54
|
+
sync_on_checkout: bool,
|
|
55
|
+
no_sync_on_checkout: bool,
|
|
56
|
+
sync_on_merge: bool,
|
|
57
|
+
no_sync_on_merge: bool,
|
|
58
|
+
confirm: bool | None,
|
|
59
|
+
force_local: bool,
|
|
60
|
+
force_github: bool,
|
|
61
|
+
) -> "GitHookAutoSyncConfig":
|
|
62
|
+
"""Build a new config by merging explicit flags onto the existing config."""
|
|
63
|
+
return GitHookAutoSyncConfig(
|
|
64
|
+
auto_sync_enabled=(
|
|
65
|
+
enable_auto_sync
|
|
66
|
+
or (not disable_auto_sync and current_config.auto_sync_enabled)
|
|
67
|
+
),
|
|
68
|
+
sync_on_commit=(
|
|
69
|
+
sync_on_commit or (not no_sync_on_commit and current_config.sync_on_commit)
|
|
70
|
+
),
|
|
71
|
+
sync_on_checkout=(
|
|
72
|
+
sync_on_checkout
|
|
73
|
+
or (not no_sync_on_checkout and current_config.sync_on_checkout)
|
|
74
|
+
),
|
|
75
|
+
sync_on_merge=(
|
|
76
|
+
sync_on_merge or (not no_sync_on_merge and current_config.sync_on_merge)
|
|
77
|
+
),
|
|
78
|
+
confirm_before_sync=(
|
|
79
|
+
confirm if confirm is not None else current_config.confirm_before_sync
|
|
80
|
+
),
|
|
81
|
+
force_local=force_local or current_config.force_local,
|
|
82
|
+
force_github=force_github or current_config.force_github,
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
@click.command(name="hooks-config")
|
|
87
|
+
@click.option(
|
|
88
|
+
"--enable-auto-sync",
|
|
89
|
+
is_flag=True,
|
|
90
|
+
help="Enable auto-sync on Git events",
|
|
91
|
+
)
|
|
92
|
+
@click.option(
|
|
93
|
+
"--disable-auto-sync",
|
|
94
|
+
is_flag=True,
|
|
95
|
+
help="Disable all auto-sync on Git events",
|
|
96
|
+
)
|
|
97
|
+
@click.option(
|
|
98
|
+
"--sync-on-commit",
|
|
99
|
+
is_flag=True,
|
|
100
|
+
help="Enable auto-sync after commits",
|
|
101
|
+
)
|
|
102
|
+
@click.option(
|
|
103
|
+
"--no-sync-on-commit",
|
|
104
|
+
is_flag=True,
|
|
105
|
+
help="Disable auto-sync after commits",
|
|
106
|
+
)
|
|
107
|
+
@click.option(
|
|
108
|
+
"--sync-on-checkout",
|
|
109
|
+
is_flag=True,
|
|
110
|
+
help="Enable auto-sync on branch changes",
|
|
111
|
+
)
|
|
112
|
+
@click.option(
|
|
113
|
+
"--no-sync-on-checkout",
|
|
114
|
+
is_flag=True,
|
|
115
|
+
help="Disable auto-sync on branch changes",
|
|
116
|
+
)
|
|
117
|
+
@click.option(
|
|
118
|
+
"--sync-on-merge",
|
|
119
|
+
is_flag=True,
|
|
120
|
+
help="Enable auto-sync on merge operations",
|
|
121
|
+
)
|
|
122
|
+
@click.option(
|
|
123
|
+
"--no-sync-on-merge",
|
|
124
|
+
is_flag=True,
|
|
125
|
+
help="Disable auto-sync on merge operations",
|
|
126
|
+
)
|
|
127
|
+
@click.option(
|
|
128
|
+
"--confirm/--no-confirm",
|
|
129
|
+
default=None,
|
|
130
|
+
help="Require confirmation before syncing",
|
|
131
|
+
)
|
|
132
|
+
@click.option(
|
|
133
|
+
"--force-local",
|
|
134
|
+
is_flag=True,
|
|
135
|
+
help="Resolve conflicts by keeping local changes",
|
|
136
|
+
)
|
|
137
|
+
@click.option(
|
|
138
|
+
"--force-github",
|
|
139
|
+
is_flag=True,
|
|
140
|
+
help="Resolve conflicts by keeping GitHub changes",
|
|
141
|
+
)
|
|
142
|
+
@click.option(
|
|
143
|
+
"--show",
|
|
144
|
+
"show_config",
|
|
145
|
+
is_flag=True,
|
|
146
|
+
help="Show current configuration",
|
|
147
|
+
)
|
|
148
|
+
@click.pass_context
|
|
149
|
+
@require_initialized
|
|
150
|
+
def hooks_config(
|
|
151
|
+
ctx: click.Context,
|
|
152
|
+
enable_auto_sync: bool,
|
|
153
|
+
disable_auto_sync: bool,
|
|
154
|
+
sync_on_commit: bool,
|
|
155
|
+
no_sync_on_commit: bool,
|
|
156
|
+
sync_on_checkout: bool,
|
|
157
|
+
no_sync_on_checkout: bool,
|
|
158
|
+
sync_on_merge: bool,
|
|
159
|
+
no_sync_on_merge: bool,
|
|
160
|
+
confirm: bool | None,
|
|
161
|
+
force_local: bool,
|
|
162
|
+
force_github: bool,
|
|
163
|
+
show_config: bool,
|
|
164
|
+
) -> None:
|
|
165
|
+
"""Configure Git hooks auto-sync behavior.
|
|
166
|
+
|
|
167
|
+
Examples:
|
|
168
|
+
# Show current configuration
|
|
169
|
+
roadmap git hooks-config --show
|
|
170
|
+
|
|
171
|
+
# Enable auto-sync on commits with confirmation
|
|
172
|
+
roadmap git hooks-config --enable-auto-sync --sync-on-commit --confirm
|
|
173
|
+
|
|
174
|
+
# Configure force-local resolution for conflicts
|
|
175
|
+
roadmap git hooks-config --sync-on-commit --force-local
|
|
176
|
+
|
|
177
|
+
# Disable auto-sync on checkouts
|
|
178
|
+
roadmap git hooks-config --no-sync-on-checkout
|
|
179
|
+
|
|
180
|
+
Configuration is stored locally in the roadmap project.
|
|
181
|
+
"""
|
|
182
|
+
core = ctx.obj.get("core") if isinstance(ctx.obj, dict) else ctx.obj
|
|
183
|
+
|
|
184
|
+
# Config file path
|
|
185
|
+
config_path = Path.cwd() / ".roadmap" / "config.json"
|
|
186
|
+
|
|
187
|
+
# Initialize service
|
|
188
|
+
service = GitHookAutoSyncService(core)
|
|
189
|
+
|
|
190
|
+
# Load existing config from storage if available
|
|
191
|
+
service.load_config_from_file(config_path)
|
|
192
|
+
current_config = service.get_config()
|
|
193
|
+
|
|
194
|
+
# Build new config from options
|
|
195
|
+
new_config = _build_new_sync_config(
|
|
196
|
+
current_config,
|
|
197
|
+
enable_auto_sync,
|
|
198
|
+
disable_auto_sync,
|
|
199
|
+
sync_on_commit,
|
|
200
|
+
no_sync_on_commit,
|
|
201
|
+
sync_on_checkout,
|
|
202
|
+
no_sync_on_checkout,
|
|
203
|
+
sync_on_merge,
|
|
204
|
+
no_sync_on_merge,
|
|
205
|
+
confirm,
|
|
206
|
+
force_local,
|
|
207
|
+
force_github,
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
# Handle conflicts in resolution options
|
|
211
|
+
if force_local and force_github:
|
|
212
|
+
console.print("[red]❌ Cannot use both --force-local and --force-github[/red]")
|
|
213
|
+
ctx.exit(1)
|
|
214
|
+
|
|
215
|
+
# Apply new config
|
|
216
|
+
service.set_config(new_config)
|
|
217
|
+
|
|
218
|
+
# Save config to file if any settings changed
|
|
219
|
+
any_flag = _any_sync_flag_set(
|
|
220
|
+
enable_auto_sync,
|
|
221
|
+
disable_auto_sync,
|
|
222
|
+
sync_on_commit,
|
|
223
|
+
no_sync_on_commit,
|
|
224
|
+
sync_on_checkout,
|
|
225
|
+
no_sync_on_checkout,
|
|
226
|
+
sync_on_merge,
|
|
227
|
+
no_sync_on_merge,
|
|
228
|
+
confirm,
|
|
229
|
+
force_local,
|
|
230
|
+
force_github,
|
|
231
|
+
)
|
|
232
|
+
if any_flag:
|
|
233
|
+
if service.save_config_to_file(config_path):
|
|
234
|
+
console.print("[dim]Config saved to .roadmap/config.json[/dim]")
|
|
235
|
+
|
|
236
|
+
# Show configuration
|
|
237
|
+
if show_config or not any_flag:
|
|
238
|
+
_display_config(new_config, service)
|
|
239
|
+
else:
|
|
240
|
+
console.print("[green]✅ Configuration updated[/green]")
|
|
241
|
+
_display_config(new_config, service)
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
def _display_config(
|
|
245
|
+
config: GitHookAutoSyncConfig, service: GitHookAutoSyncService
|
|
246
|
+
) -> None:
|
|
247
|
+
"""Display current auto-sync configuration."""
|
|
248
|
+
console.print()
|
|
249
|
+
console.print("[bold cyan]Git Hooks Auto-Sync Configuration[/bold cyan]")
|
|
250
|
+
console.print()
|
|
251
|
+
|
|
252
|
+
# Check if config is persisted
|
|
253
|
+
config_path = Path.cwd() / ".roadmap" / "config.json"
|
|
254
|
+
if config_path.exists():
|
|
255
|
+
console.print(f"[dim]Config file: {config_path}[/dim]")
|
|
256
|
+
console.print()
|
|
257
|
+
|
|
258
|
+
# Master switch
|
|
259
|
+
master_status = (
|
|
260
|
+
"[green]✓ ENABLED[/green]"
|
|
261
|
+
if config.auto_sync_enabled
|
|
262
|
+
else "[dim]✗ DISABLED[/dim]"
|
|
263
|
+
)
|
|
264
|
+
console.print(f"Master Switch: {master_status}")
|
|
265
|
+
console.print()
|
|
266
|
+
|
|
267
|
+
# Per-event configuration
|
|
268
|
+
console.print("[bold]Sync Events:[/bold]")
|
|
269
|
+
_print_setting(
|
|
270
|
+
" Commit",
|
|
271
|
+
config.sync_on_commit and config.auto_sync_enabled,
|
|
272
|
+
)
|
|
273
|
+
_print_setting(
|
|
274
|
+
" Branch Checkout",
|
|
275
|
+
config.sync_on_checkout and config.auto_sync_enabled,
|
|
276
|
+
)
|
|
277
|
+
_print_setting(
|
|
278
|
+
" Merge",
|
|
279
|
+
config.sync_on_merge and config.auto_sync_enabled,
|
|
280
|
+
)
|
|
281
|
+
console.print()
|
|
282
|
+
|
|
283
|
+
# Behavior settings
|
|
284
|
+
console.print("[bold]Behavior:[/bold]")
|
|
285
|
+
console.print(
|
|
286
|
+
f" Confirm Before Sync: {'✓ Yes' if config.confirm_before_sync else '✗ No'}"
|
|
287
|
+
)
|
|
288
|
+
console.print()
|
|
289
|
+
|
|
290
|
+
# Conflict resolution
|
|
291
|
+
console.print("[bold]Conflict Resolution:[/bold]")
|
|
292
|
+
if config.force_local:
|
|
293
|
+
console.print(" Strategy: [yellow]Force Local (keep local changes)[/yellow]")
|
|
294
|
+
elif config.force_github:
|
|
295
|
+
console.print(" Strategy: [yellow]Force GitHub (keep remote changes)[/yellow]")
|
|
296
|
+
else:
|
|
297
|
+
console.print(
|
|
298
|
+
" Strategy: [dim]None configured (manual resolution required)[/dim]"
|
|
299
|
+
)
|
|
300
|
+
console.print()
|
|
301
|
+
|
|
302
|
+
# Statistics
|
|
303
|
+
try:
|
|
304
|
+
stats = service.get_sync_stats()
|
|
305
|
+
console.print("[bold]Sync Statistics:[/bold]")
|
|
306
|
+
console.print(f" Total Issues: {stats['total_issues']}")
|
|
307
|
+
console.print(
|
|
308
|
+
f" GitHub-Linked: {stats['total_issues'] - stats['never_synced']}"
|
|
309
|
+
)
|
|
310
|
+
console.print(f" Never Synced: {stats['never_synced']}")
|
|
311
|
+
if stats["total_sync_attempts"] > 0:
|
|
312
|
+
console.print(f" Success Rate: {stats['success_rate']:.1f}%")
|
|
313
|
+
except Exception as e:
|
|
314
|
+
from roadmap.common.logging import get_logger
|
|
315
|
+
|
|
316
|
+
logger = get_logger(__name__)
|
|
317
|
+
logger.debug(
|
|
318
|
+
"sync_stats_display_failed", error=str(e), action="display_sync_stats"
|
|
319
|
+
)
|
|
320
|
+
|
|
321
|
+
console.print()
|
|
322
|
+
|
|
323
|
+
|
|
324
|
+
def _print_setting(label: str, enabled: bool) -> None:
|
|
325
|
+
"""Print a configuration setting with status."""
|
|
326
|
+
status = "[green]✓ ENABLED[/green]" if enabled else "[dim]✗ disabled[/dim]"
|
|
327
|
+
console.print(f"{label:25} {status}")
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
"""Display helper for git status command."""
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from rich.console import Console
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class GitStatusDisplay:
|
|
9
|
+
"""Handles display of git status information."""
|
|
10
|
+
|
|
11
|
+
def __init__(self, console: Console):
|
|
12
|
+
"""Initialize with a console for output.
|
|
13
|
+
|
|
14
|
+
Args:
|
|
15
|
+
console: Rich console instance for formatted output
|
|
16
|
+
"""
|
|
17
|
+
self.console = console
|
|
18
|
+
|
|
19
|
+
def show_not_git_repo(self) -> None:
|
|
20
|
+
"""Display message when not in a git repository."""
|
|
21
|
+
self.console.print("📁 Not in a Git repository", style="yellow")
|
|
22
|
+
|
|
23
|
+
def show_header(self) -> None:
|
|
24
|
+
"""Display the status header."""
|
|
25
|
+
self.console.print("🔍 Git Repository Status", style="bold blue")
|
|
26
|
+
self.console.print()
|
|
27
|
+
|
|
28
|
+
def show_repository_info(self, git_context: dict[str, Any]) -> None:
|
|
29
|
+
"""Display repository origin and GitHub information.
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
git_context: Dictionary containing git context information
|
|
33
|
+
"""
|
|
34
|
+
if git_context.get("origin_url"):
|
|
35
|
+
self.console.print(f"📍 Origin: {git_context['origin_url']}", style="cyan")
|
|
36
|
+
|
|
37
|
+
if git_context.get("github_owner") and git_context.get("github_repo"):
|
|
38
|
+
self.console.print(
|
|
39
|
+
f"🐙 GitHub: {git_context['github_owner']}/{git_context['github_repo']}",
|
|
40
|
+
style="cyan",
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
def show_current_branch(self, git_context: dict[str, Any]) -> None:
|
|
44
|
+
"""Display current branch and linked issue information.
|
|
45
|
+
|
|
46
|
+
Args:
|
|
47
|
+
git_context: Dictionary containing git context information
|
|
48
|
+
"""
|
|
49
|
+
if not git_context.get("current_branch"):
|
|
50
|
+
return
|
|
51
|
+
|
|
52
|
+
self.console.print(
|
|
53
|
+
f"🌿 Current branch: {git_context['current_branch']}", style="green"
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
linked_issue = git_context.get("linked_issue")
|
|
57
|
+
if linked_issue:
|
|
58
|
+
self._show_linked_issue_details(linked_issue)
|
|
59
|
+
else:
|
|
60
|
+
self.console.print(" 💡 No linked issue found", style="dim")
|
|
61
|
+
|
|
62
|
+
def _show_linked_issue_details(self, linked_issue: dict[str, Any]) -> None:
|
|
63
|
+
"""Display details of the linked issue.
|
|
64
|
+
|
|
65
|
+
Args:
|
|
66
|
+
linked_issue: Dictionary containing linked issue information
|
|
67
|
+
"""
|
|
68
|
+
self.console.print("🔗 Linked issue:", style="bold")
|
|
69
|
+
self.console.print(f" 📋 {linked_issue['title']}", style="cyan")
|
|
70
|
+
self.console.print(f" 🆔 {linked_issue['id']}", style="dim")
|
|
71
|
+
self.console.print(f" 📊 Status: {linked_issue['status']}", style="yellow")
|
|
72
|
+
|
|
73
|
+
priority_style = "red" if linked_issue["priority"] == "critical" else "yellow"
|
|
74
|
+
self.console.print(
|
|
75
|
+
f" ⚡ Priority: {linked_issue['priority']}", style=priority_style
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
def show_branch_issue_links(
|
|
79
|
+
self, branch_issues: dict[str, list[str]], current_branch: str, core: Any
|
|
80
|
+
) -> None:
|
|
81
|
+
"""Display all branch-issue mappings.
|
|
82
|
+
|
|
83
|
+
Args:
|
|
84
|
+
branch_issues: Dictionary mapping branches to issue IDs
|
|
85
|
+
current_branch: Name of the current branch
|
|
86
|
+
core: RoadmapCore instance for fetching issue details
|
|
87
|
+
"""
|
|
88
|
+
if not branch_issues:
|
|
89
|
+
return
|
|
90
|
+
|
|
91
|
+
self.console.print("\n🌿 Branch-Issue Links:", style="bold")
|
|
92
|
+
|
|
93
|
+
for branch, issue_ids in branch_issues.items():
|
|
94
|
+
for issue_id in issue_ids:
|
|
95
|
+
issue = core.issues.get(issue_id)
|
|
96
|
+
if issue:
|
|
97
|
+
self._show_branch_issue_link(branch, issue, current_branch)
|
|
98
|
+
|
|
99
|
+
def _show_branch_issue_link(
|
|
100
|
+
self, branch: str, issue: Any, current_branch: str
|
|
101
|
+
) -> None:
|
|
102
|
+
"""Display a single branch-issue link.
|
|
103
|
+
|
|
104
|
+
Args:
|
|
105
|
+
branch: Branch name
|
|
106
|
+
issue: Issue object
|
|
107
|
+
current_branch: Name of the current branch
|
|
108
|
+
"""
|
|
109
|
+
marker = "👉" if branch == current_branch else " "
|
|
110
|
+
title = issue.title[:50]
|
|
111
|
+
if len(issue.title) > 50:
|
|
112
|
+
title += "..."
|
|
113
|
+
|
|
114
|
+
self.console.print(f"{marker} {branch} → {title}", style="cyan")
|
|
115
|
+
|
|
116
|
+
def show_recent_commits(self, core: Any) -> None:
|
|
117
|
+
"""Display recent commits with roadmap references.
|
|
118
|
+
|
|
119
|
+
Args:
|
|
120
|
+
core: RoadmapCore instance for fetching commit information
|
|
121
|
+
"""
|
|
122
|
+
if not core.git.is_git_repository():
|
|
123
|
+
return
|
|
124
|
+
|
|
125
|
+
recent_commits = core.git.get_recent_commits(count=5)
|
|
126
|
+
roadmap_commits = [c for c in recent_commits if c.extract_roadmap_references()]
|
|
127
|
+
|
|
128
|
+
if not roadmap_commits:
|
|
129
|
+
return
|
|
130
|
+
|
|
131
|
+
self.console.print("\n📝 Recent Roadmap Commits:", style="bold")
|
|
132
|
+
|
|
133
|
+
for commit in roadmap_commits[:3]:
|
|
134
|
+
self._show_commit_details(commit)
|
|
135
|
+
|
|
136
|
+
def _show_commit_details(self, commit: Any) -> None:
|
|
137
|
+
"""Display details of a single commit.
|
|
138
|
+
|
|
139
|
+
Args:
|
|
140
|
+
commit: Commit object with message and references
|
|
141
|
+
"""
|
|
142
|
+
message = commit.message[:60]
|
|
143
|
+
if len(commit.message) > 60:
|
|
144
|
+
message += "..."
|
|
145
|
+
|
|
146
|
+
self.console.print(f" {commit.short_hash} {message}", style="dim")
|
|
147
|
+
|
|
148
|
+
refs = commit.extract_roadmap_references()
|
|
149
|
+
if refs:
|
|
150
|
+
self.console.print(f" 🔗 References: {', '.join(refs)}", style="cyan")
|
|
151
|
+
|
|
152
|
+
def show_error(self, error: Exception) -> None:
|
|
153
|
+
"""Display an error message.
|
|
154
|
+
|
|
155
|
+
Args:
|
|
156
|
+
error: Exception that occurred
|
|
157
|
+
"""
|
|
158
|
+
self.console.print(f"❌ Failed to get Git status: {error}", style="bold red")
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
"""Database integrity check command.
|
|
2
|
+
|
|
3
|
+
Checks that local issue files align with database state and baseline.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import sqlite3
|
|
9
|
+
|
|
10
|
+
import click
|
|
11
|
+
from structlog import get_logger
|
|
12
|
+
|
|
13
|
+
from roadmap.adapters.cli.cli_command_helpers import require_initialized
|
|
14
|
+
from roadmap.adapters.cli.utils.click_options import health_check_options
|
|
15
|
+
from roadmap.adapters.persistence.parser.issue import IssueParser
|
|
16
|
+
|
|
17
|
+
logger = get_logger(__name__)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _collect_local_issue_ids(issues_dir) -> tuple[set[str], list[tuple[str, str]]]:
|
|
21
|
+
local_ids: set[str] = set()
|
|
22
|
+
parse_errors: list[tuple[str, str]] = []
|
|
23
|
+
for file_path in issues_dir.glob("**/*.md"):
|
|
24
|
+
try:
|
|
25
|
+
issue = IssueParser.parse_issue_file(file_path)
|
|
26
|
+
except Exception as exc:
|
|
27
|
+
parse_errors.append((str(file_path), str(exc)))
|
|
28
|
+
continue
|
|
29
|
+
issue_id = getattr(issue, "id", None)
|
|
30
|
+
if issue_id:
|
|
31
|
+
local_ids.add(str(issue_id))
|
|
32
|
+
return local_ids, parse_errors
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _load_db_state(db_path) -> tuple[set[str], set[str], int] | None:
|
|
36
|
+
try:
|
|
37
|
+
conn = sqlite3.connect(str(db_path))
|
|
38
|
+
conn.row_factory = sqlite3.Row
|
|
39
|
+
db_ids = {row["id"] for row in conn.execute("SELECT id FROM issues").fetchall()}
|
|
40
|
+
baseline_ids = {
|
|
41
|
+
row["issue_id"]
|
|
42
|
+
for row in conn.execute("SELECT issue_id FROM sync_base_state").fetchall()
|
|
43
|
+
}
|
|
44
|
+
remote_links_count = conn.execute(
|
|
45
|
+
"SELECT COUNT(*) FROM issue_remote_links"
|
|
46
|
+
).fetchone()[0]
|
|
47
|
+
conn.close()
|
|
48
|
+
return db_ids, baseline_ids, remote_links_count
|
|
49
|
+
except Exception as exc:
|
|
50
|
+
logger.warning(
|
|
51
|
+
"db_integrity_query_failed",
|
|
52
|
+
error=str(exc),
|
|
53
|
+
error_type=type(exc).__name__,
|
|
54
|
+
severity="operational",
|
|
55
|
+
)
|
|
56
|
+
return None
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _build_report(
|
|
60
|
+
local_ids: set[str],
|
|
61
|
+
db_ids: set[str],
|
|
62
|
+
baseline_ids: set[str],
|
|
63
|
+
remote_links_count: int,
|
|
64
|
+
parse_errors: list[tuple[str, str]],
|
|
65
|
+
) -> dict:
|
|
66
|
+
missing_in_db = sorted(local_ids - db_ids)
|
|
67
|
+
extra_in_db = sorted(db_ids - local_ids)
|
|
68
|
+
baseline_missing_issue = sorted(baseline_ids - db_ids)
|
|
69
|
+
issue_missing_baseline = sorted(db_ids - baseline_ids)
|
|
70
|
+
|
|
71
|
+
return {
|
|
72
|
+
"local_issue_files": len(local_ids),
|
|
73
|
+
"db_issues": len(db_ids),
|
|
74
|
+
"sync_base_state": len(baseline_ids),
|
|
75
|
+
"remote_links": remote_links_count,
|
|
76
|
+
"missing_in_db": len(missing_in_db),
|
|
77
|
+
"extra_in_db": len(extra_in_db),
|
|
78
|
+
"baseline_missing_issue": len(baseline_missing_issue),
|
|
79
|
+
"issue_missing_baseline": len(issue_missing_baseline),
|
|
80
|
+
"missing_in_db_ids": missing_in_db,
|
|
81
|
+
"extra_in_db_ids": extra_in_db,
|
|
82
|
+
"baseline_missing_issue_ids": baseline_missing_issue,
|
|
83
|
+
"issue_missing_baseline_ids": issue_missing_baseline,
|
|
84
|
+
"parse_errors": parse_errors,
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def _print_plain_report(report: dict, show_ids: bool, limit: int) -> None:
|
|
89
|
+
click.echo("DB Integrity Report")
|
|
90
|
+
click.echo(f"local_issue_files: {report['local_issue_files']}")
|
|
91
|
+
click.echo(f"db_issues: {report['db_issues']}")
|
|
92
|
+
click.echo(f"sync_base_state: {report['sync_base_state']}")
|
|
93
|
+
click.echo(f"remote_links: {report['remote_links']}")
|
|
94
|
+
click.echo(f"missing_in_db: {report['missing_in_db']}")
|
|
95
|
+
click.echo(f"extra_in_db: {report['extra_in_db']}")
|
|
96
|
+
click.echo(f"baseline_missing_issue: {report['baseline_missing_issue']}")
|
|
97
|
+
click.echo(f"issue_missing_baseline: {report['issue_missing_baseline']}")
|
|
98
|
+
click.echo(f"parse_errors: {len(report['parse_errors'])}")
|
|
99
|
+
|
|
100
|
+
if show_ids and limit > 0:
|
|
101
|
+
if report["missing_in_db_ids"]:
|
|
102
|
+
click.echo("missing_in_db_ids:")
|
|
103
|
+
for issue_id in report["missing_in_db_ids"][:limit]:
|
|
104
|
+
click.echo(f" {issue_id}")
|
|
105
|
+
if report["extra_in_db_ids"]:
|
|
106
|
+
click.echo("extra_in_db_ids:")
|
|
107
|
+
for issue_id in report["extra_in_db_ids"][:limit]:
|
|
108
|
+
click.echo(f" {issue_id}")
|
|
109
|
+
if report["baseline_missing_issue_ids"]:
|
|
110
|
+
click.echo("baseline_missing_issue_ids:")
|
|
111
|
+
for issue_id in report["baseline_missing_issue_ids"][:limit]:
|
|
112
|
+
click.echo(f" {issue_id}")
|
|
113
|
+
if report["issue_missing_baseline_ids"]:
|
|
114
|
+
click.echo("issue_missing_baseline_ids:")
|
|
115
|
+
for issue_id in report["issue_missing_baseline_ids"][:limit]:
|
|
116
|
+
click.echo(f" {issue_id}")
|
|
117
|
+
if report["parse_errors"]:
|
|
118
|
+
click.echo("parse_errors:")
|
|
119
|
+
for path, error in report["parse_errors"][:limit]:
|
|
120
|
+
click.echo(f" {path}: {error}")
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
@click.command(name="db-integrity")
|
|
124
|
+
@health_check_options
|
|
125
|
+
@click.option(
|
|
126
|
+
"--show-ids",
|
|
127
|
+
is_flag=True,
|
|
128
|
+
help="Show missing/extra ids (limited by --limit)",
|
|
129
|
+
)
|
|
130
|
+
@click.option(
|
|
131
|
+
"--json",
|
|
132
|
+
"json_output",
|
|
133
|
+
is_flag=True,
|
|
134
|
+
help="Output results as JSON",
|
|
135
|
+
)
|
|
136
|
+
@click.option(
|
|
137
|
+
"--limit",
|
|
138
|
+
type=int,
|
|
139
|
+
default=20,
|
|
140
|
+
show_default=True,
|
|
141
|
+
help="Max ids to display when using --show-ids",
|
|
142
|
+
)
|
|
143
|
+
@click.pass_context
|
|
144
|
+
@require_initialized
|
|
145
|
+
def db_integrity(
|
|
146
|
+
ctx: click.Context,
|
|
147
|
+
show_ids: bool,
|
|
148
|
+
json_output: bool,
|
|
149
|
+
limit: int,
|
|
150
|
+
verbose: bool,
|
|
151
|
+
details: bool,
|
|
152
|
+
format: str,
|
|
153
|
+
) -> None:
|
|
154
|
+
"""Check database integrity against local issue files."""
|
|
155
|
+
core = ctx.obj["core"]
|
|
156
|
+
issues_dir = core.roadmap_dir / "issues"
|
|
157
|
+
db_path = core.db_dir / "state.db"
|
|
158
|
+
|
|
159
|
+
if not issues_dir.exists():
|
|
160
|
+
click.echo("No issues directory found.")
|
|
161
|
+
return
|
|
162
|
+
|
|
163
|
+
if not db_path.exists():
|
|
164
|
+
click.echo("Database not found.")
|
|
165
|
+
return
|
|
166
|
+
|
|
167
|
+
local_ids, parse_errors = _collect_local_issue_ids(issues_dir)
|
|
168
|
+
db_state = _load_db_state(db_path)
|
|
169
|
+
if db_state is None:
|
|
170
|
+
click.echo("Failed to read database state.")
|
|
171
|
+
return
|
|
172
|
+
db_ids, baseline_ids, remote_links_count = db_state
|
|
173
|
+
report = _build_report(
|
|
174
|
+
local_ids,
|
|
175
|
+
db_ids,
|
|
176
|
+
baseline_ids,
|
|
177
|
+
remote_links_count,
|
|
178
|
+
parse_errors,
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
if json_output:
|
|
182
|
+
import json
|
|
183
|
+
|
|
184
|
+
if show_ids and limit > 0:
|
|
185
|
+
report["missing_in_db_ids"] = report["missing_in_db_ids"][:limit]
|
|
186
|
+
report["extra_in_db_ids"] = report["extra_in_db_ids"][:limit]
|
|
187
|
+
report["baseline_missing_issue_ids"] = report["baseline_missing_issue_ids"][
|
|
188
|
+
:limit
|
|
189
|
+
]
|
|
190
|
+
report["issue_missing_baseline_ids"] = report["issue_missing_baseline_ids"][
|
|
191
|
+
:limit
|
|
192
|
+
]
|
|
193
|
+
report["parse_errors"] = [
|
|
194
|
+
{"path": path, "error": error}
|
|
195
|
+
for path, error in report["parse_errors"][:limit]
|
|
196
|
+
]
|
|
197
|
+
else:
|
|
198
|
+
report["parse_errors"] = [
|
|
199
|
+
{"path": path, "error": error} for path, error in report["parse_errors"]
|
|
200
|
+
]
|
|
201
|
+
click.echo(json.dumps(report, indent=2))
|
|
202
|
+
return
|
|
203
|
+
|
|
204
|
+
_print_plain_report(report, show_ids, limit)
|