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,355 @@
1
+ """Formatters for displaying sync metrics in the CLI.
2
+
3
+ This module provides various formatting utilities for presenting sync
4
+ metrics in a user-friendly table format.
5
+ """
6
+
7
+ from datetime import UTC, datetime, timedelta
8
+
9
+ from rich.table import Table
10
+ from rich.text import Text
11
+
12
+ from roadmap.core.observability.sync_metrics import SyncMetrics
13
+
14
+
15
+ def format_duration(seconds: float) -> str:
16
+ """Format duration in seconds to human-readable string.
17
+
18
+ Args:
19
+ seconds: Duration in seconds
20
+
21
+ Returns:
22
+ Formatted duration string
23
+ """
24
+ if seconds < 1:
25
+ return f"{seconds * 1000:.0f}ms"
26
+ elif seconds < 60:
27
+ return f"{seconds:.1f}s"
28
+ elif seconds < 3600:
29
+ minutes = seconds / 60
30
+ return f"{minutes:.1f}m"
31
+ else:
32
+ hours = seconds / 3600
33
+ return f"{hours:.1f}h"
34
+
35
+
36
+ def format_count(count: int | float) -> str:
37
+ """Format count as a readable string.
38
+
39
+ Args:
40
+ count: Number to format
41
+
42
+ Returns:
43
+ Formatted count string
44
+ """
45
+ if isinstance(count, float) and count == int(count):
46
+ count = int(count)
47
+ return str(count)
48
+
49
+
50
+ def format_percentage(value: float, decimals: int = 1) -> str:
51
+ """Format percentage value.
52
+
53
+ Args:
54
+ value: Value between 0 and 100
55
+ decimals: Number of decimal places
56
+
57
+ Returns:
58
+ Formatted percentage string
59
+ """
60
+ return f"{value:.{decimals}f}%"
61
+
62
+
63
+ def _add_dedup_rows(table: Table, metrics: SyncMetrics) -> None:
64
+ if (
65
+ metrics.local_issues_before_dedup <= 0
66
+ and metrics.remote_issues_before_dedup <= 0
67
+ ):
68
+ return
69
+
70
+ table.add_row("[bold cyan]═══ Deduplication ═══[/bold cyan]", "")
71
+
72
+ if metrics.local_issues_before_dedup > 0:
73
+ table.add_row(
74
+ " Local Issues (Before)",
75
+ format_count(metrics.local_issues_before_dedup),
76
+ )
77
+ table.add_row(
78
+ " Local Issues (After)",
79
+ format_count(metrics.local_issues_after_dedup),
80
+ )
81
+ if metrics.local_dedup_reduction_pct > 0:
82
+ table.add_row(
83
+ " Local Dedup Reduction",
84
+ Text(
85
+ format_percentage(metrics.local_dedup_reduction_pct),
86
+ style="green",
87
+ ),
88
+ )
89
+
90
+ if metrics.remote_issues_before_dedup > 0:
91
+ table.add_row(
92
+ " Remote Issues (Before)",
93
+ format_count(metrics.remote_issues_before_dedup),
94
+ )
95
+ table.add_row(
96
+ " Remote Issues (After)",
97
+ format_count(metrics.remote_issues_after_dedup),
98
+ )
99
+ if metrics.remote_dedup_reduction_pct > 0:
100
+ table.add_row(
101
+ " Remote Dedup Reduction",
102
+ Text(
103
+ format_percentage(metrics.remote_dedup_reduction_pct),
104
+ style="green",
105
+ ),
106
+ )
107
+
108
+
109
+ def _add_sync_ops_rows(table: Table, metrics: SyncMetrics) -> None:
110
+ if not (
111
+ metrics.issues_fetched > 0
112
+ or metrics.issues_pushed > 0
113
+ or metrics.issues_pulled > 0
114
+ ):
115
+ return
116
+
117
+ table.add_row("[bold cyan]═══ Sync Operations ═══[/bold cyan]", "")
118
+
119
+ if metrics.issues_fetched > 0:
120
+ table.add_row(
121
+ " Issues Fetched",
122
+ format_count(metrics.issues_fetched),
123
+ )
124
+ if metrics.issues_pushed > 0:
125
+ table.add_row(
126
+ " Issues Pushed",
127
+ format_count(metrics.issues_pushed),
128
+ )
129
+ if metrics.issues_pulled > 0:
130
+ table.add_row(
131
+ " Issues Pulled",
132
+ format_count(metrics.issues_pulled),
133
+ )
134
+
135
+
136
+ def _add_duplicate_rows(table: Table, metrics: SyncMetrics) -> None:
137
+ if metrics.duplicates_detected <= 0:
138
+ return
139
+
140
+ table.add_row("[bold cyan]═══ Duplicates ═══[/bold cyan]", "")
141
+ table.add_row(
142
+ " Duplicates Detected",
143
+ Text(format_count(metrics.duplicates_detected), style="yellow"),
144
+ )
145
+ if metrics.duplicates_auto_resolved > 0:
146
+ table.add_row(
147
+ " Auto-Resolved",
148
+ Text(
149
+ format_count(metrics.duplicates_auto_resolved),
150
+ style="green",
151
+ ),
152
+ )
153
+ if metrics.duplicates_manual_resolved > 0:
154
+ table.add_row(
155
+ " Manual Resolution",
156
+ Text(
157
+ format_count(metrics.duplicates_manual_resolved),
158
+ style="cyan",
159
+ ),
160
+ )
161
+
162
+
163
+ def _add_conflict_rows(table: Table, metrics: SyncMetrics) -> None:
164
+ if metrics.conflicts_detected <= 0:
165
+ return
166
+
167
+ table.add_row("[bold cyan]═══ Conflicts ═══[/bold cyan]", "")
168
+ table.add_row(
169
+ " Conflicts Detected",
170
+ Text(format_count(metrics.conflicts_detected), style="yellow"),
171
+ )
172
+
173
+
174
+ def _add_link_rows(table: Table, metrics: SyncMetrics) -> None:
175
+ if metrics.sync_links_created <= 0 and metrics.orphaned_links <= 0:
176
+ return
177
+
178
+ table.add_row("[bold cyan]═══ Sync Links ═══[/bold cyan]", "")
179
+ if metrics.sync_links_created > 0:
180
+ table.add_row(
181
+ " Links Created",
182
+ format_count(metrics.sync_links_created),
183
+ )
184
+ if metrics.orphaned_links > 0:
185
+ table.add_row(
186
+ " Orphaned Links",
187
+ Text(
188
+ format_count(metrics.orphaned_links),
189
+ style="yellow",
190
+ ),
191
+ )
192
+
193
+
194
+ def _add_performance_rows(table: Table, metrics: SyncMetrics) -> None:
195
+ table.add_row("[bold cyan]═══ Performance ═══[/bold cyan]", "")
196
+ if metrics.cache_hit_rate > 0:
197
+ table.add_row(
198
+ " Cache Hit Rate",
199
+ Text(
200
+ format_percentage(metrics.cache_hit_rate * 100),
201
+ style="green",
202
+ ),
203
+ )
204
+ if metrics.database_query_time > 0:
205
+ table.add_row(
206
+ " DB Query Time",
207
+ Text(format_duration(metrics.database_query_time), style="yellow"),
208
+ )
209
+ if metrics.total_api_calls > 0:
210
+ table.add_row(
211
+ " API Calls",
212
+ Text(format_count(metrics.total_api_calls), style="cyan"),
213
+ )
214
+
215
+
216
+ def create_metrics_summary_table(metrics: SyncMetrics, verbose: bool = False) -> Table:
217
+ """Create a summary table of sync metrics.
218
+
219
+ Args:
220
+ metrics: SyncMetrics object to display
221
+ verbose: Include performance and phase timing details
222
+
223
+ Returns:
224
+ Rich Table with sync summary metrics
225
+ """
226
+ table = Table(show_header=False, box=None, padding=(0, 2))
227
+ table.add_column(style="bold")
228
+ table.add_column(justify="right", style="cyan")
229
+
230
+ table.add_row("[bold cyan]═══ Summary ═══[/bold cyan]", "")
231
+ table.add_row("Backend", metrics.backend_type or "unknown")
232
+ table.add_row(
233
+ "Started",
234
+ metrics.start_time.strftime("%Y-%m-%d %H:%M"),
235
+ )
236
+ table.add_row("Duration", format_duration(metrics.duration_seconds))
237
+
238
+ _add_dedup_rows(table, metrics)
239
+ _add_sync_ops_rows(table, metrics)
240
+ _add_duplicate_rows(table, metrics)
241
+ _add_conflict_rows(table, metrics)
242
+ _add_link_rows(table, metrics)
243
+
244
+ if verbose:
245
+ _add_performance_rows(table, metrics)
246
+ if metrics.circuit_breaker_state:
247
+ state_style = (
248
+ "green"
249
+ if metrics.circuit_breaker_state == "closed"
250
+ else "yellow"
251
+ if metrics.circuit_breaker_state == "half-open"
252
+ else "red"
253
+ )
254
+ table.add_row(
255
+ " Circuit Breaker",
256
+ Text(metrics.circuit_breaker_state, style=state_style),
257
+ )
258
+
259
+ if metrics.fetch_phase_duration > 0:
260
+ table.add_row(
261
+ " Fetch Time",
262
+ format_duration(metrics.fetch_phase_duration),
263
+ )
264
+ if metrics.push_phase_duration > 0:
265
+ table.add_row(
266
+ " Push Time",
267
+ format_duration(metrics.push_phase_duration),
268
+ )
269
+ if metrics.pull_phase_duration > 0:
270
+ table.add_row(
271
+ " Pull Time",
272
+ format_duration(metrics.pull_phase_duration),
273
+ )
274
+ if metrics.analysis_phase_duration > 0:
275
+ table.add_row(
276
+ " Analysis Time",
277
+ format_duration(metrics.analysis_phase_duration),
278
+ )
279
+ if metrics.merge_phase_duration > 0:
280
+ table.add_row(
281
+ " Merge Time",
282
+ format_duration(metrics.merge_phase_duration),
283
+ )
284
+ if metrics.conflict_resolution_duration > 0:
285
+ table.add_row(
286
+ " Conflict Resolution Time",
287
+ format_duration(metrics.conflict_resolution_duration),
288
+ )
289
+ # Errors
290
+ if metrics.errors_count > 0:
291
+ table.add_row("[bold cyan]═══ Errors ═══[/bold cyan]", "")
292
+ table.add_row(
293
+ " Error Count",
294
+ Text(format_count(metrics.errors_count), style="red"),
295
+ )
296
+
297
+ return table
298
+
299
+
300
+ def create_metrics_history_table(
301
+ metrics_list: list[SyncMetrics], limit: int = 10
302
+ ) -> Table:
303
+ """Create a table showing history of sync metrics.
304
+
305
+ Args:
306
+ metrics_list: List of SyncMetrics objects
307
+ limit: Maximum number of rows to show
308
+
309
+ Returns:
310
+ Rich Table with metrics history
311
+ """
312
+ table = Table(
313
+ show_header=True,
314
+ header_style="bold cyan",
315
+ box=None,
316
+ title="Sync Metrics History",
317
+ )
318
+ table.add_column("Time", style="dim")
319
+ table.add_column("Backend", style="green")
320
+ table.add_column("Duration", justify="right")
321
+ table.add_column("Duplicates", justify="right")
322
+ table.add_column("Conflicts", justify="right")
323
+ table.add_column("Errors", justify="right")
324
+
325
+ # Reverse to show newest first
326
+ for metrics in reversed(metrics_list[:limit]):
327
+ created_time = metrics.start_time
328
+ time_diff = datetime.now(UTC) - created_time
329
+ time_str = (
330
+ "just now"
331
+ if time_diff < timedelta(seconds=60)
332
+ else (
333
+ f"{int(time_diff.total_seconds() / 60)}m ago"
334
+ if time_diff < timedelta(hours=1)
335
+ else (
336
+ f"{int(time_diff.total_seconds() / 3600)}h ago"
337
+ if time_diff < timedelta(days=1)
338
+ else created_time.strftime("%Y-%m-%d %H:%M")
339
+ )
340
+ )
341
+ )
342
+
343
+ error_count = metrics.errors_count
344
+ error_style = "red" if error_count > 0 else "green"
345
+
346
+ table.add_row(
347
+ time_str,
348
+ metrics.backend_type,
349
+ format_duration(metrics.duration_seconds),
350
+ format_count(metrics.duplicates_detected),
351
+ format_count(metrics.conflicts_detected),
352
+ Text(format_count(error_count), style=error_style),
353
+ )
354
+
355
+ return table
roadmap/settings.py ADDED
@@ -0,0 +1,345 @@
1
+ """Configuration management for roadmap CLI application.
2
+
3
+ This module provides environment-aware configuration using dynaconf,
4
+ supporting multiple environments (development, testing, production) and
5
+ various configuration sources (files, environment variables, etc.).
6
+ """
7
+
8
+ from pathlib import Path
9
+ from typing import Any
10
+
11
+ from dynaconf import Dynaconf, Validator # type: ignore[import-not-found]
12
+
13
+ from .common.logging import get_logger
14
+
15
+ logger = get_logger(__name__)
16
+
17
+
18
+ def get_config_paths() -> dict[str, Path]:
19
+ """Get standard configuration file paths."""
20
+ home_config_dir = Path.home() / ".roadmap"
21
+ home_config_dir.mkdir(exist_ok=True)
22
+
23
+ return {
24
+ "global_config": home_config_dir / "settings.toml",
25
+ "global_secrets": home_config_dir / "secrets.toml",
26
+ "local_config": Path.cwd() / ".roadmap" / "settings.toml",
27
+ "local_secrets": Path.cwd() / ".roadmap" / "secrets.toml",
28
+ }
29
+
30
+
31
+ # Configuration schema with validation
32
+ _validators = [
33
+ # Database settings
34
+ Validator("database.path", default="~/.roadmap/roadmap.db"),
35
+ Validator("database.timeout", default=30.0, gte=1.0),
36
+ Validator("database.backup_count", default=5, gte=0),
37
+ # Logging settings
38
+ Validator(
39
+ "logging.level",
40
+ default="INFO",
41
+ is_in=["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"],
42
+ ),
43
+ Validator("logging.to_file", default=True, is_type_of=bool),
44
+ Validator("logging.log_dir", default="~/.roadmap/logs"),
45
+ Validator("logging.max_file_size", default="10MB"),
46
+ Validator("logging.backup_count", default=5, gte=0),
47
+ Validator("logging.retention_days", default=90, gte=1),
48
+ Validator("logging.archive_old_logs", default=True, is_type_of=bool),
49
+ # Git settings
50
+ Validator("git.auto_sync", default=True, is_type_of=bool),
51
+ Validator("git.hooks_enabled", default=True, is_type_of=bool),
52
+ Validator("git.default_branch", default="main"),
53
+ # GitHub integration (optional)
54
+ Validator("github.enabled", default=False, is_type_of=bool),
55
+ Validator("github.token", default=""),
56
+ Validator("github.repo", default=""),
57
+ Validator("github.sync_labels", default=True, is_type_of=bool),
58
+ # CLI settings
59
+ Validator("cli.default_editor", default="nano"),
60
+ Validator("cli.pager", default="less"),
61
+ Validator(
62
+ "cli.output_format", default="table", is_in=["table", "json", "yaml", "csv"]
63
+ ),
64
+ Validator("cli.color", default=True, is_type_of=bool),
65
+ # Project defaults
66
+ Validator("defaults.issue_type", default="task"),
67
+ Validator("defaults.priority", default="medium"),
68
+ Validator("defaults.status", default="open"),
69
+ # Performance settings
70
+ Validator("performance.cache_enabled", default=True, is_type_of=bool),
71
+ Validator("performance.cache_size", default="100MB"),
72
+ Validator("performance.cache_ttl", default=3600, gte=0),
73
+ # Export settings
74
+ Validator(
75
+ "export.default_format", default="csv", is_in=["csv", "json", "markdown"]
76
+ ),
77
+ Validator("export.include_metadata", default=False, is_type_of=bool),
78
+ Validator("export.date_format", default="%Y-%m-%d"),
79
+ # Sync settings - duplicate detection
80
+ Validator("sync.enable_duplicate_detection", default=True, is_type_of=bool),
81
+ Validator("sync.duplicate_title_threshold", default=0.90, gte=0.0, lte=1.0),
82
+ Validator("sync.duplicate_content_threshold", default=0.85, gte=0.0, lte=1.0),
83
+ Validator("sync.duplicate_auto_resolve_threshold", default=0.95, gte=0.0, lte=1.0),
84
+ # GitHub API rate limiting
85
+ Validator("github.api_batch_delay_seconds", default=1.0, gte=0.1),
86
+ ]
87
+
88
+ # Initialize dynaconf with our configuration
89
+ config_paths = get_config_paths()
90
+
91
+ settings = Dynaconf(
92
+ # Configuration file locations (in priority order)
93
+ settings_files=[
94
+ str(config_paths["global_config"]),
95
+ str(config_paths["local_config"]),
96
+ ],
97
+ # Secrets file locations
98
+ secrets=[
99
+ str(config_paths["global_secrets"]),
100
+ str(config_paths["local_secrets"]),
101
+ ],
102
+ # Environment variables
103
+ envvar_prefix="ROADMAP",
104
+ # Environment-specific settings
105
+ environments=True,
106
+ env_switcher="ROADMAP_ENV",
107
+ # Validation
108
+ validators=_validators,
109
+ # Additional settings
110
+ load_dotenv=True,
111
+ dotenv_path=".env",
112
+ merge_enabled=True,
113
+ # Default environment
114
+ default_env="development",
115
+ )
116
+
117
+
118
+ def get_settings() -> Dynaconf:
119
+ """Get the global settings object."""
120
+ return settings
121
+
122
+
123
+ def validate_config() -> bool:
124
+ """Validate the current configuration."""
125
+ try:
126
+ settings.validators.validate_all()
127
+ logger.info("Configuration validation passed")
128
+ return True
129
+ except Exception as e:
130
+ logger.error(
131
+ "Configuration validation failed",
132
+ error=str(e),
133
+ severity="config_error",
134
+ )
135
+ return False
136
+
137
+
138
+ def create_default_config(force: bool = False) -> bool:
139
+ """Create default configuration files.
140
+
141
+ Args:
142
+ force: Overwrite existing configuration files
143
+
144
+ Returns:
145
+ True if configuration was created successfully
146
+ """
147
+ config_paths = get_config_paths()
148
+
149
+ # Default global configuration
150
+ global_config_content = """# Roadmap CLI Global Configuration
151
+
152
+ [default]
153
+ # Database settings
154
+ [default.database]
155
+ path = "~/.roadmap/roadmap.db"
156
+ timeout = 30.0
157
+ backup_count = 5
158
+
159
+ # Logging settings
160
+ [default.logging]
161
+ level = "INFO"
162
+ to_file = true
163
+ log_dir = "~/.roadmap/logs"
164
+ max_file_size = "10MB"
165
+ backup_count = 5
166
+
167
+ # Git settings
168
+ [default.git]
169
+ auto_sync = true
170
+ hooks_enabled = true
171
+ default_branch = "main"
172
+
173
+ # GitHub integration (optional)
174
+ [default.github]
175
+ enabled = false
176
+ sync_labels = true
177
+
178
+ # CLI settings
179
+ [default.cli]
180
+ default_editor = "nano"
181
+ pager = "less"
182
+ output_format = "table"
183
+ color = true
184
+
185
+ # Project defaults
186
+ [default.defaults]
187
+ issue_type = "task"
188
+ priority = "medium"
189
+ status = "open"
190
+
191
+ # Performance settings
192
+ [default.performance]
193
+ cache_enabled = true
194
+ cache_size = "100MB"
195
+ cache_ttl = 3600
196
+
197
+ # Export settings
198
+ [default.export]
199
+ default_format = "csv"
200
+ include_metadata = false
201
+ date_format = "%Y-%m-%d"
202
+
203
+ # Development environment overrides
204
+ [development]
205
+ [development.logging]
206
+ level = "DEBUG"
207
+
208
+ # Testing environment overrides
209
+ [testing]
210
+ [testing.database]
211
+ path = ":memory:"
212
+ [testing.logging]
213
+ level = "WARNING"
214
+ to_file = false
215
+
216
+ # Production environment overrides
217
+ [production]
218
+ [production.logging]
219
+ level = "INFO"
220
+ [production.performance]
221
+ cache_size = "500MB"
222
+ """
223
+
224
+ # Default secrets template
225
+ secrets_content = """# Roadmap CLI Secrets Configuration
226
+ # Store sensitive information here
227
+
228
+ [default]
229
+ # GitHub token (if using GitHub integration)
230
+ [default.github]
231
+ token = ""
232
+
233
+ # Custom API keys or tokens
234
+ [default.api_keys]
235
+ # example_service = "your-api-key-here"
236
+ """
237
+
238
+ try:
239
+ # Create global config if it doesn't exist or force is True
240
+ global_config_path = config_paths["global_config"]
241
+ if not global_config_path.exists() or force:
242
+ global_config_path.write_text(global_config_content)
243
+ logger.info("Created global configuration", path=str(global_config_path))
244
+
245
+ # Create secrets template if it doesn't exist or force is True
246
+ global_secrets_path = config_paths["global_secrets"]
247
+ if not global_secrets_path.exists() or force:
248
+ global_secrets_path.write_text(secrets_content)
249
+ global_secrets_path.chmod(0o600) # Secure permissions
250
+ logger.info("Created secrets configuration", path=str(global_secrets_path))
251
+
252
+ return True
253
+
254
+ except Exception as e:
255
+ logger.error(
256
+ "Failed to create default configuration",
257
+ error=str(e),
258
+ severity="config_error",
259
+ )
260
+ return False
261
+
262
+
263
+ def get_database_path() -> Path:
264
+ """Get the configured database path."""
265
+ db_path = settings.database.path
266
+ if db_path.startswith("~"):
267
+ db_path = Path(db_path).expanduser()
268
+ else:
269
+ db_path = Path(db_path)
270
+
271
+ return db_path
272
+
273
+
274
+ def get_log_directory() -> Path:
275
+ """Get the configured log directory."""
276
+ log_dir = settings.logging.log_dir
277
+ if log_dir.startswith("~"):
278
+ log_dir = Path(log_dir).expanduser()
279
+ else:
280
+ log_dir = Path(log_dir)
281
+
282
+ return log_dir
283
+
284
+
285
+ def is_github_enabled() -> bool:
286
+ """Check if GitHub integration is enabled."""
287
+ return settings.github.enabled and bool(settings.github.token)
288
+
289
+
290
+ def get_github_config() -> dict[str, Any]:
291
+ """Get GitHub configuration."""
292
+ return {
293
+ "enabled": settings.github.enabled,
294
+ "token": settings.github.token,
295
+ "repo": settings.github.repo,
296
+ "sync_labels": settings.github.sync_labels,
297
+ }
298
+
299
+
300
+ def get_export_settings() -> dict[str, Any]:
301
+ """Get export settings."""
302
+ return {
303
+ "default_format": settings.export.default_format,
304
+ "include_metadata": settings.export.include_metadata,
305
+ "date_format": settings.export.date_format,
306
+ }
307
+
308
+
309
+ def switch_environment(env_name: str) -> bool:
310
+ """Switch to a different environment.
311
+
312
+ Args:
313
+ env_name: Environment name (development, testing, production)
314
+
315
+ Returns:
316
+ True if environment was switched successfully
317
+ """
318
+ try:
319
+ import os
320
+
321
+ os.environ["ROADMAP_ENV"] = env_name
322
+ settings.setenv(env_name)
323
+ logger.info("Switched environment", environment=env_name)
324
+ return True
325
+ except Exception as e:
326
+ logger.error(
327
+ "Failed to switch environment",
328
+ environment=env_name,
329
+ error=str(e),
330
+ severity="config_error",
331
+ )
332
+ return False
333
+
334
+
335
+ # Initialize default configuration on module import
336
+ try:
337
+ validate_config()
338
+ logger.debug("Configuration loaded successfully")
339
+ except Exception as e:
340
+ logger.warning(
341
+ "Configuration validation failed, creating defaults",
342
+ error=str(e),
343
+ severity="config_error",
344
+ )
345
+ create_default_config()