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,52 @@
1
+ """Today command - daily workflow summary."""
2
+
3
+ import click
4
+
5
+ from roadmap.adapters.cli.cli_command_helpers import require_initialized
6
+ from roadmap.adapters.cli.presentation.daily_summary_presenter import (
7
+ DailySummaryPresenter,
8
+ )
9
+ from roadmap.adapters.cli.services.daily_summary_service import DailySummaryService
10
+ from roadmap.common.console import get_console
11
+ from roadmap.common.logging import verbose_output
12
+
13
+ console = get_console()
14
+
15
+
16
+ @click.command("today")
17
+ @click.option("--verbose", "-v", is_flag=True, help="Show verbose output")
18
+ @click.pass_context
19
+ @require_initialized
20
+ @verbose_output
21
+ def today(ctx: click.Context, verbose: bool = False): # noqa: F841
22
+ """Show your daily workflow summary for the upcoming milestone.
23
+
24
+ Displays your assigned issues for the next upcoming milestone:
25
+ - Current in-progress issues assigned to you
26
+ - Overdue issues
27
+ - High-priority tasks to start
28
+ - Today's completed work
29
+
30
+ Only shows issues assigned to you in the upcoming milestone.
31
+
32
+ Example:
33
+ roadmap today
34
+ """
35
+ core = ctx.obj["core"]
36
+
37
+ try:
38
+ # Use service to get daily summary data
39
+ service = DailySummaryService(core)
40
+ data = service.get_daily_summary_data()
41
+
42
+ # Use presenter to render the data
43
+ presenter = DailySummaryPresenter()
44
+ presenter.render(data)
45
+
46
+ except ValueError as e:
47
+ console.print(f"❌ {e}", style="bold red")
48
+ except Exception as e:
49
+ console.print(f"❌ Failed to generate daily summary: {e}", style="bold red")
50
+ import traceback
51
+
52
+ console.print(traceback.format_exc(), style="dim")
@@ -0,0 +1,38 @@
1
+ """Shared Click option definitions for CLI commands."""
2
+
3
+ import click
4
+
5
+
6
+ def verbose_option(f):
7
+ """Add --verbose / -v flag to a command."""
8
+ return click.option(
9
+ "--verbose",
10
+ "-v",
11
+ is_flag=True,
12
+ help="Show detailed debug information and all health check logs",
13
+ )(f)
14
+
15
+
16
+ def format_option(f):
17
+ """Add --format / -f option to a command."""
18
+ return click.option(
19
+ "--format",
20
+ "-f",
21
+ type=click.Choice(["plain", "json"], case_sensitive=False),
22
+ default="plain",
23
+ help="Output format (default: plain text)",
24
+ )(f)
25
+
26
+
27
+ def details_option(f):
28
+ """Add --details flag to a command."""
29
+ return click.option(
30
+ "--details",
31
+ is_flag=True,
32
+ help="Show detailed recommendations and fix commands for each check",
33
+ )(f)
34
+
35
+
36
+ def health_check_options(f):
37
+ """Add all health check options (--verbose, --details, --format) to a command."""
38
+ return verbose_option(details_option(format_option(f)))
@@ -0,0 +1 @@
1
+ """Git repository operations and utilities."""
@@ -0,0 +1,208 @@
1
+ """Git integration module for enhanced Git workflow support."""
2
+
3
+ import re
4
+ from dataclasses import dataclass
5
+ from datetime import datetime
6
+ from pathlib import Path
7
+ from typing import Any
8
+
9
+ from roadmap.core.domain import Issue
10
+
11
+
12
+ @dataclass
13
+ class GitCommit:
14
+ """Represents a Git commit with roadmap-relevant information."""
15
+
16
+ hash: str
17
+ author: str
18
+ date: datetime
19
+ message: str
20
+ files_changed: list[str]
21
+ insertions: int = 0
22
+ deletions: int = 0
23
+
24
+ @property
25
+ def short_hash(self) -> str:
26
+ """Get short commit hash."""
27
+ return self.hash[:8]
28
+
29
+ def extract_roadmap_references(self) -> list[str]:
30
+ """Extract roadmap issue references from commit message."""
31
+ # Enhanced patterns to support multiple formats:
32
+ # 1. [roadmap:issue-id] or [closes roadmap:issue-id] (existing)
33
+ # 2. fixes #issue-id, closes #issue-id (GitHub/GitLab style)
34
+ # 3. resolves #issue-id, resolve #issue-id
35
+ # 4. addresses #issue-id, refs #issue-id
36
+ # Issue IDs can be hex or alphanumeric
37
+ patterns = [
38
+ # Original roadmap: patterns
39
+ r"\[roadmap:([a-zA-Z0-9]{8,})\]",
40
+ r"\[closes roadmap:([a-zA-Z0-9]{8,})\]",
41
+ r"\[fixes roadmap:([a-zA-Z0-9]{8,})\]",
42
+ r"roadmap:([a-zA-Z0-9]{8,})",
43
+ # GitHub/GitLab style patterns
44
+ r"\b(?:fix|fixes|fixed|close|closes|closed|resolve|resolves|resolved)\s+#([a-zA-Z0-9]{8,})\b",
45
+ r"\b(?:addresses?|refs?)\s+#([a-zA-Z0-9]{8,})\b",
46
+ # Simple # references
47
+ r"#([a-f0-9]{8})\b", # Hex issue IDs only for this pattern to avoid false positives
48
+ ]
49
+
50
+ references = []
51
+ for pattern in patterns:
52
+ matches = re.findall(pattern, self.message, re.IGNORECASE)
53
+ references.extend(matches)
54
+
55
+ return list(set(references)) # Remove duplicates
56
+
57
+ def extract_progress_info(self) -> float | None:
58
+ """Extract progress percentage from commit message."""
59
+ # Pattern: [progress:25%] or [progress:25]
60
+ patterns = [
61
+ r"\[progress:(\d+)%?\]",
62
+ r"progress:(\d+)%?",
63
+ ]
64
+
65
+ for pattern in patterns:
66
+ match = re.search(pattern, self.message, re.IGNORECASE)
67
+ if match:
68
+ return float(match.group(1))
69
+
70
+ return None
71
+
72
+
73
+ @dataclass
74
+ class GitBranch:
75
+ """Represents a Git branch with roadmap integration info."""
76
+
77
+ name: str
78
+ current: bool = False
79
+ remote: str | None = None
80
+ last_commit: str | None = None
81
+
82
+ def extract_issue_id(self) -> str | None:
83
+ """Extract issue ID from branch name patterns."""
84
+ # Common patterns:
85
+ # feature/issue-abc12345-description
86
+ # bugfix/abc12345-fix-login
87
+ # abc12345-new-feature
88
+ patterns = [
89
+ r"(?:feature|bugfix|hotfix)/(?:issue-)?([a-f0-9]{8})",
90
+ r"^([a-f0-9]{8})-",
91
+ r"/([a-f0-9]{8})-",
92
+ ]
93
+
94
+ for pattern in patterns:
95
+ match = re.search(pattern, self.name, re.IGNORECASE)
96
+ if match:
97
+ return match.group(1)
98
+
99
+ return None
100
+
101
+ def suggests_issue_type(self) -> str | None:
102
+ """Suggest issue type based on branch name."""
103
+ if self.name.startswith(("feature/", "feat/")):
104
+ return "feature"
105
+ elif self.name.startswith(("bugfix/", "bug/", "fix/")):
106
+ return "bug"
107
+ elif self.name.startswith(("hotfix/", "urgent/")):
108
+ return "hotfix"
109
+ elif self.name.startswith(("docs/", "doc/")):
110
+ return "documentation"
111
+ elif self.name.startswith(("test/", "tests/")):
112
+ return "testing"
113
+
114
+ return None
115
+
116
+
117
+ class GitIntegration:
118
+ """Orchestrator for Git integration functionality.
119
+
120
+ Coordinates branch management, commit analysis, and repository operations.
121
+ Delegates to focused manager classes for each responsibility.
122
+ """
123
+
124
+ def __init__(self, repo_path: Path | None = None, config: object | None = None):
125
+ """Initialize Git integration orchestrator."""
126
+ from roadmap.adapters.git.git_branch_manager import GitBranchManager
127
+ from roadmap.adapters.git.git_command_executor import GitCommandExecutor
128
+ from roadmap.adapters.git.git_commit_analyzer import GitCommitAnalyzer
129
+ from roadmap.adapters.git.git_repository_info import GitRepositoryInfo
130
+
131
+ self.repo_path = repo_path or Path.cwd()
132
+ self.config = config
133
+
134
+ # Initialize managers
135
+ self.executor = GitCommandExecutor(repo_path)
136
+ self.branch_manager = GitBranchManager(repo_path, config)
137
+ self.commit_analyzer = GitCommitAnalyzer(repo_path)
138
+ self.repo_info = GitRepositoryInfo(repo_path)
139
+
140
+ def is_git_repository(self) -> bool:
141
+ """Check if current directory is in a Git repository."""
142
+ return self.executor.is_git_repository()
143
+
144
+ def get_current_user(self) -> str | None:
145
+ """Get current Git user name."""
146
+ return self.repo_info.get_current_user()
147
+
148
+ def get_current_email(self) -> str | None:
149
+ """Get current Git user email."""
150
+ return self.repo_info.get_current_email()
151
+
152
+ def get_current_branch(self) -> GitBranch | None:
153
+ """Get information about the current branch."""
154
+ return self.branch_manager.get_current_branch()
155
+
156
+ def get_all_branches(self) -> list[GitBranch]:
157
+ """Get all local branches."""
158
+ return self.branch_manager.get_all_branches()
159
+
160
+ def get_recent_commits(
161
+ self, count: int = 10, since: str | None = None
162
+ ) -> list[GitCommit]:
163
+ """Get recent commits with detailed information."""
164
+ return self.commit_analyzer.get_recent_commits(count, since)
165
+
166
+ def get_commits_for_issue(
167
+ self, issue_id: str, since: str | None = None
168
+ ) -> list[GitCommit]:
169
+ """Get all commits that reference a specific issue."""
170
+ return self.commit_analyzer.get_commits_for_issue(issue_id, since)
171
+
172
+ def suggest_branch_name(self, issue: Issue) -> str:
173
+ """Suggest a branch name based on issue information."""
174
+ return self.branch_manager.suggest_branch_name(issue)
175
+
176
+ def create_branch_for_issue(
177
+ self, issue: Issue, checkout: bool = True, force: bool = False
178
+ ) -> bool:
179
+ """Create a new branch for an issue."""
180
+ return self.branch_manager.create_branch_for_issue(issue, checkout, force)
181
+
182
+ def auto_create_issue_from_branch(
183
+ self, roadmap_core, branch_name: str | None = None
184
+ ) -> str | None:
185
+ """Automatically create an issue from a branch name if one doesn't exist."""
186
+ return self.branch_manager.auto_create_issue_from_branch(
187
+ roadmap_core, branch_name
188
+ )
189
+
190
+ def get_repository_info(self) -> dict[str, Any]:
191
+ """Get general repository information."""
192
+ return self.repo_info.get_repository_info()
193
+
194
+ def parse_commit_message_for_updates(self, commit: GitCommit) -> dict[str, Any]:
195
+ """Parse commit message for roadmap updates."""
196
+ return self.commit_analyzer.parse_commit_message_for_updates(commit)
197
+
198
+ def auto_update_issues_from_commits(
199
+ self, roadmap_core, commits: list[GitCommit] | None = None
200
+ ) -> dict[str, list[str]]:
201
+ """Automatically update issues based on commit messages."""
202
+ return self.commit_analyzer.auto_update_issues_from_commits(
203
+ roadmap_core, commits
204
+ )
205
+
206
+ def get_branch_linked_issues(self, branch_name: str) -> list[str]:
207
+ """Get issue IDs linked to a specific branch."""
208
+ return self.branch_manager.get_branch_linked_issues(branch_name)
@@ -0,0 +1,400 @@
1
+ """Git branch management and issue linking."""
2
+
3
+ import re
4
+ from pathlib import Path
5
+
6
+ from structlog import get_logger
7
+
8
+ from roadmap.adapters.git.git import GitBranch
9
+ from roadmap.adapters.git.git_command_executor import GitCommandExecutor
10
+
11
+ logger = get_logger()
12
+
13
+
14
+ class GitBranchManager:
15
+ """Manages Git branches and links them to roadmap issues."""
16
+
17
+ def __init__(self, repo_path: Path | None = None, config: object | None = None):
18
+ """Initialize branch manager."""
19
+ self.executor = GitCommandExecutor(repo_path)
20
+ self.repo_path = repo_path or Path.cwd()
21
+ self.config = config
22
+
23
+ def get_current_branch(self) -> GitBranch | None:
24
+ """Get information about the current branch."""
25
+ branch_name = self.executor.run(["rev-parse", "--abbrev-ref", "HEAD"])
26
+ if not branch_name or branch_name == "HEAD":
27
+ return None
28
+
29
+ # Get remote tracking branch
30
+ remote = self.executor.run(
31
+ ["rev-parse", "--abbrev-ref", f"{branch_name}@{{upstream}}"]
32
+ )
33
+
34
+ # Get last commit
35
+ last_commit = self.executor.run(["rev-parse", "HEAD"])
36
+
37
+ return GitBranch(
38
+ name=branch_name, current=True, remote=remote, last_commit=last_commit
39
+ )
40
+
41
+ def get_all_branches(self) -> list[GitBranch]:
42
+ """Get all local branches."""
43
+ output = self.executor.run(["branch", "--format=%(refname:short)|%(HEAD)"])
44
+ if not output:
45
+ return []
46
+
47
+ branches = []
48
+ for line in output.split("\n"):
49
+ if "|" not in line:
50
+ continue
51
+
52
+ name, head_marker = line.split("|", 1)
53
+ is_current = head_marker.strip() == "*"
54
+
55
+ branches.append(GitBranch(name=name.strip(), current=is_current))
56
+
57
+ return branches
58
+
59
+ def _generate_title_slug(self, title: str) -> str:
60
+ """Generate slug from issue title.
61
+
62
+ Args:
63
+ title: Issue title to slugify
64
+
65
+ Returns:
66
+ Slug suitable for branch name
67
+ """
68
+ title_slug = re.sub(r"[^a-zA-Z0-9\s-]", "", title.lower())
69
+ title_slug = re.sub(r"\s+", "-", title_slug)
70
+ return title_slug[:40] # Limit length
71
+
72
+ def _determine_prefix(self, issue) -> str:
73
+ """Determine branch prefix based on issue type/priority.
74
+
75
+ Args:
76
+ issue: Issue to inspect
77
+
78
+ Returns:
79
+ Prefix for branch name
80
+ """
81
+ prefix = "feature"
82
+ if hasattr(issue, "issue_type"):
83
+ if issue.issue_type == "bug":
84
+ prefix = "bugfix"
85
+ elif issue.issue_type == "documentation":
86
+ prefix = "docs"
87
+ elif issue.priority.value == "critical":
88
+ prefix = "hotfix"
89
+ return prefix
90
+
91
+ def _get_branch_template(self) -> str | None:
92
+ """Get branch name template from config.
93
+
94
+ Returns:
95
+ Template string or None
96
+ """
97
+ try:
98
+ if self.config:
99
+ defaults = getattr(self.config, "defaults", None)
100
+ if defaults and hasattr(defaults, "get"):
101
+ return defaults.get("branch_name_template")
102
+ except Exception as e:
103
+ logger.debug("get_branch_name_template_failed", error=str(e))
104
+ return None
105
+
106
+ def _format_branch_name(
107
+ self, issue_id: int, slug: str, prefix: str, template: str | None
108
+ ) -> str:
109
+ """Format branch name using template or defaults.
110
+
111
+ Args:
112
+ issue_id: Issue ID
113
+ slug: Title slug
114
+ prefix: Branch prefix
115
+ template: Template string or None
116
+
117
+ Returns:
118
+ Formatted branch name
119
+ """
120
+ if template:
121
+ try:
122
+ return template.format(id=issue_id, slug=slug, prefix=prefix)
123
+ except Exception as e:
124
+ logger.debug(
125
+ "format_branch_name_template_failed",
126
+ template=template,
127
+ error=str(e),
128
+ severity="operational",
129
+ )
130
+
131
+ return f"{prefix}/{issue_id}-{slug}"
132
+
133
+ def suggest_branch_name(self, issue) -> str:
134
+ """Suggest a branch name based on issue information."""
135
+ title_slug = self._generate_title_slug(issue.title)
136
+ prefix = self._determine_prefix(issue)
137
+ template = self._get_branch_template()
138
+ return self._format_branch_name(issue.id, title_slug, prefix, template)
139
+
140
+ def _check_working_tree_clean(self, force: bool) -> bool:
141
+ """Check if working tree has uncommitted changes.
142
+
143
+ Args:
144
+ force: Skip dirty working tree check
145
+
146
+ Returns:
147
+ True if working tree is clean or force is True
148
+ """
149
+ if force:
150
+ return True
151
+
152
+ status_output = self.executor.run(["status", "--porcelain"]) or ""
153
+ status_lines = [line for line in status_output.splitlines() if line.strip()]
154
+ has_substantive_changes = any(
155
+ not line.startswith("??") for line in status_lines
156
+ )
157
+ return not has_substantive_changes
158
+
159
+ def _branch_already_exists(self, branch_name: str) -> bool:
160
+ """Check if branch already exists.
161
+
162
+ Args:
163
+ branch_name: Name of branch to check
164
+
165
+ Returns:
166
+ True if branch exists
167
+ """
168
+ existing = self.executor.run(["rev-parse", "--verify", branch_name])
169
+ if not existing:
170
+ existing = self.executor.run(
171
+ ["rev-parse", "--verify", f"refs/heads/{branch_name}"]
172
+ )
173
+ return existing is not None
174
+
175
+ def _handle_existing_branch(self, branch_name: str, checkout: bool) -> bool:
176
+ """Handle existing branch (checkout or skip).
177
+
178
+ Args:
179
+ branch_name: Name of existing branch
180
+ checkout: Whether to checkout the branch
181
+
182
+ Returns:
183
+ True if operation succeeded
184
+ """
185
+ if checkout:
186
+ co = self.executor.run(["checkout", branch_name])
187
+ return co is not None
188
+ else:
189
+ return True
190
+
191
+ def _get_current_branch(self) -> str | None:
192
+ """Get current branch name.
193
+
194
+ Returns:
195
+ Current branch name or None
196
+ """
197
+ return self.executor.run(["rev-parse", "--abbrev-ref", "HEAD"]) or None
198
+
199
+ def _create_and_checkout_branch(self, branch_name: str, checkout: bool) -> bool:
200
+ """Create new branch and optionally return to previous branch.
201
+
202
+ Args:
203
+ branch_name: Name of new branch
204
+ checkout: Whether to stay on new branch
205
+
206
+ Returns:
207
+ True if successful
208
+ """
209
+ current_branch = self._get_current_branch()
210
+
211
+ result = self.executor.run(["checkout", "-b", branch_name])
212
+ if result is None:
213
+ return False
214
+
215
+ if not checkout:
216
+ if current_branch:
217
+ self.executor.run(["checkout", current_branch])
218
+
219
+ return True
220
+
221
+ def create_branch_for_issue(
222
+ self, issue, checkout: bool = True, force: bool = False
223
+ ) -> bool:
224
+ """Create a new branch for an issue.
225
+
226
+ Args:
227
+ issue: Issue object with id and title
228
+ checkout: Whether to checkout the new branch
229
+ force: Skip dirty working tree check
230
+
231
+ Returns:
232
+ True if successful, False otherwise
233
+ """
234
+ if not self.executor.is_git_repository():
235
+ return False
236
+
237
+ branch_name = self.suggest_branch_name(issue)
238
+
239
+ if not self._check_working_tree_clean(force):
240
+ return False
241
+
242
+ if self._branch_already_exists(branch_name):
243
+ return self._handle_existing_branch(branch_name, checkout)
244
+
245
+ return self._create_and_checkout_branch(branch_name, checkout)
246
+
247
+ def _check_existing_issue_for_branch(
248
+ self, branch_name: str, roadmap_core
249
+ ) -> str | None:
250
+ """Check if branch already has associated issue.
251
+
252
+ Args:
253
+ branch_name: Branch name
254
+ roadmap_core: RoadmapCore instance
255
+
256
+ Returns:
257
+ None if no existing issue
258
+ """
259
+ branch = GitBranch(branch_name)
260
+ existing_issue_id = branch.extract_issue_id()
261
+ if existing_issue_id and roadmap_core.issues.get(existing_issue_id):
262
+ return existing_issue_id
263
+ return None
264
+
265
+ def _build_auto_created_issue_data(
266
+ self, branch_name: str, roadmap_core
267
+ ) -> dict | None:
268
+ """Build issue data from branch name.
269
+
270
+ Args:
271
+ branch_name: Branch name
272
+ roadmap_core: RoadmapCore instance
273
+
274
+ Returns:
275
+ Issue data dict or None if can't extract
276
+ """
277
+ branch = GitBranch(branch_name)
278
+ issue_type = branch.suggests_issue_type() or "feature"
279
+ title = self._extract_title_from_branch_name(branch_name)
280
+ if not title:
281
+ return None
282
+
283
+ assignee = self._get_current_user(roadmap_core) or "Unknown"
284
+ content = f"Auto-created from branch: `{branch_name}`\n\nThis issue was automatically created when switching to the branch `{branch_name}`."
285
+
286
+ issue_data = {
287
+ "title": title,
288
+ "content": content,
289
+ "assignee": assignee,
290
+ "priority": "medium",
291
+ "status": "in_progress",
292
+ }
293
+
294
+ if hasattr(roadmap_core.issues, "create_with_type"):
295
+ issue_data["issue_type"] = issue_type
296
+
297
+ return issue_data
298
+
299
+ def auto_create_issue_from_branch(
300
+ self, roadmap_core, branch_name: str | None = None
301
+ ) -> str | None:
302
+ """Automatically create an issue from a branch name if one doesn't exist.
303
+
304
+ Args:
305
+ roadmap_core: RoadmapCore instance for issue operations
306
+ branch_name: Branch name to analyze. If None, uses current branch.
307
+
308
+ Returns:
309
+ Issue ID if created, None if not created or already exists
310
+ """
311
+ if branch_name is None:
312
+ current_branch = self.get_current_branch()
313
+ if not current_branch:
314
+ return None
315
+ branch_name = current_branch.name
316
+
317
+ # Don't create issues for main branches
318
+ if branch_name in ["main", "master", "develop", "dev"]:
319
+ return None
320
+
321
+ # Check if issue already exists
322
+ if self._check_existing_issue_for_branch(branch_name, roadmap_core):
323
+ return None
324
+
325
+ # Build issue data
326
+ issue_data = self._build_auto_created_issue_data(branch_name, roadmap_core)
327
+ if not issue_data:
328
+ return None
329
+
330
+ # Create the issue
331
+ try:
332
+ issue = roadmap_core.issues.create(**issue_data)
333
+ return issue.id
334
+ except Exception as e:
335
+ logger.error(
336
+ "auto_issue_creation_failed", error=str(e), severity="operational"
337
+ )
338
+ return None
339
+
340
+ def _extract_title_from_branch_name(self, branch_name: str) -> str | None:
341
+ """Extract a readable title from a branch name."""
342
+ # Remove common prefixes
343
+ name = branch_name
344
+ prefixes = [
345
+ "feature/",
346
+ "bugfix/",
347
+ "hotfix/",
348
+ "docs/",
349
+ "test/",
350
+ "feat/",
351
+ "bug/",
352
+ "fix/",
353
+ ]
354
+ for prefix in prefixes:
355
+ if name.startswith(prefix):
356
+ name = name[len(prefix) :]
357
+ break
358
+
359
+ # Remove issue ID if present
360
+ name = re.sub(r"^[a-f0-9]{8}-", "", name)
361
+ name = re.sub(r"^issue-[a-f0-9]{8}-", "", name)
362
+
363
+ # Replace hyphens/underscores with spaces and title case
364
+ name = re.sub(r"[-_]", " ", name)
365
+ name = name.strip()
366
+
367
+ if not name:
368
+ return None
369
+
370
+ # Title case
371
+ words = name.split()
372
+ title_words = []
373
+ for word in words:
374
+ if len(word) > 3: # Title case for longer words
375
+ title_words.append(word.capitalize())
376
+ else: # Keep short words lowercase unless first word
377
+ title_words.append(word.lower() if title_words else word.capitalize())
378
+
379
+ return " ".join(title_words)
380
+
381
+ def _get_current_user(self, roadmap_core) -> str | None:
382
+ """Get current Git user name."""
383
+ return self.executor.run(["config", "user.name"])
384
+
385
+ def get_branch_linked_issues(self, branch_name: str) -> list[str]:
386
+ """Get issue IDs linked to a specific branch."""
387
+ try:
388
+ # Create a GitBranch object and extract issue ID
389
+ branch = GitBranch(branch_name)
390
+ issue_id = branch.extract_issue_id()
391
+
392
+ if issue_id:
393
+ return [issue_id]
394
+ else:
395
+ return []
396
+ except Exception as e:
397
+ logger.debug(
398
+ "branch_issue_extraction_failed", branch_name=branch_name, error=str(e)
399
+ )
400
+ return []