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