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,325 @@
1
+ """CLI operation feedback formatting.
2
+
3
+ This module provides both functional and class-based formatters for operation results.
4
+ Use OperationFormatter class for new code. Legacy functions remain for backward compatibility.
5
+ """
6
+
7
+
8
+ class OperationFormatter:
9
+ """Unified formatter for all operation results.
10
+
11
+ Provides a consistent interface for formatting operation successes, failures,
12
+ and entity details across the CLI. This class consolidates all formatting logic
13
+ that was previously scattered across multiple functions.
14
+ """
15
+
16
+ @staticmethod
17
+ def success(
18
+ emoji: str,
19
+ action: str,
20
+ entity_title: str | None = None,
21
+ entity_id: str | None = None,
22
+ details: dict[str, str] | None = None,
23
+ ) -> str:
24
+ """Format a successful operation as a string.
25
+
26
+ Args:
27
+ emoji: Emoji to display (e.g., "✅", "🚫", "📊")
28
+ action: Action verb (e.g., "Blocked", "Closed", "Updated")
29
+ entity_title: Title/name of the entity affected (e.g., issue title)
30
+ entity_id: ID of the entity (displayed separately as cyan)
31
+ details: Optional dict of additional details to display
32
+
33
+ Returns:
34
+ Formatted string for console output
35
+ """
36
+ lines = []
37
+
38
+ # Main success line: emoji + action + title
39
+ if entity_title:
40
+ lines.append(f"{emoji} {action} issue: {entity_title}")
41
+ else:
42
+ lines.append(f"{emoji} {action}")
43
+
44
+ # Entity ID line (if provided)
45
+ if entity_id:
46
+ lines.append(f" ID: {entity_id}")
47
+
48
+ # Extra details (if provided)
49
+ if details:
50
+ for key, value in details.items():
51
+ lines.append(f" {key}: {value}")
52
+
53
+ return "\n".join(lines)
54
+
55
+ @staticmethod
56
+ def failure(
57
+ action: str,
58
+ entity_id: str | None = None,
59
+ error: str | None = None,
60
+ suggestion: str | None = None,
61
+ ) -> str:
62
+ """Format a failed operation as a string.
63
+
64
+ Args:
65
+ action: Action that failed (e.g., "block", "close", "update")
66
+ entity_id: ID of the entity that failed (optional)
67
+ error: Error message/reason for failure
68
+ suggestion: Suggested recovery action
69
+
70
+ Returns:
71
+ Formatted string for console output
72
+ """
73
+ lines = []
74
+
75
+ # Main failure line
76
+ if entity_id:
77
+ lines.append(f"❌ Failed to {action} issue: {entity_id}")
78
+ else:
79
+ lines.append(f"❌ Failed to {action}")
80
+
81
+ # Error details (if provided)
82
+ if error:
83
+ lines.append(f" Error: {error}")
84
+
85
+ # Suggestion (if provided)
86
+ if suggestion:
87
+ lines.append(f" 💡 {suggestion}")
88
+
89
+ return "\n".join(lines)
90
+
91
+ @staticmethod
92
+ def entity(
93
+ entity_id: str,
94
+ entity_title: str | None = None,
95
+ entity_type: str = "item",
96
+ status: str | None = None,
97
+ details: dict[str, str] | None = None,
98
+ ) -> str:
99
+ """Format entity details as a string.
100
+
101
+ Args:
102
+ entity_id: ID of the entity
103
+ entity_title: Title/name of the entity
104
+ entity_type: Type of entity (e.g., "issue", "milestone")
105
+ status: Current status of entity
106
+ details: Additional details dict
107
+
108
+ Returns:
109
+ Formatted string for console output
110
+ """
111
+ lines = []
112
+
113
+ # ID and title
114
+ if entity_title:
115
+ lines.append(f"📋 {entity_type.title()}: {entity_title}")
116
+ lines.append(f" ID: {entity_id}")
117
+
118
+ # Status
119
+ if status:
120
+ lines.append(f" Status: {status}")
121
+
122
+ # Additional details
123
+ if details:
124
+ for key, value in details.items():
125
+ lines.append(f" {key}: {value}")
126
+
127
+ return "\n".join(lines)
128
+
129
+
130
+ # ─────────────────────────────────────────────────────────────────────────────
131
+ # Legacy Functions (Backward Compatibility)
132
+ # ─────────────────────────────────────────────────────────────────────────────
133
+ # These functions are deprecated. Use OperationFormatter class instead.
134
+ # They are retained for backward compatibility with existing code.
135
+
136
+
137
+ def format_operation_success(
138
+ emoji: str,
139
+ action: str,
140
+ entity_title: str | None = None,
141
+ entity_id: str | None = None,
142
+ reason: str | None = None,
143
+ extra_details: dict[str, str] | None = None,
144
+ ) -> list[str]:
145
+ """Format a successful operation message with consistent styling.
146
+
147
+ Returns a list of formatted lines for console output.
148
+
149
+ Args:
150
+ emoji: Emoji to display (e.g., "✅", "🚫", "📊")
151
+ action: Action verb (e.g., "Blocked", "Closed", "Updated")
152
+ entity_title: Title/name of the entity affected (e.g., issue title)
153
+ entity_id: ID of the entity (displayed separately as cyan)
154
+ reason: Optional reason for the operation
155
+ extra_details: Optional dict of additional details to display
156
+
157
+ Returns:
158
+ List of formatted output lines
159
+ """
160
+ lines = []
161
+
162
+ # Main success line: emoji + action + title
163
+ if entity_title:
164
+ lines.append(f"{emoji} {action} issue: {entity_title}")
165
+ else:
166
+ lines.append(f"{emoji} {action}")
167
+
168
+ # Entity ID line (if provided)
169
+ if entity_id:
170
+ lines.append(f" ID: {entity_id}")
171
+
172
+ # Reason line (if provided)
173
+ if reason:
174
+ lines.append(f" Reason: {reason}")
175
+
176
+ # Extra details (if provided)
177
+ if extra_details:
178
+ for key, value in extra_details.items():
179
+ lines.append(f" {key}: {value}")
180
+
181
+ return lines
182
+
183
+
184
+ def format_operation_failure(
185
+ action: str,
186
+ entity_id: str | None = None,
187
+ error: str | None = None,
188
+ suggestion: str | None = None,
189
+ ) -> list[str]:
190
+ """Format a failed operation message with consistent styling.
191
+
192
+ Returns a list of formatted lines for console output.
193
+
194
+ Args:
195
+ action: Action that failed (e.g., "block", "close", "update")
196
+ entity_id: ID of the entity that failed (optional)
197
+ error: Error message/reason for failure
198
+ suggestion: Suggested recovery action
199
+
200
+ Returns:
201
+ List of formatted output lines
202
+ """
203
+ lines = []
204
+
205
+ # Main failure line
206
+ if entity_id:
207
+ lines.append(f"❌ Failed to {action} issue: {entity_id}")
208
+ else:
209
+ lines.append(f"❌ Failed to {action}")
210
+
211
+ # Error details (if provided)
212
+ if error:
213
+ lines.append(f" Error: {error}")
214
+
215
+ # Suggestion (if provided)
216
+ if suggestion:
217
+ lines.append(f" 💡 {suggestion}")
218
+
219
+ return lines
220
+
221
+
222
+ def format_entity_details(
223
+ entity_id: str,
224
+ entity_title: str | None = None,
225
+ entity_type: str = "item",
226
+ status: str | None = None,
227
+ details: dict[str, str] | None = None,
228
+ ) -> list[str]:
229
+ """Format entity details for consistent display across commands.
230
+
231
+ Args:
232
+ entity_id: ID of the entity
233
+ entity_title: Title/name of the entity
234
+ entity_type: Type of entity (e.g., "issue", "milestone")
235
+ status: Current status of entity
236
+ details: Additional details dict
237
+
238
+ Returns:
239
+ List of formatted output lines
240
+ """
241
+ lines = []
242
+
243
+ # ID and title
244
+ if entity_title:
245
+ lines.append(f"📋 {entity_type.title()}: {entity_title}")
246
+ lines.append(f" ID: {entity_id}")
247
+
248
+ # Status
249
+ if status:
250
+ lines.append(f" Status: {status}")
251
+
252
+ # Additional details
253
+ if details:
254
+ for key, value in details.items():
255
+ lines.append(f" {key}: {value}")
256
+
257
+ return lines
258
+
259
+
260
+ def format_list_items(
261
+ items: list[dict[str, str]],
262
+ show_count: int | None = None,
263
+ more_suffix: str = "... and {count} more",
264
+ ) -> list[str]:
265
+ """Format a list of items consistently.
266
+
267
+ Args:
268
+ items: List of dicts with 'id' and 'title' keys minimum
269
+ show_count: Maximum items to show (None = show all)
270
+ more_suffix: Format string for "... and X more" suffix
271
+
272
+ Returns:
273
+ List of formatted output lines
274
+ """
275
+ lines = []
276
+
277
+ # Determine how many to show
278
+ items_to_show = items
279
+ remaining = 0
280
+ if show_count and len(items) > show_count:
281
+ items_to_show = items[:show_count]
282
+ remaining = len(items) - show_count
283
+
284
+ # Format each item
285
+ for item in items_to_show:
286
+ item_id = item.get("id", "")[:8] # Show first 8 chars of ID
287
+ title = item.get("title", "")
288
+ lines.append(f" • {item_id} - {title}")
289
+
290
+ # Add "... and X more" if truncated
291
+ if remaining > 0:
292
+ lines.append(more_suffix.format(count=remaining))
293
+
294
+ return lines
295
+
296
+
297
+ # Console wrapper functions for CLI commands that need to print and potentially exit
298
+ def print_operation_success(
299
+ console,
300
+ emoji: str,
301
+ action: str,
302
+ entity_title: str | None = None,
303
+ entity_id: str | None = None,
304
+ reason: str | None = None,
305
+ extra_details: dict[str, str] | None = None,
306
+ ) -> None:
307
+ """Print a successful operation message to console."""
308
+ lines = format_operation_success(
309
+ emoji, action, entity_title, entity_id, reason, extra_details
310
+ )
311
+ for line in lines:
312
+ console.print(line)
313
+
314
+
315
+ def print_operation_failure(
316
+ console,
317
+ action: str,
318
+ entity_id: str | None = None,
319
+ error: str | None = None,
320
+ suggestion: str | None = None,
321
+ ) -> None:
322
+ """Print a failed operation message to console."""
323
+ lines = format_operation_failure(action, entity_id, error, suggestion)
324
+ for line in lines:
325
+ console.print(line)
@@ -0,0 +1,57 @@
1
+ """Status badge and percentage formatting."""
2
+
3
+ from rich.text import Text
4
+
5
+
6
+ def format_status_badge(status: str) -> Text:
7
+ """Format a status as a colored badge.
8
+
9
+ Args:
10
+ status: Status value
11
+
12
+ Returns:
13
+ Styled Text badge
14
+ """
15
+ status_lower = str(status).lower()
16
+
17
+ # Map status values to colors
18
+ status_colors = {
19
+ "closed": "bold green",
20
+ "in-progress": "bold blue",
21
+ "in_progress": "bold blue",
22
+ "todo": "bold yellow",
23
+ "blocked": "bold red",
24
+ "review": "bold magenta",
25
+ "open": "bold green",
26
+ "active": "bold green",
27
+ "on-hold": "bold yellow",
28
+ "on_hold": "bold yellow",
29
+ "planning": "bold blue",
30
+ "completed": "bold green",
31
+ "cancelled": "bold red",
32
+ "critical": "bold red",
33
+ "high": "bold yellow",
34
+ "medium": "bold blue",
35
+ "low": "bold green",
36
+ }
37
+
38
+ style = status_colors.get(status_lower, "white")
39
+ return Text(f"[{status}]", style=style)
40
+
41
+
42
+ def format_percentage(value: float, decimals: int = 1) -> str:
43
+ """Format a numeric value as a percentage.
44
+
45
+ Args:
46
+ value: Decimal value (0.0-1.0 or 0-100)
47
+ decimals: Number of decimal places
48
+
49
+ Returns:
50
+ Formatted percentage string
51
+ """
52
+ if value <= 1.0:
53
+ value = value * 100
54
+ return f"{value:.{decimals}f}%"
55
+
56
+
57
+ __all__ = ["format_status_badge", "format_percentage"]
@@ -0,0 +1,11 @@
1
+ """GitHub initialization services."""
2
+
3
+ from roadmap.common.initialization.github.setup_service import (
4
+ GitHubInitializationService,
5
+ )
6
+ from roadmap.common.initialization.github.setup_validator import GitHubSetupValidator
7
+
8
+ __all__ = [
9
+ "GitHubSetupValidator",
10
+ "GitHubInitializationService",
11
+ ]
@@ -0,0 +1,238 @@
1
+ """GitHub initialization service for setup workflow orchestration."""
2
+
3
+ import click
4
+ from structlog import get_logger
5
+
6
+ from roadmap.adapters.github.github import GitHubClient
7
+ from roadmap.common.configuration.github.config_manager import GitHubConfigManager
8
+ from roadmap.common.configuration.github.token_resolver import GitHubTokenResolver
9
+ from roadmap.common.console import get_console
10
+ from roadmap.common.constants import SyncBackend
11
+ from roadmap.common.initialization.github.setup_validator import GitHubSetupValidator
12
+ from roadmap.infrastructure.coordination.core import RoadmapCore
13
+ from roadmap.infrastructure.security.credentials import CredentialManager
14
+
15
+ logger = get_logger()
16
+ console = get_console()
17
+
18
+
19
+ def show_github_setup_instructions(github_repo: str, yes: bool) -> bool:
20
+ """Show GitHub setup instructions and get user confirmation.
21
+
22
+ Returns:
23
+ True if user wants to continue, False to skip
24
+ """
25
+ console.print("🔗 GitHub Integration Setup", style="bold blue")
26
+ console.print(f"\nRepository: {github_repo}")
27
+ console.print("\nTo sync with GitHub, you'll need a personal access token.")
28
+ console.print("→ Open: https://github.com/settings/tokens")
29
+ console.print(
30
+ "→ Create token with 'repo' scope (or 'public_repo' for public repos)"
31
+ )
32
+ console.print("→ Required permissions: Issues, Pull requests, Repository metadata")
33
+ console.print()
34
+
35
+ if not yes and not click.confirm("Do you want to set up GitHub integration now?"):
36
+ console.print(
37
+ "⏭️ Skipping GitHub integration (you can set this up later with 'roadmap sync setup')"
38
+ )
39
+ return False
40
+
41
+ return True
42
+
43
+
44
+ class GitHubInitializationService:
45
+ """Orchestrates GitHub integration setup during initialization."""
46
+
47
+ def __init__(self, core: RoadmapCore):
48
+ """Initialize GitHubInitializationService.
49
+
50
+ Args:
51
+ core: Core roadmap instance.
52
+ """
53
+ self.core = core
54
+ self.presenter = None # Set by caller if needed
55
+
56
+ def setup(
57
+ self,
58
+ skip_github: bool,
59
+ github_repo: str | None,
60
+ detected_info: dict,
61
+ interactive: bool,
62
+ yes: bool,
63
+ github_token: str | None,
64
+ presenter=None,
65
+ sync_backend: SyncBackend = SyncBackend.GITHUB,
66
+ ) -> bool:
67
+ """Set up GitHub integration if requested.
68
+
69
+ Args:
70
+ skip_github: If True, skip GitHub setup
71
+ github_repo: GitHub repository (owner/repo)
72
+ detected_info: Auto-detected project information
73
+ interactive: If True, prompt user for input
74
+ yes: If True, answer yes to all prompts
75
+ github_token: GitHub personal access token
76
+ presenter: UI presenter for output
77
+ sync_backend: Backend to use (SyncBackend.GITHUB or SyncBackend.GIT)
78
+
79
+ Returns:
80
+ True if configured, False if skipped or failed
81
+ """
82
+ self.presenter = presenter
83
+
84
+ if skip_github:
85
+ return False
86
+
87
+ repo_name = github_repo or detected_info.get("git_repo")
88
+ if not repo_name:
89
+ return False
90
+
91
+ return self._configure_integration(
92
+ repo_name, interactive, yes, token=github_token, sync_backend=sync_backend
93
+ )
94
+
95
+ def _validate_setup_conditions(self, github_repo, interactive, yes, token):
96
+ """Validate prerequisites for GitHub integration setup.
97
+
98
+ Returns:
99
+ True if setup should proceed, False if user wants to skip
100
+ """
101
+ if GitHubClient is None or CredentialManager is None:
102
+ raise ImportError("GitHub integration dependencies not available")
103
+
104
+ if interactive and not yes:
105
+ if not show_github_setup_instructions(github_repo, yes):
106
+ return False
107
+
108
+ return True
109
+
110
+ def _resolve_and_test_token(self, token, interactive, yes):
111
+ """Resolve token and test GitHub connection."""
112
+ cred_manager = CredentialManager() # type: ignore[call-arg]
113
+ token_resolver = GitHubTokenResolver(cred_manager)
114
+
115
+ existing_token = token_resolver.get_existing_token()
116
+ use_token, should_continue = token_resolver.resolve_token(
117
+ token, interactive, yes, existing_token
118
+ )
119
+
120
+ if not should_continue or not use_token:
121
+ return None
122
+
123
+ if self.presenter:
124
+ self.presenter.present_github_testing()
125
+ else:
126
+ console.print("🧪 Testing GitHub connection...", style="cyan")
127
+
128
+ return use_token
129
+
130
+ def _validate_github_access(self, use_token, github_repo, interactive, yes):
131
+ """Validate authentication and repository access."""
132
+ github_client = GitHubClient(use_token) # type: ignore[call-arg]
133
+ validator = GitHubSetupValidator(github_client)
134
+
135
+ auth_success, _ = validator.validate_authentication()
136
+ if not auth_success:
137
+ if interactive and not yes:
138
+ if not click.confirm(
139
+ "Continue without GitHub integration? (recommended to skip until token is fixed)"
140
+ ):
141
+ return False
142
+ return False
143
+
144
+ repo_success, _ = validator.validate_repository_access(github_repo)
145
+ if not repo_success:
146
+ if interactive and not yes:
147
+ if not click.confirm("Continue with GitHub integration anyway?"):
148
+ return False
149
+
150
+ return True
151
+
152
+ def _store_credentials_and_config(
153
+ self,
154
+ use_token: str,
155
+ existing_token: str | None,
156
+ github_repo: str,
157
+ sync_backend: SyncBackend = SyncBackend.GITHUB,
158
+ ) -> None:
159
+ """Store credentials and save configuration.
160
+
161
+ Args:
162
+ use_token: Token to store
163
+ existing_token: Previously stored token
164
+ github_repo: GitHub repository (owner/repo)
165
+ sync_backend: Backend to use (SyncBackend.GITHUB or SyncBackend.GIT)
166
+ """
167
+ if use_token != existing_token:
168
+ cred_manager = CredentialManager() # type: ignore[call-arg]
169
+ cred_manager.store_token(use_token)
170
+ if self.presenter:
171
+ self.presenter.present_github_credentials_stored()
172
+ else:
173
+ console.print("✅ GitHub credentials stored", style="green")
174
+
175
+ config_manager = GitHubConfigManager(self.core)
176
+ # Always pass sync_backend explicitly for consistency and clarity
177
+ config_manager.save_github_config(github_repo, sync_backend=sync_backend)
178
+
179
+ def _configure_integration(
180
+ self,
181
+ github_repo: str,
182
+ interactive: bool,
183
+ yes: bool = False,
184
+ token: str | None = None,
185
+ sync_backend: SyncBackend = SyncBackend.GITHUB,
186
+ ) -> bool:
187
+ """Set up GitHub integration with credential flow.
188
+
189
+ Args:
190
+ github_repo: GitHub repository (owner/repo)
191
+ interactive: If True, prompt user for input
192
+ yes: If True, answer yes to all prompts
193
+ token: GitHub personal access token
194
+ sync_backend: Backend to use (SyncBackend.GITHUB or SyncBackend.GIT)
195
+
196
+ Returns:
197
+ True if setup successful, False otherwise
198
+ """
199
+ try:
200
+ # Validate setup conditions - this will prompt user if they want to proceed
201
+ setup_result = self._validate_setup_conditions(
202
+ github_repo, interactive, yes, token
203
+ )
204
+ if not setup_result:
205
+ return False
206
+
207
+ # Resolve and test token
208
+ use_token = self._resolve_and_test_token(token, interactive, yes)
209
+ if use_token is None:
210
+ return False
211
+
212
+ # Validate GitHub access
213
+ if not self._validate_github_access(
214
+ use_token, github_repo, interactive, yes
215
+ ):
216
+ return False
217
+
218
+ # Store credentials and config
219
+ cred_manager = CredentialManager() # type: ignore[call-arg]
220
+ existing_token = GitHubTokenResolver(cred_manager).get_existing_token()
221
+ self._store_credentials_and_config(
222
+ use_token, existing_token, github_repo, sync_backend=sync_backend
223
+ )
224
+
225
+ return True
226
+
227
+ except ImportError:
228
+ if self.presenter:
229
+ self.presenter.present_github_unavailable("Dependencies not installed")
230
+ else:
231
+ console.print("⚠️ GitHub dependencies not installed", style="yellow")
232
+ return False
233
+ except Exception as e:
234
+ if self.presenter:
235
+ self.presenter.present_github_setup_failed(str(e))
236
+ else:
237
+ console.print(f"❌ GitHub setup failed: {e}", style="red")
238
+ return False