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,233 @@
1
+ """CLI presenter for Project DTOs."""
2
+
3
+ from datetime import datetime
4
+
5
+ from rich.markdown import Markdown
6
+ from rich.panel import Panel
7
+ from rich.text import Text
8
+
9
+ from roadmap.adapters.cli.dtos import ProjectDTO
10
+ from roadmap.adapters.cli.presentation.base_presenter import BasePresenter
11
+ from roadmap.adapters.cli.presentation.table_builders import (
12
+ create_list_table,
13
+ create_metadata_table,
14
+ )
15
+ from roadmap.adapters.cli.styling import STATUS_COLORS
16
+
17
+
18
+ class ProjectPresenter(BasePresenter):
19
+ """Presenter for formatting and displaying Project DTOs."""
20
+
21
+ def render(
22
+ self,
23
+ project_dto: ProjectDTO,
24
+ milestones: list | None = None,
25
+ milestone_progress: dict | None = None,
26
+ description_content: str | None = None,
27
+ comments_text: str | None = None,
28
+ effort_data: dict | None = None,
29
+ ) -> None:
30
+ """Render a project DTO in detailed view.
31
+
32
+ Args:
33
+ project_dto: The ProjectDTO to render
34
+ milestones: Optional list of milestone objects to display
35
+ milestone_progress: Optional progress information dict
36
+ description_content: Optional content with description
37
+ comments_text: Optional formatted comments text
38
+ effort_data: Optional dict with 'estimated' and 'actual' hour counts
39
+ """
40
+ # Display header
41
+ header = self._build_project_header(project_dto)
42
+ self._get_console().print(Panel(header, border_style="cyan"))
43
+
44
+ # Display metadata
45
+ metadata = self._build_metadata_table(project_dto)
46
+ self._get_console().print(
47
+ Panel(metadata, title="šŸ“‹ Details", border_style="blue")
48
+ )
49
+
50
+ # Display effort if provided
51
+ if effort_data:
52
+ effort = self._build_effort_table(effort_data)
53
+ if effort:
54
+ self._get_console().print(
55
+ Panel(effort, title="ā±ļø Effort", border_style="yellow")
56
+ )
57
+
58
+ # Display milestones if provided
59
+ if milestones:
60
+ self._display_milestones_panel(milestones, milestone_progress)
61
+
62
+ # Display description if provided
63
+ if description_content:
64
+ md = Markdown(description_content)
65
+ self._get_console().print(
66
+ Panel(md, title="šŸ“ Description", border_style="white")
67
+ )
68
+
69
+ # Display comments if provided
70
+ if comments_text:
71
+ self._get_console().print(
72
+ Panel(
73
+ comments_text,
74
+ title="šŸ’¬ Comments",
75
+ border_style="cyan",
76
+ )
77
+ )
78
+
79
+ # Display metadata footer
80
+ metadata_footer = (
81
+ f"Created: {project_dto.created} • Updated: {project_dto.updated}"
82
+ )
83
+ self._get_console().print(f"\n[dim]{metadata_footer}[/dim]")
84
+
85
+ def _build_project_header(self, project_dto: ProjectDTO) -> Text:
86
+ """Build header text with project name and status.
87
+
88
+ Args:
89
+ project_dto: The ProjectDTO to format
90
+
91
+ Returns:
92
+ Rich Text object with formatted header
93
+ """
94
+ status_colors = {
95
+ "planning": "blue",
96
+ "active": "green",
97
+ "on-hold": "yellow",
98
+ "completed": "bold green",
99
+ "cancelled": "red",
100
+ }
101
+ status_color = status_colors.get(project_dto.status, "white")
102
+
103
+ header = Text()
104
+ header.append(project_dto.name, style="bold white")
105
+ header.append("\n")
106
+ header.append(f"[{project_dto.status.upper()}]", style=f"bold {status_color}")
107
+
108
+ return header
109
+
110
+ def _build_metadata_table(self, project_dto: ProjectDTO):
111
+ """Build metadata table for project DTO.
112
+
113
+ Args:
114
+ project_dto: The ProjectDTO to format
115
+
116
+ Returns:
117
+ Rich Table with metadata rows
118
+ """
119
+ metadata = create_metadata_table()
120
+
121
+ metadata.add_row("Status", project_dto.status.upper())
122
+ metadata.add_row("Owner", project_dto.owner or "Unassigned")
123
+
124
+ if project_dto.headline:
125
+ metadata.add_row("Headline", project_dto.headline)
126
+
127
+ if isinstance(project_dto.target_end_date, datetime):
128
+ metadata.add_row(
129
+ "Target End Date", project_dto.target_end_date.strftime("%Y-%m-%d")
130
+ )
131
+ else:
132
+ metadata.add_row("Target End Date", "Not set")
133
+
134
+ if isinstance(project_dto.actual_end_date, datetime):
135
+ metadata.add_row(
136
+ "Actual End Date", project_dto.actual_end_date.strftime("%Y-%m-%d")
137
+ )
138
+
139
+ metadata.add_row("Milestones", str(project_dto.milestone_count))
140
+ metadata.add_row("Issues", str(project_dto.issue_count))
141
+
142
+ if isinstance(project_dto.created, datetime):
143
+ metadata.add_row("Created", project_dto.created.strftime("%Y-%m-%d %H:%M"))
144
+
145
+ if isinstance(project_dto.updated, datetime):
146
+ metadata.add_row("Updated", project_dto.updated.strftime("%Y-%m-%d %H:%M"))
147
+
148
+ return metadata
149
+
150
+ def _build_effort_table(self, effort_data: dict):
151
+ """Build effort/hours table if data exists.
152
+
153
+ Args:
154
+ effort_data: Dict with optional 'estimated' and 'actual' keys
155
+
156
+ Returns:
157
+ Rich Table with effort data or None if no data
158
+ """
159
+ if not (effort_data.get("estimated") or effort_data.get("actual")):
160
+ return None
161
+
162
+ effort = create_metadata_table()
163
+
164
+ if effort_data.get("estimated"):
165
+ estimated = effort_data["estimated"]
166
+ if estimated < 8:
167
+ estimated_display = f"{estimated:.1f}h"
168
+ else:
169
+ days = estimated / 8
170
+ estimated_display = f"{days:.1f}d ({estimated:.1f}h)"
171
+ effort.add_row("Estimated", estimated_display)
172
+
173
+ if effort_data.get("actual"):
174
+ actual = effort_data["actual"]
175
+ if actual < 8:
176
+ actual_display = f"{actual:.1f}h"
177
+ else:
178
+ days = actual / 8
179
+ actual_display = f"{days:.1f}d ({actual:.1f}h)"
180
+ effort.add_row("Actual", actual_display)
181
+
182
+ return effort
183
+
184
+ def _display_milestones_panel(
185
+ self, milestones: list, _progress: dict | None = None
186
+ ) -> None:
187
+ """Display milestones in a table panel.
188
+
189
+ Args:
190
+ milestones: List of milestone objects
191
+ _progress: Optional progress information dict (currently unused)
192
+ """
193
+ if not milestones:
194
+ self._get_console().print(
195
+ Panel(
196
+ "[dim]No milestones assigned to this project[/dim]",
197
+ title="šŸŽÆ Milestones",
198
+ border_style="magenta",
199
+ )
200
+ )
201
+ return
202
+
203
+ columns = [
204
+ ("Name", "white", 25),
205
+ ("Status", None, 11),
206
+ ("Due Date", None, 11),
207
+ ("Progress", None, 10),
208
+ ]
209
+ milestones_table = create_list_table(columns)
210
+
211
+ for milestone in milestones[:10]:
212
+ status_color = STATUS_COLORS.get(milestone.status.value, "white")
213
+ due_date_str = (
214
+ milestone.due_date.strftime("%Y-%m-%d")
215
+ if hasattr(milestone.due_date, "strftime")
216
+ else "Not set"
217
+ )
218
+
219
+ milestones_table.add_row(
220
+ milestone.name,
221
+ f"[{status_color}]{milestone.status.value}[/{status_color}]",
222
+ due_date_str,
223
+ f"{milestone.progress_percentage}%",
224
+ )
225
+
226
+ title = (
227
+ f"šŸŽÆ Milestones (Showing 10 of {len(milestones)})"
228
+ if len(milestones) > 10
229
+ else "šŸŽÆ Milestones"
230
+ )
231
+ self._get_console().print(
232
+ Panel(milestones_table, title=title, border_style="magenta")
233
+ )
@@ -0,0 +1,186 @@
1
+ """Presentation layer for project status display logic.
2
+
3
+ Handles all user-facing output:
4
+ - Displaying milestone progress
5
+ - Rendering issue status tables
6
+ - Showing roadmap summaries
7
+ - Formatting status information
8
+ """
9
+
10
+ from rich.progress import BarColumn, Progress, TextColumn
11
+ from rich.table import Table
12
+ from rich.text import Text
13
+
14
+ from roadmap.common.console import get_console
15
+ from roadmap.core.domain import Status
16
+
17
+
18
+ class MilestoneProgressPresenter:
19
+ """Presenter for milestone progress display."""
20
+
21
+ @staticmethod
22
+ def show_milestone_header() -> None:
23
+ """Display milestone section header."""
24
+ get_console().print("\nšŸŽÆ Milestones:", style="bold cyan")
25
+
26
+ @staticmethod
27
+ def show_milestone_progress(milestone_name: str, progress: dict) -> None:
28
+ """Display progress for a single milestone.
29
+
30
+ Args:
31
+ milestone_name: Name of the milestone
32
+ progress: Progress dictionary with 'total', 'completed', 'percentage'
33
+ """
34
+ console = get_console()
35
+ console.print(f"\n {milestone_name}")
36
+
37
+ if progress.get("total", 0) > 0:
38
+ with Progress(
39
+ TextColumn("[progress.description]{task.description}"),
40
+ BarColumn(),
41
+ TextColumn("[progress.percentage]{task.percentage:>3.0f}%"),
42
+ console=console,
43
+ transient=True,
44
+ ) as progress_bar:
45
+ completed = progress.get("completed", 0)
46
+ total = progress.get("total", 0)
47
+ progress_bar.add_task(
48
+ f" Progress ({completed}/{total})",
49
+ total=total,
50
+ completed=completed,
51
+ )
52
+ else:
53
+ console.print(" No issues assigned", style="dim")
54
+
55
+ @staticmethod
56
+ def show_all_milestones(milestones: list, milestone_progress: dict) -> None:
57
+ """Display all milestones with progress.
58
+
59
+ Args:
60
+ milestones: List of milestone objects
61
+ milestone_progress: Dictionary mapping milestone name to progress
62
+ """
63
+ MilestoneProgressPresenter.show_milestone_header()
64
+ for milestone in milestones:
65
+ progress = milestone_progress.get(milestone.name, {})
66
+ MilestoneProgressPresenter.show_milestone_progress(milestone.name, progress)
67
+
68
+
69
+ class IssueStatusPresenter:
70
+ """Presenter for issue status display."""
71
+
72
+ @staticmethod
73
+ def get_status_style(status: Status) -> str:
74
+ """Get display style for a status.
75
+
76
+ Args:
77
+ status: Status enum value
78
+
79
+ Returns:
80
+ Rich style string
81
+ """
82
+ styles = {
83
+ Status.TODO: "white",
84
+ Status.IN_PROGRESS: "yellow",
85
+ Status.BLOCKED: "red",
86
+ Status.REVIEW: "blue",
87
+ Status.CLOSED: "green",
88
+ }
89
+ return styles.get(status, "white")
90
+
91
+ @staticmethod
92
+ def show_issue_status_header() -> None:
93
+ """Display issue status section header."""
94
+ get_console().print("\nšŸ“‹ Issues by Status:", style="bold cyan")
95
+
96
+ @staticmethod
97
+ def show_issue_status_table(issue_counts: dict) -> None:
98
+ """Display table of issue counts by status.
99
+
100
+ Args:
101
+ issue_counts: Dictionary mapping Status enum to count
102
+ """
103
+ console = get_console()
104
+ if not issue_counts or not any(issue_counts.values()):
105
+ console.print(" No issues found", style="dim")
106
+ return
107
+
108
+ status_table = Table(show_header=False, box=None)
109
+ status_table.add_column("Status", style="white", width=15)
110
+ status_table.add_column("Count", style="cyan", width=10)
111
+
112
+ for status in Status:
113
+ count = issue_counts.get(status, 0)
114
+ if count > 0: # Only show statuses that have issues
115
+ style = IssueStatusPresenter.get_status_style(status)
116
+ status_table.add_row(
117
+ Text(f" {status.value}", style=style),
118
+ str(count),
119
+ )
120
+
121
+ console.print(status_table)
122
+
123
+ @staticmethod
124
+ def show_all_issue_statuses(issue_counts: dict) -> None:
125
+ """Display all issue statuses with counts.
126
+
127
+ Args:
128
+ issue_counts: Dictionary mapping Status enum to count
129
+ """
130
+ IssueStatusPresenter.show_issue_status_header()
131
+ IssueStatusPresenter.show_issue_status_table(issue_counts)
132
+
133
+
134
+ class RoadmapStatusPresenter:
135
+ """Presenter for overall roadmap status."""
136
+
137
+ @staticmethod
138
+ def show_empty_state() -> None:
139
+ """Display message when no issues or milestones exist."""
140
+ console = get_console()
141
+ console.print("\nšŸ“ No issues or milestones found.", style="yellow")
142
+ console.print("Get started with:")
143
+ console.print(" roadmap issue create 'My first issue'")
144
+ console.print(" roadmap milestone create 'My first milestone'")
145
+
146
+ @staticmethod
147
+ def show_status_header() -> None:
148
+ """Display main status header."""
149
+ get_console().print("šŸ“Š Roadmap Status", style="bold blue")
150
+
151
+ @staticmethod
152
+ def show_roadmap_summary(summary: dict) -> None:
153
+ """Display comprehensive roadmap summary.
154
+
155
+ Args:
156
+ summary: Dictionary with roadmap summary data
157
+ """
158
+ console = get_console()
159
+ console.print("\nšŸ“ˆ Summary:", style="bold cyan")
160
+ console.print(
161
+ f" Total Issues: {summary.get('total_issues', 0)}", style="white"
162
+ )
163
+ console.print(
164
+ f" Active Issues: {summary.get('active_issues', 0)}", style="yellow"
165
+ )
166
+ blocked = summary.get("blocked_issues", 0)
167
+ if blocked > 0:
168
+ console.print(f" Blocked Issues: {blocked}", style="red")
169
+ console.print(
170
+ f" Total Milestones: {summary.get('total_milestones', 0)}", style="white"
171
+ )
172
+ completed = summary.get("completed_milestones", 0)
173
+ total = summary.get("total_milestones", 0)
174
+ if total > 0:
175
+ console.print(f" Completed Milestones: {completed}/{total}", style="green")
176
+
177
+ @staticmethod
178
+ def show_error(error_message: str) -> None:
179
+ """Display error message.
180
+
181
+ Args:
182
+ error_message: Error message to display
183
+ """
184
+ get_console().print(
185
+ f"āŒ Failed to show status: {error_message}", style="bold red"
186
+ )
@@ -0,0 +1,95 @@
1
+ """Table and panel builders for consistent display formatting.
2
+
3
+ This module provides reusable Rich table and panel construction patterns for entity display:
4
+ - Two-column metadata tables (Key-Value format)
5
+ - Status and progress tables
6
+ - Panel creation with standard borders and styles
7
+ - Custom table styling
8
+
9
+ Consolidates duplicated table construction logic across view.py files.
10
+ """
11
+
12
+ from typing import Any
13
+
14
+ from rich.panel import Panel # type: ignore[import-not-found]
15
+ from rich.table import Table # type: ignore[import-not-found]
16
+
17
+ # ===== Generic Table Builders =====
18
+
19
+
20
+ def create_metadata_table() -> Table:
21
+ """Create standard 2-column metadata table (Key-Value format).
22
+
23
+ Returns:
24
+ Rich Table configured with standard metadata layout
25
+ """
26
+ table = Table(show_header=False, box=None, padding=(0, 2))
27
+ table.add_column("Key", style="dim")
28
+ table.add_column("Value")
29
+ return table
30
+
31
+
32
+ def create_list_table(
33
+ columns: list[tuple[str, str | None, int | None]] | None = None,
34
+ ) -> Table:
35
+ """Create table for list display with custom columns.
36
+
37
+ Args:
38
+ columns: List of (name, style, width) tuples for columns.
39
+ If None, creates basic 2-column table.
40
+
41
+ Returns:
42
+ Rich Table configured for list display
43
+ """
44
+ table = Table(show_header=True, box=None, padding=(0, 1))
45
+
46
+ if columns is None:
47
+ table.add_column("ID", style="cyan", width=9)
48
+ table.add_column("Title", style="white")
49
+ else:
50
+ for col_name, col_style, col_width in columns:
51
+ kwargs: dict[str, Any] = {}
52
+ if col_style:
53
+ kwargs["style"] = col_style
54
+ if col_width:
55
+ kwargs["width"] = col_width
56
+ table.add_column(col_name, **kwargs)
57
+
58
+ return table
59
+
60
+
61
+ # ===== Progress Display =====
62
+
63
+
64
+ # ===== Panel Creation =====
65
+
66
+
67
+ def create_panel(
68
+ content: Any,
69
+ title: str | None = None,
70
+ emoji: str | None = None,
71
+ border_style: str = "white",
72
+ ) -> Panel:
73
+ """Create standard panel with configurable title and border.
74
+
75
+ Args:
76
+ content: Content to display in panel (Table, Text, Markdown, etc.)
77
+ title: Optional panel title
78
+ emoji: Optional emoji to prepend to title
79
+ border_style: Border color (default: white)
80
+
81
+ Returns:
82
+ Rich Panel
83
+ """
84
+ full_title = None
85
+ if title:
86
+ full_title = f"{emoji} {title}" if emoji else title
87
+
88
+ return Panel(
89
+ content,
90
+ title=full_title,
91
+ border_style=border_style,
92
+ )
93
+
94
+
95
+ # ===== Text Builders =====
@@ -0,0 +1,42 @@
1
+ """Project commands module.
2
+
3
+ Provides all project management commands:
4
+ - create: Create a new project
5
+ - list: List all projects with optional filtering
6
+ - delete: Delete a project
7
+ - view: View detailed information about a project
8
+ - update: Update project properties
9
+ - close: Close a completed project
10
+ - archive: Archive a completed project
11
+ - restore: Restore an archived project
12
+ """
13
+
14
+ import click
15
+
16
+ from roadmap.adapters.cli.projects.archive import archive_project
17
+ from roadmap.adapters.cli.projects.close import close_project
18
+ from roadmap.adapters.cli.projects.create import create_project
19
+ from roadmap.adapters.cli.projects.delete import delete_project
20
+ from roadmap.adapters.cli.projects.list import list_projects
21
+ from roadmap.adapters.cli.projects.restore import restore_project
22
+ from roadmap.adapters.cli.projects.update import update_project
23
+ from roadmap.adapters.cli.projects.view import view_project
24
+
25
+
26
+ @click.group()
27
+ def project():
28
+ """Manage projects (top-level planning documents)."""
29
+ pass
30
+
31
+
32
+ # Register all commands
33
+ project.add_command(create_project, name="create")
34
+ project.add_command(list_projects, name="list")
35
+ project.add_command(update_project, name="update")
36
+ project.add_command(delete_project, name="delete")
37
+ project.add_command(view_project, name="view")
38
+ project.add_command(close_project, name="close")
39
+ project.add_command(archive_project, name="archive")
40
+ project.add_command(restore_project, name="restore")
41
+
42
+ __all__ = ["project"]
@@ -0,0 +1,127 @@
1
+ """Archive project command - move completed projects to archive."""
2
+
3
+ import click
4
+
5
+ from roadmap.adapters.cli.cli_command_helpers import require_initialized
6
+ from roadmap.adapters.cli.projects.archive_class import ProjectArchive
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("project_name", required=False)
16
+ @click.option(
17
+ "--all-closed",
18
+ is_flag=True,
19
+ help="Archive all closed projects",
20
+ )
21
+ @click.option(
22
+ "--list",
23
+ "list_archived",
24
+ is_flag=True,
25
+ help="List archived projects",
26
+ )
27
+ @click.option(
28
+ "--dry-run",
29
+ is_flag=True,
30
+ help="Preview what would be archived without actually doing it",
31
+ )
32
+ @click.option(
33
+ "--force",
34
+ is_flag=True,
35
+ help="Skip confirmation prompt",
36
+ )
37
+ @click.option(
38
+ "--verbose",
39
+ "-v",
40
+ is_flag=True,
41
+ help="Show detailed debug information",
42
+ )
43
+ @click.pass_context
44
+ @verbose_output
45
+ @log_command("project_archive", entity_type="project", track_duration=True)
46
+ @require_initialized
47
+ def archive_project(
48
+ ctx: click.Context,
49
+ project_name: str | None,
50
+ all_closed: bool,
51
+ list_archived: bool,
52
+ dry_run: bool,
53
+ force: bool,
54
+ verbose: bool, # noqa: F841
55
+ ):
56
+ """Archive a project by moving it to .roadmap/archive/projects/.
57
+
58
+ This is a non-destructive operation that preserves project data while
59
+ cleaning up the active workspace.
60
+
61
+ Examples:
62
+ roadmap project archive "Project Name"
63
+ roadmap project archive --all-closed
64
+ roadmap project archive --list
65
+ """
66
+ core = ctx.obj["core"]
67
+ console = get_console()
68
+
69
+ archive = ProjectArchive(core, console)
70
+
71
+ if list_archived:
72
+ from pathlib import Path
73
+
74
+ from roadmap.adapters.cli.crud.crud_helpers import (
75
+ EntityType,
76
+ collect_archive_files,
77
+ )
78
+ from roadmap.adapters.persistence.parser import ProjectParser
79
+
80
+ roadmap_dir = Path.cwd() / ".roadmap"
81
+ archive_dir = roadmap_dir / "archive" / "projects"
82
+
83
+ if not archive_dir.exists():
84
+ console.print("šŸ“‹ No archived projects.", style="yellow")
85
+ return
86
+
87
+ archived_files = collect_archive_files(archive_dir, EntityType.PROJECT)
88
+ if not archived_files:
89
+ console.print("šŸ“‹ No archived projects.", style="yellow")
90
+ return
91
+
92
+ console.print("\nšŸ“¦ Archived Projects:\n", style="bold blue")
93
+ for file_path in sorted(archived_files):
94
+ try:
95
+ project = ProjectParser.parse_project_file(file_path)
96
+ console.print(f" • {project.name} ({project.status})", style="cyan")
97
+ except Exception:
98
+ console.print(
99
+ f" • {file_path.name} (error reading file)", style="yellow"
100
+ )
101
+ return
102
+
103
+ # Validate arguments
104
+ if not project_name and not all_closed:
105
+ console.print(
106
+ "āŒ Error: Specify a project name or use --all-closed",
107
+ style="bold red",
108
+ )
109
+ ctx.exit(1)
110
+
111
+ if project_name and all_closed:
112
+ console.print(
113
+ "āŒ Error: Specify only one of: project name or --all-closed",
114
+ style="bold red",
115
+ )
116
+ ctx.exit(1)
117
+
118
+ try:
119
+ archive.execute(
120
+ entity_id=project_name,
121
+ all_closed=all_closed,
122
+ dry_run=dry_run,
123
+ force=force,
124
+ )
125
+ except Exception as e:
126
+ console.print(f"āŒ Archive operation failed: {str(e)}", style="bold red")
127
+ ctx.exit(1)