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,264 @@
1
+ """Configuration loader and manager.
2
+
3
+ Handles loading and merging user-level and project-level configurations
4
+ with support for YAML files and sensible defaults.
5
+ """
6
+
7
+ from pathlib import Path
8
+ from typing import Any
9
+
10
+ import yaml
11
+ from structlog import get_logger
12
+
13
+ from roadmap.common.models import RoadmapConfig
14
+
15
+ logger = get_logger()
16
+
17
+
18
+ class ConfigLoader:
19
+ """Loads and manages roadmap configuration."""
20
+
21
+ # Default configuration
22
+ DEFAULT_CONFIG = RoadmapConfig()
23
+
24
+ @classmethod
25
+ def get_user_config_path(cls) -> Path:
26
+ """Get the user-level config file path.
27
+
28
+ Returns:
29
+ Path to ~/.roadmap/config.yaml
30
+ """
31
+ home = Path.home()
32
+ return home / ".roadmap" / "config.yaml"
33
+
34
+ @classmethod
35
+ def get_project_config_path(cls, project_root: Path | None = None) -> Path:
36
+ """Get the project-level config file path.
37
+
38
+ Args:
39
+ project_root: Project root directory (defaults to cwd)
40
+
41
+ Returns:
42
+ Path to .roadmap/config.yaml in project
43
+ """
44
+ if project_root is None:
45
+ project_root = Path.cwd()
46
+ return project_root / ".roadmap" / "config.yaml"
47
+
48
+ @classmethod
49
+ def load_user_config(cls) -> RoadmapConfig | None:
50
+ """Load user-level configuration.
51
+
52
+ Returns:
53
+ RoadmapConfig from user config file, or None if not found
54
+ """
55
+ path = cls.get_user_config_path()
56
+ return cls._load_config_file(path)
57
+
58
+ @classmethod
59
+ def load_project_config(
60
+ cls, project_root: Path | None = None
61
+ ) -> RoadmapConfig | None:
62
+ """Load project-level configuration.
63
+
64
+ Args:
65
+ project_root: Project root directory (defaults to cwd)
66
+
67
+ Returns:
68
+ RoadmapConfig from project config file, or None if not found
69
+ """
70
+ path = cls.get_project_config_path(project_root)
71
+ return cls._load_config_file(path)
72
+
73
+ @classmethod
74
+ def _load_config_file(cls, path: Path) -> RoadmapConfig | None:
75
+ """Load a configuration file.
76
+
77
+ Args:
78
+ path: Path to config file
79
+
80
+ Returns:
81
+ RoadmapConfig if file exists and is valid, None otherwise
82
+ """
83
+ if not path.exists():
84
+ return None
85
+
86
+ try:
87
+ with open(path, encoding="utf-8") as f:
88
+ data = yaml.safe_load(f) or {}
89
+
90
+ # Ensure data is a dictionary
91
+ if not isinstance(data, dict):
92
+ logger.warning(
93
+ "invalid_config_format",
94
+ path=str(path),
95
+ error="config is not a mapping",
96
+ severity="data_error",
97
+ )
98
+ return None
99
+
100
+ # Validate and create config from data
101
+ config = RoadmapConfig(**data)
102
+ logger.info("loaded_config", path=str(path))
103
+ return config
104
+ except Exception as e:
105
+ logger.warning(
106
+ "failed_to_load_config",
107
+ path=str(path),
108
+ error=str(e),
109
+ severity="data_error",
110
+ )
111
+ return None
112
+
113
+ @classmethod
114
+ def load_config(cls, project_root: Path | None = None) -> RoadmapConfig:
115
+ """Load and merge all configurations.
116
+
117
+ Precedence: defaults < user config < project config
118
+
119
+ Args:
120
+ project_root: Project root directory (defaults to cwd)
121
+
122
+ Returns:
123
+ Merged RoadmapConfig with all layers applied
124
+ """
125
+ # Start with defaults
126
+ config = cls.DEFAULT_CONFIG
127
+
128
+ # Merge user config
129
+ user_config = cls.load_user_config()
130
+ if user_config:
131
+ config = config.merge(user_config)
132
+
133
+ # Merge project config
134
+ project_config = cls.load_project_config(project_root)
135
+ if project_config:
136
+ config = config.merge(project_config)
137
+
138
+ return config
139
+
140
+ @classmethod
141
+ def save_user_config(cls, config: RoadmapConfig) -> bool:
142
+ """Save user-level configuration.
143
+
144
+ Args:
145
+ config: Configuration to save
146
+
147
+ Returns:
148
+ True if successful, False otherwise
149
+ """
150
+ path = cls.get_user_config_path()
151
+ return cls._save_config_file(path, config)
152
+
153
+ @classmethod
154
+ def save_project_config(
155
+ cls, config: RoadmapConfig, project_root: Path | None = None
156
+ ) -> bool:
157
+ """Save project-level configuration.
158
+
159
+ Args:
160
+ config: Configuration to save
161
+ project_root: Project root directory (defaults to cwd)
162
+
163
+ Returns:
164
+ True if successful, False otherwise
165
+ """
166
+ path = cls.get_project_config_path(project_root)
167
+ return cls._save_config_file(path, config)
168
+
169
+ @classmethod
170
+ def _save_config_file(cls, path: Path, config: RoadmapConfig) -> bool:
171
+ """Save a configuration file.
172
+
173
+ Args:
174
+ path: Path to config file
175
+ config: Configuration to save
176
+
177
+ Returns:
178
+ True if successful, False otherwise
179
+ """
180
+ try:
181
+ # Create directory if needed
182
+ path.parent.mkdir(parents=True, exist_ok=True)
183
+
184
+ # Convert config to dict and save as YAML
185
+ data = config.model_dump(exclude_none=True)
186
+ with open(path, "w", encoding="utf-8") as f:
187
+ yaml.dump(data, f, default_flow_style=False, sort_keys=False)
188
+
189
+ logger.info("saved_config", path=str(path))
190
+ return True
191
+ except Exception as e:
192
+ logger.error(
193
+ "failed_to_save_config",
194
+ path=str(path),
195
+ error=str(e),
196
+ severity="system_error",
197
+ )
198
+ return False
199
+
200
+ @classmethod
201
+ def get_config_value(cls, key_path: str, project_root: Path | None = None) -> Any:
202
+ """Get a configuration value by path (e.g., 'export.directory').
203
+
204
+ Args:
205
+ key_path: Dot-separated path to config value
206
+ project_root: Project root directory (defaults to cwd)
207
+
208
+ Returns:
209
+ Configuration value, or None if not found
210
+ """
211
+ config = cls.load_config(project_root)
212
+ keys = key_path.split(".")
213
+ value = config
214
+
215
+ for key in keys:
216
+ if hasattr(value, key):
217
+ value = getattr(value, key)
218
+ elif isinstance(value, dict) and key in value:
219
+ value = value[key]
220
+ else:
221
+ return None
222
+
223
+ return value
224
+
225
+ @classmethod
226
+ def set_config_value(
227
+ cls,
228
+ key_path: str,
229
+ value: Any,
230
+ project_level: bool = False,
231
+ project_root: Path | None = None,
232
+ ) -> bool:
233
+ """Set a configuration value by path (e.g., 'export.directory').
234
+
235
+ Args:
236
+ key_path: Dot-separated path to config value
237
+ value: Value to set
238
+ project_level: If True, save to project config; else user config
239
+ project_root: Project root directory (defaults to cwd)
240
+
241
+ Returns:
242
+ True if successful, False otherwise
243
+ """
244
+ # Load existing config
245
+ if project_level:
246
+ config = cls.load_project_config(project_root) or RoadmapConfig()
247
+ else:
248
+ config = cls.load_user_config() or RoadmapConfig()
249
+
250
+ # Set value in config
251
+ keys = key_path.split(".")
252
+ target = config
253
+ for key in keys[:-1]:
254
+ if not hasattr(target, key):
255
+ setattr(target, key, {})
256
+ target = getattr(target, key)
257
+
258
+ setattr(target, keys[-1], value)
259
+
260
+ # Save config
261
+ if project_level:
262
+ return cls.save_project_config(config, project_root)
263
+ else:
264
+ return cls.save_user_config(config)
@@ -0,0 +1,164 @@
1
+ """Configuration management for roadmap."""
2
+
3
+ import subprocess
4
+ from pathlib import Path
5
+
6
+ import yaml
7
+
8
+ from .config_schema import GitHubConfig, PathsConfig, RoadmapConfig, UserConfig
9
+
10
+
11
+ class ConfigManager:
12
+ """Manages roadmap configuration files.
13
+
14
+ Supports two-level configuration:
15
+ - config.yaml: Team-level configuration (committed to git)
16
+ - config.yaml.local: User-level overrides (gitignored, local only)
17
+
18
+ When both exist, local overrides shared settings.
19
+ """
20
+
21
+ def __init__(self, config_file: Path):
22
+ """Initialize config manager with config file path."""
23
+ self.config_file = config_file
24
+ self.local_config_file = Path(str(config_file).replace(".yaml", ".yaml.local"))
25
+
26
+ def load(self) -> RoadmapConfig:
27
+ """Load configuration from file(s).
28
+
29
+ Loads team config first, then merges local overrides if present.
30
+ """
31
+ config_data: dict = {}
32
+
33
+ # Load shared config (team-level)
34
+ if self.config_file.exists():
35
+ with open(self.config_file) as f:
36
+ loaded = yaml.safe_load(f)
37
+ config_data = loaded if isinstance(loaded, dict) else {}
38
+
39
+ # Load and merge local config (user-level overrides)
40
+ if self.local_config_file.exists():
41
+ with open(self.local_config_file) as f:
42
+ loaded = yaml.safe_load(f)
43
+ local_data = loaded if isinstance(loaded, dict) else {}
44
+ # Deep merge: local overrides shared
45
+ config_data = self._deep_merge(config_data, local_data)
46
+
47
+ return RoadmapConfig.from_dict(config_data)
48
+
49
+ def save(self, config: RoadmapConfig, is_local: bool = False) -> None:
50
+ """Save configuration to file.
51
+
52
+ Args:
53
+ config: Configuration to save
54
+ is_local: If True, save to .local file (user overrides only)
55
+ """
56
+ target_file = self.local_config_file if is_local else self.config_file
57
+ target_file.parent.mkdir(parents=True, exist_ok=True)
58
+
59
+ with open(target_file, "w") as f:
60
+ yaml.dump(config.to_dict(), f, default_flow_style=False, sort_keys=False)
61
+
62
+ @staticmethod
63
+ def _deep_merge(base: dict, override: dict) -> dict:
64
+ """Deep merge override dict into base dict.
65
+
66
+ Override values take precedence over base values.
67
+ """
68
+ result = base.copy()
69
+ for key, value in override.items():
70
+ if (
71
+ key in result
72
+ and isinstance(result[key], dict)
73
+ and isinstance(value, dict)
74
+ ):
75
+ result[key] = ConfigManager._deep_merge(result[key], value)
76
+ else:
77
+ result[key] = value
78
+ return result
79
+
80
+ @staticmethod
81
+ def auto_detect_user() -> str | None:
82
+ """Auto-detect user from git config or environment."""
83
+ try:
84
+ # Try git config user.name
85
+ result = subprocess.run(
86
+ ["git", "config", "user.name"],
87
+ capture_output=True,
88
+ text=True,
89
+ timeout=5,
90
+ )
91
+ if result.returncode == 0 and result.stdout.strip():
92
+ return result.stdout.strip()
93
+ except (subprocess.TimeoutExpired, FileNotFoundError):
94
+ pass
95
+
96
+ # Try git config user.email
97
+ try:
98
+ result = subprocess.run(
99
+ ["git", "config", "user.email"],
100
+ capture_output=True,
101
+ text=True,
102
+ timeout=5,
103
+ )
104
+ if result.returncode == 0 and result.stdout.strip():
105
+ email = result.stdout.strip()
106
+ # Extract name part before @
107
+ if "@" in email:
108
+ return email.split("@")[0]
109
+ except (subprocess.TimeoutExpired, FileNotFoundError):
110
+ pass
111
+
112
+ return None
113
+
114
+ @staticmethod
115
+ def auto_detect_github_username() -> str | None:
116
+ """Auto-detect GitHub username from git remote."""
117
+ try:
118
+ result = subprocess.run(
119
+ ["git", "config", "user.github"],
120
+ capture_output=True,
121
+ text=True,
122
+ timeout=5,
123
+ )
124
+ if result.returncode == 0 and result.stdout.strip():
125
+ return result.stdout.strip()
126
+ except (subprocess.TimeoutExpired, FileNotFoundError):
127
+ pass
128
+
129
+ # Try to extract from git remote origin url
130
+ try:
131
+ result = subprocess.run(
132
+ ["git", "config", "remote.origin.url"],
133
+ capture_output=True,
134
+ text=True,
135
+ timeout=5,
136
+ )
137
+ if result.returncode == 0:
138
+ url = result.stdout.strip()
139
+ # Parse github.com:owner/repo.git or https://github.com/owner/repo.git
140
+ if "github.com" in url:
141
+ parts = url.split("/")[-2:] # Get last 2 parts
142
+ if parts and parts[0]:
143
+ return parts[0]
144
+ except (subprocess.TimeoutExpired, FileNotFoundError):
145
+ pass
146
+
147
+ return None
148
+
149
+ @staticmethod
150
+ def create_default_config(
151
+ user_name: str,
152
+ user_email: str | None = None,
153
+ github_owner: str | None = None,
154
+ github_repo: str | None = None,
155
+ github_enabled: bool = False,
156
+ ) -> RoadmapConfig:
157
+ """Create a default configuration."""
158
+ user = UserConfig(name=user_name, email=user_email)
159
+ paths = PathsConfig()
160
+ github = GitHubConfig(
161
+ owner=github_owner, repo=github_repo, enabled=github_enabled
162
+ )
163
+
164
+ return RoadmapConfig(user=user, paths=paths, github=github)
@@ -0,0 +1,145 @@
1
+ """Configuration schema for roadmap projects.
2
+
3
+ SECURITY NOTICE:
4
+ - The config.yaml file is LOCAL ONLY and should NOT be committed to version control
5
+ - It is added to .gitignore to prevent accidental exposure
6
+ - It should contain only local user preferences, NOT secrets
7
+ - GitHub tokens and other credentials should be stored in environment variables or .env files
8
+ - The config.yaml file should have restrictive permissions (e.g., mode 0600)
9
+ """
10
+
11
+ from dataclasses import asdict, dataclass, field
12
+
13
+
14
+ @dataclass
15
+ class UserConfig:
16
+ """User configuration - LOCAL ONLY, NEVER share or commit this file.
17
+
18
+ Contains only non-sensitive user identification for the roadmap.
19
+
20
+ SECURITY WARNING:
21
+ - Do NOT store passwords, API keys, or other secrets in this config
22
+ - Do NOT commit config.yaml to version control
23
+ - Use environment variables or .env files for sensitive credentials
24
+ - Keep config.yaml file permissions restrictive (mode 0600)
25
+ """
26
+
27
+ name: str # Local username/identifier
28
+ email: str | None = None
29
+
30
+
31
+ @dataclass
32
+ class PathsConfig:
33
+ """Path configuration (LOCAL ONLY - relative to project root).
34
+
35
+ These are local paths on the user's machine and should never be shared.
36
+ """
37
+
38
+ roadmap_dir: str = ".roadmap"
39
+ logs_dir: str = ".roadmap/logs"
40
+ db_dir: str = ".roadmap/db"
41
+
42
+
43
+ @dataclass
44
+ class GitHubConfig:
45
+ """GitHub integration configuration - DOES NOT store credentials.
46
+
47
+ Contains only repository metadata and sync preferences.
48
+
49
+ SECURITY WARNING:
50
+ - Do NOT store GitHub tokens or PATs (Personal Access Tokens) in this config
51
+ - Use GITHUB_TOKEN environment variable for authentication instead
52
+ - webhook_secret is stored locally but should be kept secure
53
+ - Never commit config.yaml with webhook_secret to version control
54
+ """
55
+
56
+ owner: str | None = None
57
+ repo: str | None = None
58
+ enabled: bool = False
59
+ sync_enabled: bool = False
60
+ sync_backend: str = "github" # "github" or "git"
61
+ webhook_secret: str | None = None
62
+ sync_settings: dict = field(
63
+ default_factory=lambda: {
64
+ "bidirectional": True,
65
+ "auto_close": True,
66
+ "sync_labels": True,
67
+ "sync_milestones": True,
68
+ }
69
+ )
70
+
71
+
72
+ @dataclass
73
+ class DisplayConfig:
74
+ """Display preferences and UI customization (LOCAL ONLY).
75
+
76
+ User-specific display settings for command output and formatting.
77
+ These are local preferences and should never be shared.
78
+ """
79
+
80
+ default_milestone: str | None = None
81
+ table_width: int = 100
82
+
83
+
84
+ @dataclass
85
+ class BehaviorConfig:
86
+ """Application behavior preferences.
87
+
88
+ User-specific behavior settings for CLI operations.
89
+ """
90
+
91
+ auto_branch_on_start: bool = False
92
+ confirm_destructive: bool = True
93
+ show_tips: bool = True
94
+ include_closed_in_critical_path: bool = False
95
+ default_project_id: str | None = None
96
+
97
+
98
+ @dataclass
99
+ class RoadmapConfig:
100
+ """Complete roadmap configuration - LOCAL ONLY, NEVER commit to version control.
101
+
102
+ This is the root configuration object stored in .roadmap/config.yaml.
103
+ It aggregates user, paths, GitHub, display, and behavior settings.
104
+
105
+ SECURITY REMINDER:
106
+ - This entire config object is user-specific and must not be shared
107
+ - It is added to .gitignore to prevent accidental commits
108
+ - Secrets (tokens, PATs) must go in environment variables, NOT here
109
+ - Keep the config.yaml file with restrictive permissions (mode 0600)
110
+ """
111
+
112
+ user: UserConfig
113
+ paths: PathsConfig = field(default_factory=PathsConfig)
114
+ github: GitHubConfig = field(default_factory=GitHubConfig)
115
+ display: DisplayConfig = field(default_factory=DisplayConfig)
116
+ behavior: BehaviorConfig = field(default_factory=BehaviorConfig)
117
+
118
+ def to_dict(self):
119
+ """Convert to dictionary for YAML serialization."""
120
+ return asdict(self)
121
+
122
+ @staticmethod
123
+ def from_dict(data: dict) -> "RoadmapConfig":
124
+ """Create from dictionary (e.g., from YAML)."""
125
+ user_data = data.get("user", {})
126
+ user = UserConfig(**user_data) if user_data else None
127
+
128
+ paths_data = data.get("paths", {}) or {}
129
+ paths = PathsConfig(**paths_data)
130
+
131
+ github_data = data.get("github", {}) or {}
132
+ github = GitHubConfig(**github_data)
133
+
134
+ display_data = data.get("display", {}) or {}
135
+ display = DisplayConfig(**display_data)
136
+
137
+ behavior_data = data.get("behavior", {}) or {}
138
+ behavior = BehaviorConfig(**behavior_data)
139
+
140
+ if not user:
141
+ raise ValueError("User configuration is required")
142
+
143
+ return RoadmapConfig(
144
+ user=user, paths=paths, github=github, display=display, behavior=behavior
145
+ )
@@ -0,0 +1,9 @@
1
+ """GitHub configuration management."""
2
+
3
+ from roadmap.common.configuration.github.config_manager import GitHubConfigManager
4
+ from roadmap.common.configuration.github.token_resolver import GitHubTokenResolver
5
+
6
+ __all__ = [
7
+ "GitHubTokenResolver",
8
+ "GitHubConfigManager",
9
+ ]