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,446 @@
1
+ """RoadmapCore - Lightweight Service Container and Coordinator.
2
+
3
+ RoadmapCore is a lightweight facade that provides a clean, domain-organized
4
+ API by coordinating across multiple specialized domain coordinators.
5
+
6
+ Instead of being a god object with 50+ methods, RoadmapCore now delegates
7
+ to focused domain coordinators:
8
+ - IssueCoordinator: Issue CRUD and queries
9
+ - MilestoneCoordinator: Milestone CRUD and consistency validation
10
+ - ProjectCoordinator: Project CRUD and management
11
+ - TeamCoordinator: Team member and user management
12
+ - GitCoordinator: Git integration and branch linking
13
+ - ValidationCoordinator: Validation and configuration
14
+
15
+ This design:
16
+ - Keeps RoadmapCore <200 LOC (down from 670 LOC)
17
+ - Makes each domain independently testable
18
+ - Provides a clear mental model for users
19
+ - Scales well for future features
20
+ """
21
+
22
+ from pathlib import Path
23
+
24
+ from roadmap.common.logging import get_logger
25
+ from roadmap.common.utils.path_utils import build_roadmap_paths
26
+ from roadmap.core.services import (
27
+ ConfigurationService,
28
+ GitHubIntegrationService,
29
+ IssueService,
30
+ MilestoneService,
31
+ ProjectService,
32
+ )
33
+ from roadmap.infrastructure.coordination.git_coordinator import GitCoordinator
34
+ from roadmap.infrastructure.coordination.initialization import InitializationManager
35
+ from roadmap.infrastructure.coordination.issue_coordinator import IssueCoordinator
36
+ from roadmap.infrastructure.coordination.issue_operations import IssueOperations
37
+ from roadmap.infrastructure.coordination.milestone_coordinator import (
38
+ MilestoneCoordinator,
39
+ )
40
+ from roadmap.infrastructure.coordination.milestone_operations import MilestoneOperations
41
+ from roadmap.infrastructure.coordination.project_coordinator import ProjectCoordinator
42
+ from roadmap.infrastructure.coordination.project_operations import ProjectOperations
43
+ from roadmap.infrastructure.coordination.team_coordinator import TeamCoordinator
44
+ from roadmap.infrastructure.coordination.user_operations import UserOperations
45
+ from roadmap.infrastructure.coordination.validation_coordinator import (
46
+ ValidationCoordinator,
47
+ )
48
+ from roadmap.infrastructure.coordination_gateway import CoordinationGateway
49
+ from roadmap.infrastructure.git.git_integration_ops import GitIntegrationOps
50
+
51
+ logger = get_logger(__name__)
52
+
53
+
54
+ class RoadmapCore:
55
+ """Lightweight service container and coordinator of domain coordinators.
56
+
57
+ RoadmapCore provides a clean, organized API by delegating to focused
58
+ domain coordinators for each concern (issues, milestones, projects, etc).
59
+
60
+ Usage:
61
+ core = RoadmapCore()
62
+ core.initialize() # One-time setup
63
+
64
+ # Use domain-specific coordinators
65
+ issue = core.issues.create(title="...")
66
+ milestone = core.milestones.create(name="...")
67
+ members = core.team.get_members()
68
+ commits = core.git.get_commits_for_issue("issue-123")
69
+ """
70
+
71
+ def __init__(
72
+ self, root_path: Path | None = None, roadmap_dir_name: str = ".roadmap"
73
+ ):
74
+ """Initialize RoadmapCore with service setup and coordinators.
75
+
76
+ Args:
77
+ root_path: Root path for the roadmap (defaults to cwd)
78
+ roadmap_dir_name: Name of the roadmap directory (defaults to .roadmap)
79
+ """
80
+ self.root_path = root_path or Path.cwd()
81
+ self.roadmap_dir_name = roadmap_dir_name
82
+
83
+ # Build all standard roadmap paths
84
+ paths = build_roadmap_paths(self.root_path, roadmap_dir_name)
85
+ self.roadmap_dir = paths["roadmap_dir"]
86
+ self.issues_dir = paths["issues_dir"]
87
+ self.milestones_dir = paths["milestones_dir"]
88
+ self.projects_dir = paths["projects_dir"]
89
+ self.templates_dir = paths["templates_dir"]
90
+ self.artifacts_dir = paths["artifacts_dir"]
91
+ self.config_file = paths["config_file"]
92
+ self.db_dir = paths["db_dir"]
93
+
94
+ # Initialize core infrastructure
95
+ self._git = CoordinationGateway.get_git_integration()
96
+ self._git.root_path = self.root_path
97
+
98
+ # Initialize StateManager with correct project-local database path
99
+ db_path = self.db_dir / "state.db"
100
+ self.db = CoordinationGateway.get_state_manager(db_path=db_path)
101
+
102
+ # Initialize GitSyncMonitor with StateManager for database sync
103
+ self.git_sync_monitor = CoordinationGateway.get_git_sync_monitor()
104
+ self.git_sync_monitor.repo_path = self.root_path
105
+ self.git_sync_monitor.state_manager = self.db
106
+
107
+ # Initialize remote links from YAML (Phase 3)
108
+ try:
109
+ self.db.initialize_remote_links(self.root_path)
110
+ except Exception as e:
111
+ from roadmap.common.logging import get_logger
112
+
113
+ logger = get_logger(__name__)
114
+ logger.warning(
115
+ "failed_to_initialize_remote_links_in_core",
116
+ error=str(e),
117
+ severity="operational",
118
+ )
119
+
120
+ # Wire GitSyncMonitor into StateManager for transparent cache sync
121
+ self.db._git_sync_monitor = self.git_sync_monitor
122
+
123
+ self.github_service = GitHubIntegrationService(
124
+ root_path=self.root_path, config_file=self.config_file
125
+ )
126
+ self.config_service = ConfigurationService()
127
+
128
+ # Initialize repositories (abstraction layer)
129
+ issue_repository = CoordinationGateway.get_yaml_issue_repository(
130
+ db=self.db, issues_dir=self.issues_dir
131
+ )
132
+
133
+ from roadmap.adapters.persistence.yaml_repositories import (
134
+ YAMLMilestoneRepository,
135
+ YAMLProjectRepository,
136
+ )
137
+
138
+ milestone_repository = YAMLMilestoneRepository(self.db, self.milestones_dir)
139
+ project_repository = YAMLProjectRepository(self.db, self.projects_dir)
140
+
141
+ # Initialize services with repository injection
142
+ # (decoupled from implementation)
143
+ self.issue_service = IssueService(issue_repository)
144
+ self.milestone_service = MilestoneService(
145
+ milestone_repository,
146
+ issue_repository=issue_repository,
147
+ issues_dir=self.issues_dir,
148
+ milestones_dir=self.milestones_dir,
149
+ )
150
+ self.project_service = ProjectService(project_repository, self.milestones_dir)
151
+
152
+ # Keep reference to init manager for setup (needed before creating coordinators)
153
+ self._init_manager = InitializationManager(
154
+ self.root_path, self.roadmap_dir_name
155
+ )
156
+
157
+ # Initialize operations managers
158
+ issue_ops = IssueOperations(self.issue_service, self.issues_dir)
159
+ milestone_ops = MilestoneOperations(self.milestone_service)
160
+ project_ops = ProjectOperations(self.project_service)
161
+
162
+ # Determine appropriate assignee validator based on sync backend
163
+ assignee_validator = self._get_assignee_validator()
164
+ user_ops = UserOperations(
165
+ self.github_service, self.issue_service, assignee_validator
166
+ )
167
+ git_ops = GitIntegrationOps(self._git, self)
168
+
169
+ # Initialize domain coordinators (pass self for initialization check)
170
+ self.issues = IssueCoordinator(issue_ops, core=self)
171
+ self.milestones = MilestoneCoordinator(
172
+ milestone_ops, self.milestones_dir, core=self
173
+ )
174
+ self.projects = ProjectCoordinator(project_ops, core=self)
175
+ self.team = TeamCoordinator(user_ops, core=self)
176
+ self.git = GitCoordinator(git_ops, core=self)
177
+ self.validation = ValidationCoordinator(self.github_service, core=self)
178
+
179
+ def is_initialized(self) -> bool:
180
+ """Check if roadmap is initialized in current directory."""
181
+ return self._init_manager.is_initialized()
182
+
183
+ def _check_initialized(self) -> None:
184
+ """Check that roadmap is initialized and raise if not."""
185
+ if not self.is_initialized():
186
+ raise ValueError("Roadmap not initialized. Run 'roadmap init' first.")
187
+
188
+ def _get_assignee_validator(self):
189
+ """Determine the appropriate assignee validator based on sync backend.
190
+
191
+ Returns the validator for the configured sync backend:
192
+ - "github" backend → GitHubAssigneeValidator (validates against collaborators)
193
+ - "git" or other backend → VanillaAssigneeValidator (accepts any assignee)
194
+
195
+ Defaults to vanilla/git validator if no backend is configured.
196
+
197
+ Returns:
198
+ An assignee validator instance (implements AssigneeValidator protocol)
199
+ """
200
+ from roadmap.common.configuration import ConfigManager
201
+ from roadmap.infrastructure.validation.github_assignee_validator import (
202
+ GitHubAssigneeValidator,
203
+ )
204
+ from roadmap.infrastructure.validation.vanilla_assignee_validator import (
205
+ VanillaAssigneeValidator,
206
+ )
207
+
208
+ try:
209
+ config_manager = ConfigManager(self.config_file)
210
+ config = config_manager.load()
211
+ # Access dataclass attributes, not dictionary
212
+ sync_backend = config.github.sync_backend if config.github else "git"
213
+ except Exception:
214
+ # If we can't read config, default to vanilla/git validator
215
+ sync_backend = "git"
216
+
217
+ if str(sync_backend).lower() == "github":
218
+ return GitHubAssigneeValidator(self.github_service)
219
+ else:
220
+ # Default to vanilla validator for "git" backend or if not specified
221
+ return VanillaAssigneeValidator()
222
+
223
+ @classmethod
224
+ def find_existing_roadmap(
225
+ cls, root_path: Path | None = None
226
+ ) -> "RoadmapCore | None":
227
+ """Find an existing roadmap directory in the current path.
228
+
229
+ Searches for common roadmap directory names and returns a RoadmapCore
230
+ instance if found, or None if no roadmap is detected.
231
+ """
232
+ manager = InitializationManager.find_existing_roadmap(root_path)
233
+ if manager:
234
+ return cls(
235
+ root_path=manager.root_path, roadmap_dir_name=manager.roadmap_dir_name
236
+ )
237
+ return None
238
+
239
+ def initialize(self) -> None:
240
+ """Initialize a new roadmap in the current directory."""
241
+ self._init_manager.initialize()
242
+
243
+ def close(self) -> None:
244
+ """Close any database resources held by this core."""
245
+ try:
246
+ self.db.close()
247
+ except Exception as e:
248
+ logger.warning(
249
+ "core_close_failed", error=str(e), error_type=type(e).__name__
250
+ )
251
+
252
+ def __enter__(self) -> "RoadmapCore":
253
+ """Enter context manager and return self."""
254
+ return self
255
+
256
+ def __exit__(self, _exc_type, _exc, _tb) -> None:
257
+ """Exit context manager and close resources."""
258
+ self.close()
259
+
260
+ def _update_gitignore(self) -> None:
261
+ """Update .gitignore to exclude roadmap local data from version control."""
262
+ self._init_manager._update_gitignore()
263
+
264
+ def _sync_with_progress(self, message: str) -> dict | None:
265
+ """Run database sync with progress display.
266
+
267
+ Args:
268
+ message: Progress message to display
269
+
270
+ Returns:
271
+ Sync result dictionary or None
272
+ """
273
+ from rich.progress import Progress, SpinnerColumn, TextColumn
274
+
275
+ with Progress(
276
+ SpinnerColumn(),
277
+ TextColumn(f"[bold blue]{message}..."),
278
+ transient=True,
279
+ ) as progress:
280
+ progress.add_task("sync", total=None)
281
+ return self.db.smart_sync()
282
+
283
+ def _sync_without_progress(self) -> dict | None:
284
+ """Run database sync without progress display.
285
+
286
+ Returns:
287
+ Sync result dictionary or None
288
+ """
289
+ return self.db.smart_sync()
290
+
291
+ def _display_sync_result(self, console, message: str, files_synced: int) -> None:
292
+ """Display sync result message.
293
+
294
+ Args:
295
+ console: Rich console instance
296
+ message: Message to display
297
+ files_synced: Number of files synced
298
+ """
299
+ console.print(f"✅ {message}: {files_synced} files synced")
300
+
301
+ def _handle_first_time_setup(
302
+ self, console, show_progress: bool, force_rebuild: bool
303
+ ) -> None:
304
+ """Handle first-time database initialization.
305
+
306
+ Args:
307
+ console: Rich console instance
308
+ show_progress: Whether to show progress
309
+ force_rebuild: Whether full rebuild was requested
310
+ """
311
+ if show_progress:
312
+ sync_result = self._sync_with_progress(
313
+ "Initializing database from .roadmap/ files"
314
+ )
315
+ else:
316
+ sync_result = self._sync_without_progress()
317
+
318
+ if show_progress and sync_result:
319
+ files_synced = sync_result.get("files_synced", 0)
320
+ total_files = sync_result.get("total_files", 0)
321
+ console.print(
322
+ f"✅ Database initialized: {files_synced}/{total_files} files synced"
323
+ )
324
+
325
+ if not force_rebuild and self._git.is_git_repository():
326
+ self._ensure_git_hooks_installed(console, show_progress)
327
+
328
+ def _handle_incremental_sync(self, console, show_progress: bool) -> None:
329
+ """Handle incremental database sync.
330
+
331
+ Args:
332
+ console: Rich console instance
333
+ show_progress: Whether to show progress
334
+ """
335
+ if not self.db.has_file_changes():
336
+ return
337
+
338
+ if show_progress:
339
+ sync_result = self._sync_with_progress(
340
+ "Updating database with recent changes"
341
+ )
342
+ else:
343
+ sync_result = self._sync_without_progress()
344
+
345
+ if show_progress and sync_result:
346
+ files_synced = sync_result.get("files_synced", 0)
347
+ self._display_sync_result(console, "Database updated", files_synced)
348
+
349
+ def ensure_database_synced(
350
+ self, force_rebuild: bool = False, show_progress: bool = True
351
+ ) -> None:
352
+ """Ensure database is synced with .roadmap/ files.
353
+
354
+ This is called automatically on CLI startup to keep SQLite in sync with git files.
355
+
356
+ Args:
357
+ force_rebuild: Force a full rebuild even if database exists
358
+ show_progress: Show progress indicators during sync
359
+ """
360
+ from rich.console import Console
361
+
362
+ console = Console()
363
+
364
+ if not self.is_initialized():
365
+ return
366
+
367
+ first_time_setup = not self.db.database_exists()
368
+
369
+ if first_time_setup or force_rebuild:
370
+ self._handle_first_time_setup(console, show_progress, first_time_setup)
371
+ else:
372
+ self._handle_incremental_sync(console, show_progress)
373
+
374
+ def _ensure_git_hooks_installed(self, console, show_progress: bool = True) -> None:
375
+ """Ensure git hooks are installed for automatic sync."""
376
+ try:
377
+ hook_manager = CoordinationGateway.get_git_hook_manager(self)
378
+
379
+ if show_progress:
380
+ console.print("[dim]Installing git hooks for automatic sync...[/dim]")
381
+
382
+ success = hook_manager.install_hooks()
383
+
384
+ if show_progress:
385
+ if success:
386
+ console.print("✅ Git hooks installed successfully")
387
+ else:
388
+ console.print("[yellow]⚠️ Git hooks installation failed[/yellow]")
389
+
390
+ except Exception as e:
391
+ if show_progress:
392
+ console.print(f"[yellow]⚠️ Git hooks setup failed: {e}[/yellow]")
393
+
394
+ # ========== BACKWARD COMPATIBILITY WRAPPERS ==========
395
+ # These methods delegate to domain coordinators for backward compatibility
396
+ # New code should use: core.issues.method(), core.milestones.method(), etc.
397
+
398
+ def get_issues_by_milestone(self) -> dict[str, list]:
399
+ """Get all issues grouped by milestone (backward compatibility wrapper)."""
400
+ return self.issues.get_grouped_by_milestone()
401
+
402
+ def get_next_milestone(self):
403
+ """Get the next upcoming milestone by due date (backward compatibility wrapper)."""
404
+ return self.milestones._ops.get_next_milestone()
405
+
406
+ def move_issue_to_milestone(self, issue_id: str, milestone_name: str):
407
+ """Move an issue to a specific milestone (backward compatibility wrapper)."""
408
+ return self.issues.move_to_milestone(issue_id, milestone_name)
409
+
410
+ def load_config(self):
411
+ """Load roadmap configuration."""
412
+ if not self.is_initialized():
413
+ raise ValueError("Roadmap not initialized. Run 'roadmap init' first.")
414
+
415
+ import yaml
416
+
417
+ with open(self.config_file) as f:
418
+ loaded = yaml.safe_load(f)
419
+ config_data: dict = loaded if isinstance(loaded, dict) else {}
420
+
421
+ # Ensure all expected config sections exist
422
+ if "milestones" not in config_data:
423
+ config_data["milestones"] = {
424
+ "auto_sequence": True,
425
+ "version_format": "v{major}.{minor}.{patch}",
426
+ }
427
+
428
+ if "sync" not in config_data:
429
+ config_data["sync"] = {
430
+ "github_enabled": False,
431
+ "auto_sync": False,
432
+ "sync_interval_seconds": 300,
433
+ }
434
+
435
+ if "display" not in config_data:
436
+ config_data["display"] = {
437
+ "theme": "default",
438
+ "show_progress": True,
439
+ "compact_output": False,
440
+ }
441
+
442
+ class ConfigObject:
443
+ def __init__(self, data):
444
+ self.__dict__.update(data)
445
+
446
+ return ConfigObject(config_data)
@@ -0,0 +1,160 @@
1
+ """Git Coordinator - Coordinates git-related operations.
2
+
3
+ Extracted from RoadmapCore to reduce god object complexity.
4
+ Provides a focused API for all git integration concerns.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from typing import TYPE_CHECKING, Any
10
+
11
+ from roadmap.core.domain import Issue
12
+ from roadmap.infrastructure.git.git_integration_ops import GitIntegrationOps
13
+
14
+ if TYPE_CHECKING:
15
+ from roadmap.infrastructure.coordination.core import RoadmapCore
16
+
17
+
18
+ class GitCoordinator:
19
+ """Coordinates all git-related operations."""
20
+
21
+ def __init__(self, git_ops: GitIntegrationOps, core: RoadmapCore | None = None):
22
+ """Initialize coordinator with git operations manager.
23
+
24
+ Args:
25
+ git_ops: GitIntegrationOps instance
26
+ core: RoadmapCore instance for initialization checks
27
+ """
28
+ self._ops: GitIntegrationOps = git_ops
29
+ self._git: Any = git_ops.git
30
+ self._core = core
31
+
32
+ def get_context(self) -> dict[str, Any]:
33
+ """Get Git repository context information."""
34
+ return self._ops.get_git_context()
35
+
36
+ def get_current_user(self) -> str | None:
37
+ """Get current user from Git configuration."""
38
+ return self._ops.get_current_user_from_git()
39
+
40
+ def create_issue_with_branch(self, title: str, **kwargs) -> Issue | None:
41
+ """Create an issue and optionally create a Git branch for it."""
42
+ return self._ops.create_issue_with_git_branch(title, **kwargs)
43
+
44
+ def link_issue_to_branch(self, issue_id: str) -> bool:
45
+ """Link an issue to the current Git branch."""
46
+ return self._ops.link_issue_to_current_branch(issue_id)
47
+
48
+ def get_commits_for_issue(self, issue_id: str, since: str | None = None) -> list:
49
+ """Get Git commits that reference this issue."""
50
+ return self._ops.get_commits_for_issue(issue_id, since)
51
+
52
+ def update_issue_from_activity(self, issue_id: str) -> bool:
53
+ """Update issue progress and status based on Git commit activity."""
54
+ return self._ops.update_issue_from_git_activity(issue_id)
55
+
56
+ def suggest_branch_name(self, issue_id: str) -> str | None:
57
+ """Suggest a branch name for an issue."""
58
+ return self._ops.suggest_branch_name_for_issue(issue_id)
59
+
60
+ def get_branch_linked_issues(self) -> dict[str, list[str]]:
61
+ """Get mapping of branches to their linked issue IDs."""
62
+ return self._ops.get_branch_linked_issues()
63
+
64
+ def is_git_repository(self) -> bool:
65
+ """Check if current directory is a git repository."""
66
+ return self._ops.git.is_git_repository()
67
+
68
+ def create_branch_for_issue(self, issue: Issue, checkout: bool = True) -> bool:
69
+ """Create a Git branch for an issue.
70
+
71
+ Args:
72
+ issue: The Issue object to create a branch for
73
+ checkout: If True, checkout the created branch (default: True)
74
+
75
+ Returns:
76
+ True if branch was created successfully, False otherwise
77
+ """
78
+ return self._ops.git.create_branch_for_issue(issue, checkout=checkout)
79
+
80
+ @property
81
+ def repo_path(self):
82
+ """Get the repository path (for backward compatibility)."""
83
+ return self._ops.git.repo_path
84
+
85
+ def get_repository_info(self) -> dict[str, Any]:
86
+ """Get repository information.
87
+
88
+ Returns:
89
+ Dictionary with repository metadata
90
+ """
91
+ return self._git.get_repository_info()
92
+
93
+ def get_current_branch(self) -> str | None:
94
+ """Get the current Git branch name.
95
+
96
+ Returns:
97
+ Current branch name or None if not in a git repo
98
+ """
99
+ branch = self._git.get_current_branch()
100
+ return branch.name if branch else None
101
+
102
+ def get_local_changes(self) -> list[str]:
103
+ """Get list of issue IDs that have changed locally since last sync.
104
+
105
+ Compares current issue files in .roadmap/issues with the last committed
106
+ versions (git HEAD) to detect which issues were modified locally.
107
+
108
+ Returns:
109
+ List of issue IDs that have uncommitted changes
110
+ """
111
+ try:
112
+ import subprocess
113
+ from pathlib import Path
114
+
115
+ issues_dir = self._core.issues_dir if self._core else None
116
+ if not issues_dir:
117
+ return []
118
+
119
+ # Get modified files relative to git HEAD in the issues directory
120
+ result = subprocess.run(
121
+ [
122
+ "git",
123
+ "diff",
124
+ "HEAD",
125
+ "--name-only",
126
+ "--",
127
+ str(issues_dir),
128
+ ],
129
+ capture_output=True,
130
+ text=True,
131
+ cwd=self._git.repo_path,
132
+ )
133
+
134
+ if result.returncode != 0:
135
+ # If diff fails, return empty list (might not be in git repo)
136
+ return []
137
+
138
+ # Parse filenames to extract issue IDs
139
+ changed_issue_ids = []
140
+ for line in result.stdout.strip().split("\n"):
141
+ if not line:
142
+ continue
143
+
144
+ # Extract filename from path (e.g., ".roadmap/issues/ID.md" -> "ID")
145
+ filepath = Path(line)
146
+ filename = filepath.stem # Remove .md extension
147
+
148
+ # Validate it looks like an issue ID (uuid-like)
149
+ if filename and len(filename) > 4:
150
+ changed_issue_ids.append(filename)
151
+
152
+ return changed_issue_ids
153
+
154
+ except Exception as e:
155
+ # Log but don't crash on git errors
156
+ if self._core:
157
+ import structlog
158
+
159
+ structlog.get_logger().warning("get_local_changes_failed", error=str(e))
160
+ return []