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,157 @@
|
|
|
1
|
+
"""View project command."""
|
|
2
|
+
|
|
3
|
+
import click
|
|
4
|
+
|
|
5
|
+
from roadmap.adapters.cli.cli_command_helpers import require_initialized
|
|
6
|
+
from roadmap.adapters.cli.mappers import ProjectMapper
|
|
7
|
+
from roadmap.adapters.cli.presentation.project_presenter import ProjectPresenter
|
|
8
|
+
from roadmap.common.console import get_console
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def _extract_description_and_objectives(content):
|
|
12
|
+
"""Extract description and objectives from project content."""
|
|
13
|
+
if not content:
|
|
14
|
+
return None, None
|
|
15
|
+
|
|
16
|
+
content_lines = content.split("\n")
|
|
17
|
+
description_lines = []
|
|
18
|
+
objectives_lines = []
|
|
19
|
+
in_objectives = False
|
|
20
|
+
|
|
21
|
+
for line in content_lines:
|
|
22
|
+
if "## Objectives" in line or "## objectives" in line.lower():
|
|
23
|
+
in_objectives = True
|
|
24
|
+
continue
|
|
25
|
+
elif in_objectives and line.startswith("## "):
|
|
26
|
+
in_objectives = False
|
|
27
|
+
|
|
28
|
+
if in_objectives:
|
|
29
|
+
objectives_lines.append(line)
|
|
30
|
+
elif not line.startswith("## "):
|
|
31
|
+
description_lines.append(line)
|
|
32
|
+
|
|
33
|
+
description = "\n".join(description_lines).strip() or None
|
|
34
|
+
objectives = "\n".join(objectives_lines).strip() or None
|
|
35
|
+
|
|
36
|
+
return description, objectives
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _get_project_or_exit(ctx: click.Context, project_id: str):
|
|
40
|
+
"""Fetch project by ID or exit with a user-facing error message."""
|
|
41
|
+
core = ctx.obj["core"]
|
|
42
|
+
project = core.projects.get(project_id)
|
|
43
|
+
if project:
|
|
44
|
+
return project
|
|
45
|
+
|
|
46
|
+
get_console().print(f"❌ Project '{project_id}' not found.", style="bold red")
|
|
47
|
+
get_console().print(
|
|
48
|
+
"\n💡 Tip: Use 'roadmap project list' to see all available projects.",
|
|
49
|
+
style="dim",
|
|
50
|
+
)
|
|
51
|
+
ctx.exit(1)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def _build_project_milestone_data(core, project) -> tuple[list | None, dict | None]:
|
|
55
|
+
"""Build milestone list and progress map for a project."""
|
|
56
|
+
if not project.milestones:
|
|
57
|
+
return None, None
|
|
58
|
+
|
|
59
|
+
all_milestones = core.milestones.list()
|
|
60
|
+
milestones = [m for m in all_milestones if m.name in project.milestones]
|
|
61
|
+
milestone_progress = {
|
|
62
|
+
milestone.name: core.milestones.get_progress(milestone.name)
|
|
63
|
+
for milestone in milestones
|
|
64
|
+
}
|
|
65
|
+
return milestones, milestone_progress
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def _build_effort_data(project) -> dict[str, float | int | None] | None:
|
|
69
|
+
"""Build effort payload for presenter when estimate/actual values exist."""
|
|
70
|
+
if not (project.estimated_hours or project.actual_hours):
|
|
71
|
+
return None
|
|
72
|
+
return {
|
|
73
|
+
"estimated": project.estimated_hours,
|
|
74
|
+
"actual": project.actual_hours,
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def _build_comments_text(project) -> str | None:
|
|
79
|
+
"""Build formatted comments text with threaded replies for display."""
|
|
80
|
+
if not project.comments:
|
|
81
|
+
return None
|
|
82
|
+
|
|
83
|
+
from roadmap.core.services.comment.comment_service import CommentService
|
|
84
|
+
|
|
85
|
+
threads = CommentService.build_comment_threads(project.comments)
|
|
86
|
+
chunks: list[str] = []
|
|
87
|
+
|
|
88
|
+
top_level_ids = sorted([key for key in threads.keys() if key is not None])
|
|
89
|
+
for top_level_id in top_level_ids:
|
|
90
|
+
for comment in threads.get(top_level_id, []):
|
|
91
|
+
chunks.append(CommentService.format_comment_for_display(comment, indent=0))
|
|
92
|
+
for reply in threads.get(comment.id, []):
|
|
93
|
+
chunks.append(
|
|
94
|
+
CommentService.format_comment_for_display(reply, indent=1)
|
|
95
|
+
)
|
|
96
|
+
chunks.append("")
|
|
97
|
+
|
|
98
|
+
return "\n".join(chunks).rstrip() if chunks else None
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def _render_objectives_panel(objectives: str | None) -> None:
|
|
102
|
+
"""Render objectives markdown panel when objectives are available."""
|
|
103
|
+
if not objectives:
|
|
104
|
+
return
|
|
105
|
+
|
|
106
|
+
from rich.markdown import Markdown
|
|
107
|
+
from rich.panel import Panel
|
|
108
|
+
|
|
109
|
+
get_console().print(
|
|
110
|
+
Panel(Markdown(objectives), title="✅ Objectives", border_style="green")
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
@click.command("view")
|
|
115
|
+
@click.argument("project_id")
|
|
116
|
+
@click.pass_context
|
|
117
|
+
@require_initialized
|
|
118
|
+
def view_project(ctx: click.Context, project_id: str):
|
|
119
|
+
"""Display detailed information about a specific project.
|
|
120
|
+
|
|
121
|
+
Shows complete project details including metadata, milestones,
|
|
122
|
+
objectives, and description in a formatted view.
|
|
123
|
+
|
|
124
|
+
Example:
|
|
125
|
+
roadmap project view abc123def
|
|
126
|
+
"""
|
|
127
|
+
core = ctx.obj["core"]
|
|
128
|
+
project = _get_project_or_exit(ctx, project_id)
|
|
129
|
+
|
|
130
|
+
# Convert project to DTO
|
|
131
|
+
project_dto = ProjectMapper.domain_to_dto(project)
|
|
132
|
+
|
|
133
|
+
# Get milestones for display
|
|
134
|
+
milestones, milestone_progress = _build_project_milestone_data(core, project)
|
|
135
|
+
|
|
136
|
+
# Build effort data
|
|
137
|
+
effort_data = _build_effort_data(project)
|
|
138
|
+
|
|
139
|
+
# Extract description and objectives
|
|
140
|
+
description, objectives = _extract_description_and_objectives(project.content)
|
|
141
|
+
description_content = description
|
|
142
|
+
|
|
143
|
+
# Prepare comments if any
|
|
144
|
+
comments_text = _build_comments_text(project)
|
|
145
|
+
|
|
146
|
+
# Render using presenter
|
|
147
|
+
presenter = ProjectPresenter()
|
|
148
|
+
presenter.render(
|
|
149
|
+
project_dto,
|
|
150
|
+
milestones=milestones,
|
|
151
|
+
milestone_progress=milestone_progress,
|
|
152
|
+
description_content=description_content,
|
|
153
|
+
comments_text=comments_text,
|
|
154
|
+
effort_data=effort_data,
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
_render_objectives_panel(objectives)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""CLI services package."""
|
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
"""Daily summary service for the 'today' command.
|
|
2
|
+
|
|
3
|
+
Provides business logic for generating daily workflow summaries,
|
|
4
|
+
including user identification, milestone selection, issue categorization,
|
|
5
|
+
and data aggregation for display.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import os
|
|
9
|
+
from datetime import UTC, datetime
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
from roadmap.common.constants import MilestoneStatus, Status
|
|
13
|
+
from roadmap.common.logging import get_logger
|
|
14
|
+
|
|
15
|
+
logger = get_logger(__name__)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class DailySummaryService:
|
|
19
|
+
"""Service for generating daily workflow summaries.
|
|
20
|
+
|
|
21
|
+
Handles all business logic for the 'today' command, including:
|
|
22
|
+
- User identity resolution from config or environment
|
|
23
|
+
- Upcoming milestone selection
|
|
24
|
+
- Issue categorization and filtering
|
|
25
|
+
- Data aggregation for display
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
def __init__(self, core):
|
|
29
|
+
"""Initialize daily summary service.
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
core: RoadmapCore instance for accessing data
|
|
33
|
+
"""
|
|
34
|
+
self.core = core
|
|
35
|
+
|
|
36
|
+
def get_current_user(self) -> str | None:
|
|
37
|
+
"""Get current user from config or environment.
|
|
38
|
+
|
|
39
|
+
Returns:
|
|
40
|
+
Current user name or None if not found
|
|
41
|
+
"""
|
|
42
|
+
# Get user identity from config (single source of truth)
|
|
43
|
+
current_user = self.core.team.get_current_user()
|
|
44
|
+
|
|
45
|
+
if not current_user:
|
|
46
|
+
# Fallback to environment for testing
|
|
47
|
+
current_user = os.getenv("ROADMAP_USER")
|
|
48
|
+
|
|
49
|
+
return current_user
|
|
50
|
+
|
|
51
|
+
def get_upcoming_milestone(self) -> Any | None:
|
|
52
|
+
"""Get the next upcoming milestone.
|
|
53
|
+
|
|
54
|
+
Filters open milestones and returns the one with the earliest due date.
|
|
55
|
+
|
|
56
|
+
Returns:
|
|
57
|
+
Milestone object or None if no open milestones exist
|
|
58
|
+
"""
|
|
59
|
+
milestones = self.core.milestones.list()
|
|
60
|
+
|
|
61
|
+
# Filter open milestones and sort by due date
|
|
62
|
+
open_milestones = [m for m in milestones if m.status == MilestoneStatus.OPEN]
|
|
63
|
+
|
|
64
|
+
if not open_milestones:
|
|
65
|
+
return None
|
|
66
|
+
|
|
67
|
+
# Sort by due date (None dates last), get the first with a due date
|
|
68
|
+
sorted_milestones = sorted(
|
|
69
|
+
open_milestones,
|
|
70
|
+
key=lambda m: (m.due_date is None, m.due_date or datetime.max),
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
return sorted_milestones[0] if sorted_milestones else None
|
|
74
|
+
|
|
75
|
+
def _get_in_progress_issues(self, issues: list) -> list:
|
|
76
|
+
"""Get issues currently in progress.
|
|
77
|
+
|
|
78
|
+
Args:
|
|
79
|
+
issues: List of issues to filter
|
|
80
|
+
|
|
81
|
+
Returns:
|
|
82
|
+
List of in-progress issues
|
|
83
|
+
"""
|
|
84
|
+
return [i for i in issues if i.status == Status.IN_PROGRESS]
|
|
85
|
+
|
|
86
|
+
def _get_overdue_issues(self, issues: list) -> list:
|
|
87
|
+
"""Get overdue issues that haven't been closed.
|
|
88
|
+
|
|
89
|
+
Args:
|
|
90
|
+
issues: List of issues to filter
|
|
91
|
+
|
|
92
|
+
Returns:
|
|
93
|
+
List of overdue issues
|
|
94
|
+
"""
|
|
95
|
+
overdue = []
|
|
96
|
+
now = datetime.now(UTC)
|
|
97
|
+
for i in issues:
|
|
98
|
+
if not i.due_date or i.status == Status.CLOSED:
|
|
99
|
+
continue
|
|
100
|
+
|
|
101
|
+
due = i.due_date
|
|
102
|
+
if isinstance(due, datetime):
|
|
103
|
+
if due.tzinfo:
|
|
104
|
+
due_aware = due.astimezone(UTC)
|
|
105
|
+
else:
|
|
106
|
+
due_aware = due.replace(tzinfo=UTC)
|
|
107
|
+
else:
|
|
108
|
+
due_aware = datetime.fromisoformat(str(due)).replace(tzinfo=UTC)
|
|
109
|
+
|
|
110
|
+
if due_aware < now:
|
|
111
|
+
overdue.append(i)
|
|
112
|
+
|
|
113
|
+
return overdue
|
|
114
|
+
|
|
115
|
+
def _get_blocked_issues(self, issues: list) -> list:
|
|
116
|
+
"""Get blocked issues.
|
|
117
|
+
|
|
118
|
+
Args:
|
|
119
|
+
issues: List of issues to filter
|
|
120
|
+
|
|
121
|
+
Returns:
|
|
122
|
+
List of blocked issues
|
|
123
|
+
"""
|
|
124
|
+
return [i for i in issues if i.status == Status.BLOCKED]
|
|
125
|
+
|
|
126
|
+
def _get_high_priority_todos(self, issues: list) -> list:
|
|
127
|
+
"""Get high-priority TODO issues (top 3).
|
|
128
|
+
|
|
129
|
+
Args:
|
|
130
|
+
issues: List of issues to filter
|
|
131
|
+
|
|
132
|
+
Returns:
|
|
133
|
+
List of up to 3 high-priority TODO issues
|
|
134
|
+
"""
|
|
135
|
+
candidates = [
|
|
136
|
+
i
|
|
137
|
+
for i in issues
|
|
138
|
+
if i.status == Status.TODO and i.priority.value in ["critical", "high"]
|
|
139
|
+
]
|
|
140
|
+
|
|
141
|
+
if not candidates:
|
|
142
|
+
return []
|
|
143
|
+
|
|
144
|
+
# Prioritize issues that unblock the largest downstream dependency chain.
|
|
145
|
+
# This helps "today" surface critical path blockers before standalone work.
|
|
146
|
+
active_issues = [i for i in issues if i.status != Status.CLOSED]
|
|
147
|
+
dependents_map: dict[str, list[str]] = {}
|
|
148
|
+
for issue in active_issues:
|
|
149
|
+
for dep_id in issue.depends_on or []:
|
|
150
|
+
dependents_map.setdefault(dep_id, []).append(issue.id)
|
|
151
|
+
|
|
152
|
+
depth_cache: dict[str, int] = {}
|
|
153
|
+
|
|
154
|
+
def dependency_depth(issue_id: str, visiting: set[str] | None = None) -> int:
|
|
155
|
+
if issue_id in depth_cache:
|
|
156
|
+
return depth_cache[issue_id]
|
|
157
|
+
|
|
158
|
+
visiting = visiting or set()
|
|
159
|
+
if issue_id in visiting:
|
|
160
|
+
# Cycle guard: don't recurse indefinitely; treat as no extra depth.
|
|
161
|
+
return 0
|
|
162
|
+
|
|
163
|
+
visiting.add(issue_id)
|
|
164
|
+
children = dependents_map.get(issue_id, [])
|
|
165
|
+
if not children:
|
|
166
|
+
depth = 0
|
|
167
|
+
else:
|
|
168
|
+
depth = 1 + max(dependency_depth(child, visiting) for child in children)
|
|
169
|
+
visiting.remove(issue_id)
|
|
170
|
+
|
|
171
|
+
depth_cache[issue_id] = depth
|
|
172
|
+
return depth
|
|
173
|
+
|
|
174
|
+
def sort_key(issue):
|
|
175
|
+
depth = dependency_depth(issue.id)
|
|
176
|
+
direct_dependents = len(dependents_map.get(issue.id, []))
|
|
177
|
+
critical_rank = 0 if issue.priority.value == "critical" else 1
|
|
178
|
+
return (-depth, -direct_dependents, critical_rank, issue.id)
|
|
179
|
+
|
|
180
|
+
return sorted(candidates, key=sort_key)[:3]
|
|
181
|
+
|
|
182
|
+
def _get_completed_today_issues(self, issues: list) -> list:
|
|
183
|
+
"""Get issues closed today.
|
|
184
|
+
|
|
185
|
+
Args:
|
|
186
|
+
issues: List of issues to filter
|
|
187
|
+
|
|
188
|
+
Returns:
|
|
189
|
+
List of issues closed today
|
|
190
|
+
"""
|
|
191
|
+
today = datetime.now(UTC).date()
|
|
192
|
+
return [
|
|
193
|
+
i
|
|
194
|
+
for i in issues
|
|
195
|
+
if i.status == Status.CLOSED
|
|
196
|
+
and i.actual_end_date
|
|
197
|
+
and i.actual_end_date.date() == today
|
|
198
|
+
]
|
|
199
|
+
|
|
200
|
+
def categorize_issues(self, issues: list, current_user: str) -> dict[str, list]:
|
|
201
|
+
"""Categorize issues by status and urgency.
|
|
202
|
+
|
|
203
|
+
Args:
|
|
204
|
+
issues: List of issues to categorize
|
|
205
|
+
current_user: Current user name for filtering
|
|
206
|
+
|
|
207
|
+
Returns:
|
|
208
|
+
Dictionary with keys: in_progress, overdue, blocked, todo_high_priority, completed_today
|
|
209
|
+
"""
|
|
210
|
+
return {
|
|
211
|
+
"in_progress": self._get_in_progress_issues(issues),
|
|
212
|
+
"overdue": self._get_overdue_issues(issues),
|
|
213
|
+
"blocked": self._get_blocked_issues(issues),
|
|
214
|
+
"todo_high_priority": self._get_high_priority_todos(issues),
|
|
215
|
+
"completed_today": self._get_completed_today_issues(issues),
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
def get_daily_summary_data(self) -> dict[str, Any]:
|
|
219
|
+
"""Get complete daily summary data.
|
|
220
|
+
|
|
221
|
+
Validates user and milestone, filters issues, categorizes them,
|
|
222
|
+
and returns all data needed for display.
|
|
223
|
+
|
|
224
|
+
Returns:
|
|
225
|
+
Dictionary with keys: current_user, milestone, issues (dict of categories)
|
|
226
|
+
|
|
227
|
+
Raises:
|
|
228
|
+
ValueError: If user not found or no upcoming milestones
|
|
229
|
+
"""
|
|
230
|
+
# Get user identity
|
|
231
|
+
current_user = self.get_current_user()
|
|
232
|
+
if not current_user:
|
|
233
|
+
raise ValueError(
|
|
234
|
+
"No user configured. Initialize with 'roadmap init' or set ROADMAP_USER."
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
logger.debug("Current user", user=current_user)
|
|
238
|
+
|
|
239
|
+
# Get upcoming milestone
|
|
240
|
+
upcoming_milestone = self.get_upcoming_milestone()
|
|
241
|
+
if not upcoming_milestone:
|
|
242
|
+
raise ValueError("No upcoming milestones found.")
|
|
243
|
+
|
|
244
|
+
logger.debug("Upcoming milestone", milestone=upcoming_milestone.name)
|
|
245
|
+
|
|
246
|
+
# Get all issues
|
|
247
|
+
all_issues = self.core.issues.list()
|
|
248
|
+
|
|
249
|
+
# Filter: assigned to current user AND in upcoming milestone
|
|
250
|
+
my_milestone_issues = [
|
|
251
|
+
i
|
|
252
|
+
for i in all_issues
|
|
253
|
+
if i.assignee == current_user and i.milestone == upcoming_milestone.name
|
|
254
|
+
]
|
|
255
|
+
|
|
256
|
+
if not my_milestone_issues:
|
|
257
|
+
logger.info(
|
|
258
|
+
"No issues assigned to user in upcoming milestone", user=current_user
|
|
259
|
+
)
|
|
260
|
+
return {
|
|
261
|
+
"current_user": current_user,
|
|
262
|
+
"milestone": upcoming_milestone,
|
|
263
|
+
"issues": {
|
|
264
|
+
"in_progress": [],
|
|
265
|
+
"overdue": [],
|
|
266
|
+
"blocked": [],
|
|
267
|
+
"todo_high_priority": [],
|
|
268
|
+
"completed_today": [],
|
|
269
|
+
},
|
|
270
|
+
"has_issues": False,
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
# Categorize issues
|
|
274
|
+
categorized = self.categorize_issues(my_milestone_issues, current_user)
|
|
275
|
+
|
|
276
|
+
logger.info(
|
|
277
|
+
"Daily summary data prepared",
|
|
278
|
+
user=current_user,
|
|
279
|
+
milestone=upcoming_milestone.name,
|
|
280
|
+
in_progress=len(categorized["in_progress"]),
|
|
281
|
+
overdue=len(categorized["overdue"]),
|
|
282
|
+
blocked=len(categorized["blocked"]),
|
|
283
|
+
todo_high_priority=len(categorized["todo_high_priority"]),
|
|
284
|
+
completed_today=len(categorized["completed_today"]),
|
|
285
|
+
)
|
|
286
|
+
|
|
287
|
+
return {
|
|
288
|
+
"current_user": current_user,
|
|
289
|
+
"milestone": upcoming_milestone,
|
|
290
|
+
"issues": categorized,
|
|
291
|
+
"has_issues": True,
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
def get_next_action_suggestion(self, data: dict[str, Any]) -> str | None:
|
|
295
|
+
"""Get helpful suggestion for next action based on data.
|
|
296
|
+
|
|
297
|
+
Args:
|
|
298
|
+
data: Dictionary from get_daily_summary_data()
|
|
299
|
+
|
|
300
|
+
Returns:
|
|
301
|
+
Suggestion string or None if no action recommended
|
|
302
|
+
"""
|
|
303
|
+
issues = data["issues"]
|
|
304
|
+
|
|
305
|
+
if not issues["in_progress"] and issues["todo_high_priority"]:
|
|
306
|
+
issue = issues["todo_high_priority"][0]
|
|
307
|
+
return f"Start work on an issue with: roadmap issue start {issue.id}"
|
|
308
|
+
|
|
309
|
+
if issues["overdue"]:
|
|
310
|
+
issue = issues["overdue"][0]
|
|
311
|
+
return f"View overdue issue details with: roadmap issue view {issue.id}"
|
|
312
|
+
|
|
313
|
+
return None
|