claude-mpm 3.9.11__py3-none-any.whl → 4.0.4__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 (434) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/__init__.py +2 -2
  3. claude_mpm/__main__.py +3 -2
  4. claude_mpm/agents/__init__.py +85 -79
  5. claude_mpm/agents/agent_loader.py +464 -1003
  6. claude_mpm/agents/agent_loader_integration.py +45 -45
  7. claude_mpm/agents/agents_metadata.py +29 -30
  8. claude_mpm/agents/async_agent_loader.py +156 -138
  9. claude_mpm/agents/base_agent.json +1 -1
  10. claude_mpm/agents/base_agent_loader.py +179 -151
  11. claude_mpm/agents/frontmatter_validator.py +229 -130
  12. claude_mpm/agents/schema/agent_schema.json +1 -1
  13. claude_mpm/agents/system_agent_config.py +213 -147
  14. claude_mpm/agents/templates/__init__.py +13 -13
  15. claude_mpm/agents/templates/code_analyzer.json +2 -2
  16. claude_mpm/agents/templates/data_engineer.json +1 -1
  17. claude_mpm/agents/templates/documentation.json +23 -11
  18. claude_mpm/agents/templates/engineer.json +22 -6
  19. claude_mpm/agents/templates/memory_manager.json +1 -1
  20. claude_mpm/agents/templates/ops.json +2 -2
  21. claude_mpm/agents/templates/project_organizer.json +1 -1
  22. claude_mpm/agents/templates/qa.json +1 -1
  23. claude_mpm/agents/templates/refactoring_engineer.json +222 -0
  24. claude_mpm/agents/templates/research.json +20 -14
  25. claude_mpm/agents/templates/security.json +1 -1
  26. claude_mpm/agents/templates/ticketing.json +2 -2
  27. claude_mpm/agents/templates/version_control.json +1 -1
  28. claude_mpm/agents/templates/web_qa.json +3 -1
  29. claude_mpm/agents/templates/web_ui.json +2 -2
  30. claude_mpm/cli/__init__.py +79 -51
  31. claude_mpm/cli/__main__.py +3 -2
  32. claude_mpm/cli/commands/__init__.py +20 -20
  33. claude_mpm/cli/commands/agents.py +279 -247
  34. claude_mpm/cli/commands/aggregate.py +138 -157
  35. claude_mpm/cli/commands/cleanup.py +147 -147
  36. claude_mpm/cli/commands/config.py +93 -76
  37. claude_mpm/cli/commands/info.py +17 -16
  38. claude_mpm/cli/commands/mcp.py +140 -905
  39. claude_mpm/cli/commands/mcp_command_router.py +139 -0
  40. claude_mpm/cli/commands/mcp_config_commands.py +20 -0
  41. claude_mpm/cli/commands/mcp_install_commands.py +20 -0
  42. claude_mpm/cli/commands/mcp_server_commands.py +175 -0
  43. claude_mpm/cli/commands/mcp_tool_commands.py +34 -0
  44. claude_mpm/cli/commands/memory.py +239 -203
  45. claude_mpm/cli/commands/monitor.py +330 -86
  46. claude_mpm/cli/commands/run.py +380 -429
  47. claude_mpm/cli/commands/run_config_checker.py +160 -0
  48. claude_mpm/cli/commands/socketio_monitor.py +235 -0
  49. claude_mpm/cli/commands/tickets.py +363 -220
  50. claude_mpm/cli/parser.py +24 -1156
  51. claude_mpm/cli/parsers/__init__.py +29 -0
  52. claude_mpm/cli/parsers/agents_parser.py +136 -0
  53. claude_mpm/cli/parsers/base_parser.py +331 -0
  54. claude_mpm/cli/parsers/config_parser.py +85 -0
  55. claude_mpm/cli/parsers/mcp_parser.py +152 -0
  56. claude_mpm/cli/parsers/memory_parser.py +138 -0
  57. claude_mpm/cli/parsers/monitor_parser.py +124 -0
  58. claude_mpm/cli/parsers/run_parser.py +147 -0
  59. claude_mpm/cli/parsers/tickets_parser.py +203 -0
  60. claude_mpm/cli/ticket_cli.py +7 -3
  61. claude_mpm/cli/utils.py +55 -37
  62. claude_mpm/cli_module/__init__.py +6 -6
  63. claude_mpm/cli_module/args.py +188 -140
  64. claude_mpm/cli_module/commands.py +79 -70
  65. claude_mpm/cli_module/migration_example.py +38 -60
  66. claude_mpm/config/__init__.py +32 -25
  67. claude_mpm/config/agent_config.py +151 -119
  68. claude_mpm/config/experimental_features.py +71 -73
  69. claude_mpm/config/paths.py +94 -208
  70. claude_mpm/config/socketio_config.py +84 -73
  71. claude_mpm/constants.py +35 -18
  72. claude_mpm/core/__init__.py +9 -6
  73. claude_mpm/core/agent_name_normalizer.py +68 -71
  74. claude_mpm/core/agent_registry.py +372 -521
  75. claude_mpm/core/agent_session_manager.py +74 -63
  76. claude_mpm/core/base_service.py +116 -87
  77. claude_mpm/core/cache.py +119 -153
  78. claude_mpm/core/claude_runner.py +425 -1120
  79. claude_mpm/core/config.py +263 -168
  80. claude_mpm/core/config_aliases.py +69 -61
  81. claude_mpm/core/config_constants.py +292 -0
  82. claude_mpm/core/constants.py +57 -99
  83. claude_mpm/core/container.py +211 -178
  84. claude_mpm/core/exceptions.py +233 -89
  85. claude_mpm/core/factories.py +92 -54
  86. claude_mpm/core/framework_loader.py +378 -220
  87. claude_mpm/core/hook_manager.py +198 -83
  88. claude_mpm/core/hook_performance_config.py +136 -0
  89. claude_mpm/core/injectable_service.py +61 -55
  90. claude_mpm/core/interactive_session.py +165 -155
  91. claude_mpm/core/interfaces.py +221 -195
  92. claude_mpm/core/lazy.py +96 -96
  93. claude_mpm/core/logger.py +133 -107
  94. claude_mpm/core/logging_config.py +185 -157
  95. claude_mpm/core/minimal_framework_loader.py +20 -15
  96. claude_mpm/core/mixins.py +30 -29
  97. claude_mpm/core/oneshot_session.py +215 -181
  98. claude_mpm/core/optimized_agent_loader.py +134 -138
  99. claude_mpm/core/optimized_startup.py +159 -157
  100. claude_mpm/core/pm_hook_interceptor.py +85 -72
  101. claude_mpm/core/service_registry.py +103 -101
  102. claude_mpm/core/session_manager.py +97 -87
  103. claude_mpm/core/socketio_pool.py +212 -158
  104. claude_mpm/core/tool_access_control.py +58 -51
  105. claude_mpm/core/types.py +46 -24
  106. claude_mpm/core/typing_utils.py +166 -82
  107. claude_mpm/core/unified_agent_registry.py +721 -0
  108. claude_mpm/core/unified_config.py +550 -0
  109. claude_mpm/core/unified_paths.py +549 -0
  110. claude_mpm/dashboard/index.html +1 -1
  111. claude_mpm/dashboard/open_dashboard.py +51 -17
  112. claude_mpm/dashboard/static/built/components/agent-inference.js +2 -0
  113. claude_mpm/dashboard/static/built/components/event-processor.js +2 -0
  114. claude_mpm/dashboard/static/built/components/event-viewer.js +2 -0
  115. claude_mpm/dashboard/static/built/components/export-manager.js +2 -0
  116. claude_mpm/dashboard/static/built/components/file-tool-tracker.js +2 -0
  117. claude_mpm/dashboard/static/built/components/hud-library-loader.js +2 -0
  118. claude_mpm/dashboard/static/built/components/hud-manager.js +2 -0
  119. claude_mpm/dashboard/static/built/components/hud-visualizer.js +2 -0
  120. claude_mpm/dashboard/static/built/components/module-viewer.js +2 -0
  121. claude_mpm/dashboard/static/built/components/session-manager.js +2 -0
  122. claude_mpm/dashboard/static/built/components/socket-manager.js +2 -0
  123. claude_mpm/dashboard/static/built/components/ui-state-manager.js +2 -0
  124. claude_mpm/dashboard/static/built/components/working-directory.js +2 -0
  125. claude_mpm/dashboard/static/built/dashboard.js +2 -0
  126. claude_mpm/dashboard/static/built/socket-client.js +2 -0
  127. claude_mpm/dashboard/static/css/dashboard.css +27 -8
  128. claude_mpm/dashboard/static/dist/components/agent-inference.js +2 -0
  129. claude_mpm/dashboard/static/dist/components/event-processor.js +2 -0
  130. claude_mpm/dashboard/static/dist/components/event-viewer.js +2 -0
  131. claude_mpm/dashboard/static/dist/components/export-manager.js +2 -0
  132. claude_mpm/dashboard/static/dist/components/file-tool-tracker.js +2 -0
  133. claude_mpm/dashboard/static/dist/components/hud-library-loader.js +2 -0
  134. claude_mpm/dashboard/static/dist/components/hud-manager.js +2 -0
  135. claude_mpm/dashboard/static/dist/components/hud-visualizer.js +2 -0
  136. claude_mpm/dashboard/static/dist/components/module-viewer.js +2 -0
  137. claude_mpm/dashboard/static/dist/components/session-manager.js +2 -0
  138. claude_mpm/dashboard/static/dist/components/socket-manager.js +2 -0
  139. claude_mpm/dashboard/static/dist/components/ui-state-manager.js +2 -0
  140. claude_mpm/dashboard/static/dist/components/working-directory.js +2 -0
  141. claude_mpm/dashboard/static/dist/dashboard.js +2 -0
  142. claude_mpm/dashboard/static/dist/socket-client.js +2 -0
  143. claude_mpm/dashboard/static/js/components/agent-inference.js +80 -76
  144. claude_mpm/dashboard/static/js/components/event-processor.js +71 -67
  145. claude_mpm/dashboard/static/js/components/event-viewer.js +93 -72
  146. claude_mpm/dashboard/static/js/components/export-manager.js +31 -28
  147. claude_mpm/dashboard/static/js/components/file-tool-tracker.js +110 -96
  148. claude_mpm/dashboard/static/js/components/hud-library-loader.js +11 -11
  149. claude_mpm/dashboard/static/js/components/hud-manager.js +73 -73
  150. claude_mpm/dashboard/static/js/components/hud-visualizer.js +163 -163
  151. claude_mpm/dashboard/static/js/components/module-viewer.js +305 -233
  152. claude_mpm/dashboard/static/js/components/session-manager.js +32 -29
  153. claude_mpm/dashboard/static/js/components/socket-manager.js +27 -20
  154. claude_mpm/dashboard/static/js/components/ui-state-manager.js +21 -18
  155. claude_mpm/dashboard/static/js/components/working-directory.js +74 -71
  156. claude_mpm/dashboard/static/js/dashboard.js +178 -453
  157. claude_mpm/dashboard/static/js/extension-error-handler.js +164 -0
  158. claude_mpm/dashboard/static/js/socket-client.js +133 -53
  159. claude_mpm/dashboard/templates/index.html +40 -50
  160. claude_mpm/experimental/cli_enhancements.py +60 -58
  161. claude_mpm/generators/__init__.py +1 -1
  162. claude_mpm/generators/agent_profile_generator.py +75 -65
  163. claude_mpm/hooks/__init__.py +1 -1
  164. claude_mpm/hooks/base_hook.py +33 -28
  165. claude_mpm/hooks/claude_hooks/__init__.py +1 -1
  166. claude_mpm/hooks/claude_hooks/connection_pool.py +120 -0
  167. claude_mpm/hooks/claude_hooks/event_handlers.py +743 -0
  168. claude_mpm/hooks/claude_hooks/hook_handler.py +415 -1331
  169. claude_mpm/hooks/claude_hooks/hook_wrapper.sh +4 -4
  170. claude_mpm/hooks/claude_hooks/memory_integration.py +221 -0
  171. claude_mpm/hooks/claude_hooks/response_tracking.py +348 -0
  172. claude_mpm/hooks/claude_hooks/tool_analysis.py +230 -0
  173. claude_mpm/hooks/memory_integration_hook.py +140 -100
  174. claude_mpm/hooks/tool_call_interceptor.py +89 -76
  175. claude_mpm/hooks/validation_hooks.py +57 -49
  176. claude_mpm/init.py +145 -121
  177. claude_mpm/models/__init__.py +9 -9
  178. claude_mpm/models/agent_definition.py +33 -23
  179. claude_mpm/models/agent_session.py +228 -200
  180. claude_mpm/scripts/__init__.py +1 -1
  181. claude_mpm/scripts/socketio_daemon.py +192 -75
  182. claude_mpm/scripts/socketio_server_manager.py +328 -0
  183. claude_mpm/scripts/start_activity_logging.py +25 -22
  184. claude_mpm/services/__init__.py +68 -43
  185. claude_mpm/services/agent_capabilities_service.py +271 -0
  186. claude_mpm/services/agents/__init__.py +23 -32
  187. claude_mpm/services/agents/deployment/__init__.py +3 -3
  188. claude_mpm/services/agents/deployment/agent_config_provider.py +310 -0
  189. claude_mpm/services/agents/deployment/agent_configuration_manager.py +359 -0
  190. claude_mpm/services/agents/deployment/agent_definition_factory.py +84 -0
  191. claude_mpm/services/agents/deployment/agent_deployment.py +415 -2113
  192. claude_mpm/services/agents/deployment/agent_discovery_service.py +387 -0
  193. claude_mpm/services/agents/deployment/agent_environment_manager.py +293 -0
  194. claude_mpm/services/agents/deployment/agent_filesystem_manager.py +387 -0
  195. claude_mpm/services/agents/deployment/agent_format_converter.py +453 -0
  196. claude_mpm/services/agents/deployment/agent_frontmatter_validator.py +161 -0
  197. claude_mpm/services/agents/deployment/agent_lifecycle_manager.py +345 -495
  198. claude_mpm/services/agents/deployment/agent_metrics_collector.py +279 -0
  199. claude_mpm/services/agents/deployment/agent_restore_handler.py +88 -0
  200. claude_mpm/services/agents/deployment/agent_template_builder.py +406 -0
  201. claude_mpm/services/agents/deployment/agent_validator.py +352 -0
  202. claude_mpm/services/agents/deployment/agent_version_manager.py +313 -0
  203. claude_mpm/services/agents/deployment/agent_versioning.py +6 -9
  204. claude_mpm/services/agents/deployment/agents_directory_resolver.py +79 -0
  205. claude_mpm/services/agents/deployment/async_agent_deployment.py +298 -234
  206. claude_mpm/services/agents/deployment/config/__init__.py +13 -0
  207. claude_mpm/services/agents/deployment/config/deployment_config.py +182 -0
  208. claude_mpm/services/agents/deployment/config/deployment_config_manager.py +200 -0
  209. claude_mpm/services/agents/deployment/deployment_config_loader.py +54 -0
  210. claude_mpm/services/agents/deployment/deployment_type_detector.py +124 -0
  211. claude_mpm/services/agents/deployment/facade/__init__.py +18 -0
  212. claude_mpm/services/agents/deployment/facade/async_deployment_executor.py +159 -0
  213. claude_mpm/services/agents/deployment/facade/deployment_executor.py +73 -0
  214. claude_mpm/services/agents/deployment/facade/deployment_facade.py +270 -0
  215. claude_mpm/services/agents/deployment/facade/sync_deployment_executor.py +178 -0
  216. claude_mpm/services/agents/deployment/interface_adapter.py +227 -0
  217. claude_mpm/services/agents/deployment/lifecycle_health_checker.py +85 -0
  218. claude_mpm/services/agents/deployment/lifecycle_performance_tracker.py +100 -0
  219. claude_mpm/services/agents/deployment/pipeline/__init__.py +32 -0
  220. claude_mpm/services/agents/deployment/pipeline/pipeline_builder.py +158 -0
  221. claude_mpm/services/agents/deployment/pipeline/pipeline_context.py +159 -0
  222. claude_mpm/services/agents/deployment/pipeline/pipeline_executor.py +169 -0
  223. claude_mpm/services/agents/deployment/pipeline/steps/__init__.py +19 -0
  224. claude_mpm/services/agents/deployment/pipeline/steps/agent_processing_step.py +195 -0
  225. claude_mpm/services/agents/deployment/pipeline/steps/base_step.py +119 -0
  226. claude_mpm/services/agents/deployment/pipeline/steps/configuration_step.py +79 -0
  227. claude_mpm/services/agents/deployment/pipeline/steps/target_directory_step.py +90 -0
  228. claude_mpm/services/agents/deployment/pipeline/steps/validation_step.py +100 -0
  229. claude_mpm/services/agents/deployment/processors/__init__.py +15 -0
  230. claude_mpm/services/agents/deployment/processors/agent_deployment_context.py +98 -0
  231. claude_mpm/services/agents/deployment/processors/agent_deployment_result.py +235 -0
  232. claude_mpm/services/agents/deployment/processors/agent_processor.py +258 -0
  233. claude_mpm/services/agents/deployment/refactored_agent_deployment_service.py +318 -0
  234. claude_mpm/services/agents/deployment/results/__init__.py +13 -0
  235. claude_mpm/services/agents/deployment/results/deployment_metrics.py +200 -0
  236. claude_mpm/services/agents/deployment/results/deployment_result_builder.py +249 -0
  237. claude_mpm/services/agents/deployment/strategies/__init__.py +25 -0
  238. claude_mpm/services/agents/deployment/strategies/base_strategy.py +119 -0
  239. claude_mpm/services/agents/deployment/strategies/project_strategy.py +150 -0
  240. claude_mpm/services/agents/deployment/strategies/strategy_selector.py +117 -0
  241. claude_mpm/services/agents/deployment/strategies/system_strategy.py +116 -0
  242. claude_mpm/services/agents/deployment/strategies/user_strategy.py +137 -0
  243. claude_mpm/services/agents/deployment/system_instructions_deployer.py +108 -0
  244. claude_mpm/services/agents/deployment/validation/__init__.py +19 -0
  245. claude_mpm/services/agents/deployment/validation/agent_validator.py +323 -0
  246. claude_mpm/services/agents/deployment/validation/deployment_validator.py +238 -0
  247. claude_mpm/services/agents/deployment/validation/template_validator.py +299 -0
  248. claude_mpm/services/agents/deployment/validation/validation_result.py +226 -0
  249. claude_mpm/services/agents/loading/__init__.py +2 -2
  250. claude_mpm/services/agents/loading/agent_profile_loader.py +259 -229
  251. claude_mpm/services/agents/loading/base_agent_manager.py +90 -81
  252. claude_mpm/services/agents/loading/framework_agent_loader.py +154 -129
  253. claude_mpm/services/agents/management/__init__.py +2 -2
  254. claude_mpm/services/agents/management/agent_capabilities_generator.py +72 -58
  255. claude_mpm/services/agents/management/agent_management_service.py +209 -156
  256. claude_mpm/services/agents/memory/__init__.py +9 -6
  257. claude_mpm/services/agents/memory/agent_memory_manager.py +218 -1152
  258. claude_mpm/services/agents/memory/agent_persistence_service.py +20 -16
  259. claude_mpm/services/agents/memory/analyzer.py +430 -0
  260. claude_mpm/services/agents/memory/content_manager.py +376 -0
  261. claude_mpm/services/agents/memory/template_generator.py +468 -0
  262. claude_mpm/services/agents/registry/__init__.py +7 -10
  263. claude_mpm/services/agents/registry/deployed_agent_discovery.py +122 -97
  264. claude_mpm/services/agents/registry/modification_tracker.py +351 -285
  265. claude_mpm/services/async_session_logger.py +187 -153
  266. claude_mpm/services/claude_session_logger.py +87 -72
  267. claude_mpm/services/command_handler_service.py +217 -0
  268. claude_mpm/services/communication/__init__.py +3 -2
  269. claude_mpm/services/core/__init__.py +50 -97
  270. claude_mpm/services/core/base.py +60 -53
  271. claude_mpm/services/core/interfaces/__init__.py +188 -0
  272. claude_mpm/services/core/interfaces/agent.py +351 -0
  273. claude_mpm/services/core/interfaces/communication.py +343 -0
  274. claude_mpm/services/core/interfaces/infrastructure.py +413 -0
  275. claude_mpm/services/core/interfaces/service.py +434 -0
  276. claude_mpm/services/core/interfaces.py +19 -944
  277. claude_mpm/services/event_aggregator.py +208 -170
  278. claude_mpm/services/exceptions.py +387 -308
  279. claude_mpm/services/framework_claude_md_generator/__init__.py +75 -79
  280. claude_mpm/services/framework_claude_md_generator/content_assembler.py +69 -60
  281. claude_mpm/services/framework_claude_md_generator/content_validator.py +65 -61
  282. claude_mpm/services/framework_claude_md_generator/deployment_manager.py +68 -49
  283. claude_mpm/services/framework_claude_md_generator/section_generators/__init__.py +34 -34
  284. claude_mpm/services/framework_claude_md_generator/section_generators/agents.py +25 -22
  285. claude_mpm/services/framework_claude_md_generator/section_generators/claude_pm_init.py +10 -10
  286. claude_mpm/services/framework_claude_md_generator/section_generators/core_responsibilities.py +4 -3
  287. claude_mpm/services/framework_claude_md_generator/section_generators/delegation_constraints.py +4 -3
  288. claude_mpm/services/framework_claude_md_generator/section_generators/environment_config.py +4 -3
  289. claude_mpm/services/framework_claude_md_generator/section_generators/footer.py +6 -5
  290. claude_mpm/services/framework_claude_md_generator/section_generators/header.py +8 -7
  291. claude_mpm/services/framework_claude_md_generator/section_generators/orchestration_principles.py +4 -3
  292. claude_mpm/services/framework_claude_md_generator/section_generators/role_designation.py +6 -5
  293. claude_mpm/services/framework_claude_md_generator/section_generators/subprocess_validation.py +9 -8
  294. claude_mpm/services/framework_claude_md_generator/section_generators/todo_task_tools.py +4 -3
  295. claude_mpm/services/framework_claude_md_generator/section_generators/troubleshooting.py +5 -4
  296. claude_mpm/services/framework_claude_md_generator/section_manager.py +28 -27
  297. claude_mpm/services/framework_claude_md_generator/version_manager.py +30 -28
  298. claude_mpm/services/hook_service.py +106 -114
  299. claude_mpm/services/infrastructure/__init__.py +7 -5
  300. claude_mpm/services/infrastructure/context_preservation.py +233 -199
  301. claude_mpm/services/infrastructure/daemon_manager.py +279 -0
  302. claude_mpm/services/infrastructure/logging.py +83 -76
  303. claude_mpm/services/infrastructure/monitoring.py +547 -404
  304. claude_mpm/services/mcp_gateway/__init__.py +30 -13
  305. claude_mpm/services/mcp_gateway/config/__init__.py +2 -2
  306. claude_mpm/services/mcp_gateway/config/config_loader.py +61 -56
  307. claude_mpm/services/mcp_gateway/config/config_schema.py +50 -41
  308. claude_mpm/services/mcp_gateway/config/configuration.py +82 -75
  309. claude_mpm/services/mcp_gateway/core/__init__.py +13 -20
  310. claude_mpm/services/mcp_gateway/core/base.py +80 -67
  311. claude_mpm/services/mcp_gateway/core/exceptions.py +60 -46
  312. claude_mpm/services/mcp_gateway/core/interfaces.py +87 -84
  313. claude_mpm/services/mcp_gateway/main.py +287 -137
  314. claude_mpm/services/mcp_gateway/registry/__init__.py +1 -1
  315. claude_mpm/services/mcp_gateway/registry/service_registry.py +97 -94
  316. claude_mpm/services/mcp_gateway/registry/tool_registry.py +135 -126
  317. claude_mpm/services/mcp_gateway/server/__init__.py +2 -2
  318. claude_mpm/services/mcp_gateway/server/mcp_gateway.py +105 -110
  319. claude_mpm/services/mcp_gateway/server/stdio_handler.py +105 -107
  320. claude_mpm/services/mcp_gateway/server/stdio_server.py +691 -0
  321. claude_mpm/services/mcp_gateway/tools/__init__.py +4 -2
  322. claude_mpm/services/mcp_gateway/tools/base_adapter.py +109 -119
  323. claude_mpm/services/mcp_gateway/tools/document_summarizer.py +283 -215
  324. claude_mpm/services/mcp_gateway/tools/hello_world.py +122 -120
  325. claude_mpm/services/mcp_gateway/tools/ticket_tools.py +652 -0
  326. claude_mpm/services/mcp_gateway/tools/unified_ticket_tool.py +606 -0
  327. claude_mpm/services/memory/__init__.py +2 -2
  328. claude_mpm/services/memory/builder.py +451 -362
  329. claude_mpm/services/memory/cache/__init__.py +2 -2
  330. claude_mpm/services/memory/cache/shared_prompt_cache.py +232 -194
  331. claude_mpm/services/memory/cache/simple_cache.py +107 -93
  332. claude_mpm/services/memory/indexed_memory.py +195 -193
  333. claude_mpm/services/memory/optimizer.py +267 -234
  334. claude_mpm/services/memory/router.py +571 -263
  335. claude_mpm/services/memory_hook_service.py +237 -0
  336. claude_mpm/services/port_manager.py +575 -0
  337. claude_mpm/services/project/__init__.py +3 -3
  338. claude_mpm/services/project/analyzer.py +451 -305
  339. claude_mpm/services/project/registry.py +262 -240
  340. claude_mpm/services/recovery_manager.py +287 -231
  341. claude_mpm/services/response_tracker.py +87 -67
  342. claude_mpm/services/runner_configuration_service.py +587 -0
  343. claude_mpm/services/session_management_service.py +304 -0
  344. claude_mpm/services/socketio/__init__.py +4 -4
  345. claude_mpm/services/socketio/client_proxy.py +174 -0
  346. claude_mpm/services/socketio/handlers/__init__.py +3 -3
  347. claude_mpm/services/socketio/handlers/base.py +44 -30
  348. claude_mpm/services/socketio/handlers/connection.py +166 -64
  349. claude_mpm/services/socketio/handlers/file.py +123 -108
  350. claude_mpm/services/socketio/handlers/git.py +607 -373
  351. claude_mpm/services/socketio/handlers/hook.py +185 -0
  352. claude_mpm/services/socketio/handlers/memory.py +4 -4
  353. claude_mpm/services/socketio/handlers/project.py +4 -4
  354. claude_mpm/services/socketio/handlers/registry.py +53 -38
  355. claude_mpm/services/socketio/server/__init__.py +18 -0
  356. claude_mpm/services/socketio/server/broadcaster.py +252 -0
  357. claude_mpm/services/socketio/server/core.py +399 -0
  358. claude_mpm/services/socketio/server/main.py +323 -0
  359. claude_mpm/services/socketio_client_manager.py +160 -133
  360. claude_mpm/services/socketio_server.py +36 -1885
  361. claude_mpm/services/subprocess_launcher_service.py +316 -0
  362. claude_mpm/services/system_instructions_service.py +258 -0
  363. claude_mpm/services/ticket_manager.py +19 -533
  364. claude_mpm/services/utility_service.py +285 -0
  365. claude_mpm/services/version_control/__init__.py +18 -21
  366. claude_mpm/services/version_control/branch_strategy.py +20 -10
  367. claude_mpm/services/version_control/conflict_resolution.py +37 -13
  368. claude_mpm/services/version_control/git_operations.py +52 -21
  369. claude_mpm/services/version_control/semantic_versioning.py +92 -53
  370. claude_mpm/services/version_control/version_parser.py +145 -125
  371. claude_mpm/services/version_service.py +270 -0
  372. claude_mpm/storage/__init__.py +2 -2
  373. claude_mpm/storage/state_storage.py +177 -181
  374. claude_mpm/ticket_wrapper.py +2 -2
  375. claude_mpm/utils/__init__.py +2 -2
  376. claude_mpm/utils/agent_dependency_loader.py +453 -243
  377. claude_mpm/utils/config_manager.py +157 -118
  378. claude_mpm/utils/console.py +1 -1
  379. claude_mpm/utils/dependency_cache.py +102 -107
  380. claude_mpm/utils/dependency_manager.py +52 -47
  381. claude_mpm/utils/dependency_strategies.py +131 -96
  382. claude_mpm/utils/environment_context.py +110 -102
  383. claude_mpm/utils/error_handler.py +75 -55
  384. claude_mpm/utils/file_utils.py +80 -67
  385. claude_mpm/utils/framework_detection.py +12 -11
  386. claude_mpm/utils/import_migration_example.py +12 -60
  387. claude_mpm/utils/imports.py +48 -45
  388. claude_mpm/utils/path_operations.py +100 -93
  389. claude_mpm/utils/robust_installer.py +172 -164
  390. claude_mpm/utils/session_logging.py +30 -23
  391. claude_mpm/utils/subprocess_utils.py +99 -61
  392. claude_mpm/validation/__init__.py +1 -1
  393. claude_mpm/validation/agent_validator.py +151 -111
  394. claude_mpm/validation/frontmatter_validator.py +92 -71
  395. {claude_mpm-3.9.11.dist-info → claude_mpm-4.0.4.dist-info}/METADATA +90 -22
  396. claude_mpm-4.0.4.dist-info/RECORD +417 -0
  397. {claude_mpm-3.9.11.dist-info → claude_mpm-4.0.4.dist-info}/entry_points.txt +1 -0
  398. {claude_mpm-3.9.11.dist-info → claude_mpm-4.0.4.dist-info}/licenses/LICENSE +1 -1
  399. claude_mpm/cli/commands/run_guarded.py +0 -511
  400. claude_mpm/config/memory_guardian_config.py +0 -325
  401. claude_mpm/config/memory_guardian_yaml.py +0 -335
  402. claude_mpm/core/config_paths.py +0 -150
  403. claude_mpm/core/memory_aware_runner.py +0 -353
  404. claude_mpm/dashboard/static/js/dashboard-original.js +0 -4134
  405. claude_mpm/deployment_paths.py +0 -261
  406. claude_mpm/hooks/claude_hooks/hook_handler_fixed.py +0 -454
  407. claude_mpm/models/state_models.py +0 -433
  408. claude_mpm/services/agent/__init__.py +0 -24
  409. claude_mpm/services/agent/deployment.py +0 -2548
  410. claude_mpm/services/agent/management.py +0 -598
  411. claude_mpm/services/agent/registry.py +0 -813
  412. claude_mpm/services/agents/registry/agent_registry.py +0 -813
  413. claude_mpm/services/communication/socketio.py +0 -1935
  414. claude_mpm/services/communication/websocket.py +0 -479
  415. claude_mpm/services/framework_claude_md_generator.py +0 -624
  416. claude_mpm/services/health_monitor.py +0 -893
  417. claude_mpm/services/infrastructure/graceful_degradation.py +0 -616
  418. claude_mpm/services/infrastructure/health_monitor.py +0 -775
  419. claude_mpm/services/infrastructure/memory_dashboard.py +0 -479
  420. claude_mpm/services/infrastructure/memory_guardian.py +0 -944
  421. claude_mpm/services/infrastructure/restart_protection.py +0 -642
  422. claude_mpm/services/infrastructure/state_manager.py +0 -774
  423. claude_mpm/services/mcp_gateway/manager.py +0 -334
  424. claude_mpm/services/optimized_hook_service.py +0 -542
  425. claude_mpm/services/project_analyzer.py +0 -864
  426. claude_mpm/services/project_registry.py +0 -608
  427. claude_mpm/services/standalone_socketio_server.py +0 -1300
  428. claude_mpm/services/ticket_manager_di.py +0 -318
  429. claude_mpm/services/ticketing_service_original.py +0 -510
  430. claude_mpm/utils/paths.py +0 -395
  431. claude_mpm/utils/platform_memory.py +0 -524
  432. claude_mpm-3.9.11.dist-info/RECORD +0 -306
  433. {claude_mpm-3.9.11.dist-info → claude_mpm-4.0.4.dist-info}/WHEEL +0 -0
  434. {claude_mpm-3.9.11.dist-info → claude_mpm-4.0.4.dist-info}/top_level.txt +0 -0
@@ -10,43 +10,43 @@ to provide a complete ticket management system within the claude-mpm CLI. The co
10
10
  mirror the scripts/ticket.py interface for consistency.
11
11
  """
12
12
 
13
- import sys
13
+ import json
14
14
  import subprocess
15
- from typing import Optional, List, Dict, Any
16
- from pathlib import Path
15
+ import sys
16
+ from typing import Any, Dict, List, Optional
17
17
 
18
- from ...core.logger import get_logger
19
18
  from ...constants import TicketCommands
19
+ from ...core.logger import get_logger
20
20
 
21
21
 
22
22
  def manage_tickets(args):
23
23
  """
24
24
  Main ticket command dispatcher.
25
-
25
+
26
26
  WHY: This function routes ticket subcommands to their appropriate handlers,
27
27
  providing a single entry point for all ticket-related operations.
28
-
28
+
29
29
  DESIGN DECISION: We use a subcommand pattern similar to git, allowing for
30
30
  intuitive command structure like 'claude-mpm tickets create "title"'.
31
-
31
+
32
32
  Args:
33
33
  args: Parsed command line arguments with 'tickets_command' attribute
34
-
34
+
35
35
  Returns:
36
36
  Exit code (0 for success, non-zero for errors)
37
37
  """
38
38
  logger = get_logger("cli.tickets")
39
-
39
+
40
40
  # Handle case where no subcommand is provided - default to list
41
- if not hasattr(args, 'tickets_command') or not args.tickets_command:
41
+ if not hasattr(args, "tickets_command") or not args.tickets_command:
42
42
  # Default to list command for backward compatibility
43
43
  args.tickets_command = TicketCommands.LIST.value
44
44
  # Set default limit if not present
45
- if not hasattr(args, 'limit'):
45
+ if not hasattr(args, "limit"):
46
46
  args.limit = 10
47
- if not hasattr(args, 'verbose'):
47
+ if not hasattr(args, "verbose"):
48
48
  args.verbose = False
49
-
49
+
50
50
  # Map subcommands to handler functions
51
51
  handlers = {
52
52
  TicketCommands.CREATE.value: create_ticket,
@@ -59,7 +59,7 @@ def manage_tickets(args):
59
59
  TicketCommands.COMMENT.value: add_comment,
60
60
  TicketCommands.WORKFLOW.value: update_workflow,
61
61
  }
62
-
62
+
63
63
  # Execute the appropriate handler
64
64
  handler = handlers.get(args.tickets_command)
65
65
  if handler:
@@ -70,8 +70,9 @@ def manage_tickets(args):
70
70
  return 1
71
71
  except Exception as e:
72
72
  logger.error(f"Error executing {args.tickets_command}: {e}")
73
- if hasattr(args, 'debug') and args.debug:
73
+ if hasattr(args, "debug") and args.debug:
74
74
  import traceback
75
+
75
76
  traceback.print_exc()
76
77
  return 1
77
78
  else:
@@ -82,34 +83,34 @@ def manage_tickets(args):
82
83
  def create_ticket(args):
83
84
  """
84
85
  Create a new ticket.
85
-
86
+
86
87
  WHY: Users need to create tickets to track work items, bugs, and features.
87
88
  This command provides a streamlined interface for ticket creation.
88
-
89
+
89
90
  DESIGN DECISION: We parse description from remaining args to allow natural
90
91
  command line usage like: tickets create "title" -d This is a description
91
-
92
+
92
93
  Args:
93
94
  args: Arguments with title, type, priority, description, tags, etc.
94
-
95
+
95
96
  Returns:
96
97
  Exit code (0 for success, non-zero for errors)
97
98
  """
98
99
  logger = get_logger("cli.tickets")
99
-
100
+
100
101
  try:
101
102
  from ...services.ticket_manager import TicketManager
102
103
  except ImportError:
103
104
  from claude_mpm.services.ticket_manager import TicketManager
104
-
105
+
105
106
  ticket_manager = TicketManager()
106
-
107
+
107
108
  # Parse description from remaining args or use default
108
109
  description = " ".join(args.description) if args.description else ""
109
-
110
+
110
111
  # Parse tags
111
112
  tags = args.tags.split(",") if args.tags else []
112
-
113
+
113
114
  # Create ticket with all provided parameters
114
115
  ticket_id = ticket_manager.create_ticket(
115
116
  title=args.title,
@@ -118,10 +119,10 @@ def create_ticket(args):
118
119
  priority=args.priority,
119
120
  tags=tags,
120
121
  source="claude-mpm-cli",
121
- parent_epic=getattr(args, 'parent_epic', None),
122
- parent_issue=getattr(args, 'parent_issue', None)
122
+ parent_epic=getattr(args, "parent_epic", None),
123
+ parent_issue=getattr(args, "parent_issue", None),
123
124
  )
124
-
125
+
125
126
  if ticket_id:
126
127
  print(f"✅ Created ticket: {ticket_id}")
127
128
  if args.verbose:
@@ -129,9 +130,9 @@ def create_ticket(args):
129
130
  print(f" Priority: {args.priority}")
130
131
  if tags:
131
132
  print(f" Tags: {', '.join(tags)}")
132
- if getattr(args, 'parent_epic', None):
133
+ if getattr(args, "parent_epic", None):
133
134
  print(f" Parent Epic: {args.parent_epic}")
134
- if getattr(args, 'parent_issue', None):
135
+ if getattr(args, "parent_issue", None):
135
136
  print(f" Parent Issue: {args.parent_issue}")
136
137
  return 0
137
138
  else:
@@ -142,82 +143,158 @@ def create_ticket(args):
142
143
  def list_tickets(args):
143
144
  """
144
145
  List recent tickets with optional filtering.
145
-
146
+
146
147
  WHY: Users need to review tickets created during Claude sessions. This command
147
148
  provides a quick way to see recent tickets with their status and metadata.
148
-
149
+
149
150
  DESIGN DECISION: We show tickets in a compact format with emoji status indicators
150
151
  for better visual scanning. Filters allow focusing on specific ticket types/statuses.
151
-
152
+
152
153
  Args:
153
154
  args: Arguments with limit, type filter, status filter, verbose flag
154
-
155
+
155
156
  Returns:
156
157
  Exit code (0 for success, non-zero for errors)
157
158
  """
158
159
  logger = get_logger("cli.tickets")
159
-
160
+
160
161
  try:
162
+ # Get pagination parameters
163
+ page = getattr(args, "page", 1)
164
+ page_size = getattr(args, "page_size", 20)
165
+ limit = getattr(args, "limit", page_size) # Use page_size as default limit
166
+
167
+ # Validate pagination parameters
168
+ if page < 1:
169
+ print("❌ Page number must be 1 or greater")
170
+ return 1
171
+ if page_size < 1:
172
+ print("❌ Page size must be 1 or greater")
173
+ return 1
174
+
175
+ # Try to use ai-trackdown CLI directly for better pagination support
176
+ tickets = []
161
177
  try:
162
- from ...services.ticket_manager import TicketManager
163
- except ImportError:
164
- from claude_mpm.services.ticket_manager import TicketManager
165
-
166
- ticket_manager = TicketManager()
167
-
168
- # Get tickets with limit
169
- limit = getattr(args, 'limit', 10)
170
- tickets = ticket_manager.list_recent_tickets(limit=limit * 2) # Get extra for filtering
171
-
172
- # Apply filters if specified
173
- filtered_tickets = []
174
- for ticket in tickets:
175
- # Type filter
176
- type_filter = getattr(args, 'type', 'all')
177
- if type_filter != 'all':
178
- ticket_type = ticket.get('metadata', {}).get('ticket_type', 'unknown')
179
- if ticket_type != type_filter:
180
- continue
181
-
182
- # Status filter
183
- status_filter = getattr(args, 'status', 'all')
184
- if status_filter != 'all':
185
- if ticket.get('status') != status_filter:
186
- continue
187
-
188
- filtered_tickets.append(ticket)
189
- if len(filtered_tickets) >= limit:
190
- break
191
-
192
- if not filtered_tickets:
178
+ # Build aitrackdown command with pagination
179
+ cmd = ["aitrackdown", "status", "tasks"]
180
+
181
+ # Calculate offset for pagination
182
+ offset = (page - 1) * page_size
183
+ total_needed = offset + page_size
184
+
185
+ # Request more tickets than needed to handle filtering
186
+ cmd.extend(["--limit", str(total_needed * 2)])
187
+
188
+ # Add filters
189
+ type_filter = getattr(args, "type", None) or "all"
190
+ if type_filter != "all" and type_filter is not None:
191
+ cmd.extend(["--type", type_filter])
192
+
193
+ status_filter = getattr(args, "status", None) or "all"
194
+ if status_filter != "all" and status_filter is not None:
195
+ cmd.extend(["--status", status_filter])
196
+
197
+ # Execute command
198
+ result = subprocess.run(cmd, capture_output=True, text=True, check=True)
199
+
200
+ # Parse JSON output
201
+ if result.stdout.strip():
202
+ try:
203
+ all_tickets = json.loads(result.stdout)
204
+ if isinstance(all_tickets, list):
205
+ # Apply pagination
206
+ start_idx = offset
207
+ end_idx = start_idx + page_size
208
+ tickets = all_tickets[start_idx:end_idx]
209
+ else:
210
+ tickets = []
211
+ except json.JSONDecodeError:
212
+ logger.warning(
213
+ "Failed to parse aitrackdown JSON output, falling back to stub"
214
+ )
215
+ tickets = []
216
+
217
+ except (subprocess.CalledProcessError, FileNotFoundError) as e:
218
+ logger.warning(f"aitrackdown command failed: {e}, falling back to stub")
219
+ # Fallback to stub implementation
220
+ try:
221
+ from ...services.ticket_manager import TicketManager
222
+ except ImportError:
223
+ from claude_mpm.services.ticket_manager import TicketManager
224
+
225
+ ticket_manager = TicketManager()
226
+ all_tickets = ticket_manager.list_recent_tickets(limit=limit * 2)
227
+
228
+ # Apply filters and pagination manually for stub
229
+ filtered_tickets = []
230
+ for ticket in all_tickets:
231
+ # Type filter
232
+ if type_filter != "all":
233
+ ticket_type = ticket.get("metadata", {}).get(
234
+ "ticket_type", "unknown"
235
+ )
236
+ if ticket_type != type_filter:
237
+ continue
238
+
239
+ # Status filter
240
+ if status_filter != "all":
241
+ if ticket.get("status") != status_filter:
242
+ continue
243
+
244
+ filtered_tickets.append(ticket)
245
+
246
+ # Apply pagination
247
+ start_idx = (page - 1) * page_size
248
+ end_idx = start_idx + page_size
249
+ tickets = filtered_tickets[start_idx:end_idx]
250
+
251
+ if not tickets:
193
252
  print("No tickets found matching criteria")
253
+ if page > 1:
254
+ print(f"Try a lower page number (current: {page})")
194
255
  return 0
195
-
196
- print(f"Recent tickets (showing {len(filtered_tickets)}):")
256
+
257
+ # Display pagination info
258
+ total_shown = len(tickets)
259
+ print(f"Tickets (page {page}, showing {total_shown} tickets):")
197
260
  print("-" * 80)
198
-
199
- for ticket in filtered_tickets:
261
+
262
+ for ticket in tickets:
200
263
  # Use emoji to indicate status visually
201
264
  status_emoji = {
202
265
  "open": "🔵",
203
266
  "in_progress": "🟡",
204
267
  "done": "🟢",
205
268
  "closed": "⚫",
206
- "blocked": "🔴"
207
- }.get(ticket.get('status', 'unknown'), "⚪")
208
-
269
+ "blocked": "🔴",
270
+ }.get(ticket.get("status", "unknown"), "⚪")
271
+
209
272
  print(f"{status_emoji} [{ticket['id']}] {ticket['title']}")
210
-
211
- if getattr(args, 'verbose', False):
212
- ticket_type = ticket.get('metadata', {}).get('ticket_type', 'task')
213
- print(f" Type: {ticket_type} | Status: {ticket['status']} | Priority: {ticket['priority']}")
214
- if ticket.get('tags'):
273
+
274
+ if getattr(args, "verbose", False):
275
+ ticket_type = ticket.get("metadata", {}).get("ticket_type", "task")
276
+ print(
277
+ f" Type: {ticket_type} | Status: {ticket['status']} | Priority: {ticket['priority']}"
278
+ )
279
+ if ticket.get("tags"):
215
280
  print(f" Tags: {', '.join(ticket['tags'])}")
216
281
  print(f" Created: {ticket['created_at']}")
217
282
  print()
218
-
283
+
284
+ # Show pagination navigation hints
285
+ if total_shown == page_size:
286
+ print("-" * 80)
287
+ print(f"📄 Page {page} | Showing {total_shown} tickets")
288
+ print(
289
+ f"💡 Next page: claude-mpm tickets list --page {page + 1} --page-size {page_size}"
290
+ )
291
+ if page > 1:
292
+ print(
293
+ f"💡 Previous page: claude-mpm tickets list --page {page - 1} --page-size {page_size}"
294
+ )
295
+
219
296
  return 0
220
-
297
+
221
298
  except ImportError:
222
299
  logger.error("ai-trackdown-pytools not installed")
223
300
  print("Error: ai-trackdown-pytools not installed")
@@ -232,126 +309,141 @@ def list_tickets(args):
232
309
  def view_ticket(args):
233
310
  """
234
311
  View a specific ticket in detail.
235
-
312
+
236
313
  WHY: Users need to see full ticket details including description, metadata,
237
314
  and all associated information for understanding context and status.
238
-
315
+
239
316
  Args:
240
317
  args: Arguments with ticket id and verbose flag
241
-
318
+
242
319
  Returns:
243
320
  Exit code (0 for success, non-zero for errors)
244
321
  """
245
322
  logger = get_logger("cli.tickets")
246
-
323
+
247
324
  try:
248
325
  from ...services.ticket_manager import TicketManager
249
326
  except ImportError:
250
327
  from claude_mpm.services.ticket_manager import TicketManager
251
-
328
+
252
329
  ticket_manager = TicketManager()
253
- ticket = ticket_manager.get_ticket(args.id)
254
-
330
+ # Handle both 'id' and 'ticket_id' attributes for compatibility
331
+ ticket_id = getattr(args, 'ticket_id', getattr(args, 'id', None))
332
+ if not ticket_id:
333
+ print("❌ No ticket ID provided")
334
+ return 1
335
+ ticket = ticket_manager.get_ticket(ticket_id)
336
+
255
337
  if not ticket:
256
- print(f"❌ Ticket {args.id} not found")
338
+ print(f"❌ Ticket {ticket_id} not found")
257
339
  return 1
258
-
340
+
259
341
  print(f"Ticket: {ticket['id']}")
260
342
  print("=" * 80)
261
343
  print(f"Title: {ticket['title']}")
262
344
  print(f"Type: {ticket.get('metadata', {}).get('ticket_type', 'unknown')}")
263
345
  print(f"Status: {ticket['status']}")
264
346
  print(f"Priority: {ticket['priority']}")
265
-
266
- if ticket.get('tags'):
347
+
348
+ if ticket.get("tags"):
267
349
  print(f"Tags: {', '.join(ticket['tags'])}")
268
-
269
- if ticket.get('assignees'):
350
+
351
+ if ticket.get("assignees"):
270
352
  print(f"Assignees: {', '.join(ticket['assignees'])}")
271
-
353
+
272
354
  # Show parent references if they exist
273
- metadata = ticket.get('metadata', {})
274
- if metadata.get('parent_epic'):
355
+ metadata = ticket.get("metadata", {})
356
+ if metadata.get("parent_epic"):
275
357
  print(f"Parent Epic: {metadata['parent_epic']}")
276
- if metadata.get('parent_issue'):
358
+ if metadata.get("parent_issue"):
277
359
  print(f"Parent Issue: {metadata['parent_issue']}")
278
-
360
+
279
361
  print(f"\nDescription:")
280
362
  print("-" * 40)
281
- print(ticket.get('description', 'No description'))
282
-
363
+ print(ticket.get("description", "No description"))
364
+
283
365
  print(f"\nCreated: {ticket['created_at']}")
284
366
  print(f"Updated: {ticket['updated_at']}")
285
-
286
- if args.verbose and ticket.get('metadata'):
367
+
368
+ if args.verbose and ticket.get("metadata"):
287
369
  print(f"\nMetadata:")
288
370
  print("-" * 40)
289
- for key, value in ticket['metadata'].items():
290
- if key not in ['parent_epic', 'parent_issue', 'ticket_type']: # Already shown above
371
+ for key, value in ticket["metadata"].items():
372
+ if key not in [
373
+ "parent_epic",
374
+ "parent_issue",
375
+ "ticket_type",
376
+ ]: # Already shown above
291
377
  print(f" {key}: {value}")
292
-
378
+
293
379
  return 0
294
380
 
295
381
 
296
382
  def update_ticket(args):
297
383
  """
298
384
  Update a ticket's properties.
299
-
385
+
300
386
  WHY: Tickets need to be updated as work progresses, priorities change,
301
387
  or additional information becomes available.
302
-
388
+
303
389
  DESIGN DECISION: For complex updates, we delegate to aitrackdown CLI
304
390
  for operations not directly supported by our TicketManager interface.
305
-
391
+
306
392
  Args:
307
393
  args: Arguments with ticket id and update fields
308
-
394
+
309
395
  Returns:
310
396
  Exit code (0 for success, non-zero for errors)
311
397
  """
312
398
  logger = get_logger("cli.tickets")
313
-
399
+
314
400
  try:
315
401
  from ...services.ticket_manager import TicketManager
316
402
  except ImportError:
317
403
  from claude_mpm.services.ticket_manager import TicketManager
318
-
404
+
319
405
  ticket_manager = TicketManager()
320
-
406
+
407
+ # Handle both 'id' and 'ticket_id' attributes for compatibility
408
+ ticket_id = getattr(args, 'ticket_id', getattr(args, 'id', None))
409
+ if not ticket_id:
410
+ print("❌ No ticket ID provided")
411
+ return 1
412
+
321
413
  # Build update dictionary
322
414
  updates = {}
323
-
415
+
324
416
  if args.status:
325
- updates['status'] = args.status
326
-
417
+ updates["status"] = args.status
418
+
327
419
  if args.priority:
328
- updates['priority'] = args.priority
329
-
420
+ updates["priority"] = args.priority
421
+
330
422
  if args.description:
331
- updates['description'] = " ".join(args.description)
332
-
423
+ updates["description"] = " ".join(args.description)
424
+
333
425
  if args.tags:
334
- updates['tags'] = args.tags.split(",")
335
-
426
+ updates["tags"] = args.tags.split(",")
427
+
336
428
  if args.assign:
337
- updates['assignees'] = [args.assign]
338
-
429
+ updates["assignees"] = [args.assign]
430
+
339
431
  if not updates:
340
432
  print("❌ No updates specified")
341
433
  return 1
342
-
434
+
343
435
  # Try to update using TicketManager
344
- success = ticket_manager.update_task(args.id, **updates)
345
-
436
+ success = ticket_manager.update_task(ticket_id, **updates)
437
+
346
438
  if success:
347
- print(f"✅ Updated ticket: {args.id}")
439
+ print(f"✅ Updated ticket: {ticket_id}")
348
440
  return 0
349
441
  else:
350
442
  # Fallback to aitrackdown CLI for status transitions
351
443
  if args.status:
352
444
  logger.info("Attempting update via aitrackdown CLI")
353
- cmd = ["aitrackdown", "transition", args.id, args.status]
354
-
445
+ cmd = ["aitrackdown", "transition", ticket_id, args.status]
446
+
355
447
  # Add comment with other updates
356
448
  comment_parts = []
357
449
  if args.priority:
@@ -360,180 +452,219 @@ def update_ticket(args):
360
452
  comment_parts.append(f"Assigned to: {args.assign}")
361
453
  if args.tags:
362
454
  comment_parts.append(f"Tags: {args.tags}")
363
-
455
+
364
456
  if comment_parts:
365
457
  comment = " | ".join(comment_parts)
366
458
  cmd.extend(["--comment", comment])
367
-
459
+
368
460
  try:
369
461
  subprocess.run(cmd, check=True, capture_output=True, text=True)
370
- print(f"✅ Updated ticket: {args.id}")
462
+ print(f"✅ Updated ticket: {ticket_id}")
371
463
  return 0
372
464
  except subprocess.CalledProcessError as e:
373
465
  logger.error(f"Failed to update via CLI: {e}")
374
- print(f"❌ Failed to update ticket: {args.id}")
466
+ print(f"❌ Failed to update ticket: {ticket_id}")
375
467
  return 1
376
468
  else:
377
- print(f"❌ Failed to update ticket: {args.id}")
469
+ print(f"❌ Failed to update ticket: {ticket_id}")
378
470
  return 1
379
471
 
380
472
 
381
473
  def close_ticket(args):
382
474
  """
383
475
  Close a ticket.
384
-
476
+
385
477
  WHY: Tickets need to be closed when work is completed or no longer relevant.
386
-
478
+
387
479
  Args:
388
480
  args: Arguments with ticket id and optional resolution
389
-
481
+
390
482
  Returns:
391
483
  Exit code (0 for success, non-zero for errors)
392
484
  """
393
485
  logger = get_logger("cli.tickets")
394
-
486
+
395
487
  try:
396
488
  from ...services.ticket_manager import TicketManager
397
489
  except ImportError:
398
490
  from claude_mpm.services.ticket_manager import TicketManager
399
-
491
+
400
492
  ticket_manager = TicketManager()
401
-
493
+
494
+ # Handle both 'id' and 'ticket_id' attributes for compatibility
495
+ ticket_id = getattr(args, 'ticket_id', getattr(args, 'id', None))
496
+ if not ticket_id:
497
+ print("❌ No ticket ID provided")
498
+ return 1
499
+
402
500
  # Try to close using TicketManager
403
- resolution = getattr(args, 'resolution', None)
404
- success = ticket_manager.close_task(args.id, resolution=resolution)
405
-
501
+ resolution = getattr(args, "resolution", getattr(args, "comment", None))
502
+ success = ticket_manager.close_task(ticket_id, resolution=resolution)
503
+
406
504
  if success:
407
- print(f"✅ Closed ticket: {args.id}")
505
+ print(f"✅ Closed ticket: {ticket_id}")
408
506
  return 0
409
507
  else:
410
508
  # Fallback to aitrackdown CLI
411
509
  logger.info("Attempting close via aitrackdown CLI")
412
- cmd = ["aitrackdown", "close", args.id]
413
-
510
+ cmd = ["aitrackdown", "close", ticket_id]
511
+
414
512
  if resolution:
415
513
  cmd.extend(["--comment", resolution])
416
-
514
+
417
515
  try:
418
516
  subprocess.run(cmd, check=True, capture_output=True, text=True)
419
- print(f"✅ Closed ticket: {args.id}")
517
+ print(f"✅ Closed ticket: {ticket_id}")
420
518
  return 0
421
519
  except subprocess.CalledProcessError:
422
- print(f"❌ Failed to close ticket: {args.id}")
520
+ print(f"❌ Failed to close ticket: {ticket_id}")
423
521
  return 1
424
522
 
425
523
 
426
524
  def delete_ticket(args):
427
525
  """
428
526
  Delete a ticket.
429
-
527
+
430
528
  WHY: Sometimes tickets are created in error or are no longer needed
431
529
  and should be removed from the system.
432
-
530
+
433
531
  DESIGN DECISION: We delegate to aitrackdown CLI as deletion is a
434
532
  destructive operation that should use the official tool.
435
-
533
+
436
534
  Args:
437
535
  args: Arguments with ticket id and force flag
438
-
536
+
439
537
  Returns:
440
538
  Exit code (0 for success, non-zero for errors)
441
539
  """
442
540
  logger = get_logger("cli.tickets")
443
-
541
+
542
+ # Handle both 'id' and 'ticket_id' attributes for compatibility
543
+ ticket_id = getattr(args, 'ticket_id', getattr(args, 'id', None))
544
+ if not ticket_id:
545
+ print("❌ No ticket ID provided")
546
+ return 1
547
+
444
548
  # Confirm deletion unless forced
445
549
  if not args.force:
446
- response = input(f"Are you sure you want to delete ticket {args.id}? (y/N): ")
447
- if response.lower() != 'y':
550
+ sys.stdout.flush() # Ensure prompt is displayed before input
551
+
552
+ # Check if we're in a TTY environment for proper input handling
553
+ if not sys.stdin.isatty():
554
+ # In non-TTY environment (like pipes), use readline
555
+ print(
556
+ f"Are you sure you want to delete ticket {ticket_id}? (y/N): ",
557
+ end="",
558
+ flush=True,
559
+ )
560
+ try:
561
+ response = sys.stdin.readline().strip().lower()
562
+ # Handle various line endings and control characters
563
+ response = response.replace("\r", "").replace("\n", "").strip()
564
+ except (EOFError, KeyboardInterrupt):
565
+ response = "n"
566
+ else:
567
+ # In TTY environment, use normal input()
568
+ try:
569
+ response = (
570
+ input(f"Are you sure you want to delete ticket {ticket_id}? (y/N): ")
571
+ .strip()
572
+ .lower()
573
+ )
574
+ except (EOFError, KeyboardInterrupt):
575
+ response = "n"
576
+
577
+ if response != "y":
448
578
  print("Deletion cancelled")
449
579
  return 0
450
-
580
+
451
581
  # Use aitrackdown CLI for deletion
452
- cmd = ["aitrackdown", "delete", args.id]
582
+ cmd = ["aitrackdown", "delete", ticket_id]
453
583
  if args.force:
454
584
  cmd.append("--force")
455
-
585
+
456
586
  try:
457
587
  subprocess.run(cmd, check=True, capture_output=True, text=True)
458
- print(f"✅ Deleted ticket: {args.id}")
588
+ print(f"✅ Deleted ticket: {ticket_id}")
459
589
  return 0
460
590
  except subprocess.CalledProcessError:
461
- print(f"❌ Failed to delete ticket: {args.id}")
591
+ print(f"❌ Failed to delete ticket: {ticket_id}")
462
592
  return 1
463
593
 
464
594
 
465
595
  def search_tickets(args):
466
596
  """
467
597
  Search tickets by query string.
468
-
598
+
469
599
  WHY: Users need to find specific tickets based on content, tags, or other criteria.
470
-
600
+
471
601
  DESIGN DECISION: We perform simple text matching on ticket data. For more advanced
472
602
  search, users should use the aitrackdown CLI directly.
473
-
603
+
474
604
  Args:
475
605
  args: Arguments with search query and filters
476
-
606
+
477
607
  Returns:
478
608
  Exit code (0 for success, non-zero for errors)
479
609
  """
480
610
  logger = get_logger("cli.tickets")
481
-
611
+
482
612
  try:
483
613
  from ...services.ticket_manager import TicketManager
484
614
  except ImportError:
485
615
  from claude_mpm.services.ticket_manager import TicketManager
486
-
616
+
487
617
  ticket_manager = TicketManager()
488
-
618
+
489
619
  # Get all available tickets for searching
490
620
  all_tickets = ticket_manager.list_recent_tickets(limit=100)
491
-
621
+
492
622
  # Search tickets
493
623
  query = args.query.lower()
494
624
  matched_tickets = []
495
-
625
+
496
626
  for ticket in all_tickets:
497
627
  # Check if query matches title, description, or tags
498
- if (query in ticket.get('title', '').lower() or
499
- query in ticket.get('description', '').lower() or
500
- any(query in tag.lower() for tag in ticket.get('tags', []))):
501
-
628
+ if (
629
+ query in ticket.get("title", "").lower()
630
+ or query in ticket.get("description", "").lower()
631
+ or any(query in tag.lower() for tag in ticket.get("tags", []))
632
+ ):
502
633
  # Apply type filter
503
- if args.type != 'all':
504
- ticket_type = ticket.get('metadata', {}).get('ticket_type', 'unknown')
634
+ if args.type != "all":
635
+ ticket_type = ticket.get("metadata", {}).get("ticket_type", "unknown")
505
636
  if ticket_type != args.type:
506
637
  continue
507
-
638
+
508
639
  # Apply status filter
509
- if args.status != 'all':
510
- if ticket.get('status') != args.status:
640
+ if args.status != "all":
641
+ if ticket.get("status") != args.status:
511
642
  continue
512
-
643
+
513
644
  matched_tickets.append(ticket)
514
645
  if len(matched_tickets) >= args.limit:
515
646
  break
516
-
647
+
517
648
  if not matched_tickets:
518
649
  print(f"No tickets found matching '{args.query}'")
519
650
  return 0
520
-
651
+
521
652
  print(f"Search results for '{args.query}' (showing {len(matched_tickets)}):")
522
653
  print("-" * 80)
523
-
654
+
524
655
  for ticket in matched_tickets:
525
656
  status_emoji = {
526
657
  "open": "🔵",
527
658
  "in_progress": "🟡",
528
659
  "done": "🟢",
529
660
  "closed": "⚫",
530
- "blocked": "🔴"
531
- }.get(ticket.get('status', 'unknown'), "⚪")
532
-
661
+ "blocked": "🔴",
662
+ }.get(ticket.get("status", "unknown"), "⚪")
663
+
533
664
  print(f"{status_emoji} [{ticket['id']}] {ticket['title']}")
534
-
665
+
535
666
  # Show snippet of description if it contains the query
536
- desc = ticket.get('description', '')
667
+ desc = ticket.get("description", "")
537
668
  if query in desc.lower():
538
669
  # Find and show context around the match
539
670
  idx = desc.lower().index(query)
@@ -545,83 +676,95 @@ def search_tickets(args):
545
676
  if end < len(desc):
546
677
  snippet = snippet + "..."
547
678
  print(f" {snippet}")
548
-
679
+
549
680
  return 0
550
681
 
551
682
 
552
683
  def add_comment(args):
553
684
  """
554
685
  Add a comment to a ticket.
555
-
686
+
556
687
  WHY: Comments allow tracking progress, decisions, and additional context
557
688
  on tickets over time.
558
-
689
+
559
690
  DESIGN DECISION: We delegate to aitrackdown CLI as it has proper comment
560
691
  tracking infrastructure.
561
-
692
+
562
693
  Args:
563
694
  args: Arguments with ticket id and comment text
564
-
695
+
565
696
  Returns:
566
697
  Exit code (0 for success, non-zero for errors)
567
698
  """
568
699
  logger = get_logger("cli.tickets")
569
-
700
+
701
+ # Handle both 'id' and 'ticket_id' attributes for compatibility
702
+ ticket_id = getattr(args, 'ticket_id', getattr(args, 'id', None))
703
+ if not ticket_id:
704
+ print("❌ No ticket ID provided")
705
+ return 1
706
+
570
707
  # Join comment parts into single string
571
- comment = " ".join(args.comment)
572
-
708
+ comment = " ".join(args.comment) if isinstance(args.comment, list) else args.comment
709
+
573
710
  # Use aitrackdown CLI for comments
574
- cmd = ["aitrackdown", "comment", args.id, comment]
575
-
711
+ cmd = ["aitrackdown", "comment", ticket_id, comment]
712
+
576
713
  try:
577
714
  subprocess.run(cmd, check=True, capture_output=True, text=True)
578
- print(f"✅ Added comment to ticket: {args.id}")
715
+ print(f"✅ Added comment to ticket: {ticket_id}")
579
716
  return 0
580
717
  except subprocess.CalledProcessError:
581
- print(f"❌ Failed to add comment to ticket: {args.id}")
718
+ print(f"❌ Failed to add comment to ticket: {ticket_id}")
582
719
  return 1
583
720
 
584
721
 
585
722
  def update_workflow(args):
586
723
  """
587
724
  Update ticket workflow state.
588
-
725
+
589
726
  WHY: Workflow states track the progress of tickets through defined stages
590
727
  like todo, in_progress, ready, tested, done.
591
-
728
+
592
729
  DESIGN DECISION: We use aitrackdown's transition command for workflow updates
593
730
  as it maintains proper state machine transitions.
594
-
731
+
595
732
  Args:
596
733
  args: Arguments with ticket id, new state, and optional comment
597
-
734
+
598
735
  Returns:
599
736
  Exit code (0 for success, non-zero for errors)
600
737
  """
601
738
  logger = get_logger("cli.tickets")
602
-
739
+
740
+ # Handle both 'id' and 'ticket_id' attributes for compatibility
741
+ ticket_id = getattr(args, 'ticket_id', getattr(args, 'id', None))
742
+ if not ticket_id:
743
+ print("❌ No ticket ID provided")
744
+ return 1
745
+
603
746
  # Map workflow states to status if needed
604
747
  state_mapping = {
605
- 'todo': 'open',
606
- 'in_progress': 'in_progress',
607
- 'ready': 'ready',
608
- 'tested': 'tested',
609
- 'done': 'done',
610
- 'blocked': 'blocked'
748
+ "todo": "open",
749
+ "in_progress": "in_progress",
750
+ "ready": "ready",
751
+ "tested": "tested",
752
+ "done": "done",
753
+ "blocked": "blocked",
611
754
  }
612
-
755
+
613
756
  # Use aitrackdown transition command
614
- cmd = ["aitrackdown", "transition", args.id, args.state]
615
-
616
- if getattr(args, 'comment', None):
757
+ cmd = ["aitrackdown", "transition", ticket_id, args.state]
758
+
759
+ if getattr(args, "comment", None):
617
760
  cmd.extend(["--comment", args.comment])
618
-
761
+
619
762
  try:
620
763
  subprocess.run(cmd, check=True, capture_output=True, text=True)
621
- print(f"✅ Updated workflow state for {args.id} to: {args.state}")
764
+ print(f"✅ Updated workflow state for {ticket_id} to: {args.state}")
622
765
  return 0
623
766
  except subprocess.CalledProcessError:
624
- print(f"❌ Failed to update workflow state for ticket: {args.id}")
767
+ print(f"❌ Failed to update workflow state for ticket: {ticket_id}")
625
768
  return 1
626
769
 
627
770
 
@@ -629,12 +772,12 @@ def update_workflow(args):
629
772
  def list_tickets_legacy(args):
630
773
  """
631
774
  Legacy list_tickets function for backward compatibility.
632
-
775
+
633
776
  WHY: The old CLI interface expected a simple list_tickets function.
634
777
  This wrapper maintains that interface while using the new implementation.
635
-
778
+
636
779
  Args:
637
780
  args: Parsed command line arguments with 'limit' attribute
638
781
  """
639
782
  # Call the new list_tickets function
640
- return list_tickets(args)
783
+ return list_tickets(args)