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
roadmap/__init__.py
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""Roadmap CLI - A command line tool for creating and managing roadmaps."""
|
|
2
|
+
|
|
3
|
+
from importlib.metadata import PackageNotFoundError, version
|
|
4
|
+
|
|
5
|
+
# Define version BEFORE importing cli modules (they import __version__ from here)
|
|
6
|
+
# Keep a source-checkout fallback for direct imports. Installed wheels and sdists
|
|
7
|
+
# obtain the authoritative version from their distribution metadata because the
|
|
8
|
+
# repository-level pyproject.toml is not part of an installed wheel.
|
|
9
|
+
__version__ = "0.1.1"
|
|
10
|
+
try:
|
|
11
|
+
__version__ = version("roadmap-cli")
|
|
12
|
+
except PackageNotFoundError:
|
|
13
|
+
__version__ = "0.1.1"
|
|
14
|
+
|
|
15
|
+
# Now import cli (which will import __version__ from this module)
|
|
16
|
+
from roadmap.adapters import cli # noqa: F401, E402
|
|
17
|
+
|
|
18
|
+
# Legacy exports - use layer-specific imports instead
|
|
19
|
+
# from roadmap.core.domain import Issue, Milestone
|
|
20
|
+
# from roadmap.adapters.github.github import GitHubClient
|
|
21
|
+
# from roadmap.infrastructure.security.credentials import CredentialManager
|
|
22
|
+
# from roadmap.core.services import ... etc
|
|
23
|
+
|
|
24
|
+
__all__ = [
|
|
25
|
+
"__version__",
|
|
26
|
+
"cli",
|
|
27
|
+
]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""External system adapters for GitHub, Git, and synchronization."""
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"""Base adapter for REST APIs with pagination support.
|
|
2
|
+
|
|
3
|
+
This module provides a reusable foundation for API adapters that implement
|
|
4
|
+
pagination. Any backend (GitHub, GitLab, etc.) that needs to paginate through
|
|
5
|
+
REST API results should inherit from this class.
|
|
6
|
+
|
|
7
|
+
The pagination implementation is backend-agnostic - it works with any REST API
|
|
8
|
+
that follows standard pagination patterns (page number, per_page, Link headers).
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from typing import Any
|
|
12
|
+
|
|
13
|
+
from structlog import get_logger
|
|
14
|
+
|
|
15
|
+
logger = get_logger(__name__)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class BasePaginatedAdapter:
|
|
19
|
+
"""Base class for REST API adapters with pagination support.
|
|
20
|
+
|
|
21
|
+
Provides a reusable `_paginate_request()` method that handles pagination
|
|
22
|
+
for any REST API endpoint. Backends should inherit from this class to
|
|
23
|
+
automatically get pagination capability.
|
|
24
|
+
|
|
25
|
+
Subclasses must implement `_make_request()` to perform actual API calls.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
def _make_request(self, method: str, endpoint: str, **kwargs) -> Any:
|
|
29
|
+
"""Make a request to the API (must be implemented by subclass).
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
method: HTTP method (GET, POST, etc.)
|
|
33
|
+
endpoint: API endpoint path
|
|
34
|
+
**kwargs: Additional arguments (params, json, headers, etc.)
|
|
35
|
+
|
|
36
|
+
Returns:
|
|
37
|
+
Response object with .json() method and .headers dict
|
|
38
|
+
|
|
39
|
+
Raises:
|
|
40
|
+
Subclass-specific exception on error
|
|
41
|
+
"""
|
|
42
|
+
raise NotImplementedError("Subclasses must implement _make_request()")
|
|
43
|
+
|
|
44
|
+
def _paginate_request(
|
|
45
|
+
self,
|
|
46
|
+
method: str,
|
|
47
|
+
endpoint: str,
|
|
48
|
+
params: dict | None = None,
|
|
49
|
+
per_page: int = 100,
|
|
50
|
+
) -> list[dict[str, Any]]:
|
|
51
|
+
"""Make a paginated request to a REST API.
|
|
52
|
+
|
|
53
|
+
Automatically handles pagination by checking the Link header
|
|
54
|
+
for next page references. Returns all items across all pages.
|
|
55
|
+
|
|
56
|
+
This method is backend-agnostic and works with any REST API that:
|
|
57
|
+
1. Supports page and per_page query parameters
|
|
58
|
+
2. Returns a Link header with rel="next" for pagination
|
|
59
|
+
3. Returns an empty list when no more items exist
|
|
60
|
+
|
|
61
|
+
Args:
|
|
62
|
+
method: HTTP method (GET, POST, etc.)
|
|
63
|
+
endpoint: API endpoint path
|
|
64
|
+
params: Query parameters (will add page and per_page)
|
|
65
|
+
per_page: Items per page (backend default is usually 30-100)
|
|
66
|
+
|
|
67
|
+
Returns:
|
|
68
|
+
List of all items from all pages (flattened)
|
|
69
|
+
|
|
70
|
+
Raises:
|
|
71
|
+
Subclass-specific exceptions if any request fails
|
|
72
|
+
"""
|
|
73
|
+
all_items = []
|
|
74
|
+
page = 1
|
|
75
|
+
params = params or {}
|
|
76
|
+
|
|
77
|
+
while True:
|
|
78
|
+
# Add pagination params
|
|
79
|
+
page_params = {**params, "page": page, "per_page": per_page}
|
|
80
|
+
|
|
81
|
+
response = self._make_request(method, endpoint, params=page_params)
|
|
82
|
+
items_page = response.json()
|
|
83
|
+
|
|
84
|
+
if not items_page:
|
|
85
|
+
# No more items on this page
|
|
86
|
+
logger.debug(
|
|
87
|
+
"pagination_complete",
|
|
88
|
+
endpoint=endpoint,
|
|
89
|
+
total_pages=page - 1,
|
|
90
|
+
total_items=len(all_items),
|
|
91
|
+
)
|
|
92
|
+
break
|
|
93
|
+
|
|
94
|
+
all_items.extend(items_page)
|
|
95
|
+
logger.debug(
|
|
96
|
+
"pagination_page_fetched",
|
|
97
|
+
endpoint=endpoint,
|
|
98
|
+
page=page,
|
|
99
|
+
page_count=len(items_page),
|
|
100
|
+
total_so_far=len(all_items),
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
# Check if there are more pages by looking at the Link header
|
|
104
|
+
link_header = response.headers.get("Link", "")
|
|
105
|
+
if 'rel="next"' not in link_header:
|
|
106
|
+
# No next page link, we're done
|
|
107
|
+
logger.debug(
|
|
108
|
+
"pagination_no_next_link",
|
|
109
|
+
endpoint=endpoint,
|
|
110
|
+
final_page=page,
|
|
111
|
+
total_items=len(all_items),
|
|
112
|
+
)
|
|
113
|
+
break
|
|
114
|
+
|
|
115
|
+
page += 1
|
|
116
|
+
|
|
117
|
+
return all_items
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
"""Modular CLI architecture for the Roadmap tool.
|
|
2
|
+
|
|
3
|
+
This module provides the main CLI entry point and implements plugin-style
|
|
4
|
+
lazy-loading of command groups to improve performance and maintainability.
|
|
5
|
+
|
|
6
|
+
## Architecture
|
|
7
|
+
|
|
8
|
+
Commands are registered via a plugin registry that specifies:
|
|
9
|
+
- Command name/group name
|
|
10
|
+
- Module path where the command is defined
|
|
11
|
+
- Variable name of the command in that module
|
|
12
|
+
|
|
13
|
+
This allows commands to be imported only when actually invoked, reducing
|
|
14
|
+
startup time and improving modularity.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
import importlib
|
|
18
|
+
from typing import Any
|
|
19
|
+
|
|
20
|
+
import click
|
|
21
|
+
|
|
22
|
+
from roadmap import __version__
|
|
23
|
+
from roadmap.adapters.cli.exception_handler import handle_cli_exception
|
|
24
|
+
|
|
25
|
+
# Initialize console for rich output
|
|
26
|
+
from roadmap.common.console import get_console
|
|
27
|
+
from roadmap.common.errors.exceptions import RoadmapException
|
|
28
|
+
|
|
29
|
+
# Initialize OpenTelemetry tracing
|
|
30
|
+
from roadmap.common.observability.otel_init import initialize_tracing
|
|
31
|
+
|
|
32
|
+
# Import core classes for backward compatibility with tests
|
|
33
|
+
from roadmap.infrastructure.coordination.core import RoadmapCore
|
|
34
|
+
|
|
35
|
+
console = get_console()
|
|
36
|
+
|
|
37
|
+
# Plugin registry: maps command names to their import locations
|
|
38
|
+
# Format: "command_name": ("module_path", "variable_name")
|
|
39
|
+
# This allows lazy loading of commands - they're only imported when invoked
|
|
40
|
+
_COMMAND_REGISTRY: dict[str, tuple[str, str]] = {
|
|
41
|
+
# Core v1.0 standalone commands
|
|
42
|
+
"init": ("roadmap.adapters.cli.core", "init"),
|
|
43
|
+
"status": ("roadmap.adapters.cli.core", "status"),
|
|
44
|
+
"health": ("roadmap.adapters.cli.core", "health"),
|
|
45
|
+
"today": ("roadmap.adapters.cli.today", "today"),
|
|
46
|
+
"cleanup": ("roadmap.infrastructure.maintenance", "cleanup"),
|
|
47
|
+
# Command groups
|
|
48
|
+
"analysis": ("roadmap.adapters.cli.analysis", "analysis"),
|
|
49
|
+
"comment": ("roadmap.adapters.cli.comment", "comment"),
|
|
50
|
+
"config": ("roadmap.adapters.cli.config", "config"),
|
|
51
|
+
"data": ("roadmap.adapters.cli.data", "data"),
|
|
52
|
+
"git": ("roadmap.adapters.cli.git", "git"),
|
|
53
|
+
"issue": ("roadmap.adapters.cli.issues", "issue"),
|
|
54
|
+
"milestone": ("roadmap.adapters.cli.milestones", "milestone"),
|
|
55
|
+
"project": ("roadmap.adapters.cli.projects", "project"),
|
|
56
|
+
"sync": ("roadmap.adapters.cli.sync", "sync"),
|
|
57
|
+
"validate-links": ("roadmap.adapters.cli.sync_validation", "validate_links"),
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
# Cache for loaded commands to avoid re-importing
|
|
61
|
+
_command_cache: dict[str, Any] = {}
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def _load_command(command_name: str) -> Any:
|
|
65
|
+
"""Lazily load a command from the registry.
|
|
66
|
+
|
|
67
|
+
Args:
|
|
68
|
+
command_name: Name of command to load
|
|
69
|
+
|
|
70
|
+
Returns:
|
|
71
|
+
The Click command/group object
|
|
72
|
+
|
|
73
|
+
Raises:
|
|
74
|
+
ValueError: If command not found in registry
|
|
75
|
+
"""
|
|
76
|
+
if command_name in _command_cache:
|
|
77
|
+
return _command_cache[command_name]
|
|
78
|
+
|
|
79
|
+
if command_name not in _COMMAND_REGISTRY:
|
|
80
|
+
raise ValueError(f"Unknown command: {command_name}")
|
|
81
|
+
|
|
82
|
+
module_path, var_name = _COMMAND_REGISTRY[command_name]
|
|
83
|
+
module = importlib.import_module(module_path)
|
|
84
|
+
command = getattr(module, var_name)
|
|
85
|
+
|
|
86
|
+
_command_cache[command_name] = command
|
|
87
|
+
return command
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
# Flag to track if commands have been registered to avoid duplicate registration
|
|
91
|
+
_commands_registered = False
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
class RoadmapClickGroup(click.Group):
|
|
95
|
+
"""Custom Click Group that handles RoadmapException instances."""
|
|
96
|
+
|
|
97
|
+
def invoke(self, ctx: click.Context) -> click.Context | None:
|
|
98
|
+
"""Invoke the group with centralized exception handling."""
|
|
99
|
+
try:
|
|
100
|
+
# Allow Click to handle help and version flags normally
|
|
101
|
+
result = super().invoke(ctx)
|
|
102
|
+
return result
|
|
103
|
+
except RoadmapException as exc:
|
|
104
|
+
# Handle roadmap exceptions with our formatter
|
|
105
|
+
handle_cli_exception(ctx, exc, show_traceback=False)
|
|
106
|
+
except click.exceptions.Exit:
|
|
107
|
+
# Let Click Exit exceptions propagate (--help, --version)
|
|
108
|
+
raise
|
|
109
|
+
except click.ClickException:
|
|
110
|
+
# Let Click exceptions propagate
|
|
111
|
+
raise
|
|
112
|
+
except click.Abort:
|
|
113
|
+
# Let Click Abort propagate
|
|
114
|
+
raise
|
|
115
|
+
except SystemExit:
|
|
116
|
+
# Let SystemExit propagate (used by Click for --help, --version, etc.)
|
|
117
|
+
raise
|
|
118
|
+
except Exception as exc:
|
|
119
|
+
# Handle unexpected exceptions
|
|
120
|
+
handle_cli_exception(ctx, exc, show_traceback=False)
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
# Import utility functions that tests need
|
|
124
|
+
try:
|
|
125
|
+
import git as gitpython
|
|
126
|
+
except ImportError:
|
|
127
|
+
gitpython = None
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def _get_current_user():
|
|
131
|
+
"""Get current user from git config."""
|
|
132
|
+
if gitpython is None:
|
|
133
|
+
import os
|
|
134
|
+
|
|
135
|
+
return os.environ.get("USER") or os.environ.get("USERNAME")
|
|
136
|
+
|
|
137
|
+
try:
|
|
138
|
+
repo = gitpython.Repo(search_parent_directories=True) # type: ignore[attr-defined]
|
|
139
|
+
try:
|
|
140
|
+
name = repo.config_reader().get_value("user", "name")
|
|
141
|
+
return name
|
|
142
|
+
except Exception as e:
|
|
143
|
+
from roadmap.common.logging import get_logger
|
|
144
|
+
|
|
145
|
+
logger = get_logger(__name__)
|
|
146
|
+
logger.debug(
|
|
147
|
+
"git_user_name_read_failed", error=str(e), action="get_git_user_name"
|
|
148
|
+
)
|
|
149
|
+
except Exception as e:
|
|
150
|
+
from roadmap.common.logging import get_logger
|
|
151
|
+
|
|
152
|
+
logger = get_logger(__name__)
|
|
153
|
+
logger.debug("git_repo_open_failed", error=str(e), action="detect_git_repo")
|
|
154
|
+
|
|
155
|
+
# Fallback to environment variables
|
|
156
|
+
import os
|
|
157
|
+
|
|
158
|
+
return os.environ.get("USER") or os.environ.get("USERNAME")
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def _detect_project_context():
|
|
162
|
+
"""Detect project context from current directory."""
|
|
163
|
+
import pathlib
|
|
164
|
+
|
|
165
|
+
context = {
|
|
166
|
+
"project_name": pathlib.Path.cwd().name,
|
|
167
|
+
"has_git": False,
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
# Check if we're in a git repository
|
|
171
|
+
if gitpython is not None:
|
|
172
|
+
try:
|
|
173
|
+
gitpython.Repo(search_parent_directories=True) # type: ignore[attr-defined]
|
|
174
|
+
context["has_git"] = True
|
|
175
|
+
except Exception as e:
|
|
176
|
+
from roadmap.common.logging import get_logger
|
|
177
|
+
|
|
178
|
+
logger = get_logger(__name__)
|
|
179
|
+
logger.debug(
|
|
180
|
+
"git_repo_detection_failed",
|
|
181
|
+
error=str(e),
|
|
182
|
+
action="detect_project_context",
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
return context
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
@click.group(cls=RoadmapClickGroup)
|
|
189
|
+
@click.version_option(version=__version__)
|
|
190
|
+
@click.pass_context
|
|
191
|
+
def main(ctx: click.Context):
|
|
192
|
+
"""Roadmap CLI - A command line tool for creating and managing roadmaps."""
|
|
193
|
+
# Initialize OpenTelemetry tracing
|
|
194
|
+
initialize_tracing()
|
|
195
|
+
|
|
196
|
+
# Ensure that ctx.obj exists and is a dict (in case `cli()` is called
|
|
197
|
+
# by means other than the `if` block below)
|
|
198
|
+
ctx.ensure_object(dict)
|
|
199
|
+
|
|
200
|
+
# Initialize core with default roadmap directory (skip for init command)
|
|
201
|
+
if ctx.invoked_subcommand != "init":
|
|
202
|
+
try:
|
|
203
|
+
ctx.obj["core"] = RoadmapCore()
|
|
204
|
+
except Exception as exc:
|
|
205
|
+
# Handle exceptions during core initialization
|
|
206
|
+
handle_cli_exception(ctx, exc, show_traceback=False)
|
|
207
|
+
|
|
208
|
+
# If no subcommand was provided, show help
|
|
209
|
+
if ctx.invoked_subcommand is None:
|
|
210
|
+
click.echo(ctx.get_help())
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
def register_commands():
|
|
214
|
+
"""Register all commands from the plugin registry.
|
|
215
|
+
|
|
216
|
+
Commands are loaded lazily from the registry, which maps command names
|
|
217
|
+
to their module paths. This approach:
|
|
218
|
+
- Reduces startup time by deferring imports
|
|
219
|
+
- Makes it easy to add/remove commands
|
|
220
|
+
- Provides a single source of truth for command list
|
|
221
|
+
"""
|
|
222
|
+
for command_name, _ in _COMMAND_REGISTRY.items():
|
|
223
|
+
try:
|
|
224
|
+
command = _load_command(command_name)
|
|
225
|
+
main.add_command(command, name=command_name)
|
|
226
|
+
except Exception as e:
|
|
227
|
+
console.print(
|
|
228
|
+
f"⚠️ Failed to register command '{command_name}': {e}", style="yellow"
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
# Register commands at module load time
|
|
233
|
+
# This ensures CLI commands are available for tests and direct usage
|
|
234
|
+
try:
|
|
235
|
+
register_commands()
|
|
236
|
+
_commands_registered = True
|
|
237
|
+
except ImportError as e:
|
|
238
|
+
# If there's a circular import, commands will still be registered
|
|
239
|
+
# when main() is invoked through Click's mechanism
|
|
240
|
+
if "partially initialized" not in str(e):
|
|
241
|
+
# Re-raise if it's not a circular import issue
|
|
242
|
+
raise
|
|
243
|
+
else:
|
|
244
|
+
# For circular imports, we'll register commands lazily when needed
|
|
245
|
+
_commands_registered = False
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
if __name__ == "__main__":
|
|
249
|
+
main() # type: ignore[call-arg]
|