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,123 @@
1
+ """Milestone kanban board command."""
2
+
3
+ import click
4
+
5
+ from roadmap.adapters.cli.cli_command_helpers import require_initialized
6
+ from roadmap.common.console import get_console
7
+ from roadmap.common.formatters import KanbanLayout, KanbanOrganizer
8
+
9
+ console = get_console()
10
+
11
+
12
+ @click.command("kanban")
13
+ @click.argument("milestone_name")
14
+ @click.option("--compact", is_flag=True, help="Compact view with less spacing")
15
+ @click.option("--no-color", is_flag=True, help="Disable color coding")
16
+ @click.pass_context
17
+ @require_initialized
18
+ def milestone_kanban(
19
+ ctx: click.Context, milestone_name: str, compact: bool, no_color: bool
20
+ ):
21
+ """Display milestone issues in a kanban board layout."""
22
+ core = ctx.obj["core"]
23
+
24
+ try:
25
+ # Get milestone
26
+ milestone = core.milestones.get(milestone_name)
27
+ if not milestone:
28
+ console.print(
29
+ f"❌ Milestone '{milestone_name}' not found", style="bold red"
30
+ )
31
+ return
32
+
33
+ # Get all issues for this milestone
34
+ all_issues = core.issues.list()
35
+ milestone_issues = [
36
+ issue for issue in all_issues if issue.milestone == milestone_name
37
+ ]
38
+
39
+ if not milestone_issues:
40
+ console.print(
41
+ f"📋 No issues found for milestone '{milestone_name}'", style="yellow"
42
+ )
43
+ console.print(
44
+ "Create issues with: roadmap issue create 'Issue title' --milestone <milestone>",
45
+ style="dim",
46
+ )
47
+ return
48
+
49
+ # Organize into categories
50
+ categories = KanbanOrganizer.categorize_issues(milestone_issues)
51
+ columns = KanbanOrganizer.create_column_definitions(categories, no_color)
52
+
53
+ # Display kanban board
54
+ _display_header(milestone, milestone_issues)
55
+ _display_board(
56
+ columns,
57
+ compact,
58
+ col_width=KanbanLayout.calculate_column_width(len(columns)),
59
+ )
60
+ _display_summary(categories)
61
+
62
+ except Exception as e:
63
+ console.print(f"❌ Failed to display kanban board: {e}", style="bold red")
64
+
65
+
66
+ def _display_header(milestone, milestone_issues):
67
+ """Display board header with milestone info."""
68
+ console.print(f"\n🎯 Kanban Board: {milestone.name}", style="bold blue")
69
+ console.print(
70
+ f"📅 Due: {milestone.due_date.strftime('%Y-%m-%d') if milestone.due_date else 'No due date'}"
71
+ )
72
+ done_count = sum(1 for i in milestone_issues if i.status.value == "closed")
73
+ console.print(
74
+ f"📊 Progress: {done_count}/{len(milestone_issues)} issues completed\n"
75
+ )
76
+
77
+
78
+ def _display_board(columns, compact: bool, col_width: int):
79
+ """Display the kanban board columns."""
80
+ # Print column headers
81
+ header_line = ""
82
+ separator_line = ""
83
+ for title, _issues, _style in columns:
84
+ header_line += f"{title:<{col_width}}"
85
+ separator_line += "─" * col_width
86
+
87
+ console.print(header_line, style="bold")
88
+ console.print(separator_line, style="dim")
89
+
90
+ # Print rows
91
+ max_issues = max(len(issues) for _, issues, _ in columns) if columns else 0
92
+ for row in range(max_issues):
93
+ row_line = ""
94
+ for _title, issues, _style in columns:
95
+ if row < len(issues):
96
+ issue = issues[row]
97
+ # Format issue card
98
+ title_space = col_width - 12 # Space for ID and padding
99
+ display_title = issue.title
100
+ if len(display_title) > title_space:
101
+ display_title = display_title[: title_space - 3] + "..."
102
+ card_text = f"#{issue.id[:8]} {display_title}"
103
+ row_line += f"{card_text:<{col_width}}"
104
+ else:
105
+ row_line += " " * col_width
106
+
107
+ console.print(row_line)
108
+
109
+ if not compact:
110
+ # Add spacing between cards
111
+ if row < max_issues - 1:
112
+ console.print("")
113
+
114
+
115
+ def _display_summary(categories: dict):
116
+ """Display summary statistics."""
117
+ console.print("\n📈 Summary:")
118
+ console.print(
119
+ f" Overdue: {len(categories['overdue'])} | Blocked: {len(categories['blocked'])} | In Progress: {len(categories['in_progress'])}"
120
+ )
121
+ console.print(
122
+ f" Not Started: {len(categories['not_started'])} | Done: {len(categories['closed'])}"
123
+ )
@@ -0,0 +1,87 @@
1
+ """List milestones command."""
2
+
3
+ import click
4
+
5
+ from roadmap.adapters.cli.cli_command_helpers import require_initialized
6
+ from roadmap.adapters.cli.cli_error_handlers import handle_cli_error
7
+ from roadmap.adapters.cli.decorators import with_output_support
8
+ from roadmap.adapters.cli.services.milestone_list_service import MilestoneListService
9
+ from roadmap.common.console import get_console
10
+ from roadmap.common.formatters import MilestoneTableFormatter
11
+ from roadmap.common.logging import verbose_output
12
+ from roadmap.common.models import ColumnType
13
+
14
+
15
+ def _get_console():
16
+ """Get console instance at runtime to respect Click's test environment."""
17
+ return get_console()
18
+
19
+
20
+ @click.command("list")
21
+ @click.option("--overdue", is_flag=True, help="Show only overdue milestones")
22
+ @click.pass_context
23
+ @with_output_support(
24
+ available_columns=[
25
+ "name",
26
+ "description",
27
+ "status",
28
+ "due_date",
29
+ "progress",
30
+ "estimate",
31
+ ],
32
+ column_types={
33
+ "name": ColumnType.STRING,
34
+ "description": ColumnType.STRING,
35
+ "status": ColumnType.ENUM,
36
+ "due_date": ColumnType.DATE,
37
+ "progress": ColumnType.STRING,
38
+ "estimate": ColumnType.STRING,
39
+ },
40
+ )
41
+ @require_initialized
42
+ @verbose_output
43
+ def list_milestones(ctx: click.Context, overdue: bool):
44
+ """List all milestones.
45
+
46
+ Supports output formatting with --format, --columns, --sort-by, --filter flags.
47
+ """
48
+ core = ctx.obj["core"]
49
+
50
+ try:
51
+ # Use MilestoneListService to get and filter milestones
52
+ service = MilestoneListService(core)
53
+ milestones_data = service.get_milestones_list_data(overdue_only=overdue)
54
+
55
+ # Extract milestone list from service response
56
+ milestone_list = milestones_data.get("milestones", [])
57
+
58
+ # Handle empty result
59
+ if not milestone_list:
60
+ _get_console().print("📋 No milestones found.", style="yellow")
61
+ _get_console().print(
62
+ "Create one with: roadmap milestone create 'Milestone name' --due-date YYYY-MM-DD",
63
+ style="dim",
64
+ )
65
+ return
66
+
67
+ # Convert to TableData for structured output
68
+ description = "overdue" if overdue else "all"
69
+ table_data = MilestoneTableFormatter.milestones_to_table_data(
70
+ milestone_list,
71
+ title="Milestones",
72
+ description=description,
73
+ estimates=milestones_data.get("estimates", {}),
74
+ )
75
+
76
+ return table_data
77
+
78
+ except Exception as e:
79
+ handle_cli_error(
80
+ error=e,
81
+ operation="list_milestones",
82
+ entity_type="milestone",
83
+ entity_id="all",
84
+ context={"overdue": overdue},
85
+ fatal=True,
86
+ )
87
+ _get_console().print(f"❌ Error listing milestones: {str(e)}", style="bold red")
@@ -0,0 +1,79 @@
1
+ """Recalculate milestone progress command."""
2
+
3
+ import click
4
+
5
+ from roadmap.common.console import get_console
6
+
7
+ console = get_console()
8
+
9
+
10
+ @click.command("recalculate")
11
+ @click.argument("milestone_name", required=False)
12
+ @click.option(
13
+ "--method",
14
+ type=click.Choice(["effort_weighted", "count_based"]),
15
+ default="effort_weighted",
16
+ help="Calculation method",
17
+ )
18
+ @click.pass_context
19
+ def recalculate_milestone_progress(
20
+ ctx: click.Context, milestone_name: str, method: str
21
+ ):
22
+ """Recalculate progress for a milestone or all milestones."""
23
+ core = ctx.obj["core"]
24
+
25
+ try:
26
+ # Import the progress engine
27
+ from roadmap.common.progress import ProgressCalculationEngine
28
+
29
+ # Load all data
30
+ all_issues = core.issues.list()
31
+ all_milestones = core.milestones.list()
32
+
33
+ # Create progress engine
34
+ engine = ProgressCalculationEngine(method=method)
35
+
36
+ if milestone_name:
37
+ # Recalculate specific milestone
38
+ milestone = core.milestones.get(milestone_name)
39
+ if not milestone:
40
+ console.print(
41
+ f"❌ Milestone '{milestone_name}' not found", style="bold red"
42
+ )
43
+ return
44
+
45
+ old_progress = milestone.calculated_progress
46
+ updated = engine.update_milestone_progress(milestone, all_issues)
47
+
48
+ if updated:
49
+ # Save the updated milestone (needs to be implemented properly)
50
+ console.print(
51
+ f"✅ Updated milestone '{milestone_name}':", style="bold green"
52
+ )
53
+ console.print(
54
+ f" Progress: {old_progress or 0:.1f}% → {milestone.calculated_progress:.1f}%"
55
+ )
56
+ console.print(f" Status: {milestone.status}")
57
+ console.print(f" Method: {method}")
58
+ else:
59
+ console.print(
60
+ f"ℹ️ Milestone '{milestone_name}' progress unchanged", style="blue"
61
+ )
62
+ console.print(
63
+ f" Current progress: {milestone.calculated_progress or 0:.1f}%"
64
+ )
65
+ else:
66
+ # Recalculate all milestones
67
+ updated_count = 0
68
+ for milestone in all_milestones:
69
+ if engine.update_milestone_progress(milestone, all_issues):
70
+ updated_count += 1
71
+
72
+ console.print("✅ Recalculation complete:", style="bold green")
73
+ console.print(
74
+ f" {updated_count}/{len(all_milestones)} milestones updated"
75
+ )
76
+ console.print(f" Method: {method}")
77
+
78
+ except Exception as e:
79
+ console.print(f"❌ Failed to recalculate progress: {e}", style="bold red")
@@ -0,0 +1,87 @@
1
+ """Restore milestone command - move archived milestones back to active."""
2
+
3
+ import click
4
+
5
+ from roadmap.adapters.cli.cli_command_helpers import require_initialized
6
+ from roadmap.adapters.cli.milestones.restore_class import MilestoneRestore
7
+ from roadmap.common.console import get_console
8
+ from roadmap.common.logging import (
9
+ log_command,
10
+ verbose_output,
11
+ )
12
+
13
+
14
+ @click.command()
15
+ @click.argument("milestone_name", required=False)
16
+ @click.option(
17
+ "--all",
18
+ is_flag=True,
19
+ help="Restore all archived milestones",
20
+ )
21
+ @click.option(
22
+ "--dry-run",
23
+ is_flag=True,
24
+ help="Preview what would be restored without actually doing it",
25
+ )
26
+ @click.option(
27
+ "--force",
28
+ is_flag=True,
29
+ help="Skip confirmation prompt",
30
+ )
31
+ @click.option(
32
+ "--verbose",
33
+ "-v",
34
+ is_flag=True,
35
+ help="Show detailed debug information",
36
+ )
37
+ @click.pass_context
38
+ @verbose_output
39
+ @log_command("milestone_restore", entity_type="milestone", track_duration=True)
40
+ @require_initialized
41
+ def restore_milestone(
42
+ ctx: click.Context,
43
+ milestone_name: str | None,
44
+ all: bool,
45
+ dry_run: bool,
46
+ force: bool,
47
+ verbose: bool, # noqa: F841
48
+ ):
49
+ """Restore an archived milestone back to active milestones.
50
+
51
+ This moves a milestone from .roadmap/archive/milestones/ back to
52
+ .roadmap/milestones/, making it active again.
53
+
54
+ Examples:
55
+ roadmap milestone restore "v1.0"
56
+ roadmap milestone restore --all
57
+ roadmap milestone restore "v1.0" --dry-run
58
+ """
59
+ core = ctx.obj["core"]
60
+ console = get_console()
61
+
62
+ restore = MilestoneRestore(core, console)
63
+
64
+ if not milestone_name and not all:
65
+ console.print(
66
+ "❌ Error: Specify a milestone name or use --all",
67
+ style="bold red",
68
+ )
69
+ ctx.exit(1)
70
+
71
+ if milestone_name and all:
72
+ console.print(
73
+ "❌ Error: Cannot specify milestone name with --all",
74
+ style="bold red",
75
+ )
76
+ ctx.exit(1)
77
+
78
+ try:
79
+ restore.execute(
80
+ entity_id=milestone_name,
81
+ all=all,
82
+ dry_run=dry_run,
83
+ force=force,
84
+ )
85
+ except Exception as e:
86
+ console.print(f"❌ Restore operation failed: {str(e)}", style="bold red")
87
+ ctx.exit(1)
@@ -0,0 +1,99 @@
1
+ """MilestoneRestore command implementation using base class."""
2
+
3
+ import shutil
4
+ from pathlib import Path
5
+
6
+ from roadmap.adapters.cli.crud import BaseRestore, EntityType
7
+ from roadmap.adapters.persistence.parser import MilestoneParser
8
+
9
+
10
+ class MilestoneRestore(BaseRestore):
11
+ """Restore milestone command implementation."""
12
+
13
+ entity_type = EntityType.MILESTONE
14
+
15
+ def get_archived_files_to_restore(
16
+ self,
17
+ entity_id: str | None = None,
18
+ **filters, # noqa: ARG001
19
+ ) -> list[Path]:
20
+ """Get archived milestone files to restore.
21
+
22
+ Args:
23
+ entity_id: Specific milestone name to restore
24
+ **filters: Additional filter criteria
25
+
26
+ Returns:
27
+ List of archived file paths
28
+ """
29
+ from roadmap.adapters.cli.crud.crud_helpers import get_archive_dir
30
+
31
+ archive_dir = get_archive_dir(self.entity_type)
32
+
33
+ if not archive_dir.exists():
34
+ return []
35
+
36
+ if entity_id:
37
+ # Find specific milestone file by name
38
+ for md_file in archive_dir.glob("*.md"):
39
+ try:
40
+ milestone = MilestoneParser.parse_milestone_file(md_file)
41
+ if milestone.name == entity_id:
42
+ return [md_file]
43
+ except Exception as e:
44
+ from roadmap.common.logging import get_logger
45
+
46
+ logger = get_logger(__name__)
47
+ logger.debug(
48
+ "milestone_parse_failed",
49
+ file=str(md_file),
50
+ error=str(e),
51
+ action="find_restore_files",
52
+ )
53
+ continue
54
+ return []
55
+ else:
56
+ # Return all archived milestones
57
+ return list(archive_dir.glob("*.md"))
58
+
59
+ def post_restore_hook(self, restored_files: list[Path], **kwargs) -> None:
60
+ """Handle post-restore state updates.
61
+
62
+ Args:
63
+ restored_files: Files that were restored
64
+ **kwargs: Additional arguments
65
+ """
66
+ # Move associated issues folder back from archive
67
+ roadmap_dir = Path.cwd() / ".roadmap"
68
+
69
+ for file_path in restored_files:
70
+ try:
71
+ milestone = MilestoneParser.parse_milestone_file(file_path)
72
+ milestone_name = milestone.name
73
+
74
+ archive_issues_dir = roadmap_dir / "archive" / "issues" / milestone_name
75
+ if archive_issues_dir.exists():
76
+ active_issues_dir = roadmap_dir / "issues" / milestone_name
77
+ active_issues_dir.parent.mkdir(parents=True, exist_ok=True)
78
+
79
+ try:
80
+ shutil.move(str(archive_issues_dir), str(active_issues_dir))
81
+ except Exception as e:
82
+ self.console.print(
83
+ f"⚠️ Warning: Failed to restore issues for milestone {milestone_name}: {e}",
84
+ style="yellow",
85
+ )
86
+
87
+ # Update database
88
+ try:
89
+ self.core.db.mark_milestone_archived(milestone_name, archived=False)
90
+ except Exception as e:
91
+ self.console.print(
92
+ f"⚠️ Warning: Failed to update restoration status for {milestone_name}: {e}",
93
+ style="yellow",
94
+ )
95
+ except Exception as e:
96
+ self.console.print(
97
+ f"⚠️ Failed to process milestone file {file_path.name}: {e}",
98
+ style="yellow",
99
+ )
@@ -0,0 +1,61 @@
1
+ """Update milestone command."""
2
+
3
+ import click
4
+
5
+ from roadmap.adapters.cli.cli_command_helpers import require_initialized
6
+ from roadmap.adapters.cli.crud import BaseUpdate, EntityType
7
+ from roadmap.adapters.cli.crud.entity_builders import MilestoneBuilder
8
+ from roadmap.common.logging import log_command
9
+
10
+
11
+ class MilestoneUpdate(BaseUpdate):
12
+ """Update milestone command implementation."""
13
+
14
+ entity_type = EntityType.MILESTONE
15
+
16
+ def build_update_dict(self, entity_id: str, **kwargs) -> dict:
17
+ """Build update dictionary for milestone."""
18
+ return MilestoneBuilder.build_update_dict(
19
+ name=kwargs.get("name"),
20
+ description=kwargs.get("description"),
21
+ due_date=kwargs.get("due_date"),
22
+ status=kwargs.get("status"),
23
+ project_id=kwargs.get("project"),
24
+ )
25
+
26
+
27
+ @click.command("update")
28
+ @click.argument("milestone_id")
29
+ @click.option("--name", help="Update milestone name")
30
+ @click.option("--description", "-d", help="Update milestone description")
31
+ @click.option("--due-date", help="Update due date (YYYY-MM-DD format)")
32
+ @click.option(
33
+ "--status",
34
+ type=click.Choice(["open", "closed"]),
35
+ help="Set milestone status (open|closed)",
36
+ )
37
+ @click.option("--project", "-p", default=None, help="Project ID to assign milestone to")
38
+ @click.pass_context
39
+ @require_initialized
40
+ @log_command("milestone_update", entity_type="milestone", track_duration=True)
41
+ def update_milestone(
42
+ ctx: click.Context,
43
+ milestone_id: str,
44
+ name: str,
45
+ description: str,
46
+ due_date: str,
47
+ status: str,
48
+ project: str,
49
+ ):
50
+ """Update an existing milestone."""
51
+ core = ctx.obj["core"]
52
+ updater = MilestoneUpdate(core)
53
+
54
+ updater.execute(
55
+ entity_id=milestone_id,
56
+ name=name,
57
+ description=description,
58
+ due_date=due_date,
59
+ status=status,
60
+ project=project,
61
+ )
@@ -0,0 +1,142 @@
1
+ """View milestone command."""
2
+
3
+ import click
4
+
5
+ from roadmap.adapters.cli.cli_command_helpers import require_initialized
6
+ from roadmap.adapters.cli.mappers import MilestoneMapper
7
+ from roadmap.adapters.cli.presentation.milestone_presenter import MilestonePresenter
8
+ from roadmap.common.console import get_console
9
+
10
+
11
+ def _filter_milestone_issues(issues, status_filters, priority_filters, only_open):
12
+ """Filter milestone issues by status, priority, and open status."""
13
+ if not (status_filters or priority_filters or only_open):
14
+ return issues
15
+
16
+ status_lower = tuple(s.lower() for s in status_filters) if status_filters else ()
17
+ priority_lower = (
18
+ tuple(p.lower() for p in priority_filters) if priority_filters else ()
19
+ )
20
+
21
+ def matches_filters(issue):
22
+ if status_lower and issue.status.value not in status_lower:
23
+ return False
24
+ if priority_lower and issue.priority.value not in priority_lower:
25
+ return False
26
+ if only_open and issue.status.value == "closed":
27
+ return False
28
+ return True
29
+
30
+ return [issue for issue in issues if matches_filters(issue)]
31
+
32
+
33
+ @click.command("view")
34
+ @click.argument("milestone_name")
35
+ @click.option(
36
+ "--status",
37
+ type=click.Choice(
38
+ ["todo", "in-progress", "blocked", "review", "closed"], case_sensitive=False
39
+ ),
40
+ multiple=True,
41
+ help="Filter issues by status (can specify multiple times)",
42
+ )
43
+ @click.option(
44
+ "--priority",
45
+ type=click.Choice(["critical", "high", "medium", "low"], case_sensitive=False),
46
+ multiple=True,
47
+ help="Filter issues by priority (can specify multiple times)",
48
+ )
49
+ @click.option(
50
+ "--only-open",
51
+ is_flag=True,
52
+ help="Show only open issues (excludes closed)",
53
+ )
54
+ @click.pass_context
55
+ @require_initialized
56
+ def view_milestone(
57
+ ctx: click.Context,
58
+ milestone_name: str,
59
+ status: tuple,
60
+ priority: tuple,
61
+ only_open: bool,
62
+ ):
63
+ """Display detailed information about a specific milestone.
64
+
65
+ Shows complete milestone details including progress, statistics,
66
+ issues breakdown, description, and goals in a formatted view.
67
+
68
+ Use --status and --priority to filter which issues are displayed.
69
+
70
+ Examples:
71
+ roadmap milestone view v.0.5.0
72
+ roadmap milestone view v.0.5.0 --status todo --status in-progress
73
+ roadmap milestone view v.0.5.0 --priority high --priority critical
74
+ roadmap milestone view v.0.5.0 --only-open
75
+ """
76
+ core = ctx.obj["core"]
77
+
78
+ milestone = core.milestones.get(milestone_name)
79
+ if not milestone:
80
+ get_console().print(
81
+ f"❌ Milestone '{milestone_name}' not found.", style="bold red"
82
+ )
83
+ get_console().print(
84
+ "\n💡 Tip: Use 'roadmap milestone list' to see all available milestones.",
85
+ style="dim",
86
+ )
87
+ ctx.exit(1)
88
+
89
+ # Convert milestone to DTO
90
+ milestone_dto = MilestoneMapper.domain_to_dto(milestone)
91
+
92
+ # Get milestone issues and filter
93
+ all_issues = core.issues.list()
94
+ milestone_issues = milestone.get_issues(all_issues)
95
+ milestone_issues = _filter_milestone_issues(
96
+ milestone_issues, status, priority, only_open
97
+ )
98
+
99
+ # Get progress data
100
+ progress_data = core.milestones.get_progress(milestone_name)
101
+
102
+ # Extract description and goals
103
+ description_content = milestone.content if milestone.content else None
104
+
105
+ # Prepare comments if any
106
+ comments_text = None
107
+ if milestone.comments:
108
+ from roadmap.core.services.comment.comment_service import CommentService
109
+
110
+ threads = CommentService.build_comment_threads(milestone.comments)
111
+ comment_text = ""
112
+
113
+ top_level_ids = [k for k in threads.keys() if k is not None]
114
+ for top_level_id in sorted(top_level_ids):
115
+ for comment in threads.get(top_level_id, []):
116
+ comment_text += (
117
+ CommentService.format_comment_for_display(comment, indent=0) + "\n"
118
+ )
119
+
120
+ if comment.id in threads:
121
+ for reply in threads[comment.id]:
122
+ comment_text += (
123
+ CommentService.format_comment_for_display(reply, indent=1)
124
+ + "\n"
125
+ )
126
+
127
+ comment_text += "\n"
128
+
129
+ comments_text = comment_text.rstrip()
130
+
131
+ # Render using presenter
132
+ presenter = MilestonePresenter()
133
+ presenter.render(
134
+ milestone_dto,
135
+ issues=milestone_issues if milestone_issues else None,
136
+ progress_data=progress_data,
137
+ description_content=description_content,
138
+ comments_text=comments_text,
139
+ )
140
+
141
+ # Display file info
142
+ get_console().print(f"[dim]File: .roadmap/milestones/{milestone.name}.md[/dim]")