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,432 @@
1
+ """Roadmap exception hierarchy.
2
+
3
+ All exceptions include:
4
+ - domain_message: Technical message for logging
5
+ - user_message: User-friendly message for CLI output
6
+ - exit_code: CLI exit code (for Click commands)
7
+
8
+ This separation allows logging the full technical context while showing
9
+ users a clean, actionable message.
10
+
11
+ Example:
12
+ try:
13
+ service.create_issue(...)
14
+ except NotInitializedError as e:
15
+ logger.error(e.domain_message) # Technical details
16
+ click.echo(e.user_message, err=True) # What user sees
17
+ ctx.exit(e.exit_code)
18
+ """
19
+
20
+
21
+ class RoadmapException(Exception):
22
+ """Base exception for all roadmap errors.
23
+
24
+ Attributes:
25
+ domain_message: Technical message for logging/debugging
26
+ user_message: User-friendly message for CLI output
27
+ exit_code: Exit code for CLI commands
28
+ """
29
+
30
+ exit_code: int = 1
31
+
32
+ def __init__(
33
+ self,
34
+ domain_message: str,
35
+ user_message: str | None = None,
36
+ ):
37
+ """Initialize exception with messages.
38
+
39
+ Args:
40
+ domain_message: Technical message (what happened technically)
41
+ user_message: User-friendly message (what user should do)
42
+ Defaults to domain_message if not provided
43
+ """
44
+ self.domain_message = domain_message
45
+ self.user_message = user_message or domain_message
46
+ super().__init__(domain_message)
47
+
48
+ def __str__(self) -> str:
49
+ """Return user message for str()."""
50
+ return self.user_message
51
+
52
+
53
+ # ============================================================================
54
+ # Initialization & Setup Errors
55
+ # ============================================================================
56
+
57
+
58
+ class NotInitializedError(RoadmapException):
59
+ """Roadmap directory (.roadmap/) not initialized."""
60
+
61
+ exit_code = 1
62
+
63
+ def __init__(self):
64
+ """Initialize NotInitializedError."""
65
+ super().__init__(
66
+ domain_message="Roadmap .roadmap/ directory not found",
67
+ user_message="Roadmap not initialized. Run 'roadmap init' first.",
68
+ )
69
+
70
+
71
+ class AlreadyInitializedError(RoadmapException):
72
+ """Roadmap already initialized in this directory."""
73
+
74
+ exit_code = 1
75
+
76
+ def __init__(self, directory: str):
77
+ """Initialize AlreadyInitializedError.
78
+
79
+ Args:
80
+ directory: Path where roadmap is already initialized.
81
+ """
82
+ super().__init__(
83
+ domain_message=f"Roadmap already initialized in {directory}",
84
+ user_message=f"Roadmap already initialized in {directory}/. "
85
+ "Use --force to reinitialize.",
86
+ )
87
+
88
+
89
+ # ============================================================================
90
+ # Entity Not Found Errors
91
+ # ============================================================================
92
+
93
+
94
+ class EntityNotFoundError(RoadmapException):
95
+ """Entity (issue, milestone, project) not found."""
96
+
97
+ exit_code = 1
98
+
99
+ def __init__(self, entity_type: str, entity_id: str):
100
+ """Initialize EntityNotFoundError.
101
+
102
+ Args:
103
+ entity_type: Type of entity (Issue, Milestone, Project).
104
+ entity_id: ID of the missing entity.
105
+ """
106
+ super().__init__(
107
+ domain_message=f"{entity_type} {entity_id} not found in database",
108
+ user_message=f"{entity_type} not found: {entity_id}",
109
+ )
110
+
111
+
112
+ class IssueNotFoundError(EntityNotFoundError):
113
+ """Specific: Issue not found."""
114
+
115
+ def __init__(self, issue_id: str):
116
+ """Initialize IssueNotFoundError.
117
+
118
+ Args:
119
+ issue_id: ID of the missing issue.
120
+ """
121
+ super().__init__("Issue", issue_id)
122
+
123
+
124
+ class MilestoneNotFoundError(EntityNotFoundError):
125
+ """Specific: Milestone not found."""
126
+
127
+ def __init__(self, milestone_id: str):
128
+ """Initialize MilestoneNotFoundError.
129
+
130
+ Args:
131
+ milestone_id: ID of the missing milestone.
132
+ """
133
+ super().__init__("Milestone", milestone_id)
134
+
135
+
136
+ class ProjectNotFoundError(EntityNotFoundError):
137
+ """Specific: Project not found."""
138
+
139
+ def __init__(self, project_id: str):
140
+ """Initialize ProjectNotFoundError.
141
+
142
+ Args:
143
+ project_id: ID of the missing project.
144
+ """
145
+ super().__init__("Project", project_id)
146
+
147
+
148
+ # ============================================================================
149
+ # Validation Errors
150
+ # ============================================================================
151
+
152
+
153
+ class ValidationError(RoadmapException):
154
+ """Input validation failed."""
155
+
156
+ exit_code = 2
157
+
158
+ def __init__(self, domain_message: str, user_message: str | None = None):
159
+ """Initialize ValidationError.
160
+
161
+ Args:
162
+ domain_message: Technical message describing validation failure.
163
+ user_message: User-friendly message. Defaults to domain_message.
164
+ """
165
+ super().__init__(
166
+ domain_message=domain_message,
167
+ user_message=user_message or domain_message,
168
+ )
169
+
170
+
171
+ class InvalidStatusError(ValidationError):
172
+ """Invalid status value."""
173
+
174
+ def __init__(self, status: str, valid_statuses: list[str]):
175
+ """Initialize InvalidStatusError.
176
+
177
+ Args:
178
+ status: Invalid status value provided.
179
+ valid_statuses: List of valid status values.
180
+ """
181
+ domain_message = f"Validation failed: status '{status}' is not valid"
182
+ user_message = (
183
+ f"Invalid status: '{status}'. Valid values: {', '.join(valid_statuses)}"
184
+ )
185
+ super().__init__(domain_message=domain_message, user_message=user_message)
186
+
187
+
188
+ class InvalidPriorityError(ValidationError):
189
+ """Invalid priority value."""
190
+
191
+ def __init__(self, priority: str, valid_priorities: list[str]):
192
+ """Initialize InvalidPriorityError.
193
+
194
+ Args:
195
+ priority: Invalid priority value provided.
196
+ valid_priorities: List of valid priority values.
197
+ """
198
+ domain_message = f"Validation failed: priority '{priority}' is not valid"
199
+ user_message = f"Invalid priority: '{priority}'. Valid values: {', '.join(valid_priorities)}"
200
+ super().__init__(domain_message=domain_message, user_message=user_message)
201
+
202
+
203
+ # ============================================================================
204
+ # Operation Errors
205
+ # ============================================================================
206
+
207
+
208
+ class OperationError(RoadmapException):
209
+ """Operation failed (creation, update, deletion, etc.)."""
210
+
211
+ exit_code = 1
212
+
213
+ def __init__(self, operation: str, reason: str):
214
+ """Initialize OperationError.
215
+
216
+ Args:
217
+ operation: Description of the operation that failed.
218
+ reason: Reason why the operation failed.
219
+ """
220
+ super().__init__(
221
+ domain_message=f"Failed to {operation}: {reason}",
222
+ user_message=f"Failed to {operation}: {reason}",
223
+ )
224
+
225
+
226
+ class CreateError(OperationError):
227
+ """Failed to create entity."""
228
+
229
+ def __init__(self, entity_type: str, reason: str):
230
+ """Initialize CreateError.
231
+
232
+ Args:
233
+ entity_type: Type of entity that failed to create.
234
+ reason: Reason for creation failure.
235
+ """
236
+ super().__init__(f"create {entity_type}", reason)
237
+
238
+
239
+ class UpdateError(OperationError):
240
+ """Failed to update entity."""
241
+
242
+ def __init__(self, entity_type: str, reason: str):
243
+ """Initialize UpdateError.
244
+
245
+ Args:
246
+ entity_type: Type of entity that failed to update.
247
+ reason: Reason for update failure.
248
+ """
249
+ super().__init__(f"update {entity_type}", reason)
250
+
251
+
252
+ class DeleteError(OperationError):
253
+ """Failed to delete entity."""
254
+
255
+ def __init__(self, entity_type: str, reason: str):
256
+ """Initialize DeleteError.
257
+
258
+ Args:
259
+ entity_type: Type of entity that failed to delete.
260
+ reason: Reason for deletion failure.
261
+ """
262
+ super().__init__(f"delete {entity_type}", reason)
263
+
264
+
265
+ # ============================================================================
266
+ # Database/Persistence Errors
267
+ # ============================================================================
268
+
269
+
270
+ class PersistenceError(RoadmapException):
271
+ """Database or file persistence operation failed."""
272
+
273
+ exit_code = 1
274
+
275
+ def __init__(self, operation: str, reason: str):
276
+ """Initialize PersistenceError.
277
+
278
+ Args:
279
+ operation: Name of the persistence operation that failed.
280
+ reason: Reason for failure.
281
+ """
282
+ super().__init__(
283
+ domain_message=f"Persistence operation '{operation}' failed: {reason}",
284
+ user_message="Storage operation failed. Check disk space and permissions.",
285
+ )
286
+
287
+
288
+ class DatabaseError(PersistenceError):
289
+ """Database operation failed."""
290
+
291
+ def __init__(self, reason: str):
292
+ """Initialize DatabaseError.
293
+
294
+ Args:
295
+ reason: Reason for database operation failure.
296
+ """
297
+ super().__init__("database", reason)
298
+
299
+
300
+ class FileOperationError(PersistenceError):
301
+ """File operation failed."""
302
+
303
+ def __init__(self, reason: str):
304
+ """Initialize FileOperationError.
305
+
306
+ Args:
307
+ reason: Reason for file operation failure.
308
+ """
309
+ super().__init__("file_operation", reason)
310
+
311
+
312
+ # ============================================================================
313
+ # Git/GitHub Errors
314
+ # ============================================================================
315
+
316
+
317
+ class GitError(RoadmapException):
318
+ """Git operation failed."""
319
+
320
+ exit_code = 1
321
+
322
+ def __init__(self, operation: str, reason: str):
323
+ """Initialize GitError.
324
+
325
+ Args:
326
+ operation: Name of the Git operation that failed.
327
+ reason: Reason for failure.
328
+ """
329
+ super().__init__(
330
+ domain_message=f"Git operation '{operation}' failed: {reason}",
331
+ user_message=f"Git operation failed: {reason}",
332
+ )
333
+
334
+
335
+ class GitHubError(RoadmapException):
336
+ """GitHub integration failed."""
337
+
338
+ exit_code = 1
339
+
340
+ def __init__(self, operation: str, reason: str):
341
+ """Initialize GitHubError.
342
+
343
+ Args:
344
+ operation: Name of the GitHub operation that failed.
345
+ reason: Reason for failure.
346
+ """
347
+ super().__init__(
348
+ domain_message=f"GitHub operation '{operation}' failed: {reason}",
349
+ user_message=f"GitHub operation failed: {reason}",
350
+ )
351
+
352
+
353
+ class NoGitRepositoryError(GitError):
354
+ """Not in a Git repository."""
355
+
356
+ def __init__(self):
357
+ """Initialize NoGitRepositoryError."""
358
+ super().__init__(
359
+ "repository_check",
360
+ "Not in a Git repository",
361
+ )
362
+
363
+
364
+ class GitConfigError(GitError):
365
+ """Git configuration missing or invalid."""
366
+
367
+ def __init__(self, config_key: str):
368
+ """Initialize GitConfigError.
369
+
370
+ Args:
371
+ config_key: Name of the missing Git configuration key.
372
+ """
373
+ super().__init__(
374
+ "config_read",
375
+ f"Git config '{config_key}' not found. Run: git config user.name 'Your Name'",
376
+ )
377
+
378
+
379
+ # ============================================================================
380
+ # Configuration/Setup Errors
381
+ # ============================================================================
382
+
383
+
384
+ class ConfigurationError(RoadmapException):
385
+ """Configuration is invalid or missing."""
386
+
387
+ exit_code = 1
388
+
389
+ def __init__(self, issue: str):
390
+ """Initialize ConfigurationError.
391
+
392
+ Args:
393
+ issue: Description of the configuration problem.
394
+ """
395
+ super().__init__(
396
+ domain_message=f"Configuration error: {issue}",
397
+ user_message=f"Configuration error: {issue}",
398
+ )
399
+
400
+
401
+ # ============================================================================
402
+ # Security Errors
403
+ # ============================================================================
404
+
405
+
406
+ class SecurityError(RoadmapException):
407
+ """Security validation failed."""
408
+
409
+ exit_code = 1
410
+
411
+ def __init__(self, issue: str):
412
+ """Initialize SecurityError.
413
+
414
+ Args:
415
+ issue: Description of the security check failure.
416
+ """
417
+ super().__init__(
418
+ domain_message=f"Security check failed: {issue}",
419
+ user_message="Security validation failed. Check file permissions.",
420
+ )
421
+
422
+
423
+ class PathValidationError(SecurityError):
424
+ """Path traversal or invalid path detected."""
425
+
426
+ def __init__(self, path: str):
427
+ """Initialize PathValidationError.
428
+
429
+ Args:
430
+ path: The invalid path that failed validation.
431
+ """
432
+ super().__init__(f"Invalid path: {path}")
@@ -0,0 +1,29 @@
1
+ """Formatters package - unified formatting utilities.
2
+
3
+ Organized into focused modules:
4
+ - text: Basic text, duration, status, operation formatting
5
+ - output: Generic TableData rendering (JSON, CSV, Markdown, plain-text)
6
+ - tables: Domain object to TableData conversion (Issue, Milestone, Project)
7
+ - export: Issue export with multiple format support
8
+ - kanban: Kanban board organization and layout
9
+
10
+ Use submodule imports for clarity:
11
+ from roadmap.common.formatters.text import format_table, format_header
12
+ from roadmap.common.formatters.output import OutputFormatter
13
+ from roadmap.common.formatters.tables import IssueTableFormatter
14
+ from roadmap.common.formatters.export import IssueExporter
15
+ from roadmap.common.formatters.kanban import KanbanOrganizer, KanbanLayout
16
+ """
17
+
18
+ from .export import IssueExporter
19
+ from .kanban import KanbanLayout, KanbanOrganizer
20
+ from .tables import IssueTableFormatter, MilestoneTableFormatter, ProjectTableFormatter
21
+
22
+ __all__ = [
23
+ "IssueExporter",
24
+ "IssueTableFormatter",
25
+ "MilestoneTableFormatter",
26
+ "ProjectTableFormatter",
27
+ "KanbanOrganizer",
28
+ "KanbanLayout",
29
+ ]
@@ -0,0 +1,96 @@
1
+ """Base table formatter for DRY consolidation.
2
+
3
+ This module provides abstract base class for all table formatters,
4
+ eliminating duplicate code across Issue, Project, and Milestone formatters.
5
+ """
6
+
7
+ from abc import ABC, abstractmethod
8
+ from typing import Any
9
+
10
+ from roadmap.common.console import get_console
11
+ from roadmap.common.models import TableData
12
+
13
+
14
+ class BaseTableFormatter[T](ABC):
15
+ """Abstract base class for table formatters."""
16
+
17
+ @property
18
+ def console(self):
19
+ """Get console instance dynamically for test compatibility."""
20
+ return get_console()
21
+
22
+ @abstractmethod
23
+ def create_table(self) -> Any:
24
+ """Create and configure the rich Table for this formatter.
25
+
26
+ Returns:
27
+ Configured Rich Table with columns
28
+ """
29
+ pass
30
+
31
+ @abstractmethod
32
+ def add_row(self, table: Any, item: T) -> None:
33
+ """Add a single item row to the table.
34
+
35
+ Args:
36
+ table: The Rich Table to add row to
37
+ item: The domain object to format
38
+ """
39
+ pass
40
+
41
+ @abstractmethod
42
+ def get_filter_description(self, items: list[T]) -> str:
43
+ """Get human-readable description of filtered items.
44
+
45
+ Args:
46
+ items: List of items being displayed
47
+
48
+ Returns:
49
+ Description string (e.g., "2 open issue")
50
+ """
51
+ pass
52
+
53
+ def display_items(self, items: list[T], filter_description: str = "all") -> None:
54
+ """Display items in a formatted table.
55
+
56
+ Args:
57
+ items: List of items to display
58
+ filter_description: Human-readable filter description
59
+ """
60
+ if not items:
61
+ self.console.print(
62
+ f"📋 No {filter_description} items found.", style="yellow"
63
+ )
64
+ self.console.print(
65
+ "Create one with: roadmap [command] create '[Name]'",
66
+ style="dim",
67
+ )
68
+ return
69
+
70
+ # Display header with filter info
71
+ filter_desc = self.get_filter_description(items)
72
+ self.console.print(f"📋 {filter_desc}", style="bold cyan")
73
+ self.console.print()
74
+
75
+ # Create and populate table
76
+ table = self.create_table()
77
+ for item in items:
78
+ self.add_row(table, item)
79
+
80
+ self.console.print(table)
81
+
82
+ @abstractmethod
83
+ def items_to_table_data(
84
+ self, items: list[T], title: str = "Items", description: str = ""
85
+ ) -> TableData:
86
+ """Convert items to TableData for structured output.
87
+
88
+ Args:
89
+ items: List of items to convert
90
+ title: Table title
91
+ description: Optional description
92
+
93
+ Returns:
94
+ TableData object for rendering in any format
95
+ """
96
+ pass
@@ -0,0 +1,7 @@
1
+ """Export module public API."""
2
+
3
+ from .issue_exporter import IssueExporter
4
+
5
+ __all__ = [
6
+ "IssueExporter",
7
+ ]
@@ -0,0 +1,97 @@
1
+ """Issue export functionality."""
2
+
3
+ import csv
4
+ import json
5
+ from collections.abc import Sequence
6
+ from io import StringIO
7
+
8
+ from roadmap.common.formatters.output import OutputFormatter
9
+ from roadmap.common.formatters.tables import IssueTableFormatter
10
+ from roadmap.core.domain import Issue
11
+
12
+
13
+ class IssueExporter:
14
+ """Export issues to various formats.
15
+
16
+ Supports two export styles:
17
+ 1. Unified export() - recommended for production use
18
+ 2. Legacy to_json/to_csv/to_markdown - for backward compatibility
19
+ """
20
+
21
+ # Legacy CSV fields definition
22
+ CSV_FIELDS = [
23
+ "id",
24
+ "title",
25
+ "status",
26
+ "assignee",
27
+ "priority",
28
+ "estimated_hours",
29
+ "milestone",
30
+ "created",
31
+ "updated",
32
+ ]
33
+
34
+ @staticmethod
35
+ def export(issues: Sequence[Issue], format_type: str, title: str = "Issues") -> str:
36
+ """Export issues using the unified OutputFormatter.
37
+
38
+ Converts issues to TableData and uses OutputFormatter for consistent
39
+ rendering across all formats.
40
+
41
+ Args:
42
+ issues: List of Issue objects
43
+ format_type: One of 'json', 'csv', 'markdown'
44
+ title: Optional table title
45
+
46
+ Returns:
47
+ Formatted string ready for output
48
+ """
49
+ table_data = IssueTableFormatter.issues_to_table_data(issues, title=title) # type: ignore
50
+ formatter = OutputFormatter(table_data)
51
+
52
+ if format_type == "json":
53
+ return formatter.to_json()
54
+ elif format_type == "csv":
55
+ return formatter.to_csv()
56
+ elif format_type == "markdown":
57
+ return formatter.to_markdown()
58
+ else:
59
+ raise ValueError(
60
+ f"Unknown format: {format_type}. Use 'json', 'csv', or 'markdown'."
61
+ )
62
+
63
+ @staticmethod
64
+ def to_json(issues: Sequence[Issue], serializer_func) -> str:
65
+ """Legacy JSON export with custom serializer (for backward compatibility)."""
66
+ payload = [serializer_func(i) for i in issues]
67
+ return json.dumps(payload, indent=2)
68
+
69
+ @classmethod
70
+ def to_csv(cls, issues: Sequence[Issue], serializer_func) -> str:
71
+ """Legacy CSV export with custom serializer (for backward compatibility)."""
72
+ buf = StringIO()
73
+ writer = csv.DictWriter(buf, fieldnames=cls.CSV_FIELDS)
74
+ writer.writeheader()
75
+ for issue in issues:
76
+ row = serializer_func(issue)
77
+ writer.writerow({f: row.get(f, "") for f in cls.CSV_FIELDS})
78
+ return buf.getvalue()
79
+
80
+ @staticmethod
81
+ def to_markdown(issues: Sequence[Issue]) -> str:
82
+ """Legacy Markdown export (for backward compatibility)."""
83
+ lines = [
84
+ "| id | title | status | assignee | milestone | estimated |",
85
+ "|---|---|---:|---|---|---:|",
86
+ ]
87
+ for i in issues:
88
+ est = (
89
+ i.estimated_time_display
90
+ if hasattr(i, "estimated_time_display")
91
+ else (i.estimated_hours or "")
92
+ )
93
+ status_val = i.status.value if hasattr(i.status, "value") else i.status
94
+ lines.append(
95
+ f"| {i.id} | {i.title} | {status_val} | {i.assignee or ''} | {i.milestone or ''} | {est} |"
96
+ )
97
+ return "\n".join(lines)
@@ -0,0 +1,28 @@
1
+ """Formatting helpers - wrapper to avoid circular dependencies.
2
+
3
+ This module re-exports OutputFormatHandler from cli_helpers to break
4
+ the circular dependency between formatting and utils modules.
5
+ """
6
+
7
+ from typing import TYPE_CHECKING
8
+
9
+ # Import only for type checking to avoid circular imports
10
+ if TYPE_CHECKING:
11
+ from roadmap.common.utils.cli_helpers import OutputFormatHandler, format_output
12
+
13
+
14
+ def __getattr__(name: str): # noqa: ANN001, ANN201
15
+ """Lazy load OutputFormatHandler and format_output from cli_helpers."""
16
+ if name == "OutputFormatHandler":
17
+ from roadmap.common.utils.cli_helpers import OutputFormatHandler
18
+
19
+ return OutputFormatHandler
20
+ elif name == "format_output":
21
+ from roadmap.common.utils.cli_helpers import format_output
22
+
23
+ return format_output
24
+ msg = f"module {__name__!r} has no attribute {name!r}"
25
+ raise AttributeError(msg)
26
+
27
+
28
+ __all__ = ["format_output", "OutputFormatHandler"]
@@ -0,0 +1,9 @@
1
+ """Kanban module public API."""
2
+
3
+ from .layout import KanbanLayout
4
+ from .organizer import KanbanOrganizer
5
+
6
+ __all__ = [
7
+ "KanbanOrganizer",
8
+ "KanbanLayout",
9
+ ]