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,492 @@
1
+ """Database infrastructure manager for SQLite persistence layer.
2
+
3
+ This module handles database connection, initialization, and migrations.
4
+ It separates database infrastructure concerns from the state management layer.
5
+ """
6
+
7
+ import sqlite3
8
+ import threading
9
+ from contextlib import contextmanager
10
+ from datetime import UTC, datetime
11
+ from pathlib import Path
12
+
13
+ from roadmap.common.logging import get_logger
14
+
15
+ logger = get_logger(__name__)
16
+
17
+
18
+ class DatabaseError(Exception):
19
+ """Base exception for database operations."""
20
+
21
+ pass
22
+
23
+
24
+ class DatabaseManager:
25
+ """Manages SQLite database connections, initialization, and migrations."""
26
+
27
+ def __init__(self, db_path: str | Path | None = None):
28
+ """Initialize the database manager.
29
+
30
+ Args:
31
+ db_path: Path to SQLite database file. Defaults to ~/.roadmap/roadmap.db
32
+ """
33
+ if db_path is None:
34
+ db_path = Path.home() / ".roadmap" / "roadmap.db"
35
+
36
+ self.db_path = Path(db_path)
37
+ try:
38
+ self.db_path.parent.mkdir(parents=True, exist_ok=True)
39
+ except OSError as e:
40
+ logger.error(
41
+ "database_directory_create_failed",
42
+ db_path=str(self.db_path),
43
+ error=str(e),
44
+ error_type=type(e).__name__,
45
+ severity="system_error",
46
+ )
47
+ raise DatabaseError(
48
+ f"Failed to create database directory for {self.db_path}: {e}"
49
+ ) from e
50
+
51
+ # Thread-local storage for connections
52
+ self._local = threading.local()
53
+
54
+ logger.info("Initializing database manager", db_path=str(self.db_path))
55
+ try:
56
+ self._init_database()
57
+ except sqlite3.Error as e:
58
+ logger.error(
59
+ "database_schema_init_failed",
60
+ db_path=str(self.db_path),
61
+ error=str(e),
62
+ error_type=type(e).__name__,
63
+ severity="system_error",
64
+ )
65
+ raise DatabaseError(
66
+ f"Failed to initialize database schema at {self.db_path}: {e}"
67
+ ) from e
68
+
69
+ def validate_read_write(self) -> None:
70
+ """Validate that the database is readable and writable."""
71
+ conn = self._get_connection()
72
+ try:
73
+ conn.execute(
74
+ """
75
+ CREATE TABLE IF NOT EXISTS db_health_check (
76
+ id INTEGER PRIMARY KEY,
77
+ checked_at TIMESTAMP
78
+ )
79
+ """
80
+ )
81
+ conn.execute(
82
+ "INSERT INTO db_health_check (checked_at) VALUES (?)",
83
+ (datetime.now(UTC).isoformat(),),
84
+ )
85
+ row_id = conn.execute("SELECT last_insert_rowid()").fetchone()[0]
86
+ conn.execute("DELETE FROM db_health_check WHERE id = ?", (row_id,))
87
+ except sqlite3.Error as e:
88
+ logger.error(
89
+ "database_read_write_failed",
90
+ db_path=str(self.db_path),
91
+ error=str(e),
92
+ error_type=type(e).__name__,
93
+ severity="system_error",
94
+ )
95
+ raise DatabaseError(
96
+ f"Database read/write check failed for {self.db_path}: {e}"
97
+ ) from e
98
+
99
+ def _get_connection(self) -> sqlite3.Connection:
100
+ """Get thread-local database connection."""
101
+ if not hasattr(self._local, "connection"):
102
+ self._local.connection = sqlite3.connect(
103
+ str(self.db_path),
104
+ check_same_thread=False,
105
+ timeout=30.0,
106
+ isolation_level=None, # Autocommit mode
107
+ )
108
+ # Register datetime adapter for Python 3.12+
109
+ sqlite3.register_adapter(datetime, lambda dt: dt.isoformat())
110
+ sqlite3.register_converter(
111
+ "TIMESTAMP", lambda val: datetime.fromisoformat(val.decode())
112
+ )
113
+
114
+ # Enable foreign keys and WAL mode for better performance
115
+ self._local.connection.execute("PRAGMA foreign_keys = ON")
116
+ self._local.connection.execute("PRAGMA journal_mode = WAL")
117
+ self._local.connection.execute("PRAGMA synchronous = NORMAL")
118
+ self._local.connection.row_factory = sqlite3.Row
119
+
120
+ return self._local.connection
121
+
122
+ @contextmanager
123
+ def transaction(self):
124
+ """Context manager for database transactions."""
125
+ conn = self._get_connection()
126
+ try:
127
+ conn.execute("BEGIN IMMEDIATE")
128
+ yield conn
129
+ conn.execute("COMMIT")
130
+ except Exception:
131
+ try:
132
+ conn.execute("ROLLBACK")
133
+ except sqlite3.OperationalError:
134
+ # Transaction might not be active
135
+ logger.debug(
136
+ "rollback_failed_transaction_not_active", severity="operational"
137
+ )
138
+ raise
139
+
140
+ def _init_database(self):
141
+ """Initialize database schema."""
142
+ schema_sql = """
143
+ -- Projects table
144
+ CREATE TABLE IF NOT EXISTS projects (
145
+ id TEXT PRIMARY KEY,
146
+ name TEXT NOT NULL,
147
+ description TEXT,
148
+ status TEXT NOT NULL DEFAULT 'active',
149
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
150
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
151
+ metadata TEXT -- JSON for additional data
152
+ );
153
+
154
+ -- Milestones table
155
+ CREATE TABLE IF NOT EXISTS milestones (
156
+ id TEXT PRIMARY KEY,
157
+ project_id TEXT NOT NULL,
158
+ title TEXT NOT NULL,
159
+ description TEXT,
160
+ status TEXT NOT NULL DEFAULT 'open',
161
+ due_date DATE,
162
+ progress_percentage REAL DEFAULT 0.0,
163
+ archived BOOLEAN DEFAULT 0,
164
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
165
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
166
+ metadata TEXT, -- JSON for additional data
167
+ FOREIGN KEY (project_id) REFERENCES projects (id) ON DELETE CASCADE
168
+ );
169
+
170
+ -- Issues table
171
+ CREATE TABLE IF NOT EXISTS issues (
172
+ id TEXT PRIMARY KEY,
173
+ project_id TEXT,
174
+ milestone_id TEXT,
175
+ title TEXT NOT NULL,
176
+ headline TEXT DEFAULT '', -- Short summary for list views
177
+ description TEXT, -- Full markdown content
178
+ status TEXT NOT NULL DEFAULT 'open',
179
+ priority TEXT NOT NULL DEFAULT 'medium',
180
+ issue_type TEXT NOT NULL DEFAULT 'task',
181
+ assignee TEXT,
182
+ estimate_hours REAL,
183
+ archived BOOLEAN DEFAULT 0,
184
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
185
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
186
+ due_date DATE,
187
+ metadata TEXT, -- JSON for additional data
188
+ FOREIGN KEY (project_id) REFERENCES projects (id) ON DELETE CASCADE,
189
+ FOREIGN KEY (milestone_id) REFERENCES milestones (id) ON DELETE SET NULL
190
+ );
191
+
192
+ -- Issue dependencies table
193
+ CREATE TABLE IF NOT EXISTS issue_dependencies (
194
+ issue_id TEXT NOT NULL,
195
+ depends_on_id TEXT NOT NULL,
196
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
197
+ PRIMARY KEY (issue_id, depends_on_id),
198
+ FOREIGN KEY (issue_id) REFERENCES issues (id) ON DELETE CASCADE,
199
+ FOREIGN KEY (depends_on_id) REFERENCES issues (id) ON DELETE CASCADE
200
+ );
201
+
202
+ -- Issue labels table (many-to-many)
203
+ CREATE TABLE IF NOT EXISTS issue_labels (
204
+ issue_id TEXT NOT NULL,
205
+ label TEXT NOT NULL,
206
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
207
+ PRIMARY KEY (issue_id, label),
208
+ FOREIGN KEY (issue_id) REFERENCES issues (id) ON DELETE CASCADE
209
+ );
210
+
211
+ -- Comments table
212
+ CREATE TABLE IF NOT EXISTS comments (
213
+ id TEXT PRIMARY KEY,
214
+ issue_id TEXT NOT NULL,
215
+ author TEXT NOT NULL,
216
+ content TEXT NOT NULL,
217
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
218
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
219
+ metadata TEXT, -- JSON for additional data
220
+ FOREIGN KEY (issue_id) REFERENCES issues (id) ON DELETE CASCADE
221
+ );
222
+
223
+ -- Sync base state (three-way merge baseline from last successful sync)
224
+ -- Note: issue_id does NOT have a foreign key constraint because sync baseline
225
+ -- can be saved before issues are persisted to the database (during sync operations)
226
+ CREATE TABLE IF NOT EXISTS sync_base_state (
227
+ issue_id TEXT PRIMARY KEY,
228
+ status TEXT NOT NULL,
229
+ assignee TEXT,
230
+ milestone TEXT,
231
+ description TEXT,
232
+ labels TEXT, -- JSON array of label strings
233
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
234
+ synced_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
235
+ );
236
+
237
+ -- Sync metadata (overall sync state)
238
+ CREATE TABLE IF NOT EXISTS sync_metadata (
239
+ key TEXT PRIMARY KEY,
240
+ value TEXT NOT NULL,
241
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
242
+ );
243
+
244
+ -- File synchronization tracking
245
+ CREATE TABLE IF NOT EXISTS file_sync_state (
246
+ file_path TEXT PRIMARY KEY,
247
+ content_hash TEXT NOT NULL,
248
+ file_size INTEGER,
249
+ last_modified TIMESTAMP,
250
+ last_synced TIMESTAMP DEFAULT CURRENT_TIMESTAMP
251
+ );
252
+
253
+ -- Indexes for performance
254
+ CREATE INDEX IF NOT EXISTS idx_sync_base_state_synced_at ON sync_base_state (synced_at);
255
+ CREATE INDEX IF NOT EXISTS idx_milestones_project_id ON milestones (project_id);
256
+ CREATE INDEX IF NOT EXISTS idx_issues_project_id ON issues (project_id);
257
+ CREATE INDEX IF NOT EXISTS idx_issues_milestone_id ON issues (milestone_id);
258
+ CREATE INDEX IF NOT EXISTS idx_issues_assignee ON issues (assignee);
259
+ CREATE INDEX IF NOT EXISTS idx_issues_status ON issues (status);
260
+ CREATE INDEX IF NOT EXISTS idx_comments_issue_id ON comments (issue_id);
261
+
262
+ -- Triggers for updated_at timestamps
263
+ CREATE TRIGGER IF NOT EXISTS update_projects_timestamp
264
+ AFTER UPDATE ON projects
265
+ BEGIN
266
+ UPDATE projects SET updated_at = CURRENT_TIMESTAMP WHERE id = NEW.id;
267
+ END;
268
+
269
+ CREATE TRIGGER IF NOT EXISTS update_milestones_timestamp
270
+ AFTER UPDATE ON milestones
271
+ BEGIN
272
+ UPDATE milestones SET updated_at = CURRENT_TIMESTAMP WHERE id = NEW.id;
273
+ END;
274
+
275
+ CREATE TRIGGER IF NOT EXISTS update_issues_timestamp
276
+ AFTER UPDATE ON issues
277
+ BEGIN
278
+ UPDATE issues SET updated_at = CURRENT_TIMESTAMP WHERE id = NEW.id;
279
+ END;
280
+
281
+ CREATE TRIGGER IF NOT EXISTS update_comments_timestamp
282
+ AFTER UPDATE ON comments
283
+ BEGIN
284
+ UPDATE comments SET updated_at = CURRENT_TIMESTAMP WHERE id = NEW.id;
285
+ END;
286
+ """
287
+
288
+ conn = self._get_connection()
289
+ conn.executescript(schema_sql)
290
+
291
+ logger.info("Database schema initialized")
292
+
293
+ # Run migrations
294
+ self._run_migrations()
295
+
296
+ def _migration_archive_columns(self, cursor) -> list[str]:
297
+ """Migration 1: add archived/archived_at to projects, milestones, issues."""
298
+ result = []
299
+ for table in ("projects", "milestones", "issues"):
300
+ cursor.execute(f"PRAGMA table_info({table})") # noqa: S608
301
+ if "archived" not in [col[1] for col in cursor.fetchall()]:
302
+ result.append(
303
+ f"ALTER TABLE {table} ADD COLUMN archived INTEGER DEFAULT 0;\n"
304
+ f"ALTER TABLE {table} ADD COLUMN archived_at TIMESTAMP NULL;"
305
+ )
306
+ return result
307
+
308
+ def _migration_sync_base_state(self, cursor) -> list[str]:
309
+ """Migration 2: create sync_base_state table."""
310
+ cursor.execute(
311
+ "SELECT name FROM sqlite_master WHERE type='table' AND name='sync_base_state'"
312
+ )
313
+ if cursor.fetchone():
314
+ return []
315
+ return [
316
+ """
317
+ CREATE TABLE sync_base_state (
318
+ issue_id TEXT PRIMARY KEY,
319
+ status TEXT NOT NULL,
320
+ assignee TEXT,
321
+ milestone TEXT,
322
+ description TEXT,
323
+ labels TEXT,
324
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
325
+ synced_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
326
+ );
327
+ CREATE INDEX idx_sync_base_state_synced_at ON sync_base_state (synced_at);
328
+ """
329
+ ]
330
+
331
+ def _migration_issue_remote_links(self, cursor) -> list[str]:
332
+ """Migration 3: create issue_remote_links table."""
333
+ cursor.execute(
334
+ "SELECT name FROM sqlite_master WHERE type='table' AND name='issue_remote_links'"
335
+ )
336
+ if cursor.fetchone():
337
+ return []
338
+ return [
339
+ """
340
+ CREATE TABLE issue_remote_links (
341
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
342
+ issue_uuid TEXT NOT NULL,
343
+ backend_name TEXT NOT NULL,
344
+ remote_id TEXT NOT NULL,
345
+ linked_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
346
+ UNIQUE(issue_uuid, backend_name),
347
+ FOREIGN KEY (issue_uuid) REFERENCES issues (id) ON DELETE CASCADE
348
+ );
349
+ CREATE INDEX idx_issue_remote_links_backend ON issue_remote_links (backend_name);
350
+ CREATE INDEX idx_issue_remote_links_issue_uuid ON issue_remote_links (issue_uuid);
351
+ """
352
+ ]
353
+
354
+ def _migration_headline_column(self, cursor) -> list[str]:
355
+ """Migration 4: add headline column to issues table."""
356
+ cursor.execute("PRAGMA table_info(issues)")
357
+ issue_columns = (
358
+ [row[1] for row in cursor.fetchall()] if cursor.fetchone() else []
359
+ )
360
+ if "headline" in issue_columns:
361
+ return []
362
+ return ["ALTER TABLE issues ADD COLUMN headline TEXT DEFAULT '';"]
363
+
364
+ def _migration_sync_base_state_columns(self, cursor) -> list[str]:
365
+ """Migration 5: add headline and content columns to sync_base_state."""
366
+ cursor.execute("PRAGMA table_info(sync_base_state)")
367
+ columns = [row[1] for row in cursor.fetchall()] if cursor.fetchone() else []
368
+ if "headline" in columns or "content" in columns:
369
+ return []
370
+ return [
371
+ """
372
+ ALTER TABLE sync_base_state ADD COLUMN headline TEXT DEFAULT '';
373
+ ALTER TABLE sync_base_state ADD COLUMN content TEXT DEFAULT '';
374
+ """
375
+ ]
376
+
377
+ def _migration_sync_metrics(self, cursor) -> list[str]:
378
+ """Migration 6: create sync_metrics table."""
379
+ cursor.execute(
380
+ "SELECT name FROM sqlite_master WHERE type='table' AND name='sync_metrics'"
381
+ )
382
+ if cursor.fetchone():
383
+ return []
384
+ return [
385
+ """
386
+ CREATE TABLE sync_metrics (
387
+ id TEXT PRIMARY KEY,
388
+ operation_id TEXT NOT NULL UNIQUE,
389
+ backend_type TEXT NOT NULL,
390
+ duration_seconds REAL NOT NULL DEFAULT 0.0,
391
+ metrics_json TEXT NOT NULL, -- Full SyncMetrics.to_dict() as JSON
392
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
393
+ );
394
+ CREATE INDEX IF NOT EXISTS idx_sync_metrics_backend_type ON sync_metrics (backend_type);
395
+ CREATE INDEX IF NOT EXISTS idx_sync_metrics_created_at ON sync_metrics (created_at);
396
+ CREATE INDEX IF NOT EXISTS idx_sync_metrics_operation_id ON sync_metrics (operation_id);
397
+ """
398
+ ]
399
+
400
+ def _run_migrations(self):
401
+ """Run database migrations for schema updates."""
402
+ conn = self._get_connection()
403
+ cursor = conn.cursor()
404
+
405
+ migrations = (
406
+ self._migration_archive_columns(cursor)
407
+ + self._migration_sync_base_state(cursor)
408
+ + self._migration_issue_remote_links(cursor)
409
+ + self._migration_headline_column(cursor)
410
+ + self._migration_sync_base_state_columns(cursor)
411
+ + self._migration_sync_metrics(cursor)
412
+ )
413
+
414
+ for migration_sql in migrations:
415
+ try:
416
+ conn.executescript(migration_sql)
417
+ logger.info("applied_database_migration")
418
+ except Exception as e:
419
+ logger.warning(
420
+ "migration_may_have_been_applied",
421
+ error=str(e),
422
+ severity="operational",
423
+ )
424
+
425
+ conn.commit()
426
+
427
+ def is_initialized(self) -> bool:
428
+ """Check if database is properly initialized."""
429
+ try:
430
+ conn = self._get_connection()
431
+ result = conn.execute(
432
+ "SELECT name FROM sqlite_master WHERE type='table' AND name='projects'"
433
+ ).fetchone()
434
+ return result is not None
435
+ except Exception as e:
436
+ logger.error(
437
+ "Failed to check database initialization",
438
+ error=str(e),
439
+ severity="infrastructure",
440
+ )
441
+ return False
442
+
443
+ def close(self):
444
+ """Close database connections."""
445
+ if hasattr(self._local, "connection"):
446
+ self._local.connection.close()
447
+ delattr(self._local, "connection")
448
+
449
+ def vacuum(self):
450
+ """Optimize database."""
451
+ conn = self._get_connection()
452
+ conn.execute("VACUUM")
453
+ logger.info("Database vacuumed")
454
+
455
+ def database_exists(self) -> bool:
456
+ """Check if database file exists and has tables."""
457
+ if not self.db_path.exists():
458
+ return False
459
+
460
+ try:
461
+ conn = self._get_connection()
462
+ # Check if our main tables exist
463
+ tables = conn.execute("""
464
+ SELECT name FROM sqlite_master
465
+ WHERE type='table' AND name IN ('issues', 'milestones', 'projects')
466
+ """).fetchall()
467
+
468
+ # We should have at least our core tables
469
+ return len(tables) >= 3
470
+
471
+ except Exception as e:
472
+ logger.warning(
473
+ "Error checking database existence",
474
+ error=str(e),
475
+ severity="infrastructure",
476
+ )
477
+ return False
478
+
479
+ def is_safe_for_writes(self) -> tuple[bool, str]:
480
+ """Check if database is safe for write operations."""
481
+ try:
482
+ # Check database integrity
483
+ conn = self._get_connection()
484
+ try:
485
+ conn.execute("PRAGMA integrity_check").fetchone()
486
+ except sqlite3.DatabaseError as e:
487
+ return False, f"Database corruption detected: {e}"
488
+
489
+ return True, "Database ready for operations"
490
+
491
+ except Exception as e:
492
+ return False, f"Safety check failed: {e}"