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,195 @@
1
+ """Project table formatting and display."""
2
+
3
+ from typing import TYPE_CHECKING, Any, cast
4
+
5
+ from roadmap.common.console import get_console
6
+ from roadmap.common.formatters.base_table_formatter import BaseTableFormatter
7
+ from roadmap.common.formatters.tables.column_factory import create_project_columns
8
+ from roadmap.common.models import TableData
9
+
10
+ if TYPE_CHECKING:
11
+ pass
12
+
13
+
14
+ def _get_console():
15
+ """Get a fresh console instance for test compatibility."""
16
+ return get_console()
17
+
18
+
19
+ class ProjectTableFormatter(BaseTableFormatter):
20
+ """Formats projects for display and structured output."""
21
+
22
+ def __init__(self):
23
+ """Initialize project formatter with headers and columns."""
24
+ super().__init__()
25
+ self.columns_config = [
26
+ {"name": "ID", "style": "cyan", "width": 10},
27
+ {"name": "Name", "style": "white", "width": 25},
28
+ {"name": "Headline", "style": "white", "width": 30},
29
+ {"name": "Status", "style": "magenta", "width": 12},
30
+ {"name": "Priority", "style": "yellow", "width": 10},
31
+ {"name": "Owner", "style": "green", "width": 15},
32
+ ]
33
+
34
+ def create_table(self) -> Any:
35
+ """Create a rich table with project columns."""
36
+ from rich.table import Table
37
+
38
+ table = Table(show_header=True, header_style="bold cyan")
39
+ for col in self.columns_config:
40
+ table.add_column(col["name"], style=col["style"], width=col["width"])
41
+ return table
42
+
43
+ def add_row(self, table: Any, item) -> None:
44
+ """Add a single project row to the table.
45
+
46
+ Args:
47
+ table: Rich Table object
48
+ item: Project object (dict or object) to add
49
+ """
50
+ from rich.text import Text
51
+
52
+ # Handle both dict and object formats
53
+ if isinstance(item, dict):
54
+ project_id = item.get("id", "unknown")[:8]
55
+ project_name = item.get("name", "Unnamed")
56
+ project_headline = item.get("headline", "")
57
+ project_status = item.get("status", "unknown")
58
+ project_priority = item.get("priority", "medium")
59
+ project_owner = item.get("owner", "Unassigned")
60
+ else:
61
+ # Handle Project object
62
+ project_id = getattr(item, "id", "unknown")[:8]
63
+ project_name = getattr(item, "name", "Unnamed")
64
+ project_headline = getattr(item, "headline", "")
65
+ raw_status = getattr(item, "status", "unknown")
66
+ project_status = (
67
+ str(cast(Any, raw_status).value)
68
+ if hasattr(raw_status, "value")
69
+ else str(raw_status)
70
+ )
71
+ raw_priority = getattr(item, "priority", "medium")
72
+ project_priority = (
73
+ str(cast(Any, raw_priority).value)
74
+ if hasattr(raw_priority, "value")
75
+ else str(raw_priority)
76
+ )
77
+ project_owner = getattr(item, "owner", "Unassigned")
78
+
79
+ table.add_row(
80
+ Text(project_id, style="cyan"),
81
+ Text(project_name, style="white"),
82
+ Text(project_headline or "", style="white"),
83
+ Text(str(project_status), style="magenta"),
84
+ Text(str(project_priority), style="yellow"),
85
+ Text(str(project_owner), style="green"),
86
+ )
87
+
88
+ def get_filter_description(self, items: list) -> str:
89
+ """Get human-readable description of filtered projects.
90
+
91
+ Args:
92
+ items: List of projects being displayed
93
+
94
+ Returns:
95
+ Description string (e.g., "5 active projects")
96
+ """
97
+ count = len(items)
98
+ return f"🎯 {count} project{'s' if count != 1 else ''}"
99
+
100
+ def display_items(self, items: list, filter_description: str = "all") -> None:
101
+ """Display projects in a formatted table.
102
+
103
+ Args:
104
+ items: List of project objects
105
+ filter_description: Description of filter applied
106
+ """
107
+ if not items:
108
+ _get_console().print(
109
+ f"🎯 No {filter_description} projects found.", style="yellow"
110
+ )
111
+ _get_console().print(
112
+ "Create one with: roadmap project create 'Project name'", style="dim"
113
+ )
114
+ return
115
+
116
+ # Display header with filter info
117
+ header_text = f"🎯 {len(items)} {filter_description} project{'s' if len(items) != 1 else ''}"
118
+ _get_console().print(header_text, style="bold cyan")
119
+ _get_console().print()
120
+
121
+ # Rich table display
122
+ table = self.create_table()
123
+ for item in items:
124
+ self.add_row(table, item)
125
+
126
+ _get_console().print(table)
127
+
128
+ def items_to_table_data(
129
+ self, items: list, title: str = "Projects", description: str = ""
130
+ ) -> TableData:
131
+ """Convert Project list to TableData for structured output.
132
+
133
+ Args:
134
+ items: List of project metadata dictionaries or Project objects.
135
+ title: Optional table title.
136
+ description: Optional filter description.
137
+
138
+ Returns:
139
+ TableData object ready for rendering in any format.
140
+ """
141
+ columns = create_project_columns()
142
+
143
+ rows = []
144
+ for project in items:
145
+ # Handle both dict and object formats
146
+ if isinstance(project, dict):
147
+ project_id = project.get("id", "unknown")[:8]
148
+ project_name = project.get("name", "Unnamed")
149
+ project_status = project.get("status", "unknown")
150
+ project_priority = project.get("priority", "medium")
151
+ project_owner = project.get("owner", "Unassigned")
152
+ else:
153
+ # Handle Project object
154
+ project_id = getattr(project, "id", "unknown")[:8]
155
+ project_name = getattr(project, "name", "Unnamed")
156
+ raw_status = getattr(project, "status", "unknown")
157
+ project_status = (
158
+ str(cast(Any, raw_status).value)
159
+ if hasattr(raw_status, "value")
160
+ else str(raw_status)
161
+ )
162
+ raw_priority = getattr(project, "priority", "medium")
163
+ project_priority = (
164
+ str(cast(Any, raw_priority).value)
165
+ if hasattr(raw_priority, "value")
166
+ else str(raw_priority)
167
+ )
168
+ project_owner = getattr(project, "owner", "Unassigned")
169
+
170
+ rows.append(
171
+ [
172
+ project_id,
173
+ project_name,
174
+ project_status,
175
+ project_priority,
176
+ project_owner,
177
+ ]
178
+ )
179
+
180
+ return TableData(
181
+ columns=columns,
182
+ rows=rows,
183
+ title=title,
184
+ headline=description,
185
+ total_count=len(items),
186
+ returned_count=len(items),
187
+ )
188
+
189
+ # Backward compatibility methods (for existing code that uses static methods)
190
+ @staticmethod
191
+ def projects_to_table_data(
192
+ projects: list, title: str = "Projects", description: str = ""
193
+ ) -> TableData:
194
+ """Convert Project list to TableData for structured output (backward compatible)."""
195
+ return ProjectTableFormatter().items_to_table_data(projects, title, description)
@@ -0,0 +1,57 @@
1
+ """Text module public API."""
2
+
3
+ from .basic import (
4
+ _format_table_simple,
5
+ format_error,
6
+ format_header,
7
+ format_info,
8
+ format_json,
9
+ format_key_value_pairs,
10
+ format_list,
11
+ format_panel,
12
+ format_success,
13
+ format_table,
14
+ format_warning,
15
+ truncate_text,
16
+ )
17
+ from .display import format_display_list, format_display_pairs
18
+ from .duration import format_count, format_duration
19
+ from .operations import (
20
+ OperationFormatter,
21
+ format_entity_details,
22
+ format_list_items,
23
+ format_operation_failure,
24
+ format_operation_success,
25
+ )
26
+ from .status_badges import format_percentage, format_status_badge
27
+
28
+ __all__ = [
29
+ # Basic formatting
30
+ "format_table",
31
+ "format_panel",
32
+ "format_header",
33
+ "format_success",
34
+ "format_error",
35
+ "format_warning",
36
+ "format_info",
37
+ "format_list",
38
+ "format_key_value_pairs",
39
+ "format_json",
40
+ "truncate_text",
41
+ "_format_table_simple",
42
+ # Status and badges
43
+ "format_status_badge",
44
+ "format_percentage",
45
+ # Duration and counts
46
+ "format_duration",
47
+ "format_count",
48
+ # Operations
49
+ "OperationFormatter",
50
+ "format_operation_success",
51
+ "format_operation_failure",
52
+ "format_entity_details",
53
+ "format_list_items",
54
+ # Display
55
+ "format_display_list",
56
+ "format_display_pairs",
57
+ ]
@@ -0,0 +1,217 @@
1
+ """Basic text formatting utilities for terminal display."""
2
+
3
+ from typing import TYPE_CHECKING, Any, Optional
4
+
5
+ import structlog
6
+ from rich.text import Text
7
+
8
+ logger = structlog.get_logger(__name__)
9
+
10
+ if TYPE_CHECKING:
11
+ from rich.console import Console
12
+ from rich.panel import Panel
13
+
14
+
15
+ def format_table(
16
+ title: str,
17
+ columns: list[str],
18
+ rows: list[tuple],
19
+ console_obj: Optional["Console"] = None,
20
+ ) -> str:
21
+ """Format data as a rich table for display.
22
+
23
+ Args:
24
+ title: Table title
25
+ columns: Column headers
26
+ rows: List of row tuples
27
+ console_obj: Optional console instance
28
+
29
+ Returns:
30
+ Formatted table as string
31
+ """
32
+ logger.debug(
33
+ "formatting_table", title=title, col_count=len(columns), row_count=len(rows)
34
+ )
35
+ try:
36
+ from rich.console import Console
37
+ except ImportError as e:
38
+ logger.debug("rich_import_failed", error=str(e))
39
+ # Fallback to simple formatting
40
+ return _format_table_simple(title, columns, rows)
41
+
42
+ if console_obj is None:
43
+ from rich.console import Console
44
+
45
+ console_obj = Console()
46
+
47
+ from rich.table import Table
48
+
49
+ table = Table(title=title)
50
+ for col in columns:
51
+ table.add_column(col)
52
+
53
+ for row in rows:
54
+ table.add_row(*[str(cell) for cell in row])
55
+
56
+ return str(table)
57
+
58
+
59
+ def _format_table_simple(title: str, columns: list[str], rows: list[tuple]) -> str:
60
+ """Fallback simple table formatting without rich."""
61
+ logger.debug(
62
+ "formatting_table_simple",
63
+ title=title,
64
+ col_count=len(columns),
65
+ row_count=len(rows),
66
+ )
67
+ output = [f"\n{title}", "-" * len(title)]
68
+ output.append(" | ".join(columns))
69
+ output.append("-" * (sum(len(c) for c in columns) + len(columns) * 3 - 1))
70
+ for row in rows:
71
+ output.append(" | ".join(str(cell) for cell in row))
72
+ return "\n".join(output)
73
+
74
+
75
+ def format_panel(
76
+ content: str,
77
+ title: str | None = None,
78
+ expand: bool = False,
79
+ ) -> "Panel":
80
+ """Format content in a rich panel.
81
+
82
+ Args:
83
+ content: Panel content
84
+ title: Optional panel title
85
+ expand: Whether to expand to console width
86
+
87
+ Returns:
88
+ Rich Panel object
89
+ """
90
+ logger.debug(
91
+ "formatting_panel", title=title, expand=expand, content_length=len(content)
92
+ )
93
+ from rich.panel import Panel
94
+
95
+ return Panel(content, title=title, expand=expand)
96
+
97
+
98
+ def format_header(text: str, level: int = 1) -> Text:
99
+ """Format text as a header with styling.
100
+
101
+ Args:
102
+ text: Header text
103
+ level: Header level (1-3, controls styling)
104
+
105
+ Returns:
106
+ Styled Text object
107
+ """
108
+ logger.debug("formatting_header", text=text, level=level)
109
+ if level == 1:
110
+ return Text(text, style="bold cyan", justify="center")
111
+ elif level == 2:
112
+ return Text(text, style="bold blue")
113
+ else:
114
+ return Text(text, style="bold")
115
+
116
+
117
+ def format_success(text: str) -> Text:
118
+ """Format text as a success message."""
119
+ logger.debug("formatting_success", text=text)
120
+ return Text(text, style="bold green")
121
+
122
+
123
+ def format_error(text: str) -> Text:
124
+ """Format text as an error message."""
125
+ logger.debug("formatting_error", text=text)
126
+ return Text(text, style="bold red")
127
+
128
+
129
+ def format_warning(text: str) -> Text:
130
+ """Format text as a warning message."""
131
+ logger.debug("formatting_warning", text=text)
132
+ return Text(text, style="bold yellow")
133
+
134
+
135
+ def format_info(text: str) -> Text:
136
+ """Format text as info message."""
137
+ logger.debug("formatting_info", text=text)
138
+ return Text(text, style="bold cyan")
139
+
140
+
141
+ def format_list(items: list[str], title: str | None = None) -> str:
142
+ """Format items as a bulleted list.
143
+
144
+ Args:
145
+ items: List of items to format
146
+ title: Optional list title
147
+
148
+ Returns:
149
+ Formatted list as string
150
+ """
151
+ logger.debug("formatting_list", item_count=len(items), title=title)
152
+ output = []
153
+ if title:
154
+ output.append(f"[bold]{title}[/bold]")
155
+ for item in items:
156
+ output.append(f" • {item}")
157
+ return "\n".join(output)
158
+
159
+
160
+ def format_key_value_pairs(pairs: dict[str, Any], title: str | None = None) -> str:
161
+ """Format key-value pairs for display.
162
+
163
+ Args:
164
+ pairs: Dictionary of key-value pairs
165
+ title: Optional title
166
+
167
+ Returns:
168
+ Formatted output as string
169
+ """
170
+ logger.debug("formatting_key_value_pairs", pair_count=len(pairs), title=title)
171
+ output = []
172
+ if title:
173
+ output.append(f"[bold]{title}[/bold]")
174
+
175
+ max_key_length = max(len(k) for k in pairs.keys()) if pairs else 0
176
+ for key, value in pairs.items():
177
+ output.append(f" {key.ljust(max_key_length)}: {value}")
178
+
179
+ return "\n".join(output)
180
+
181
+
182
+ def truncate_text(text: str, max_length: int = 50, suffix: str = "...") -> str:
183
+ """Truncate text to maximum length with optional suffix.
184
+
185
+ Args:
186
+ text: Text to truncate
187
+ max_length: Maximum length
188
+ suffix: Suffix to add if truncated
189
+
190
+ Returns:
191
+ Truncated text
192
+ """
193
+ logger.debug(
194
+ "truncating_text",
195
+ text_length=len(text),
196
+ max_length=max_length,
197
+ was_truncated=len(text) > max_length,
198
+ )
199
+ if len(text) <= max_length:
200
+ return text
201
+ return text[: max_length - len(suffix)] + suffix
202
+
203
+
204
+ def format_json(data: Any, indent: int = 2) -> str:
205
+ """Format data as JSON.
206
+
207
+ Args:
208
+ data: Data to format
209
+ indent: JSON indentation level
210
+
211
+ Returns:
212
+ Formatted JSON string
213
+ """
214
+ logger.debug("formatting_json", data_type=type(data).__name__, indent=indent)
215
+ import json
216
+
217
+ return json.dumps(data, indent=indent, default=str)
@@ -0,0 +1,42 @@
1
+ """Display formatting utilities for lists and panels."""
2
+
3
+ from typing import Any
4
+
5
+
6
+ def format_display_list(items: list[str], title: str | None = None) -> str:
7
+ """Format items as a bulleted list.
8
+
9
+ Args:
10
+ items: List of items to format
11
+ title: Optional list title
12
+
13
+ Returns:
14
+ Formatted list as string
15
+ """
16
+ output = []
17
+ if title:
18
+ output.append(f"[bold]{title}[/bold]")
19
+ for item in items:
20
+ output.append(f" • {item}")
21
+ return "\n".join(output)
22
+
23
+
24
+ def format_display_pairs(pairs: dict[str, Any], title: str | None = None) -> str:
25
+ """Format key-value pairs for display.
26
+
27
+ Args:
28
+ pairs: Dictionary of key-value pairs
29
+ title: Optional title
30
+
31
+ Returns:
32
+ Formatted output as string
33
+ """
34
+ output = []
35
+ if title:
36
+ output.append(f"[bold]{title}[/bold]")
37
+
38
+ max_key_length = max(len(k) for k in pairs.keys()) if pairs else 0
39
+ for key, value in pairs.items():
40
+ output.append(f" {key.ljust(max_key_length)}: {value}")
41
+
42
+ return "\n".join(output)
@@ -0,0 +1,36 @@
1
+ """Duration and count formatting utilities."""
2
+
3
+
4
+ def format_duration(seconds: float) -> str:
5
+ """Format duration in seconds to human-readable format.
6
+
7
+ Args:
8
+ seconds: Duration in seconds
9
+
10
+ Returns:
11
+ Formatted duration string
12
+ """
13
+ if seconds < 60:
14
+ return f"{int(seconds)}s"
15
+ elif seconds < 3600:
16
+ minutes = seconds / 60
17
+ return f"{minutes:.1f}m"
18
+ else:
19
+ hours = seconds / 3600
20
+ return f"{hours:.1f}h"
21
+
22
+
23
+ def format_count(count: int, singular: str, plural: str | None = None) -> str:
24
+ """Format a count with proper singular/plural form.
25
+
26
+ Args:
27
+ count: The count
28
+ singular: Singular form
29
+ plural: Plural form (defaults to singular + 's')
30
+
31
+ Returns:
32
+ Formatted count string
33
+ """
34
+ if plural is None:
35
+ plural = f"{singular}s"
36
+ return f"{count} {singular if count == 1 else plural}"