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.
Files changed (521) hide show
  1. roadmap/__init__.py +27 -0
  2. roadmap/adapters/__init__.py +1 -0
  3. roadmap/adapters/base_paginated_adapter.py +117 -0
  4. roadmap/adapters/cli/__init__.py +249 -0
  5. roadmap/adapters/cli/analysis/__init__.py +5 -0
  6. roadmap/adapters/cli/analysis/commands.py +370 -0
  7. roadmap/adapters/cli/analysis/presenter.py +125 -0
  8. roadmap/adapters/cli/archive_operations.py +86 -0
  9. roadmap/adapters/cli/cli_command_helpers.py +109 -0
  10. roadmap/adapters/cli/cli_confirmations.py +74 -0
  11. roadmap/adapters/cli/cli_error_handlers.py +146 -0
  12. roadmap/adapters/cli/cli_validators.py +39 -0
  13. roadmap/adapters/cli/commands/sync_status.py +308 -0
  14. roadmap/adapters/cli/comment/__init__.py +5 -0
  15. roadmap/adapters/cli/comment/commands.py +106 -0
  16. roadmap/adapters/cli/config/__init__.py +5 -0
  17. roadmap/adapters/cli/config/commands.py +198 -0
  18. roadmap/adapters/cli/console_exports.py +10 -0
  19. roadmap/adapters/cli/core.py +26 -0
  20. roadmap/adapters/cli/crud/__init__.py +47 -0
  21. roadmap/adapters/cli/crud/base_archive.py +208 -0
  22. roadmap/adapters/cli/crud/base_create.py +154 -0
  23. roadmap/adapters/cli/crud/base_delete.py +155 -0
  24. roadmap/adapters/cli/crud/base_restore.py +283 -0
  25. roadmap/adapters/cli/crud/base_update.py +193 -0
  26. roadmap/adapters/cli/crud/crud_helpers.py +206 -0
  27. roadmap/adapters/cli/crud/crud_utils.py +152 -0
  28. roadmap/adapters/cli/crud/entity_builders.py +374 -0
  29. roadmap/adapters/cli/data/__init__.py +8 -0
  30. roadmap/adapters/cli/data/commands.py +149 -0
  31. roadmap/adapters/cli/decorators.py +208 -0
  32. roadmap/adapters/cli/dtos/__init__.py +155 -0
  33. roadmap/adapters/cli/exception_handler.py +105 -0
  34. roadmap/adapters/cli/git/__init__.py +8 -0
  35. roadmap/adapters/cli/git/commands.py +475 -0
  36. roadmap/adapters/cli/git/handlers/__init__.py +1 -0
  37. roadmap/adapters/cli/git/handlers/git_authentication_handler.py +124 -0
  38. roadmap/adapters/cli/git/handlers/git_branch_handler.py +238 -0
  39. roadmap/adapters/cli/git/handlers/git_connectivity_handler.py +122 -0
  40. roadmap/adapters/cli/git/handlers/git_hooks_handler.py +105 -0
  41. roadmap/adapters/cli/git/hooks_config.py +327 -0
  42. roadmap/adapters/cli/git/status_display.py +158 -0
  43. roadmap/adapters/cli/health/__init__.py +8 -0
  44. roadmap/adapters/cli/health/db_integrity.py +204 -0
  45. roadmap/adapters/cli/health/enhancer.py +227 -0
  46. roadmap/adapters/cli/health/fixer.py +295 -0
  47. roadmap/adapters/cli/health/fixers/__init__.py +10 -0
  48. roadmap/adapters/cli/health/fixers/corrupted_comments_fixer.py +231 -0
  49. roadmap/adapters/cli/health/fixers/data_integrity_fixer.py +137 -0
  50. roadmap/adapters/cli/health/fixers/duplicate_issues_fixer.py +138 -0
  51. roadmap/adapters/cli/health/fixers/folder_structure_fixer.py +114 -0
  52. roadmap/adapters/cli/health/fixers/label_normalization_fixer.py +162 -0
  53. roadmap/adapters/cli/health/fixers/milestone_name_normalization_fixer.py +181 -0
  54. roadmap/adapters/cli/health/fixers/milestone_naming_compliance_fixer.py +244 -0
  55. roadmap/adapters/cli/health/fixers/milestone_parse_error_fixer.py +162 -0
  56. roadmap/adapters/cli/health/fixers/milestone_validation_fixer.py +211 -0
  57. roadmap/adapters/cli/health/fixers/old_backups_fixer.py +135 -0
  58. roadmap/adapters/cli/health/fixers/orphaned_issues_fixer.py +252 -0
  59. roadmap/adapters/cli/health/formatter.py +250 -0
  60. roadmap/adapters/cli/health/formatters.py +427 -0
  61. roadmap/adapters/cli/health/scan.py +293 -0
  62. roadmap/adapters/cli/init/__init__.py +5 -0
  63. roadmap/adapters/cli/init/commands.py +587 -0
  64. roadmap/adapters/cli/init.py +9 -0
  65. roadmap/adapters/cli/issues/__init__.py +70 -0
  66. roadmap/adapters/cli/issues/archive.py +137 -0
  67. roadmap/adapters/cli/issues/archive_class.py +253 -0
  68. roadmap/adapters/cli/issues/block.py +39 -0
  69. roadmap/adapters/cli/issues/close.py +216 -0
  70. roadmap/adapters/cli/issues/comment.py +178 -0
  71. roadmap/adapters/cli/issues/create.py +162 -0
  72. roadmap/adapters/cli/issues/delete.py +31 -0
  73. roadmap/adapters/cli/issues/deps.py +161 -0
  74. roadmap/adapters/cli/issues/issue_status_helpers.py +111 -0
  75. roadmap/adapters/cli/issues/link.py +308 -0
  76. roadmap/adapters/cli/issues/list.py +402 -0
  77. roadmap/adapters/cli/issues/lookup.py +96 -0
  78. roadmap/adapters/cli/issues/progress.py +102 -0
  79. roadmap/adapters/cli/issues/restore.py +96 -0
  80. roadmap/adapters/cli/issues/restore_class.py +57 -0
  81. roadmap/adapters/cli/issues/start.py +165 -0
  82. roadmap/adapters/cli/issues/sync_status.py +374 -0
  83. roadmap/adapters/cli/issues/unblock.py +43 -0
  84. roadmap/adapters/cli/issues/unlink.py +104 -0
  85. roadmap/adapters/cli/issues/update.py +120 -0
  86. roadmap/adapters/cli/issues/view.py +36 -0
  87. roadmap/adapters/cli/layout.py +220 -0
  88. roadmap/adapters/cli/mappers.py +200 -0
  89. roadmap/adapters/cli/milestones/__init__.py +52 -0
  90. roadmap/adapters/cli/milestones/archive.py +125 -0
  91. roadmap/adapters/cli/milestones/archive_class.py +236 -0
  92. roadmap/adapters/cli/milestones/assign.py +62 -0
  93. roadmap/adapters/cli/milestones/close.py +177 -0
  94. roadmap/adapters/cli/milestones/create.py +131 -0
  95. roadmap/adapters/cli/milestones/delete.py +27 -0
  96. roadmap/adapters/cli/milestones/kanban.py +123 -0
  97. roadmap/adapters/cli/milestones/list.py +87 -0
  98. roadmap/adapters/cli/milestones/recalculate.py +79 -0
  99. roadmap/adapters/cli/milestones/restore.py +87 -0
  100. roadmap/adapters/cli/milestones/restore_class.py +99 -0
  101. roadmap/adapters/cli/milestones/update.py +61 -0
  102. roadmap/adapters/cli/milestones/view.py +142 -0
  103. roadmap/adapters/cli/output_manager.py +319 -0
  104. roadmap/adapters/cli/presentation/base_presenter.py +121 -0
  105. roadmap/adapters/cli/presentation/cleanup_presenter.py +235 -0
  106. roadmap/adapters/cli/presentation/core_initialization_presenter.py +277 -0
  107. roadmap/adapters/cli/presentation/crud_presenter.py +134 -0
  108. roadmap/adapters/cli/presentation/daily_summary_presenter.py +266 -0
  109. roadmap/adapters/cli/presentation/issue_presenter.py +258 -0
  110. roadmap/adapters/cli/presentation/milestone_list_presenter.py +135 -0
  111. roadmap/adapters/cli/presentation/milestone_presenter.py +297 -0
  112. roadmap/adapters/cli/presentation/project_presenter.py +233 -0
  113. roadmap/adapters/cli/presentation/project_status_presenter.py +186 -0
  114. roadmap/adapters/cli/presentation/table_builders.py +95 -0
  115. roadmap/adapters/cli/projects/__init__.py +42 -0
  116. roadmap/adapters/cli/projects/archive.py +127 -0
  117. roadmap/adapters/cli/projects/archive_class.py +96 -0
  118. roadmap/adapters/cli/projects/close.py +94 -0
  119. roadmap/adapters/cli/projects/create.py +50 -0
  120. roadmap/adapters/cli/projects/delete.py +27 -0
  121. roadmap/adapters/cli/projects/list.py +227 -0
  122. roadmap/adapters/cli/projects/restore.py +87 -0
  123. roadmap/adapters/cli/projects/restore_class.py +56 -0
  124. roadmap/adapters/cli/projects/update.py +57 -0
  125. roadmap/adapters/cli/projects/view.py +157 -0
  126. roadmap/adapters/cli/services/__init__.py +1 -0
  127. roadmap/adapters/cli/services/daily_summary_service.py +313 -0
  128. roadmap/adapters/cli/services/export_manager.py +268 -0
  129. roadmap/adapters/cli/services/milestone_list_service.py +266 -0
  130. roadmap/adapters/cli/services/project_initialization_service.py +29 -0
  131. roadmap/adapters/cli/services/project_status_service.py +462 -0
  132. roadmap/adapters/cli/services/sync_service.py +62 -0
  133. roadmap/adapters/cli/status.py +408 -0
  134. roadmap/adapters/cli/styling.py +21 -0
  135. roadmap/adapters/cli/sync.py +1117 -0
  136. roadmap/adapters/cli/sync_context.py +432 -0
  137. roadmap/adapters/cli/sync_handlers/__init__.py +77 -0
  138. roadmap/adapters/cli/sync_handlers/apply_ops.py +522 -0
  139. roadmap/adapters/cli/sync_handlers/baseline_ops.py +366 -0
  140. roadmap/adapters/cli/sync_handlers/conflict_ops.py +161 -0
  141. roadmap/adapters/cli/sync_handlers/dry_run_display.py +237 -0
  142. roadmap/adapters/cli/sync_handlers/interactive_resolver.py +396 -0
  143. roadmap/adapters/cli/sync_handlers/progress_tracker.py +128 -0
  144. roadmap/adapters/cli/sync_metrics_command.py +190 -0
  145. roadmap/adapters/cli/sync_presenter.py +58 -0
  146. roadmap/adapters/cli/sync_validation.py +295 -0
  147. roadmap/adapters/cli/today.py +52 -0
  148. roadmap/adapters/cli/utils/click_options.py +38 -0
  149. roadmap/adapters/git/__init__.py +1 -0
  150. roadmap/adapters/git/git.py +208 -0
  151. roadmap/adapters/git/git_branch_manager.py +400 -0
  152. roadmap/adapters/git/git_command_executor.py +56 -0
  153. roadmap/adapters/git/git_commit_analyzer.py +334 -0
  154. roadmap/adapters/git/git_hooks.py +22 -0
  155. roadmap/adapters/git/git_hooks_manager.py +455 -0
  156. roadmap/adapters/git/git_repository_info.py +65 -0
  157. roadmap/adapters/git/hook_installer.py +106 -0
  158. roadmap/adapters/git/hook_registry.py +97 -0
  159. roadmap/adapters/git/hook_script_generator.py +58 -0
  160. roadmap/adapters/git/sync_monitor.py +409 -0
  161. roadmap/adapters/git/workflow_automation.py +286 -0
  162. roadmap/adapters/github/__init__.py +1 -0
  163. roadmap/adapters/github/github.py +360 -0
  164. roadmap/adapters/github/handlers/__init__.py +17 -0
  165. roadmap/adapters/github/handlers/base.py +337 -0
  166. roadmap/adapters/github/handlers/collaborators.py +165 -0
  167. roadmap/adapters/github/handlers/comments.py +118 -0
  168. roadmap/adapters/github/handlers/issues.py +150 -0
  169. roadmap/adapters/github/handlers/labels.py +164 -0
  170. roadmap/adapters/github/handlers/milestones.py +104 -0
  171. roadmap/adapters/persistence/__init__.py +35 -0
  172. roadmap/adapters/persistence/conflict_resolver.py +190 -0
  173. roadmap/adapters/persistence/database_manager.py +492 -0
  174. roadmap/adapters/persistence/entity_sync_coordinators.py +672 -0
  175. roadmap/adapters/persistence/file_locking.py +348 -0
  176. roadmap/adapters/persistence/file_parser.py +92 -0
  177. roadmap/adapters/persistence/file_synchronizer.py +107 -0
  178. roadmap/adapters/persistence/focused_managers.py +140 -0
  179. roadmap/adapters/persistence/git_history.py +330 -0
  180. roadmap/adapters/persistence/parser/__init__.py +20 -0
  181. roadmap/adapters/persistence/parser/frontmatter.py +152 -0
  182. roadmap/adapters/persistence/parser/issue.py +245 -0
  183. roadmap/adapters/persistence/parser/milestone.py +146 -0
  184. roadmap/adapters/persistence/parser/project.py +106 -0
  185. roadmap/adapters/persistence/persistence.py +288 -0
  186. roadmap/adapters/persistence/repositories/__init__.py +15 -0
  187. roadmap/adapters/persistence/repositories/issue_repository.py +223 -0
  188. roadmap/adapters/persistence/repositories/milestone_repository.py +140 -0
  189. roadmap/adapters/persistence/repositories/project_repository.py +168 -0
  190. roadmap/adapters/persistence/repositories/remote_link_repository.py +355 -0
  191. roadmap/adapters/persistence/repositories/sync_state_repository.py +66 -0
  192. roadmap/adapters/persistence/storage/__init__.py +36 -0
  193. roadmap/adapters/persistence/storage/conflicts.py +101 -0
  194. roadmap/adapters/persistence/storage/connection_manager.py +79 -0
  195. roadmap/adapters/persistence/storage/issue_storage.py +89 -0
  196. roadmap/adapters/persistence/storage/milestone_storage.py +80 -0
  197. roadmap/adapters/persistence/storage/project_storage.py +102 -0
  198. roadmap/adapters/persistence/storage/queries.py +227 -0
  199. roadmap/adapters/persistence/storage/state_manager.py +652 -0
  200. roadmap/adapters/persistence/storage/sync_state_storage.py +207 -0
  201. roadmap/adapters/persistence/sync_metrics_repository.py +342 -0
  202. roadmap/adapters/persistence/sync_orchestrator.py +293 -0
  203. roadmap/adapters/persistence/sync_state_tracker.py +86 -0
  204. roadmap/adapters/persistence/yaml_repositories.py +711 -0
  205. roadmap/adapters/sync/__init__.py +21 -0
  206. roadmap/adapters/sync/backend_factory.py +108 -0
  207. roadmap/adapters/sync/backends/__init__.py +15 -0
  208. roadmap/adapters/sync/backends/converters.py +209 -0
  209. roadmap/adapters/sync/backends/github_backend_helpers.py +380 -0
  210. roadmap/adapters/sync/backends/github_client.py +99 -0
  211. roadmap/adapters/sync/backends/github_sync_backend.py +792 -0
  212. roadmap/adapters/sync/backends/github_sync_ops.py +908 -0
  213. roadmap/adapters/sync/backends/services/__init__.py +1 -0
  214. roadmap/adapters/sync/backends/services/github_authentication_service.py +105 -0
  215. roadmap/adapters/sync/backends/services/github_issue_delete_service.py +423 -0
  216. roadmap/adapters/sync/backends/services/github_issue_dependency_service.py +63 -0
  217. roadmap/adapters/sync/backends/services/github_issue_fetch_service.py +158 -0
  218. roadmap/adapters/sync/backends/services/github_label_sync_service.py +126 -0
  219. roadmap/adapters/sync/backends/services/github_local_issue_sync_service.py +229 -0
  220. roadmap/adapters/sync/backends/services/github_milestone_fetch_service.py +204 -0
  221. roadmap/adapters/sync/backends/vanilla_git_sync_backend.py +198 -0
  222. roadmap/adapters/sync/services/__init__.py +11 -0
  223. roadmap/adapters/sync/services/baseline_state_handler.py +83 -0
  224. roadmap/adapters/sync/services/conflict_converter.py +85 -0
  225. roadmap/adapters/sync/services/issue_persistence_service.py +263 -0
  226. roadmap/adapters/sync/services/issue_state_service.py +243 -0
  227. roadmap/adapters/sync/services/local_change_filter.py +129 -0
  228. roadmap/adapters/sync/services/pull_result_processor.py +71 -0
  229. roadmap/adapters/sync/services/remote_issue_creation_service.py +112 -0
  230. roadmap/adapters/sync/services/sync_analysis_service.py +250 -0
  231. roadmap/adapters/sync/services/sync_authentication_service.py +57 -0
  232. roadmap/adapters/sync/services/sync_data_fetch_service.py +212 -0
  233. roadmap/adapters/sync/services/sync_linking_service.py +329 -0
  234. roadmap/adapters/sync/services/sync_report_service.py +62 -0
  235. roadmap/adapters/sync/services/sync_state_update_service.py +51 -0
  236. roadmap/adapters/sync/sync_cache_orchestrator.py +454 -0
  237. roadmap/adapters/sync/sync_merge_engine.py +416 -0
  238. roadmap/adapters/sync/sync_merge_orchestrator.py +1313 -0
  239. roadmap/adapters/sync/sync_retrieval_orchestrator.py +810 -0
  240. roadmap/application/services/__init__.py +1 -0
  241. roadmap/application/services/deduplicate_service.py +517 -0
  242. roadmap/application/use_cases/__init__.py +1 -0
  243. roadmap/common/__init__.py +308 -0
  244. roadmap/common/cache.py +209 -0
  245. roadmap/common/cli_errors.py +147 -0
  246. roadmap/common/configuration/__init__.py +33 -0
  247. roadmap/common/configuration/config_loader.py +264 -0
  248. roadmap/common/configuration/config_manager.py +164 -0
  249. roadmap/common/configuration/config_schema.py +145 -0
  250. roadmap/common/configuration/github/__init__.py +9 -0
  251. roadmap/common/configuration/github/config_manager.py +154 -0
  252. roadmap/common/configuration/github/token_resolver.py +84 -0
  253. roadmap/common/console.py +184 -0
  254. roadmap/common/constants.py +134 -0
  255. roadmap/common/datetime_parser.py +319 -0
  256. roadmap/common/error_formatter.py +84 -0
  257. roadmap/common/errors/__init__.py +143 -0
  258. roadmap/common/errors/error_base.py +73 -0
  259. roadmap/common/errors/error_file.py +82 -0
  260. roadmap/common/errors/error_git.py +67 -0
  261. roadmap/common/errors/error_handler.py +166 -0
  262. roadmap/common/errors/error_network.py +90 -0
  263. roadmap/common/errors/error_security.py +78 -0
  264. roadmap/common/errors/error_standards.py +561 -0
  265. roadmap/common/errors/error_validation.py +111 -0
  266. roadmap/common/errors/exceptions.py +432 -0
  267. roadmap/common/formatters/__init__.py +29 -0
  268. roadmap/common/formatters/base_table_formatter.py +96 -0
  269. roadmap/common/formatters/export/__init__.py +7 -0
  270. roadmap/common/formatters/export/issue_exporter.py +97 -0
  271. roadmap/common/formatters/helpers.py +28 -0
  272. roadmap/common/formatters/kanban/__init__.py +9 -0
  273. roadmap/common/formatters/kanban/layout.py +48 -0
  274. roadmap/common/formatters/kanban/organizer.py +81 -0
  275. roadmap/common/formatters/output/__init__.py +15 -0
  276. roadmap/common/formatters/tables/__init__.py +11 -0
  277. roadmap/common/formatters/tables/column_factory.py +312 -0
  278. roadmap/common/formatters/tables/issue_table.py +276 -0
  279. roadmap/common/formatters/tables/milestone_table.py +247 -0
  280. roadmap/common/formatters/tables/project_table.py +195 -0
  281. roadmap/common/formatters/text/__init__.py +57 -0
  282. roadmap/common/formatters/text/basic.py +217 -0
  283. roadmap/common/formatters/text/display.py +42 -0
  284. roadmap/common/formatters/text/duration.py +36 -0
  285. roadmap/common/formatters/text/operations.py +325 -0
  286. roadmap/common/formatters/text/status_badges.py +57 -0
  287. roadmap/common/initialization/github/__init__.py +11 -0
  288. roadmap/common/initialization/github/setup_service.py +238 -0
  289. roadmap/common/initialization/github/setup_validator.py +78 -0
  290. roadmap/common/logging/__init__.py +113 -0
  291. roadmap/common/logging/audit_logging.py +5 -0
  292. roadmap/common/logging/correlation.py +45 -0
  293. roadmap/common/logging/decorators.py +267 -0
  294. roadmap/common/logging/error_logging.py +217 -0
  295. roadmap/common/logging/formatters.py +124 -0
  296. roadmap/common/logging/loggers.py +68 -0
  297. roadmap/common/logging/performance_tracking.py +267 -0
  298. roadmap/common/logging/utils.py +335 -0
  299. roadmap/common/models/__init__.py +44 -0
  300. roadmap/common/models/cli_models.py +118 -0
  301. roadmap/common/models/config_models.py +199 -0
  302. roadmap/common/models/output_models.py +381 -0
  303. roadmap/common/observability/__init__.py +28 -0
  304. roadmap/common/observability/instrumentation.py +103 -0
  305. roadmap/common/observability/observability.py +76 -0
  306. roadmap/common/observability/otel_init.py +109 -0
  307. roadmap/common/output_formatter.py +393 -0
  308. roadmap/common/progress.py +379 -0
  309. roadmap/common/result.py +396 -0
  310. roadmap/common/security/__init__.py +48 -0
  311. roadmap/common/security/exceptions.py +13 -0
  312. roadmap/common/security/export_cleanup.py +79 -0
  313. roadmap/common/security/file_operations.py +104 -0
  314. roadmap/common/security/filename_sanitization.py +54 -0
  315. roadmap/common/security/logging.py +27 -0
  316. roadmap/common/security/path_validation.py +137 -0
  317. roadmap/common/security/temp_files.py +41 -0
  318. roadmap/common/services/__init__.py +65 -0
  319. roadmap/common/services/decorators.py +183 -0
  320. roadmap/common/services/logging_utils.py +208 -0
  321. roadmap/common/services/metrics.py +161 -0
  322. roadmap/common/services/performance.py +219 -0
  323. roadmap/common/services/profiling.py +288 -0
  324. roadmap/common/services/retry.py +288 -0
  325. roadmap/common/status_style_manager.py +114 -0
  326. roadmap/common/union_find.py +88 -0
  327. roadmap/common/update_constants.py +13 -0
  328. roadmap/common/utils/__init__.py +65 -0
  329. roadmap/common/utils/cli_helpers.py +381 -0
  330. roadmap/common/utils/file_utils.py +440 -0
  331. roadmap/common/utils/path_utils.py +38 -0
  332. roadmap/common/utils/status_utils.py +130 -0
  333. roadmap/common/utils/timezone_utils.py +480 -0
  334. roadmap/common/validation/__init__.py +37 -0
  335. roadmap/common/validation/field_validator.py +104 -0
  336. roadmap/common/validation/result.py +37 -0
  337. roadmap/common/validation/roadmap_validator.py +310 -0
  338. roadmap/common/validation/schema_validator.py +36 -0
  339. roadmap/common/validation/validators.py +50 -0
  340. roadmap/core/__init__.py +1 -0
  341. roadmap/core/domain/__init__.py +30 -0
  342. roadmap/core/domain/comment.py +24 -0
  343. roadmap/core/domain/health.py +15 -0
  344. roadmap/core/domain/issue.py +274 -0
  345. roadmap/core/domain/milestone.py +173 -0
  346. roadmap/core/domain/ports/__init__.py +1 -0
  347. roadmap/core/domain/ports/baseline_repository.py +24 -0
  348. roadmap/core/domain/ports/issue_repository.py +34 -0
  349. roadmap/core/domain/ports/remote_backend_port.py +31 -0
  350. roadmap/core/domain/project.py +130 -0
  351. roadmap/core/interfaces/__init__.py +109 -0
  352. roadmap/core/interfaces/assignee_validator.py +40 -0
  353. roadmap/core/interfaces/backend_factory.py +175 -0
  354. roadmap/core/interfaces/github.py +102 -0
  355. roadmap/core/interfaces/parsers.py +140 -0
  356. roadmap/core/interfaces/persistence.py +45 -0
  357. roadmap/core/interfaces/repositories.py +174 -0
  358. roadmap/core/interfaces/state_managers.py +111 -0
  359. roadmap/core/interfaces/state_storage.py +64 -0
  360. roadmap/core/interfaces/sync_backend.py +275 -0
  361. roadmap/core/interfaces/sync_services.py +119 -0
  362. roadmap/core/interfaces/sync_validators.py +180 -0
  363. roadmap/core/models/__init__.py +31 -0
  364. roadmap/core/models/sync_models.py +162 -0
  365. roadmap/core/models.py +115 -0
  366. roadmap/core/observability/__init__.py +17 -0
  367. roadmap/core/observability/sync_metrics.py +699 -0
  368. roadmap/core/repositories.py +200 -0
  369. roadmap/core/services/__init__.py +313 -0
  370. roadmap/core/services/baseline/__init__.py +1 -0
  371. roadmap/core/services/baseline/baseline_builder_progress.py +274 -0
  372. roadmap/core/services/baseline/baseline_retriever.py +63 -0
  373. roadmap/core/services/baseline/baseline_selector.py +248 -0
  374. roadmap/core/services/baseline/baseline_state_retriever.py +284 -0
  375. roadmap/core/services/baseline/optimized_baseline_builder.py +425 -0
  376. roadmap/core/services/comment/__init__.py +1 -0
  377. roadmap/core/services/comment/comment_service.py +277 -0
  378. roadmap/core/services/git/__init__.py +1 -0
  379. roadmap/core/services/git/git_hook_auto_sync_service.py +403 -0
  380. roadmap/core/services/github/__init__.py +1 -0
  381. roadmap/core/services/github/github_change_detector.py +162 -0
  382. roadmap/core/services/github/github_config_validator.py +148 -0
  383. roadmap/core/services/github/github_conflict_detector.py +179 -0
  384. roadmap/core/services/github/github_entity_classifier.py +51 -0
  385. roadmap/core/services/github/github_integration_service.py +439 -0
  386. roadmap/core/services/github/github_issue_client.py +346 -0
  387. roadmap/core/services/health/__init__.py +1 -0
  388. roadmap/core/services/health/backup_cleanup_service.py +202 -0
  389. roadmap/core/services/health/data_integrity_validator_service.py +90 -0
  390. roadmap/core/services/health/entity_health_scanner.py +460 -0
  391. roadmap/core/services/health/file_repair_service.py +193 -0
  392. roadmap/core/services/health/health_check_service.py +166 -0
  393. roadmap/core/services/health/health_models.py +80 -0
  394. roadmap/core/services/health/infrastructure_validator_service.py +328 -0
  395. roadmap/core/services/health/issue_health_scanner.py +263 -0
  396. roadmap/core/services/helpers/__init__.py +18 -0
  397. roadmap/core/services/helpers/status_change_helpers.py +88 -0
  398. roadmap/core/services/initialization/__init__.py +19 -0
  399. roadmap/core/services/initialization/utils.py +104 -0
  400. roadmap/core/services/initialization/validator.py +78 -0
  401. roadmap/core/services/initialization/workflow.py +182 -0
  402. roadmap/core/services/initialization_service.py +111 -0
  403. roadmap/core/services/issue/__init__.py +1 -0
  404. roadmap/core/services/issue/assignee_validation_service.py +312 -0
  405. roadmap/core/services/issue/issue_creation_service.py +309 -0
  406. roadmap/core/services/issue/issue_filter_service.py +302 -0
  407. roadmap/core/services/issue/issue_matching_service.py +127 -0
  408. roadmap/core/services/issue/issue_service.py +765 -0
  409. roadmap/core/services/issue/issue_update_service.py +144 -0
  410. roadmap/core/services/issue/start_issue_service.py +174 -0
  411. roadmap/core/services/issue_helpers/__init__.py +18 -0
  412. roadmap/core/services/issue_helpers/issue_filters.py +296 -0
  413. roadmap/core/services/milestone_service.py +438 -0
  414. roadmap/core/services/project/__init__.py +1 -0
  415. roadmap/core/services/project/project_service.py +434 -0
  416. roadmap/core/services/project/project_status_service.py +125 -0
  417. roadmap/core/services/project_init/__init__.py +22 -0
  418. roadmap/core/services/project_init/context_detection.py +146 -0
  419. roadmap/core/services/project_init/creation.py +76 -0
  420. roadmap/core/services/project_init/detection.py +51 -0
  421. roadmap/core/services/project_init/template.py +228 -0
  422. roadmap/core/services/status_change_service.py +88 -0
  423. roadmap/core/services/sync/__init__.py +1 -0
  424. roadmap/core/services/sync/batch_processor.py +290 -0
  425. roadmap/core/services/sync/conflict_resolver.py +154 -0
  426. roadmap/core/services/sync/dependency_resolver.py +367 -0
  427. roadmap/core/services/sync/duplicate_detector.py +771 -0
  428. roadmap/core/services/sync/duplicate_resolver.py +315 -0
  429. roadmap/core/services/sync/error_classification.py +470 -0
  430. roadmap/core/services/sync/performance.py +263 -0
  431. roadmap/core/services/sync/sync_change_computer.py +211 -0
  432. roadmap/core/services/sync/sync_checkpoint.py +348 -0
  433. roadmap/core/services/sync/sync_conflict_detector.py +147 -0
  434. roadmap/core/services/sync/sync_conflict_resolver.py +448 -0
  435. roadmap/core/services/sync/sync_errors.py +383 -0
  436. roadmap/core/services/sync/sync_key_normalizer.py +222 -0
  437. roadmap/core/services/sync/sync_metadata_service.py +361 -0
  438. roadmap/core/services/sync/sync_plan.py +171 -0
  439. roadmap/core/services/sync/sync_plan_executor.py +447 -0
  440. roadmap/core/services/sync/sync_report.py +296 -0
  441. roadmap/core/services/sync/sync_state.py +173 -0
  442. roadmap/core/services/sync/sync_state_comparator.py +690 -0
  443. roadmap/core/services/sync/sync_state_manager.py +431 -0
  444. roadmap/core/services/sync/sync_state_normalizer.py +98 -0
  445. roadmap/core/services/sync/sync_three_way.py +78 -0
  446. roadmap/core/services/sync/three_way_merger.py +178 -0
  447. roadmap/core/services/utils/__init__.py +1 -0
  448. roadmap/core/services/utils/configuration_service.py +113 -0
  449. roadmap/core/services/utils/critical_path_calculator.py +418 -0
  450. roadmap/core/services/utils/dependency_analyzer.py +360 -0
  451. roadmap/core/services/utils/field_conflict_detector.py +126 -0
  452. roadmap/core/services/utils/remote_fetcher.py +202 -0
  453. roadmap/core/services/utils/remote_state_normalizer.py +110 -0
  454. roadmap/core/services/utils/retry_policy.py +51 -0
  455. roadmap/core/services/validator_base.py +130 -0
  456. roadmap/core/services/validators/__init__.py +28 -0
  457. roadmap/core/services/validators/_utils.py +20 -0
  458. roadmap/core/services/validators/archivable_issues_validator.py +71 -0
  459. roadmap/core/services/validators/archivable_milestones_validator.py +69 -0
  460. roadmap/core/services/validators/backup_validator.py +89 -0
  461. roadmap/core/services/validators/data_integrity_validator.py +84 -0
  462. roadmap/core/services/validators/duplicate_issues_validator.py +75 -0
  463. roadmap/core/services/validators/duplicate_milestones_validator.py +141 -0
  464. roadmap/core/services/validators/folder_structure_validator.py +165 -0
  465. roadmap/core/services/validators/health_status_utils.py +28 -0
  466. roadmap/core/services/validators/milestone_naming_validator.py +146 -0
  467. roadmap/core/services/validators/missing_headlines_validator.py +97 -0
  468. roadmap/core/services/validators/orphaned_issues_validator.py +142 -0
  469. roadmap/core/services/validators/orphaned_milestones_validator.py +85 -0
  470. roadmap/core/utils/git_remote_parser.py +83 -0
  471. roadmap/core/utils/github_urls.py +66 -0
  472. roadmap/domain/validation.py +30 -0
  473. roadmap/infrastructure/__init__.py +55 -0
  474. roadmap/infrastructure/coordination/__init__.py +8 -0
  475. roadmap/infrastructure/coordination/coordinator_params.py +82 -0
  476. roadmap/infrastructure/coordination/core.py +446 -0
  477. roadmap/infrastructure/coordination/git_coordinator.py +160 -0
  478. roadmap/infrastructure/coordination/initialization.py +348 -0
  479. roadmap/infrastructure/coordination/issue_coordinator.py +154 -0
  480. roadmap/infrastructure/coordination/issue_operations.py +456 -0
  481. roadmap/infrastructure/coordination/milestone_coordinator.py +135 -0
  482. roadmap/infrastructure/coordination/milestone_operations.py +201 -0
  483. roadmap/infrastructure/coordination/project_coordinator.py +113 -0
  484. roadmap/infrastructure/coordination/project_operations.py +144 -0
  485. roadmap/infrastructure/coordination/team_coordinator.py +107 -0
  486. roadmap/infrastructure/coordination/user_operations.py +150 -0
  487. roadmap/infrastructure/coordination/validation_coordinator.py +249 -0
  488. roadmap/infrastructure/coordination_gateway.py +152 -0
  489. roadmap/infrastructure/git/__init__.py +6 -0
  490. roadmap/infrastructure/git/git_integration_ops.py +234 -0
  491. roadmap/infrastructure/git_gateway.py +35 -0
  492. roadmap/infrastructure/github_gateway.py +52 -0
  493. roadmap/infrastructure/maintenance/__init__.py +5 -0
  494. roadmap/infrastructure/maintenance/cleanup.py +483 -0
  495. roadmap/infrastructure/observability/__init__.py +7 -0
  496. roadmap/infrastructure/observability/health.py +196 -0
  497. roadmap/infrastructure/observability/health_formatter.py +73 -0
  498. roadmap/infrastructure/observability/specialized_health_checkers.py +188 -0
  499. roadmap/infrastructure/persistence_gateway.py +164 -0
  500. roadmap/infrastructure/security/__init__.py +1 -0
  501. roadmap/infrastructure/security/credentials.py +489 -0
  502. roadmap/infrastructure/sync_gateway.py +94 -0
  503. roadmap/infrastructure/validation/__init__.py +7 -0
  504. roadmap/infrastructure/validation/file_enumeration.py +219 -0
  505. roadmap/infrastructure/validation/github_assignee_validator.py +47 -0
  506. roadmap/infrastructure/validation/github_validator.py +101 -0
  507. roadmap/infrastructure/validation/milestone_consistency_validator.py +96 -0
  508. roadmap/infrastructure/validation/vanilla_assignee_validator.py +37 -0
  509. roadmap/infrastructure/validation_gateway.py +96 -0
  510. roadmap/presentation/formatters/sync_metrics_formatter.py +355 -0
  511. roadmap/settings.py +345 -0
  512. roadmap/templates/github_workflows/ci-cd-roadmap.yml +241 -0
  513. roadmap/templates/github_workflows/issue-lifecycle.yml +236 -0
  514. roadmap/templates/github_workflows/roadmap-integration.yml +137 -0
  515. roadmap/templates/github_workflows/roadmap-starter.yml +59 -0
  516. roadmap/version.py +381 -0
  517. roadmap_cli-0.1.1.dist-info/METADATA +448 -0
  518. roadmap_cli-0.1.1.dist-info/RECORD +521 -0
  519. roadmap_cli-0.1.1.dist-info/WHEEL +4 -0
  520. roadmap_cli-0.1.1.dist-info/entry_points.txt +2 -0
  521. roadmap_cli-0.1.1.dist-info/licenses/LICENSE.md +21 -0
@@ -0,0 +1,462 @@
1
+ """Service layer for project status computation and aggregation.
2
+
3
+ Handles all business logic related to:
4
+ - Computing milestone progress
5
+ - Aggregating issue statistics
6
+ - Computing status summaries
7
+ - Gathering roadmap metrics
8
+ """
9
+
10
+ from collections import Counter
11
+ from pathlib import Path
12
+
13
+ from roadmap.adapters.persistence.parser import MilestoneParser, ProjectParser
14
+ from roadmap.common.logging import get_logger
15
+ from roadmap.common.models import ColumnDef, ColumnType, TableData
16
+ from roadmap.core.domain import MilestoneStatus, ProjectStatus, Status
17
+ from roadmap.infrastructure.coordination.core import RoadmapCore
18
+ from roadmap.infrastructure.validation.file_enumeration import FileEnumerationService
19
+
20
+ logger = get_logger(__name__)
21
+
22
+
23
+ class StatusDataService:
24
+ """Service for gathering and computing status data."""
25
+
26
+ @staticmethod
27
+ def gather_status_data(core: RoadmapCore) -> dict:
28
+ """Gather all status data from roadmap.
29
+
30
+ Args:
31
+ core: RoadmapCore instance
32
+
33
+ Returns:
34
+ Dictionary with status_data:
35
+ {
36
+ 'issues': list of issues,
37
+ 'milestones': list of milestones,
38
+ 'has_data': bool,
39
+ 'issue_count': int,
40
+ 'milestone_count': int,
41
+ }
42
+ """
43
+ try:
44
+ issues = core.issues.list()
45
+ milestones = core.milestones.list()
46
+
47
+ return {
48
+ "issues": issues,
49
+ "milestones": milestones,
50
+ "has_data": bool(issues or milestones),
51
+ "issue_count": len(issues),
52
+ "milestone_count": len(milestones),
53
+ }
54
+ except Exception as e:
55
+ logger.error(
56
+ "failed_to_gather_status_data", error=str(e), severity="operational"
57
+ )
58
+ return {
59
+ "issues": [],
60
+ "milestones": [],
61
+ "has_data": False,
62
+ "issue_count": 0,
63
+ "milestone_count": 0,
64
+ }
65
+
66
+
67
+ class MilestoneProgressService:
68
+ """Service for computing milestone progress."""
69
+
70
+ @staticmethod
71
+ def get_milestone_progress(core: RoadmapCore, milestone_name: str) -> dict:
72
+ """Get progress for a specific milestone.
73
+
74
+ Args:
75
+ core: RoadmapCore instance
76
+ milestone_name: Name of the milestone
77
+
78
+ Returns:
79
+ Dictionary with progress data:
80
+ {
81
+ 'total': total issues in milestone,
82
+ 'completed': completed issues in milestone,
83
+ 'percentage': completion percentage,
84
+ }
85
+ """
86
+ try:
87
+ progress = core.db.get_milestone_progress(milestone_name)
88
+ if progress and progress["total"] > 0:
89
+ percentage = (progress["completed"] / progress["total"]) * 100
90
+ else:
91
+ percentage = 0
92
+
93
+ return {
94
+ "total": progress.get("total", 0),
95
+ "completed": progress.get("completed", 0),
96
+ "percentage": percentage,
97
+ }
98
+ except Exception as e:
99
+ logger.error(
100
+ "failed_to_get_milestone_progress",
101
+ milestone=milestone_name,
102
+ error=str(e),
103
+ severity="operational",
104
+ )
105
+ return {"total": 0, "completed": 0, "percentage": 0}
106
+
107
+ @staticmethod
108
+ def get_all_milestones_progress(core: RoadmapCore, milestones: list) -> dict:
109
+ """Get progress for all milestones.
110
+
111
+ Args:
112
+ core: RoadmapCore instance
113
+ milestones: List of milestone objects
114
+
115
+ Returns:
116
+ Dictionary mapping milestone name to progress dict
117
+ """
118
+ progress_data = {}
119
+ for milestone in milestones:
120
+ progress_data[milestone.name] = (
121
+ MilestoneProgressService.get_milestone_progress(core, milestone.name)
122
+ )
123
+ return progress_data
124
+
125
+
126
+ class IssueStatisticsService:
127
+ """Service for computing issue statistics."""
128
+
129
+ @staticmethod
130
+ def get_issue_status_counts(issues: list) -> dict:
131
+ """Count issues by status.
132
+
133
+ Args:
134
+ issues: List of issue objects
135
+
136
+ Returns:
137
+ Dictionary mapping Status enum to count
138
+ """
139
+ status_counts = Counter(issue.status for issue in issues)
140
+ return dict(status_counts)
141
+
142
+ @staticmethod
143
+ def get_status_styling() -> dict:
144
+ """Get color styling for each status.
145
+
146
+ Returns:
147
+ Dictionary mapping Status enum to style string
148
+ """
149
+ return {
150
+ Status.TODO: "white",
151
+ Status.IN_PROGRESS: "yellow",
152
+ Status.BLOCKED: "red",
153
+ Status.REVIEW: "blue",
154
+ Status.CLOSED: "green",
155
+ }
156
+
157
+ @staticmethod
158
+ def get_all_status_counts(issues: list) -> dict:
159
+ """Get counts for all status values, including zeros.
160
+
161
+ Args:
162
+ issues: List of issue objects
163
+
164
+ Returns:
165
+ Dictionary with all Status enum values and their counts
166
+ """
167
+ status_counts = IssueStatisticsService.get_issue_status_counts(issues)
168
+
169
+ # Ensure all statuses are represented
170
+ all_counts = {}
171
+ for status in Status:
172
+ all_counts[status] = status_counts.get(status, 0)
173
+
174
+ return all_counts
175
+
176
+ @staticmethod
177
+ def get_active_issue_count(issues: list) -> int:
178
+ """Count non-closed issues.
179
+
180
+ Args:
181
+ issues: List of issue objects
182
+
183
+ Returns:
184
+ Count of active (non-closed) issues
185
+ """
186
+ return sum(1 for issue in issues if issue.status != Status.CLOSED)
187
+
188
+ @staticmethod
189
+ def get_blocked_issue_count(issues: list) -> int:
190
+ """Count blocked issues.
191
+
192
+ Args:
193
+ issues: List of issue objects
194
+
195
+ Returns:
196
+ Count of blocked issues
197
+ """
198
+ return sum(1 for issue in issues if issue.status == Status.BLOCKED)
199
+
200
+
201
+ class StatusSnapshotService:
202
+ """Service for building a snapshot of entity status counts."""
203
+
204
+ @staticmethod
205
+ def build_snapshot_tables(core: RoadmapCore) -> dict[str, TableData]:
206
+ """Build status snapshot tables for entities and issue status.
207
+
208
+ Args:
209
+ core: RoadmapCore instance
210
+
211
+ Returns:
212
+ Dictionary with TableData objects for snapshot output.
213
+ """
214
+ projects = list(core.projects.list())
215
+ milestones = list(core.milestones.list())
216
+ issues = list(core.issues.list_all_including_archived())
217
+
218
+ archived_projects = StatusSnapshotService._list_archived_projects(core)
219
+ archived_milestones = StatusSnapshotService._list_archived_milestones(core)
220
+
221
+ project_counts = StatusSnapshotService._count_projects(projects)
222
+ milestone_counts = StatusSnapshotService._count_milestones(milestones)
223
+ issue_counts = StatusSnapshotService._count_issues(issues)
224
+
225
+ entities_table = StatusSnapshotService._build_entities_table(
226
+ project_counts=project_counts,
227
+ milestone_counts=milestone_counts,
228
+ issue_counts=issue_counts,
229
+ archived_projects=len(archived_projects),
230
+ archived_milestones=len(archived_milestones),
231
+ )
232
+ issue_status_table = StatusSnapshotService._build_issue_status_table(
233
+ issue_counts=issue_counts
234
+ )
235
+
236
+ return {
237
+ "entities": entities_table,
238
+ "issue_status": issue_status_table,
239
+ }
240
+
241
+ @staticmethod
242
+ def _list_archived_projects(core: RoadmapCore) -> list:
243
+ archive_dir = core.projects_dir.parent / "archive" / "projects"
244
+ return FileEnumerationService.enumerate_and_parse(
245
+ archive_dir, ProjectParser.parse_project_file
246
+ )
247
+
248
+ @staticmethod
249
+ def _list_archived_milestones(core: RoadmapCore) -> list:
250
+ archive_dir = core.milestones_dir.parent / "archive" / "milestones"
251
+ return FileEnumerationService.enumerate_and_parse(
252
+ archive_dir, MilestoneParser.parse_milestone_file
253
+ )
254
+
255
+ @staticmethod
256
+ def _is_archived_issue(issue) -> bool:
257
+ if getattr(issue, "archived", False):
258
+ return True
259
+ if getattr(issue, "status", None) == Status.ARCHIVED:
260
+ return True
261
+ file_path = getattr(issue, "file_path", "") or ""
262
+ return "archive" in Path(file_path).parts
263
+
264
+ @staticmethod
265
+ def _count_projects(projects: list) -> dict[ProjectStatus, int]:
266
+ counts = dict.fromkeys(ProjectStatus, 0)
267
+ for project in projects:
268
+ if project.status in counts:
269
+ counts[project.status] += 1
270
+ return counts
271
+
272
+ @staticmethod
273
+ def _count_milestones(milestones: list) -> dict[MilestoneStatus, int]:
274
+ counts = dict.fromkeys(MilestoneStatus, 0)
275
+ for milestone in milestones:
276
+ if milestone.status in counts:
277
+ counts[milestone.status] += 1
278
+ return counts
279
+
280
+ @staticmethod
281
+ def _count_issues(issues: list) -> dict[Status, int]:
282
+ counts = dict.fromkeys(Status, 0)
283
+ for issue in issues:
284
+ if StatusSnapshotService._is_archived_issue(issue):
285
+ counts[Status.ARCHIVED] += 1
286
+ else:
287
+ counts[issue.status] = counts.get(issue.status, 0) + 1
288
+ return counts
289
+
290
+ @staticmethod
291
+ def _build_entities_table(
292
+ project_counts: dict[ProjectStatus, int],
293
+ milestone_counts: dict[MilestoneStatus, int],
294
+ issue_counts: dict[Status, int],
295
+ archived_projects: int,
296
+ archived_milestones: int,
297
+ ) -> TableData:
298
+ columns = [
299
+ ColumnDef("entity", "Entity", ColumnType.STRING, width=12),
300
+ ColumnDef("open", "Open", ColumnType.INTEGER, width=6),
301
+ ColumnDef("closed", "Closed", ColumnType.INTEGER, width=7),
302
+ ColumnDef("archived", "Archived", ColumnType.INTEGER, width=9),
303
+ ColumnDef("total", "Total", ColumnType.INTEGER, width=6),
304
+ ]
305
+
306
+ project_open = sum(
307
+ project_counts.get(status, 0)
308
+ for status in (
309
+ ProjectStatus.PLANNING,
310
+ ProjectStatus.ACTIVE,
311
+ ProjectStatus.ON_HOLD,
312
+ )
313
+ )
314
+ project_closed = sum(
315
+ project_counts.get(status, 0)
316
+ for status in (ProjectStatus.COMPLETED, ProjectStatus.CANCELLED)
317
+ )
318
+ project_total = project_open + project_closed + archived_projects
319
+
320
+ milestone_open = milestone_counts.get(MilestoneStatus.OPEN, 0)
321
+ milestone_closed = milestone_counts.get(MilestoneStatus.CLOSED, 0)
322
+ milestone_total = milestone_open + milestone_closed + archived_milestones
323
+
324
+ issue_open = sum(
325
+ issue_counts.get(status, 0)
326
+ for status in (
327
+ Status.TODO,
328
+ Status.IN_PROGRESS,
329
+ Status.BLOCKED,
330
+ Status.REVIEW,
331
+ )
332
+ )
333
+ issue_closed = issue_counts.get(Status.CLOSED, 0)
334
+ issue_archived = issue_counts.get(Status.ARCHIVED, 0)
335
+ issue_total = issue_open + issue_closed + issue_archived
336
+
337
+ rows = [
338
+ [
339
+ "Projects",
340
+ project_open,
341
+ project_closed,
342
+ archived_projects,
343
+ project_total,
344
+ ],
345
+ [
346
+ "Milestones",
347
+ milestone_open,
348
+ milestone_closed,
349
+ archived_milestones,
350
+ milestone_total,
351
+ ],
352
+ ["Issues", issue_open, issue_closed, issue_archived, issue_total],
353
+ ]
354
+
355
+ totals = [
356
+ "Total",
357
+ project_open + milestone_open + issue_open,
358
+ project_closed + milestone_closed + issue_closed,
359
+ archived_projects + archived_milestones + issue_archived,
360
+ project_total + milestone_total + issue_total,
361
+ ]
362
+ rows.append(totals)
363
+
364
+ return TableData(columns=columns, rows=rows, title="Entities")
365
+
366
+ @staticmethod
367
+ def _build_issue_status_table(issue_counts: dict[Status, int]) -> TableData:
368
+ columns = [
369
+ ColumnDef("status", "Status", ColumnType.STRING, width=14),
370
+ ColumnDef("count", "Count", ColumnType.INTEGER, width=7),
371
+ ]
372
+ rows = []
373
+ total = 0
374
+ for status in Status:
375
+ count = issue_counts.get(status, 0)
376
+ total += count
377
+ rows.append([status.value, count])
378
+
379
+ rows.append(["Total", total])
380
+
381
+ return TableData(columns=columns, rows=rows, title="Issue Status")
382
+
383
+
384
+ class RoadmapSummaryService:
385
+ """Service for computing high-level roadmap summaries."""
386
+
387
+ @staticmethod
388
+ def compute_roadmap_summary(
389
+ core: RoadmapCore, issues: list, milestones: list
390
+ ) -> dict:
391
+ """Compute comprehensive roadmap summary.
392
+
393
+ Args:
394
+ core: RoadmapCore instance
395
+ issues: List of issue objects
396
+ milestones: List of milestone objects
397
+
398
+ Returns:
399
+ Dictionary with summary data:
400
+ {
401
+ 'total_issues': int,
402
+ 'active_issues': int,
403
+ 'blocked_issues': int,
404
+ 'total_milestones': int,
405
+ 'completed_milestones': int,
406
+ 'milestone_progress': dict,
407
+ 'issue_status_counts': dict,
408
+ 'milestone_details': list,
409
+ }
410
+ """
411
+ try:
412
+ milestone_progress = MilestoneProgressService.get_all_milestones_progress(
413
+ core, milestones
414
+ )
415
+
416
+ completed_milestones = sum(
417
+ 1
418
+ for progress in milestone_progress.values()
419
+ if progress["percentage"] == 100 and progress["total"] > 0
420
+ )
421
+
422
+ milestone_details = []
423
+ for milestone in milestones:
424
+ progress = milestone_progress.get(milestone.name, {})
425
+ milestone_details.append(
426
+ {
427
+ "name": milestone.name,
428
+ "progress": progress,
429
+ "due_date": getattr(milestone, "due_date", None),
430
+ }
431
+ )
432
+
433
+ return {
434
+ "total_issues": len(issues),
435
+ "active_issues": IssueStatisticsService.get_active_issue_count(issues),
436
+ "blocked_issues": IssueStatisticsService.get_blocked_issue_count(
437
+ issues
438
+ ),
439
+ "total_milestones": len(milestones),
440
+ "completed_milestones": completed_milestones,
441
+ "milestone_progress": milestone_progress,
442
+ "issue_status_counts": IssueStatisticsService.get_all_status_counts(
443
+ issues
444
+ ),
445
+ "milestone_details": milestone_details,
446
+ }
447
+ except Exception as e:
448
+ logger.error(
449
+ "failed_to_compute_roadmap_summary",
450
+ error=str(e),
451
+ severity="operational",
452
+ )
453
+ return {
454
+ "total_issues": len(issues),
455
+ "active_issues": 0,
456
+ "blocked_issues": 0,
457
+ "total_milestones": len(milestones),
458
+ "completed_milestones": 0,
459
+ "milestone_progress": {},
460
+ "issue_status_counts": {},
461
+ "milestone_details": [],
462
+ }
@@ -0,0 +1,62 @@
1
+ """CLI-facing sync service - provides backend creation and testing.
2
+
3
+ This service wraps the sync backend factory to prevent direct
4
+ CLI imports from lower-level adapter modules (layer violation fix).
5
+ """
6
+
7
+ from typing import Literal
8
+
9
+ import structlog
10
+
11
+ from roadmap.adapters.sync.backend_factory import get_sync_backend as _get_backend
12
+ from roadmap.core.interfaces.sync_backend import SyncBackendInterface
13
+ from roadmap.infrastructure.coordination.core import RoadmapCore
14
+
15
+ log = structlog.get_logger()
16
+
17
+
18
+ def get_sync_backend(
19
+ backend_type: Literal["github", "git"],
20
+ core: RoadmapCore,
21
+ config: dict | None = None,
22
+ ) -> SyncBackendInterface | None:
23
+ """Create a sync backend (CLI-facing wrapper over factory).
24
+
25
+ This wrapper provides a clean, CLI-facing interface for backend creation
26
+ without requiring direct imports of the backend factory.
27
+
28
+ Args:
29
+ backend_type: Type of backend ('github' or 'git')
30
+ core: RoadmapCore instance
31
+ config: Optional backend configuration
32
+
33
+ Returns:
34
+ Backend instance or None if creation fails
35
+ """
36
+ return _get_backend(backend_type, core, config)
37
+
38
+
39
+ def test_backend_connectivity(
40
+ backend: SyncBackendInterface, backend_type: str
41
+ ) -> tuple[bool, str]:
42
+ """Test backend connectivity.
43
+
44
+ Args:
45
+ backend: The backend to test
46
+ backend_type: Type name for logging
47
+
48
+ Returns:
49
+ Tuple of (success: bool, message: str)
50
+ """
51
+ try:
52
+ if backend.authenticate():
53
+ return True, f"✅ {backend_type.capitalize()} connectivity verified"
54
+ else:
55
+ return False, f"❌ {backend_type.capitalize()} authentication failed"
56
+ except Exception as e:
57
+ log.error(
58
+ "backend_connectivity_test_failed",
59
+ backend_type=backend_type,
60
+ error=str(e),
61
+ )
62
+ return False, f"❌ Connection test failed: {str(e)}"