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,178 @@
|
|
|
1
|
+
"""Comment management commands for issues."""
|
|
2
|
+
|
|
3
|
+
import click
|
|
4
|
+
|
|
5
|
+
from roadmap.adapters.cli.cli_command_helpers import (
|
|
6
|
+
ensure_entity_exists,
|
|
7
|
+
require_initialized,
|
|
8
|
+
)
|
|
9
|
+
from roadmap.common.console import get_console
|
|
10
|
+
from roadmap.common.logging import log_command, verbose_output
|
|
11
|
+
from roadmap.core.services.comment.comment_service import CommentService
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@click.group("comment")
|
|
15
|
+
def comment_group():
|
|
16
|
+
"""Manage comments on issues."""
|
|
17
|
+
pass
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@click.command("add")
|
|
21
|
+
@click.argument("issue_id")
|
|
22
|
+
@click.argument("body")
|
|
23
|
+
@click.option(
|
|
24
|
+
"--author",
|
|
25
|
+
"-a",
|
|
26
|
+
default=None,
|
|
27
|
+
help="Author of comment (defaults to current user)",
|
|
28
|
+
)
|
|
29
|
+
@click.option(
|
|
30
|
+
"--reply-to",
|
|
31
|
+
"-r",
|
|
32
|
+
type=int,
|
|
33
|
+
default=None,
|
|
34
|
+
help="Comment ID this is a reply to",
|
|
35
|
+
)
|
|
36
|
+
@click.pass_context
|
|
37
|
+
@require_initialized
|
|
38
|
+
@verbose_output
|
|
39
|
+
@log_command("issue_comment_add", entity_type="comment", track_duration=True)
|
|
40
|
+
def add_comment(
|
|
41
|
+
ctx: click.Context,
|
|
42
|
+
issue_id: str,
|
|
43
|
+
body: str,
|
|
44
|
+
author: str | None,
|
|
45
|
+
reply_to: int | None,
|
|
46
|
+
):
|
|
47
|
+
"""Add a comment to an issue.
|
|
48
|
+
|
|
49
|
+
BODY: Comment text (markdown supported)
|
|
50
|
+
|
|
51
|
+
Example:
|
|
52
|
+
roadmap issue comment add abc123 "Great work! This looks good."
|
|
53
|
+
roadmap issue comment add abc123 "I disagree" --reply-to 12345
|
|
54
|
+
"""
|
|
55
|
+
core = ctx.obj["core"]
|
|
56
|
+
console = get_console()
|
|
57
|
+
|
|
58
|
+
# Ensure issue exists
|
|
59
|
+
issue = ensure_entity_exists(core, "issue", issue_id)
|
|
60
|
+
|
|
61
|
+
# Resolve author (use provided or default to system user)
|
|
62
|
+
if not author:
|
|
63
|
+
import getpass
|
|
64
|
+
|
|
65
|
+
author = getpass.getuser()
|
|
66
|
+
|
|
67
|
+
# Validate comment body
|
|
68
|
+
if not body or not body.strip():
|
|
69
|
+
console.print("❌ Comment body cannot be empty", style="red")
|
|
70
|
+
return
|
|
71
|
+
|
|
72
|
+
# Create the comment
|
|
73
|
+
try:
|
|
74
|
+
comment = CommentService.create_comment(
|
|
75
|
+
author=author,
|
|
76
|
+
body=body,
|
|
77
|
+
entity_id=issue.id,
|
|
78
|
+
in_reply_to=reply_to,
|
|
79
|
+
)
|
|
80
|
+
except Exception as e:
|
|
81
|
+
console.print(f"❌ Failed to create comment: {e}", style="red")
|
|
82
|
+
return
|
|
83
|
+
|
|
84
|
+
# If replying to a comment, validate that comment exists
|
|
85
|
+
if reply_to is not None:
|
|
86
|
+
if not any(c.id == reply_to for c in issue.comments):
|
|
87
|
+
console.print(
|
|
88
|
+
f"❌ Cannot find comment {reply_to} to reply to",
|
|
89
|
+
style="red",
|
|
90
|
+
)
|
|
91
|
+
return
|
|
92
|
+
|
|
93
|
+
# Add comment to issue
|
|
94
|
+
issue.comments.append(comment)
|
|
95
|
+
|
|
96
|
+
# Update the issue
|
|
97
|
+
try:
|
|
98
|
+
core.issues.update(issue.id, {"comments": issue.comments})
|
|
99
|
+
console.print(
|
|
100
|
+
f"✅ Comment added to issue {issue.id}",
|
|
101
|
+
style="green",
|
|
102
|
+
)
|
|
103
|
+
console.print(f" By: {comment.author}")
|
|
104
|
+
console.print(f" ID: {comment.id}")
|
|
105
|
+
if reply_to:
|
|
106
|
+
console.print(f" Replying to: {reply_to}")
|
|
107
|
+
except Exception as e:
|
|
108
|
+
console.print(f"❌ Failed to update issue: {e}", style="red")
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
@click.command("list")
|
|
112
|
+
@click.argument("issue_id")
|
|
113
|
+
@click.option(
|
|
114
|
+
"--format",
|
|
115
|
+
"-f",
|
|
116
|
+
type=click.Choice(["text", "json"]),
|
|
117
|
+
default="text",
|
|
118
|
+
help="Output format",
|
|
119
|
+
)
|
|
120
|
+
@click.pass_context
|
|
121
|
+
@require_initialized
|
|
122
|
+
def list_comments(ctx: click.Context, issue_id: str, format: str):
|
|
123
|
+
"""List comments on an issue.
|
|
124
|
+
|
|
125
|
+
Example:
|
|
126
|
+
roadmap issue comment list abc123
|
|
127
|
+
roadmap issue comment list abc123 --format json
|
|
128
|
+
"""
|
|
129
|
+
core = ctx.obj["core"]
|
|
130
|
+
console = get_console()
|
|
131
|
+
|
|
132
|
+
# Ensure issue exists
|
|
133
|
+
issue = ensure_entity_exists(core, "issue", issue_id)
|
|
134
|
+
|
|
135
|
+
if not issue.comments:
|
|
136
|
+
console.print("[dim]No comments yet[/dim]")
|
|
137
|
+
return
|
|
138
|
+
|
|
139
|
+
if format == "json":
|
|
140
|
+
# Convert comments to JSON-serializable format
|
|
141
|
+
comments_data = []
|
|
142
|
+
for comment in issue.comments:
|
|
143
|
+
comments_data.append(
|
|
144
|
+
{
|
|
145
|
+
"id": comment.id,
|
|
146
|
+
"author": comment.author,
|
|
147
|
+
"body": comment.body,
|
|
148
|
+
"created_at": comment.created_at.isoformat(),
|
|
149
|
+
"updated_at": comment.updated_at.isoformat(),
|
|
150
|
+
"in_reply_to": comment.in_reply_to,
|
|
151
|
+
}
|
|
152
|
+
)
|
|
153
|
+
console.print_json(data=comments_data)
|
|
154
|
+
else:
|
|
155
|
+
# Text format - show comments in thread view
|
|
156
|
+
threads = CommentService.build_comment_threads(issue.comments)
|
|
157
|
+
|
|
158
|
+
# Show top-level comments and their replies
|
|
159
|
+
top_level_ids = [k for k in threads.keys() if k is not None]
|
|
160
|
+
for top_level_id in sorted(top_level_ids):
|
|
161
|
+
for comment in threads.get(top_level_id, []):
|
|
162
|
+
console.print(
|
|
163
|
+
CommentService.format_comment_for_display(comment, indent=0)
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
# Show replies to this comment
|
|
167
|
+
if comment.id in threads:
|
|
168
|
+
for reply in threads[comment.id]:
|
|
169
|
+
console.print(
|
|
170
|
+
CommentService.format_comment_for_display(reply, indent=1)
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
console.print() # Blank line between threads
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
# Register commands with group
|
|
177
|
+
comment_group.add_command(add_comment, name="add")
|
|
178
|
+
comment_group.add_command(list_comments, name="list")
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
"""Create issue command."""
|
|
2
|
+
|
|
3
|
+
import click
|
|
4
|
+
|
|
5
|
+
from roadmap.adapters.cli.cli_command_helpers import require_initialized
|
|
6
|
+
from roadmap.adapters.cli.crud import BaseCreate, EntityType
|
|
7
|
+
from roadmap.adapters.cli.crud.entity_builders import IssueBuilder
|
|
8
|
+
from roadmap.common.logging import (
|
|
9
|
+
log_command,
|
|
10
|
+
verbose_output,
|
|
11
|
+
)
|
|
12
|
+
from roadmap.common.models import IssueCreateParams, IssueGitParams
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class IssueCreate(BaseCreate):
|
|
16
|
+
"""Create issue command implementation."""
|
|
17
|
+
|
|
18
|
+
entity_type = EntityType.ISSUE
|
|
19
|
+
|
|
20
|
+
def build_entity_dict(self, title: str, **kwargs) -> dict:
|
|
21
|
+
"""Build entity dictionary for issue creation."""
|
|
22
|
+
from roadmap.core.services import IssueCreationService
|
|
23
|
+
|
|
24
|
+
# Resolve and validate assignee (with auto-detection from Git)
|
|
25
|
+
assignee = kwargs.get("assignee")
|
|
26
|
+
service = IssueCreationService(self.core)
|
|
27
|
+
assignee = service.resolve_and_validate_assignee(assignee, auto_detect=True)
|
|
28
|
+
|
|
29
|
+
return IssueBuilder.build_create_dict(
|
|
30
|
+
title=title,
|
|
31
|
+
priority=kwargs.get("priority"),
|
|
32
|
+
issue_type=kwargs.get("issue_type"),
|
|
33
|
+
milestone=kwargs.get("milestone"),
|
|
34
|
+
assignee=assignee,
|
|
35
|
+
labels=list(kwargs.get("labels", [])),
|
|
36
|
+
estimate=kwargs.get("estimate"),
|
|
37
|
+
depends_on=list(kwargs.get("depends_on", [])),
|
|
38
|
+
blocks=list(kwargs.get("blocks", [])),
|
|
39
|
+
description=kwargs.get("content"),
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
def _display_success(self, entity) -> None:
|
|
43
|
+
"""Display success message using presenter."""
|
|
44
|
+
# Use detailed service display which includes assignee, priority, etc.
|
|
45
|
+
from roadmap.core.services import IssueCreationService
|
|
46
|
+
|
|
47
|
+
service = IssueCreationService(self.core)
|
|
48
|
+
service.format_created_issue_display(
|
|
49
|
+
entity, milestone=getattr(entity, "milestone", None)
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
def post_create_hook(self, entity, **kwargs) -> None:
|
|
53
|
+
"""Handle post-creation tasks like Git branch creation."""
|
|
54
|
+
if kwargs.get("git_branch"):
|
|
55
|
+
from roadmap.core.services import IssueCreationService
|
|
56
|
+
|
|
57
|
+
service = IssueCreationService(self.core)
|
|
58
|
+
service.create_branch_for_issue(
|
|
59
|
+
entity,
|
|
60
|
+
kwargs.get("branch_name"),
|
|
61
|
+
kwargs.get("checkout", True),
|
|
62
|
+
kwargs.get("force", False),
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@click.command("create")
|
|
67
|
+
@click.option("--title", required=True, help="Issue title")
|
|
68
|
+
@click.option(
|
|
69
|
+
"--priority",
|
|
70
|
+
"-p",
|
|
71
|
+
type=click.Choice(["critical", "high", "medium", "low"]),
|
|
72
|
+
default="medium",
|
|
73
|
+
help="Issue priority",
|
|
74
|
+
)
|
|
75
|
+
@click.option(
|
|
76
|
+
"--type",
|
|
77
|
+
"-t",
|
|
78
|
+
"issue_type",
|
|
79
|
+
type=click.Choice(["feature", "bug", "other"]),
|
|
80
|
+
default="other",
|
|
81
|
+
help="Issue type",
|
|
82
|
+
)
|
|
83
|
+
@click.option("--milestone", "-m", help="Assign to milestone")
|
|
84
|
+
@click.option("--assignee", "-a", help="Assign to team member")
|
|
85
|
+
@click.option("--labels", "-l", multiple=True, help="Add labels")
|
|
86
|
+
@click.option(
|
|
87
|
+
"--estimate", "-e", type=float, help="Estimated time to complete (in hours)"
|
|
88
|
+
)
|
|
89
|
+
@click.option("--depends-on", multiple=True, help="Issue IDs this depends on")
|
|
90
|
+
@click.option("--blocks", multiple=True, help="Issue IDs this blocks")
|
|
91
|
+
@click.option("--content", "-d", help="Markdown content/description of the issue")
|
|
92
|
+
@click.option("--git-branch", is_flag=True, help="Create a Git branch for this issue")
|
|
93
|
+
@click.option(
|
|
94
|
+
"--checkout/--no-checkout",
|
|
95
|
+
default=True,
|
|
96
|
+
help="Checkout the branch after creation (with --git-branch)",
|
|
97
|
+
)
|
|
98
|
+
@click.option("--branch-name", default=None, help="Override suggested branch name")
|
|
99
|
+
@click.option(
|
|
100
|
+
"--force", is_flag=True, help="Force branch creation even if working tree is dirty"
|
|
101
|
+
)
|
|
102
|
+
@click.pass_context
|
|
103
|
+
@verbose_output
|
|
104
|
+
@log_command("issue_create", entity_type="issue", track_duration=True)
|
|
105
|
+
@require_initialized
|
|
106
|
+
def create_issue(
|
|
107
|
+
ctx: click.Context,
|
|
108
|
+
title: str,
|
|
109
|
+
priority: str,
|
|
110
|
+
issue_type: str,
|
|
111
|
+
milestone: str,
|
|
112
|
+
assignee: str,
|
|
113
|
+
labels: tuple,
|
|
114
|
+
estimate: float,
|
|
115
|
+
depends_on: tuple,
|
|
116
|
+
blocks: tuple,
|
|
117
|
+
content: str,
|
|
118
|
+
git_branch: bool,
|
|
119
|
+
checkout: bool,
|
|
120
|
+
branch_name: str,
|
|
121
|
+
force: bool,
|
|
122
|
+
):
|
|
123
|
+
"""Create a new issue."""
|
|
124
|
+
core = ctx.obj["core"]
|
|
125
|
+
creator = IssueCreate(core)
|
|
126
|
+
|
|
127
|
+
# Create structured parameter objects
|
|
128
|
+
issue_params = IssueCreateParams(
|
|
129
|
+
title=title,
|
|
130
|
+
priority=priority,
|
|
131
|
+
issue_type=issue_type,
|
|
132
|
+
milestone=milestone,
|
|
133
|
+
assignee=assignee,
|
|
134
|
+
labels=labels,
|
|
135
|
+
estimate=estimate,
|
|
136
|
+
depends_on=depends_on,
|
|
137
|
+
blocks=blocks,
|
|
138
|
+
content=content,
|
|
139
|
+
)
|
|
140
|
+
git_params = IssueGitParams(
|
|
141
|
+
git_branch=git_branch,
|
|
142
|
+
checkout=checkout,
|
|
143
|
+
branch_name=branch_name,
|
|
144
|
+
force=force,
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
creator.execute(
|
|
148
|
+
title=issue_params.title,
|
|
149
|
+
priority=issue_params.priority,
|
|
150
|
+
issue_type=issue_params.issue_type,
|
|
151
|
+
milestone=issue_params.milestone,
|
|
152
|
+
assignee=issue_params.assignee,
|
|
153
|
+
labels=issue_params.labels,
|
|
154
|
+
estimate=issue_params.estimate,
|
|
155
|
+
depends_on=issue_params.depends_on,
|
|
156
|
+
blocks=issue_params.blocks,
|
|
157
|
+
content=issue_params.content,
|
|
158
|
+
git_branch=git_params.git_branch,
|
|
159
|
+
checkout=git_params.checkout,
|
|
160
|
+
branch_name=git_params.branch_name,
|
|
161
|
+
force=git_params.force,
|
|
162
|
+
)
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"""Delete issue command."""
|
|
2
|
+
|
|
3
|
+
import click
|
|
4
|
+
|
|
5
|
+
from roadmap.adapters.cli.cli_command_helpers import require_initialized
|
|
6
|
+
from roadmap.adapters.cli.crud import BaseDelete, EntityType
|
|
7
|
+
from roadmap.common.logging import log_command
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class IssueDelete(BaseDelete):
|
|
11
|
+
"""Delete issue command implementation."""
|
|
12
|
+
|
|
13
|
+
entity_type = EntityType.ISSUE
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@click.command("delete")
|
|
17
|
+
@click.argument("issue_id")
|
|
18
|
+
@click.option("--yes", "-y", is_flag=True, help="Skip confirmation prompt")
|
|
19
|
+
@click.pass_context
|
|
20
|
+
@log_command("issue_delete", entity_type="issue", track_duration=True)
|
|
21
|
+
@require_initialized
|
|
22
|
+
def delete_issue(
|
|
23
|
+
ctx: click.Context,
|
|
24
|
+
issue_id: str,
|
|
25
|
+
yes: bool,
|
|
26
|
+
):
|
|
27
|
+
"""Delete an issue."""
|
|
28
|
+
core = ctx.obj["core"]
|
|
29
|
+
deleter = IssueDelete(core)
|
|
30
|
+
|
|
31
|
+
deleter.execute(entity_id=issue_id, force=yes)
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
"""Dependencies command group."""
|
|
2
|
+
|
|
3
|
+
import click
|
|
4
|
+
|
|
5
|
+
from roadmap.adapters.cli.cli_command_helpers import (
|
|
6
|
+
ensure_entity_exists,
|
|
7
|
+
require_initialized,
|
|
8
|
+
)
|
|
9
|
+
from roadmap.adapters.cli.cli_error_handlers import handle_cli_error
|
|
10
|
+
from roadmap.common.console import get_console
|
|
11
|
+
|
|
12
|
+
console = get_console()
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@click.group("deps")
|
|
16
|
+
def deps():
|
|
17
|
+
"""Manage issue dependencies."""
|
|
18
|
+
pass
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@deps.command("add")
|
|
22
|
+
@click.argument("issue_id")
|
|
23
|
+
@click.argument("dependency_id")
|
|
24
|
+
@click.pass_context
|
|
25
|
+
@require_initialized
|
|
26
|
+
def add_dependency(ctx: click.Context, issue_id: str, dependency_id: str):
|
|
27
|
+
"""Add a dependency to an issue."""
|
|
28
|
+
core = ctx.obj["core"]
|
|
29
|
+
|
|
30
|
+
try:
|
|
31
|
+
# Get the issue
|
|
32
|
+
issue = ensure_entity_exists(core, "issue", issue_id)
|
|
33
|
+
|
|
34
|
+
# Check if dependency issue exists
|
|
35
|
+
dependency_issue = ensure_entity_exists(core, "issue", dependency_id)
|
|
36
|
+
|
|
37
|
+
# Add dependency
|
|
38
|
+
current_deps = issue.depends_on or []
|
|
39
|
+
if dependency_id not in current_deps:
|
|
40
|
+
current_deps.append(dependency_id)
|
|
41
|
+
core.issues.update(issue_id, depends_on=current_deps)
|
|
42
|
+
console.print(
|
|
43
|
+
f"✅ Added dependency: {dependency_issue.title}", style="bold green"
|
|
44
|
+
)
|
|
45
|
+
console.print(
|
|
46
|
+
f" {issue.title} now depends on {dependency_issue.title}", style="dim"
|
|
47
|
+
)
|
|
48
|
+
else:
|
|
49
|
+
console.print("⚠️ Dependency already exists", style="yellow")
|
|
50
|
+
|
|
51
|
+
except Exception as e:
|
|
52
|
+
handle_cli_error(
|
|
53
|
+
error=e,
|
|
54
|
+
operation="add_dependency",
|
|
55
|
+
entity_type="issue",
|
|
56
|
+
entity_id=issue_id,
|
|
57
|
+
context={"dependency_id": dependency_id},
|
|
58
|
+
fatal=True,
|
|
59
|
+
)
|
|
60
|
+
console.print(f"❌ Failed to add dependency: {e}", style="bold red")
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
@deps.command("remove")
|
|
64
|
+
@click.argument("issue_id")
|
|
65
|
+
@click.argument("dependency_id")
|
|
66
|
+
@click.pass_context
|
|
67
|
+
@require_initialized
|
|
68
|
+
def remove_dependency(ctx: click.Context, issue_id: str, dependency_id: str):
|
|
69
|
+
"""Remove a dependency from an issue."""
|
|
70
|
+
core = ctx.obj["core"]
|
|
71
|
+
|
|
72
|
+
try:
|
|
73
|
+
issue = ensure_entity_exists(core, "issue", issue_id)
|
|
74
|
+
|
|
75
|
+
current_deps = list(issue.depends_on or [])
|
|
76
|
+
if dependency_id not in current_deps:
|
|
77
|
+
console.print(
|
|
78
|
+
f"⚠️ Dependency '{dependency_id}' not found on issue {issue_id}",
|
|
79
|
+
style="yellow",
|
|
80
|
+
)
|
|
81
|
+
return
|
|
82
|
+
|
|
83
|
+
updated_deps = [dep for dep in current_deps if dep != dependency_id]
|
|
84
|
+
core.issues.update(issue_id, depends_on=updated_deps)
|
|
85
|
+
|
|
86
|
+
console.print(
|
|
87
|
+
f"✅ Removed dependency: {dependency_id}",
|
|
88
|
+
style="bold green",
|
|
89
|
+
)
|
|
90
|
+
console.print(
|
|
91
|
+
f" {issue.title} no longer depends on {dependency_id}", style="dim"
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
except Exception as e:
|
|
95
|
+
handle_cli_error(
|
|
96
|
+
error=e,
|
|
97
|
+
operation="remove_dependency",
|
|
98
|
+
entity_type="issue",
|
|
99
|
+
entity_id=issue_id,
|
|
100
|
+
context={"dependency_id": dependency_id},
|
|
101
|
+
fatal=True,
|
|
102
|
+
)
|
|
103
|
+
console.print(f"❌ Failed to remove dependency: {e}", style="bold red")
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
@deps.command("update")
|
|
107
|
+
@click.argument("issue_id")
|
|
108
|
+
@click.argument("old_dependency_id")
|
|
109
|
+
@click.argument("new_dependency_id")
|
|
110
|
+
@click.pass_context
|
|
111
|
+
@require_initialized
|
|
112
|
+
def update_dependency(
|
|
113
|
+
ctx: click.Context,
|
|
114
|
+
issue_id: str,
|
|
115
|
+
old_dependency_id: str,
|
|
116
|
+
new_dependency_id: str,
|
|
117
|
+
):
|
|
118
|
+
"""Replace one dependency with another on an issue."""
|
|
119
|
+
core = ctx.obj["core"]
|
|
120
|
+
|
|
121
|
+
try:
|
|
122
|
+
issue = ensure_entity_exists(core, "issue", issue_id)
|
|
123
|
+
ensure_entity_exists(core, "issue", new_dependency_id)
|
|
124
|
+
|
|
125
|
+
current_deps = list(issue.depends_on or [])
|
|
126
|
+
if old_dependency_id not in current_deps:
|
|
127
|
+
console.print(
|
|
128
|
+
(f"⚠️ Dependency '{old_dependency_id}' not found on issue {issue_id}"),
|
|
129
|
+
style="yellow",
|
|
130
|
+
)
|
|
131
|
+
return
|
|
132
|
+
|
|
133
|
+
if new_dependency_id in current_deps and new_dependency_id != old_dependency_id:
|
|
134
|
+
updated_deps = [dep for dep in current_deps if dep != old_dependency_id]
|
|
135
|
+
else:
|
|
136
|
+
updated_deps = [
|
|
137
|
+
(new_dependency_id if dep == old_dependency_id else dep)
|
|
138
|
+
for dep in current_deps
|
|
139
|
+
]
|
|
140
|
+
|
|
141
|
+
core.issues.update(issue_id, depends_on=updated_deps)
|
|
142
|
+
|
|
143
|
+
console.print(
|
|
144
|
+
f"✅ Updated dependency: {old_dependency_id} → {new_dependency_id}",
|
|
145
|
+
style="bold green",
|
|
146
|
+
)
|
|
147
|
+
console.print(f" {issue.title} dependency updated successfully", style="dim")
|
|
148
|
+
|
|
149
|
+
except Exception as e:
|
|
150
|
+
handle_cli_error(
|
|
151
|
+
error=e,
|
|
152
|
+
operation="update_dependency",
|
|
153
|
+
entity_type="issue",
|
|
154
|
+
entity_id=issue_id,
|
|
155
|
+
context={
|
|
156
|
+
"old_dependency_id": old_dependency_id,
|
|
157
|
+
"new_dependency_id": new_dependency_id,
|
|
158
|
+
},
|
|
159
|
+
fatal=True,
|
|
160
|
+
)
|
|
161
|
+
console.print(f"❌ Failed to update dependency: {e}", style="bold red")
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
"""Generic helper for status change commands (block, unblock, close, start, etc.).
|
|
2
|
+
|
|
3
|
+
This module provides a unified pattern for syntactic sugar commands that update
|
|
4
|
+
issue status. Each command defines a StatusChangeConfig and calls apply_status_change()
|
|
5
|
+
to handle validation, updates, and user feedback consistently.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from collections.abc import Callable
|
|
9
|
+
from dataclasses import dataclass
|
|
10
|
+
from typing import TYPE_CHECKING, Any
|
|
11
|
+
|
|
12
|
+
import click
|
|
13
|
+
|
|
14
|
+
from roadmap.common.console import get_console
|
|
15
|
+
from roadmap.common.formatters.text.operations import (
|
|
16
|
+
format_operation_failure,
|
|
17
|
+
format_operation_success,
|
|
18
|
+
)
|
|
19
|
+
from roadmap.core.domain import Status
|
|
20
|
+
|
|
21
|
+
if TYPE_CHECKING:
|
|
22
|
+
from roadmap.infrastructure.coordination.core import RoadmapCore # noqa: F401
|
|
23
|
+
|
|
24
|
+
console = get_console()
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@dataclass
|
|
28
|
+
class StatusChangeConfig:
|
|
29
|
+
"""Configuration for a status change command.
|
|
30
|
+
|
|
31
|
+
Attributes:
|
|
32
|
+
status: Target Status value
|
|
33
|
+
emoji: Emoji to display in success message
|
|
34
|
+
title_verb: Verb describing the action ("Blocked", "Unblocked", "Closed")
|
|
35
|
+
title_style: Rich style for title line ("bold red", "bold green", etc.)
|
|
36
|
+
status_display: String to show in status output ("🚫 Blocked", "🔄 In Progress", etc.)
|
|
37
|
+
pre_check: Optional validation function that returns (is_valid: bool, error_msg: str | None)
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
status: Status
|
|
41
|
+
emoji: str
|
|
42
|
+
title_verb: str
|
|
43
|
+
title_style: str
|
|
44
|
+
status_display: str
|
|
45
|
+
pre_check: Callable[[Any], tuple[bool, str | None]] | None = None
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def apply_status_change(
|
|
49
|
+
core: "RoadmapCore",
|
|
50
|
+
issue_id: str,
|
|
51
|
+
config: StatusChangeConfig,
|
|
52
|
+
reason: str | None = None,
|
|
53
|
+
) -> None:
|
|
54
|
+
"""Apply a status change to an issue with consistent feedback.
|
|
55
|
+
|
|
56
|
+
This function:
|
|
57
|
+
1. Ensures the issue exists
|
|
58
|
+
2. Runs optional pre-checks (e.g., "is issue already blocked?")
|
|
59
|
+
3. Updates the issue status
|
|
60
|
+
4. Displays consistent success/failure feedback
|
|
61
|
+
5. Raises click.Abort on failure
|
|
62
|
+
|
|
63
|
+
Args:
|
|
64
|
+
core: Core application object with issues manager
|
|
65
|
+
issue_id: ID of issue to update
|
|
66
|
+
config: StatusChangeConfig defining behavior
|
|
67
|
+
reason: Optional reason for the change (displayed in feedback)
|
|
68
|
+
|
|
69
|
+
Raises:
|
|
70
|
+
click.Abort: If issue doesn't exist or update fails
|
|
71
|
+
"""
|
|
72
|
+
from roadmap.adapters.cli.cli_command_helpers import ensure_entity_exists
|
|
73
|
+
|
|
74
|
+
# Ensure issue exists
|
|
75
|
+
issue = ensure_entity_exists(core, "issue", issue_id)
|
|
76
|
+
|
|
77
|
+
# Run pre-check if configured (e.g., "is issue already blocked?")
|
|
78
|
+
if config.pre_check:
|
|
79
|
+
is_valid, error_msg = config.pre_check(issue)
|
|
80
|
+
if not is_valid:
|
|
81
|
+
console.print(f"[yellow]⚠️ {error_msg}[/yellow]")
|
|
82
|
+
return
|
|
83
|
+
|
|
84
|
+
# Update status
|
|
85
|
+
updated = core.issues.update(issue_id, status=config.status)
|
|
86
|
+
|
|
87
|
+
if updated:
|
|
88
|
+
# Build extra details for display
|
|
89
|
+
extra_details = {"Status": config.status_display}
|
|
90
|
+
|
|
91
|
+
# Use formatter for consistent output
|
|
92
|
+
lines = format_operation_success(
|
|
93
|
+
emoji=config.emoji,
|
|
94
|
+
action=config.title_verb,
|
|
95
|
+
entity_title=updated.title,
|
|
96
|
+
entity_id=issue_id,
|
|
97
|
+
reason=reason,
|
|
98
|
+
extra_details=extra_details,
|
|
99
|
+
)
|
|
100
|
+
for line in lines:
|
|
101
|
+
console.print(line, style=config.title_style)
|
|
102
|
+
else:
|
|
103
|
+
# Failure feedback
|
|
104
|
+
lines = format_operation_failure(
|
|
105
|
+
action=config.title_verb.lower(),
|
|
106
|
+
entity_id=issue_id,
|
|
107
|
+
error="Failed to update status",
|
|
108
|
+
)
|
|
109
|
+
for line in lines:
|
|
110
|
+
console.print(line, style="bold red")
|
|
111
|
+
raise click.Abort()
|