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,268 @@
1
+ """Export management service for handling data exports with config integration."""
2
+
3
+ import json
4
+ from datetime import UTC, datetime
5
+ from pathlib import Path
6
+ from typing import Any
7
+
8
+ from structlog import get_logger
9
+
10
+ from roadmap.common.configuration import ConfigLoader
11
+ from roadmap.common.console import get_console
12
+
13
+ logger = get_logger()
14
+ console = get_console()
15
+
16
+
17
+ class ExportManager:
18
+ """Manages data exports using configuration system for defaults and locations."""
19
+
20
+ def __init__(self, project_root: Path | None = None):
21
+ """Initialize export manager.
22
+
23
+ Args:
24
+ project_root: Project root directory (defaults to cwd)
25
+ """
26
+ self.project_root = project_root or Path.cwd()
27
+ self.config = ConfigLoader.load_config(self.project_root)
28
+
29
+ def get_export_dir(self) -> Path:
30
+ """Get the export directory, creating it if needed.
31
+
32
+ Returns:
33
+ Path to export directory
34
+ """
35
+ export_dir = self.config.export.directory
36
+
37
+ # Handle relative vs absolute paths
38
+ if export_dir.startswith("/"):
39
+ # Absolute path
40
+ path = Path(export_dir)
41
+ else:
42
+ # Relative to project root
43
+ path = self.project_root / export_dir
44
+
45
+ # Create directory if it doesn't exist
46
+ path.mkdir(parents=True, exist_ok=True)
47
+
48
+ # Add to .gitignore if configured
49
+ if self.config.export.auto_gitignore:
50
+ self._add_to_gitignore(path)
51
+
52
+ return path
53
+
54
+ def _add_to_gitignore(self, path: Path) -> None:
55
+ """Add export directory to .gitignore if not already there.
56
+
57
+ Args:
58
+ path: Path to add to gitignore
59
+ """
60
+ gitignore_path = self.project_root / ".gitignore"
61
+
62
+ # Make path relative for .gitignore
63
+ try:
64
+ rel_path = path.relative_to(self.project_root)
65
+ except ValueError:
66
+ # Path is absolute, can't make it relative
67
+ rel_path = path
68
+
69
+ # Read existing gitignore
70
+ if gitignore_path.exists():
71
+ content = gitignore_path.read_text()
72
+ else:
73
+ content = ""
74
+
75
+ # Check if already present
76
+ gitignore_entry = str(rel_path) + "/"
77
+ if gitignore_entry in content:
78
+ return # Already there
79
+
80
+ # Add to gitignore
81
+ if content and not content.endswith("\n"):
82
+ content += "\n"
83
+
84
+ content += f"{gitignore_entry}\n"
85
+ gitignore_path.write_text(content)
86
+ logger.info("added_to_gitignore", path=str(rel_path))
87
+
88
+ def generate_export_filename(self, entity_type: str, format_type: str) -> str:
89
+ """Generate a timestamped export filename.
90
+
91
+ Args:
92
+ entity_type: Type of entity being exported (issues, milestones, projects)
93
+ format_type: Export format (json, csv, markdown)
94
+
95
+ Returns:
96
+ Filename with timestamp
97
+ """
98
+ timestamp = datetime.now(UTC).strftime("%Y-%m-%d-%H%M%S")
99
+ return f"{entity_type}-{timestamp}.{format_type}"
100
+
101
+ def get_export_path(self, entity_type: str, format_type: str | None = None) -> Path:
102
+ """Get full export path with auto-generated filename.
103
+
104
+ Args:
105
+ entity_type: Type of entity being exported
106
+ format_type: Export format (uses config default if not specified)
107
+
108
+ Returns:
109
+ Full path to export file
110
+ """
111
+ if format_type is None:
112
+ format_type = self.config.export.format
113
+
114
+ export_dir = self.get_export_dir()
115
+ filename = self.generate_export_filename(entity_type, format_type)
116
+ return export_dir / filename
117
+
118
+ def export_json(self, data: Any, output_path: Path | None = None) -> str:
119
+ """Export data as JSON.
120
+
121
+ Args:
122
+ data: Data to export (usually list or dict)
123
+ output_path: Optional custom output path
124
+
125
+ Returns:
126
+ JSON string
127
+ """
128
+ if isinstance(data, list):
129
+ # Convert objects to dicts if needed
130
+ serializable = [
131
+ item.model_dump() if hasattr(item, "model_dump") else item
132
+ for item in data
133
+ ]
134
+ else:
135
+ serializable = data.model_dump() if hasattr(data, "model_dump") else data
136
+
137
+ json_str = json.dumps(serializable, indent=2, default=str)
138
+
139
+ if output_path:
140
+ output_path.parent.mkdir(parents=True, exist_ok=True)
141
+ output_path.write_text(json_str)
142
+ logger.info("exported_json", path=str(output_path))
143
+
144
+ return json_str
145
+
146
+ def export_csv(self, data: list[Any], output_path: Path | None = None) -> str:
147
+ """Export data as CSV.
148
+
149
+ Args:
150
+ data: List of objects to export
151
+ output_path: Optional custom output path
152
+
153
+ Returns:
154
+ CSV string
155
+ """
156
+ if not data:
157
+ csv_str = ""
158
+ else:
159
+ import csv
160
+ from io import StringIO
161
+
162
+ # Get headers from first item
163
+ first_item = data[0]
164
+ if hasattr(first_item, "model_dump"):
165
+ headers = first_item.model_dump().keys()
166
+ rows = [item.model_dump() for item in data]
167
+ else:
168
+ headers = first_item.keys() if isinstance(first_item, dict) else []
169
+ rows = data
170
+
171
+ # Write CSV
172
+ output = StringIO()
173
+ writer = csv.DictWriter(output, fieldnames=headers)
174
+ writer.writeheader()
175
+ writer.writerows(rows)
176
+ csv_str = output.getvalue()
177
+
178
+ if output_path:
179
+ output_path.parent.mkdir(parents=True, exist_ok=True)
180
+ output_path.write_text(csv_str)
181
+ logger.info("exported_csv", path=str(output_path))
182
+
183
+ return csv_str
184
+
185
+ def export_markdown(self, data: list[Any], output_path: Path | None = None) -> str:
186
+ """Export data as Markdown table.
187
+
188
+ Args:
189
+ data: List of objects to export
190
+ output_path: Optional custom output path
191
+
192
+ Returns:
193
+ Markdown string
194
+ """
195
+ if not data:
196
+ md_str = ""
197
+ else:
198
+ # Get headers from first item
199
+ first_item = data[0]
200
+ if hasattr(first_item, "model_dump"):
201
+ headers = list(first_item.model_dump().keys())
202
+ rows = [item.model_dump() for item in data]
203
+ else:
204
+ headers = (
205
+ list(first_item.keys()) if isinstance(first_item, dict) else []
206
+ )
207
+ rows = data
208
+
209
+ # Build markdown table
210
+ md_lines = [
211
+ "| " + " | ".join(headers) + " |",
212
+ "| " + " | ".join(["---"] * len(headers)) + " |",
213
+ ]
214
+
215
+ for row in rows:
216
+ values = [
217
+ str(row.get(h, "")) if isinstance(row, dict) else ""
218
+ for h in headers
219
+ ]
220
+ md_lines.append("| " + " | ".join(values) + " |")
221
+
222
+ md_str = "\n".join(md_lines)
223
+
224
+ if output_path:
225
+ output_path.parent.mkdir(parents=True, exist_ok=True)
226
+ output_path.write_text(md_str)
227
+ logger.info("exported_markdown", path=str(output_path))
228
+
229
+ return md_str
230
+
231
+ def export_data(
232
+ self,
233
+ data: Any,
234
+ entity_type: str,
235
+ format_type: str | None = None,
236
+ output_path: Path | None = None,
237
+ ) -> tuple[str, Path]:
238
+ """Export data in specified format.
239
+
240
+ Args:
241
+ data: Data to export
242
+ entity_type: Type of entity being exported
243
+ format_type: Export format (uses config default if not specified)
244
+ output_path: Optional custom output path
245
+
246
+ Returns:
247
+ Tuple of (exported_content, file_path)
248
+ """
249
+ if format_type is None:
250
+ format_type = self.config.export.format
251
+
252
+ if output_path is None:
253
+ output_path = self.get_export_path(entity_type, format_type)
254
+
255
+ if format_type == "json":
256
+ content = self.export_json(data, output_path)
257
+ elif format_type == "csv":
258
+ content = self.export_csv(
259
+ data if isinstance(data, list) else [data], output_path
260
+ )
261
+ elif format_type == "markdown":
262
+ content = self.export_markdown(
263
+ data if isinstance(data, list) else [data], output_path
264
+ )
265
+ else:
266
+ raise ValueError(f"Unsupported export format: {format_type}")
267
+
268
+ return content, output_path
@@ -0,0 +1,266 @@
1
+ """Service layer for milestone list command.
2
+
3
+ Handles all business logic related to:
4
+ - Filtering milestones (overdue, open/closed, etc.)
5
+ - Computing milestone progress
6
+ - Calculating time estimates
7
+ - Aggregating milestone data
8
+ """
9
+
10
+ from datetime import UTC, datetime
11
+
12
+ from roadmap.common.logging import get_logger
13
+ from roadmap.infrastructure.coordination.core import RoadmapCore
14
+
15
+ logger = get_logger(__name__)
16
+
17
+
18
+ class MilestoneFilterService:
19
+ """Service for filtering and selecting milestones."""
20
+
21
+ @staticmethod
22
+ def filter_overdue_milestones(milestones: list) -> list:
23
+ """Filter to only overdue milestones.
24
+
25
+ Args:
26
+ milestones: List of milestone objects
27
+
28
+ Returns:
29
+ List of overdue milestones (past due date, status='open')
30
+ """
31
+ try:
32
+ now = datetime.now(UTC).replace(tzinfo=None)
33
+ filtered = []
34
+
35
+ for ms in milestones:
36
+ if ms.due_date:
37
+ ms_due_date = (
38
+ ms.due_date.replace(tzinfo=None)
39
+ if ms.due_date.tzinfo
40
+ else ms.due_date
41
+ )
42
+ if ms_due_date < now and ms.status.value == "open":
43
+ filtered.append(ms)
44
+
45
+ return filtered
46
+ except Exception as e:
47
+ logger.error(
48
+ "failed_to_filter_overdue", error=str(e), severity="operational"
49
+ )
50
+ return []
51
+
52
+ @staticmethod
53
+ def filter_milestones(milestones: list, overdue_only: bool = False) -> list:
54
+ """Filter milestones based on criteria.
55
+
56
+ Args:
57
+ milestones: List of milestone objects
58
+ overdue_only: If True, only return overdue milestones
59
+
60
+ Returns:
61
+ Filtered list of milestones
62
+ """
63
+ if overdue_only:
64
+ return MilestoneFilterService.filter_overdue_milestones(milestones)
65
+ return milestones
66
+
67
+
68
+ class MilestoneProgressService:
69
+ """Service for computing milestone progress."""
70
+
71
+ @staticmethod
72
+ def get_milestone_progress(core: RoadmapCore, milestone_name: str) -> dict:
73
+ """Get progress for a specific milestone.
74
+
75
+ Args:
76
+ core: RoadmapCore instance
77
+ milestone_name: Name of the milestone
78
+
79
+ Returns:
80
+ Dictionary with progress data:
81
+ {
82
+ 'total': total issues,
83
+ 'completed': completed issues,
84
+ 'percentage': completion percentage,
85
+ }
86
+ """
87
+ try:
88
+ progress = core.milestones.get_progress(milestone_name)
89
+ return {
90
+ "total": progress.get("total", 0),
91
+ "completed": progress.get("completed", 0),
92
+ "percentage": (
93
+ (progress.get("completed", 0) / progress.get("total", 1)) * 100
94
+ if progress.get("total", 0) > 0
95
+ else 0
96
+ ),
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 ms in milestones:
120
+ progress_data[ms.name] = MilestoneProgressService.get_milestone_progress(
121
+ core, ms.name
122
+ )
123
+ return progress_data
124
+
125
+
126
+ class MilestoneTimeEstimateService:
127
+ """Service for calculating time estimates."""
128
+
129
+ @staticmethod
130
+ def get_milestone_time_estimate(milestone, all_issues: list) -> str:
131
+ """Get time estimate display string for a milestone.
132
+
133
+ Args:
134
+ milestone: Milestone object
135
+ all_issues: List of all issues in roadmap
136
+
137
+ Returns:
138
+ Formatted time estimate string (e.g., "32 hours", "N/A")
139
+ """
140
+ try:
141
+ estimate = milestone.get_estimated_time_display(all_issues)
142
+ return estimate if estimate else "-"
143
+ except Exception as e:
144
+ logger.error(
145
+ "failed_to_get_time_estimate",
146
+ milestone=milestone.name,
147
+ severity="operational",
148
+ error=str(e),
149
+ )
150
+ return "-"
151
+
152
+
153
+ class MilestoneListService:
154
+ """Service for gathering and processing milestone list data."""
155
+
156
+ def __init__(self, core: RoadmapCore):
157
+ """Initialize milestone list service.
158
+
159
+ Args:
160
+ core: RoadmapCore instance
161
+ """
162
+ self.core = core
163
+
164
+ def get_milestones_list_data(self, overdue_only: bool = False) -> dict:
165
+ """Get all data needed for milestone list display.
166
+
167
+ Args:
168
+ overdue_only: If True, only return overdue milestones
169
+
170
+ Returns:
171
+ Dictionary with milestone list data:
172
+ {
173
+ 'milestones': list of milestone objects,
174
+ 'progress': dict mapping milestone name to progress,
175
+ 'estimates': dict mapping milestone name to estimate,
176
+ 'has_data': bool indicating if there are milestones,
177
+ 'count': number of milestones,
178
+ }
179
+ """
180
+ try:
181
+ # Get milestones
182
+ milestones = self.core.milestones.list()
183
+
184
+ # Apply filters
185
+ milestones = MilestoneFilterService.filter_milestones(
186
+ milestones, overdue_only=overdue_only
187
+ )
188
+
189
+ # Get all issues for estimates
190
+ all_issues = self.core.issues.list()
191
+
192
+ # Compute progress for each milestone
193
+ progress = MilestoneProgressService.get_all_milestones_progress(
194
+ self.core, milestones
195
+ )
196
+
197
+ # Compute time estimates
198
+ estimates = {}
199
+ for ms in milestones:
200
+ estimates[ms.name] = (
201
+ MilestoneTimeEstimateService.get_milestone_time_estimate(
202
+ ms, all_issues
203
+ )
204
+ )
205
+
206
+ return {
207
+ "milestones": milestones,
208
+ "progress": progress,
209
+ "estimates": estimates,
210
+ "has_data": bool(milestones),
211
+ "count": len(milestones),
212
+ }
213
+ except Exception as e:
214
+ logger.error(
215
+ "failed_to_get_milestones_list_data",
216
+ severity="operational",
217
+ error=str(e),
218
+ )
219
+ return {
220
+ "milestones": [],
221
+ "progress": {},
222
+ "estimates": {},
223
+ "has_data": False,
224
+ "count": 0,
225
+ }
226
+
227
+ def get_milestone_due_date_status(self, milestone) -> tuple[str, str | None]:
228
+ """Get due date string and styling for a milestone.
229
+
230
+ Args:
231
+ milestone: Milestone object
232
+
233
+ Returns:
234
+ Tuple of (due_date_string, style_markup or None)
235
+ - style_markup is None for no special styling
236
+ - style_markup is "bold red" for overdue
237
+ - style_markup is "yellow" for due within 7 days
238
+ """
239
+ if not milestone.due_date:
240
+ return ("-", None)
241
+
242
+ try:
243
+ now = datetime.now(UTC).replace(tzinfo=None)
244
+ ms_due_date = (
245
+ milestone.due_date.replace(tzinfo=None)
246
+ if milestone.due_date.tzinfo
247
+ else milestone.due_date
248
+ )
249
+
250
+ due_date_text = ms_due_date.strftime("%Y-%m-%d")
251
+
252
+ # Determine styling based on due date and status
253
+ if ms_due_date < now and milestone.status.value == "open":
254
+ return (due_date_text, "bold red")
255
+ elif (ms_due_date - now).days <= 7 and milestone.status.value == "open":
256
+ return (due_date_text, "yellow")
257
+
258
+ return (due_date_text, None)
259
+ except Exception as e:
260
+ logger.error(
261
+ "failed_to_get_due_date_status",
262
+ milestone=milestone.name,
263
+ severity="operational",
264
+ error=str(e),
265
+ )
266
+ return ("-", None)
@@ -0,0 +1,29 @@
1
+ """Project Initialization Service - Backward Compatibility Facade.
2
+
3
+ DEPRECATED: This module is maintained for backward compatibility.
4
+ Use roadmap.core.services.project_init package instead.
5
+
6
+ New structure:
7
+ - roadmap.core.services.project_init.detection - ProjectDetectionService
8
+ - roadmap.core.services.project_init.context_detection - ProjectContextDetectionService
9
+ - roadmap.core.services.project_init.template - ProjectTemplateService
10
+ - roadmap.core.services.project_init.creation - ProjectCreationService
11
+ """
12
+
13
+ from roadmap.adapters.persistence.parser import ProjectParser
14
+ from roadmap.core.services.project_init import (
15
+ ProjectContextDetectionService,
16
+ ProjectCreationService,
17
+ ProjectDetectionService,
18
+ ProjectTemplateService,
19
+ )
20
+ from roadmap.infrastructure.coordination.core import RoadmapCore
21
+
22
+ __all__ = [
23
+ "ProjectDetectionService",
24
+ "ProjectContextDetectionService",
25
+ "ProjectTemplateService",
26
+ "ProjectCreationService",
27
+ "ProjectParser",
28
+ "RoadmapCore",
29
+ ]