claude-mpm 4.1.26__py3-none-any.whl → 5.0.9__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 (792) hide show
  1. claude_mpm/BUILD_NUMBER +1 -1
  2. claude_mpm/VERSION +1 -1
  3. claude_mpm/__init__.py +20 -5
  4. claude_mpm/agents/BASE_AGENT_TEMPLATE.md +118 -0
  5. claude_mpm/agents/BASE_DOCUMENTATION.md +53 -0
  6. claude_mpm/agents/BASE_ENGINEER.md +658 -0
  7. claude_mpm/agents/BASE_OPS.md +219 -0
  8. claude_mpm/agents/BASE_PM.md +432 -158
  9. claude_mpm/agents/BASE_PROMPT_ENGINEER.md +787 -0
  10. claude_mpm/agents/BASE_QA.md +167 -0
  11. claude_mpm/agents/BASE_RESEARCH.md +53 -0
  12. claude_mpm/agents/OUTPUT_STYLE.md +254 -29
  13. claude_mpm/agents/PM_INSTRUCTIONS.md +969 -0
  14. claude_mpm/agents/PM_INSTRUCTIONS_TEACH.md +1322 -0
  15. claude_mpm/agents/WORKFLOW.md +355 -191
  16. claude_mpm/agents/__init__.py +6 -0
  17. claude_mpm/agents/agent_loader.py +41 -14
  18. claude_mpm/agents/agent_loader_integration.py +3 -2
  19. claude_mpm/agents/async_agent_loader.py +3 -3
  20. claude_mpm/agents/base_agent.json +6 -3
  21. claude_mpm/agents/base_agent_loader.py +21 -44
  22. claude_mpm/agents/frontmatter_validator.py +292 -252
  23. claude_mpm/agents/system_agent_config.py +3 -2
  24. claude_mpm/agents/templates/README.md +465 -0
  25. claude_mpm/agents/templates/circuit-breakers.md +1005 -0
  26. claude_mpm/agents/templates/context-management-examples.md +544 -0
  27. claude_mpm/agents/templates/git-file-tracking.md +584 -0
  28. claude_mpm/agents/templates/pm-examples.md +474 -0
  29. claude_mpm/agents/templates/pm-red-flags.md +310 -0
  30. claude_mpm/agents/templates/pr-workflow-examples.md +427 -0
  31. claude_mpm/agents/templates/research-gate-examples.md +669 -0
  32. claude_mpm/agents/templates/response-format.md +583 -0
  33. claude_mpm/agents/templates/structured-questions-examples.md +615 -0
  34. claude_mpm/agents/templates/ticket-completeness-examples.md +139 -0
  35. claude_mpm/agents/templates/ticketing-examples.md +277 -0
  36. claude_mpm/agents/templates/validation-templates.md +312 -0
  37. claude_mpm/cli/__init__.py +72 -376
  38. claude_mpm/cli/commands/__init__.py +4 -0
  39. claude_mpm/cli/commands/agent_manager.py +675 -20
  40. claude_mpm/cli/commands/agent_source.py +774 -0
  41. claude_mpm/cli/commands/agent_state_manager.py +344 -0
  42. claude_mpm/cli/commands/agents.py +1673 -178
  43. claude_mpm/cli/commands/agents_cleanup.py +210 -0
  44. claude_mpm/cli/commands/agents_detect.py +380 -0
  45. claude_mpm/cli/commands/agents_discover.py +338 -0
  46. claude_mpm/cli/commands/agents_recommend.py +309 -0
  47. claude_mpm/cli/commands/aggregate.py +11 -7
  48. claude_mpm/cli/commands/analyze.py +18 -13
  49. claude_mpm/cli/commands/analyze_code.py +8 -4
  50. claude_mpm/cli/commands/auto_configure.py +566 -0
  51. claude_mpm/cli/commands/cleanup.py +12 -12
  52. claude_mpm/cli/commands/config.py +54 -17
  53. claude_mpm/cli/commands/configure.py +1184 -1055
  54. claude_mpm/cli/commands/configure_agent_display.py +261 -0
  55. claude_mpm/cli/commands/configure_behavior_manager.py +204 -0
  56. claude_mpm/cli/commands/configure_hook_manager.py +225 -0
  57. claude_mpm/cli/commands/configure_models.py +18 -0
  58. claude_mpm/cli/commands/configure_navigation.py +184 -0
  59. claude_mpm/cli/commands/configure_paths.py +104 -0
  60. claude_mpm/cli/commands/configure_persistence.py +254 -0
  61. claude_mpm/cli/commands/configure_startup_manager.py +646 -0
  62. claude_mpm/cli/commands/configure_template_editor.py +497 -0
  63. claude_mpm/cli/commands/configure_validators.py +73 -0
  64. claude_mpm/cli/commands/dashboard.py +50 -52
  65. claude_mpm/cli/commands/debug.py +19 -19
  66. claude_mpm/cli/commands/doctor.py +51 -7
  67. claude_mpm/cli/commands/hook_errors.py +277 -0
  68. claude_mpm/cli/commands/info.py +3 -4
  69. claude_mpm/cli/commands/local_deploy.py +534 -0
  70. claude_mpm/cli/commands/mcp.py +17 -10
  71. claude_mpm/cli/commands/mcp_command_router.py +11 -0
  72. claude_mpm/cli/commands/mcp_config.py +154 -0
  73. claude_mpm/cli/commands/mcp_external_commands.py +249 -0
  74. claude_mpm/cli/commands/mcp_install_commands.py +101 -32
  75. claude_mpm/cli/commands/mcp_pipx_config.py +2 -2
  76. claude_mpm/cli/commands/mcp_setup_external.py +868 -0
  77. claude_mpm/cli/commands/memory.py +55 -21
  78. claude_mpm/cli/commands/monitor.py +160 -70
  79. claude_mpm/cli/commands/mpm_init/__init__.py +73 -0
  80. claude_mpm/cli/commands/mpm_init/core.py +573 -0
  81. claude_mpm/cli/commands/mpm_init/display.py +341 -0
  82. claude_mpm/cli/commands/mpm_init/git_activity.py +427 -0
  83. claude_mpm/cli/commands/mpm_init/modes.py +397 -0
  84. claude_mpm/cli/commands/mpm_init/prompts.py +442 -0
  85. claude_mpm/cli/commands/mpm_init_cli.py +396 -0
  86. claude_mpm/cli/commands/mpm_init_handler.py +114 -4
  87. claude_mpm/cli/commands/postmortem.py +401 -0
  88. claude_mpm/cli/commands/run.py +252 -167
  89. claude_mpm/cli/commands/search.py +458 -0
  90. claude_mpm/cli/commands/skill_source.py +694 -0
  91. claude_mpm/cli/commands/skills.py +1225 -0
  92. claude_mpm/cli/commands/uninstall.py +176 -0
  93. claude_mpm/cli/commands/upgrade.py +152 -0
  94. claude_mpm/cli/commands/verify.py +119 -0
  95. claude_mpm/cli/executor.py +279 -0
  96. claude_mpm/cli/helpers.py +105 -0
  97. claude_mpm/cli/interactive/__init__.py +21 -0
  98. claude_mpm/cli/interactive/agent_wizard.py +1872 -0
  99. claude_mpm/cli/interactive/skills_wizard.py +491 -0
  100. claude_mpm/cli/parser.py +79 -2
  101. claude_mpm/cli/parsers/__init__.py +7 -1
  102. claude_mpm/cli/parsers/agent_manager_parser.py +161 -1
  103. claude_mpm/cli/parsers/agent_source_parser.py +171 -0
  104. claude_mpm/cli/parsers/agents_parser.py +369 -1
  105. claude_mpm/cli/parsers/auto_configure_parser.py +245 -0
  106. claude_mpm/cli/parsers/base_parser.py +196 -3
  107. claude_mpm/cli/parsers/config_parser.py +96 -43
  108. claude_mpm/cli/parsers/configure_parser.py +11 -15
  109. claude_mpm/cli/parsers/local_deploy_parser.py +227 -0
  110. claude_mpm/cli/parsers/mcp_parser.py +15 -0
  111. claude_mpm/cli/parsers/monitor_parser.py +12 -2
  112. claude_mpm/cli/parsers/mpm_init_parser.py +179 -9
  113. claude_mpm/cli/parsers/run_parser.py +5 -0
  114. claude_mpm/cli/parsers/search_parser.py +245 -0
  115. claude_mpm/cli/parsers/skill_source_parser.py +169 -0
  116. claude_mpm/cli/parsers/skills_parser.py +282 -0
  117. claude_mpm/cli/parsers/source_parser.py +138 -0
  118. claude_mpm/cli/shared/argument_patterns.py +20 -13
  119. claude_mpm/cli/shared/base_command.py +2 -2
  120. claude_mpm/cli/shared/output_formatters.py +28 -19
  121. claude_mpm/cli/startup.py +994 -0
  122. claude_mpm/cli/startup_display.py +480 -0
  123. claude_mpm/cli/startup_logging.py +179 -13
  124. claude_mpm/cli/utils.py +54 -3
  125. claude_mpm/cli_module/commands.py +1 -1
  126. claude_mpm/commands/mpm-agents-auto-configure.md +278 -0
  127. claude_mpm/commands/mpm-agents-detect.md +177 -0
  128. claude_mpm/commands/mpm-agents-list.md +131 -0
  129. claude_mpm/commands/mpm-agents-recommend.md +223 -0
  130. claude_mpm/commands/mpm-config-view.md +150 -0
  131. claude_mpm/commands/mpm-doctor.md +9 -0
  132. claude_mpm/commands/mpm-help.md +297 -5
  133. claude_mpm/commands/mpm-init.md +401 -17
  134. claude_mpm/commands/mpm-monitor.md +418 -0
  135. claude_mpm/commands/mpm-postmortem.md +123 -0
  136. claude_mpm/commands/mpm-session-resume.md +381 -0
  137. claude_mpm/commands/mpm-status.md +79 -8
  138. claude_mpm/commands/mpm-ticket-organize.md +304 -0
  139. claude_mpm/commands/mpm-ticket-view.md +552 -0
  140. claude_mpm/commands/mpm-version.md +122 -0
  141. claude_mpm/commands/mpm.md +12 -0
  142. claude_mpm/config/agent_config.py +4 -4
  143. claude_mpm/config/agent_presets.py +488 -0
  144. claude_mpm/config/agent_sources.py +325 -0
  145. claude_mpm/config/experimental_features.py +7 -7
  146. claude_mpm/config/model_config.py +428 -0
  147. claude_mpm/config/paths.py +3 -2
  148. claude_mpm/config/skill_presets.py +392 -0
  149. claude_mpm/config/skill_sources.py +590 -0
  150. claude_mpm/config/socketio_config.py +3 -3
  151. claude_mpm/constants.py +28 -1
  152. claude_mpm/core/__init__.py +53 -17
  153. claude_mpm/core/agent_name_normalizer.py +3 -2
  154. claude_mpm/core/agent_registry.py +2 -2
  155. claude_mpm/core/agent_session_manager.py +10 -10
  156. claude_mpm/core/api_validator.py +330 -0
  157. claude_mpm/core/base_service.py +33 -23
  158. claude_mpm/core/cache.py +9 -9
  159. claude_mpm/core/claude_runner.py +24 -42
  160. claude_mpm/core/config.py +101 -8
  161. claude_mpm/core/config_aliases.py +7 -6
  162. claude_mpm/core/constants.py +66 -1
  163. claude_mpm/core/container.py +11 -5
  164. claude_mpm/core/enums.py +452 -0
  165. claude_mpm/core/error_handler.py +623 -0
  166. claude_mpm/core/factories.py +1 -1
  167. claude_mpm/core/file_utils.py +764 -0
  168. claude_mpm/core/framework/__init__.py +25 -0
  169. claude_mpm/core/framework/formatters/__init__.py +11 -0
  170. claude_mpm/core/framework/formatters/capability_generator.py +367 -0
  171. claude_mpm/core/framework/formatters/content_formatter.py +288 -0
  172. claude_mpm/core/framework/formatters/context_generator.py +185 -0
  173. claude_mpm/core/framework/loaders/__init__.py +13 -0
  174. claude_mpm/core/framework/loaders/agent_loader.py +210 -0
  175. claude_mpm/core/framework/loaders/file_loader.py +176 -0
  176. claude_mpm/core/framework/loaders/instruction_loader.py +181 -0
  177. claude_mpm/core/framework/loaders/packaged_loader.py +232 -0
  178. claude_mpm/core/framework/processors/__init__.py +11 -0
  179. claude_mpm/core/framework/processors/memory_processor.py +230 -0
  180. claude_mpm/core/framework/processors/metadata_processor.py +146 -0
  181. claude_mpm/core/framework/processors/template_processor.py +244 -0
  182. claude_mpm/core/framework_loader.py +321 -1631
  183. claude_mpm/core/hook_error_memory.py +381 -0
  184. claude_mpm/core/hook_manager.py +49 -8
  185. claude_mpm/core/injectable_service.py +11 -8
  186. claude_mpm/core/instruction_reinforcement_hook.py +4 -3
  187. claude_mpm/core/interactive_session.py +146 -18
  188. claude_mpm/core/interfaces.py +56 -1
  189. claude_mpm/core/lazy.py +3 -3
  190. claude_mpm/core/log_manager.py +92 -23
  191. claude_mpm/core/logger.py +22 -15
  192. claude_mpm/core/logging_config.py +6 -2
  193. claude_mpm/core/logging_utils.py +520 -0
  194. claude_mpm/core/oneshot_session.py +122 -15
  195. claude_mpm/core/optimized_agent_loader.py +9 -9
  196. claude_mpm/core/optimized_startup.py +1 -1
  197. claude_mpm/core/output_style_manager.py +12 -192
  198. claude_mpm/core/pm_hook_interceptor.py +18 -12
  199. claude_mpm/core/protocols/__init__.py +23 -0
  200. claude_mpm/core/protocols/runner_protocol.py +103 -0
  201. claude_mpm/core/protocols/session_protocol.py +131 -0
  202. claude_mpm/core/service_registry.py +7 -3
  203. claude_mpm/core/session_manager.py +14 -12
  204. claude_mpm/core/shared/config_loader.py +1 -1
  205. claude_mpm/core/shared/singleton_manager.py +11 -4
  206. claude_mpm/core/socketio_pool.py +15 -15
  207. claude_mpm/core/system_context.py +38 -0
  208. claude_mpm/core/tool_access_control.py +3 -2
  209. claude_mpm/core/types.py +4 -11
  210. claude_mpm/core/typing_utils.py +7 -6
  211. claude_mpm/core/unified_agent_registry.py +115 -11
  212. claude_mpm/core/unified_config.py +6 -6
  213. claude_mpm/core/unified_paths.py +23 -20
  214. claude_mpm/dashboard/analysis_runner.py +4 -4
  215. claude_mpm/dashboard/api/simple_directory.py +261 -0
  216. claude_mpm/dashboard/static/css/activity.css +69 -69
  217. claude_mpm/dashboard/static/css/connection-status.css +10 -10
  218. claude_mpm/dashboard/static/css/dashboard.css +600 -18
  219. claude_mpm/dashboard/static/js/components/activity-tree.js +181 -195
  220. claude_mpm/dashboard/static/js/components/agent-hierarchy.js +105 -102
  221. claude_mpm/dashboard/static/js/components/agent-inference.js +34 -31
  222. claude_mpm/dashboard/static/js/components/build-tracker.js +67 -59
  223. claude_mpm/dashboard/static/js/components/code-simple.js +857 -0
  224. claude_mpm/dashboard/static/js/components/connection-debug.js +101 -101
  225. claude_mpm/dashboard/static/js/components/diff-viewer.js +891 -0
  226. claude_mpm/dashboard/static/js/components/event-processor.js +3 -0
  227. claude_mpm/dashboard/static/js/components/event-viewer.js +50 -13
  228. claude_mpm/dashboard/static/js/components/export-manager.js +3 -0
  229. claude_mpm/dashboard/static/js/components/file-change-tracker.js +443 -0
  230. claude_mpm/dashboard/static/js/components/file-change-viewer.js +690 -0
  231. claude_mpm/dashboard/static/js/components/file-tool-tracker.js +36 -16
  232. claude_mpm/dashboard/static/js/components/file-viewer.js +580 -0
  233. claude_mpm/dashboard/static/js/components/module-viewer.js +49 -23
  234. claude_mpm/dashboard/static/js/components/session-manager.js +19 -19
  235. claude_mpm/dashboard/static/js/components/socket-manager.js +5 -1
  236. claude_mpm/dashboard/static/js/components/ui-state-manager.js +356 -41
  237. claude_mpm/dashboard/static/js/components/unified-data-viewer.js +520 -88
  238. claude_mpm/dashboard/static/js/components/working-directory.js +46 -11
  239. claude_mpm/dashboard/static/js/connection-manager.js +76 -76
  240. claude_mpm/dashboard/static/js/dashboard.js +309 -178
  241. claude_mpm/dashboard/static/js/extension-error-handler.js +22 -22
  242. claude_mpm/dashboard/static/js/socket-client.js +183 -139
  243. claude_mpm/dashboard/static/js/tab-isolation-fix.js +185 -0
  244. claude_mpm/dashboard/static/socket.io.min.js +7 -0
  245. claude_mpm/dashboard/static/socket.io.v4.8.1.backup.js +7 -0
  246. claude_mpm/dashboard/templates/code_simple.html +153 -0
  247. claude_mpm/dashboard/templates/index.html +125 -122
  248. claude_mpm/experimental/cli_enhancements.py +5 -7
  249. claude_mpm/generators/agent_profile_generator.py +5 -3
  250. claude_mpm/hooks/__init__.py +37 -1
  251. claude_mpm/hooks/base_hook.py +5 -4
  252. claude_mpm/hooks/claude_hooks/__pycache__/__init__.cpython-313.pyc +0 -0
  253. claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-313.pyc +0 -0
  254. claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-313.pyc +0 -0
  255. claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-313.pyc +0 -0
  256. claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-313.pyc +0 -0
  257. claude_mpm/hooks/claude_hooks/__pycache__/tool_analysis.cpython-313.pyc +0 -0
  258. claude_mpm/hooks/claude_hooks/connection_pool.py +4 -4
  259. claude_mpm/hooks/claude_hooks/event_handlers.py +24 -19
  260. claude_mpm/hooks/claude_hooks/hook_handler.py +29 -22
  261. claude_mpm/hooks/claude_hooks/installer.py +67 -22
  262. claude_mpm/hooks/claude_hooks/memory_integration.py +3 -3
  263. claude_mpm/hooks/claude_hooks/response_tracking.py +57 -17
  264. claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-313.pyc +0 -0
  265. claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-313.pyc +0 -0
  266. claude_mpm/hooks/claude_hooks/services/__pycache__/duplicate_detector.cpython-313.pyc +0 -0
  267. claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-313.pyc +0 -0
  268. claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-313.pyc +0 -0
  269. claude_mpm/hooks/claude_hooks/services/connection_manager.py +62 -64
  270. claude_mpm/hooks/claude_hooks/services/connection_manager_http.py +140 -76
  271. claude_mpm/hooks/claude_hooks/services/state_manager.py +11 -9
  272. claude_mpm/hooks/claude_hooks/services/subagent_processor.py +3 -3
  273. claude_mpm/hooks/failure_learning/__init__.py +54 -0
  274. claude_mpm/hooks/failure_learning/failure_detection_hook.py +230 -0
  275. claude_mpm/hooks/failure_learning/fix_detection_hook.py +212 -0
  276. claude_mpm/hooks/failure_learning/learning_extraction_hook.py +281 -0
  277. claude_mpm/hooks/instruction_reinforcement.py +301 -0
  278. claude_mpm/hooks/kuzu_enrichment_hook.py +263 -0
  279. claude_mpm/hooks/kuzu_memory_hook.py +386 -0
  280. claude_mpm/hooks/kuzu_response_hook.py +179 -0
  281. claude_mpm/hooks/memory_integration_hook.py +1 -1
  282. claude_mpm/hooks/session_resume_hook.py +121 -0
  283. claude_mpm/hooks/templates/pre_tool_use_simple.py +78 -0
  284. claude_mpm/hooks/templates/pre_tool_use_template.py +323 -0
  285. claude_mpm/hooks/tool_call_interceptor.py +8 -5
  286. claude_mpm/hooks/validation_hooks.py +3 -3
  287. claude_mpm/init.py +23 -4
  288. claude_mpm/models/agent_session.py +8 -6
  289. claude_mpm/models/git_repository.py +198 -0
  290. claude_mpm/models/resume_log.py +340 -0
  291. claude_mpm/scripts/claude-hook-handler.sh +35 -9
  292. claude_mpm/scripts/launch_monitor.py +85 -0
  293. claude_mpm/scripts/mcp_server.py +3 -5
  294. claude_mpm/scripts/mpm_doctor.py +3 -2
  295. claude_mpm/scripts/socketio_daemon.py +159 -512
  296. claude_mpm/scripts/start_activity_logging.py +3 -1
  297. claude_mpm/services/__init__.py +144 -160
  298. claude_mpm/services/agents/__init__.py +18 -5
  299. claude_mpm/services/agents/agent_builder.py +56 -18
  300. claude_mpm/services/agents/agent_preset_service.py +238 -0
  301. claude_mpm/services/agents/agent_selection_service.py +484 -0
  302. claude_mpm/services/agents/auto_config_manager.py +796 -0
  303. claude_mpm/services/agents/auto_deploy_index_parser.py +569 -0
  304. claude_mpm/services/agents/cache_git_manager.py +621 -0
  305. claude_mpm/services/agents/deployment/agent_configuration_manager.py +1 -1
  306. claude_mpm/services/agents/deployment/agent_deployment.py +164 -17
  307. claude_mpm/services/agents/deployment/agent_discovery_service.py +191 -41
  308. claude_mpm/services/agents/deployment/agent_filesystem_manager.py +5 -5
  309. claude_mpm/services/agents/deployment/agent_format_converter.py +56 -12
  310. claude_mpm/services/agents/deployment/agent_lifecycle_manager.py +5 -7
  311. claude_mpm/services/agents/deployment/agent_metrics_collector.py +3 -3
  312. claude_mpm/services/agents/deployment/agent_operation_service.py +2 -2
  313. claude_mpm/services/agents/deployment/agent_record_service.py +4 -4
  314. claude_mpm/services/agents/deployment/agent_restore_handler.py +1 -4
  315. claude_mpm/services/agents/deployment/agent_state_service.py +2 -2
  316. claude_mpm/services/agents/deployment/agent_template_builder.py +939 -50
  317. claude_mpm/services/agents/deployment/agent_validator.py +31 -7
  318. claude_mpm/services/agents/deployment/agent_version_manager.py +8 -5
  319. claude_mpm/services/agents/deployment/agent_versioning.py +1 -1
  320. claude_mpm/services/agents/deployment/agents_directory_resolver.py +101 -15
  321. claude_mpm/services/agents/deployment/async_agent_deployment.py +3 -2
  322. claude_mpm/services/agents/deployment/deployment_config_loader.py +131 -7
  323. claude_mpm/services/agents/deployment/deployment_type_detector.py +10 -14
  324. claude_mpm/services/agents/deployment/deployment_wrapper.py +58 -0
  325. claude_mpm/services/agents/deployment/facade/deployment_facade.py +3 -3
  326. claude_mpm/services/agents/deployment/interface_adapter.py +3 -2
  327. claude_mpm/services/agents/deployment/local_template_deployment.py +360 -0
  328. claude_mpm/services/agents/deployment/multi_source_deployment_service.py +249 -53
  329. claude_mpm/services/agents/deployment/pipeline/pipeline_executor.py +2 -2
  330. claude_mpm/services/agents/deployment/pipeline/steps/agent_processing_step.py +8 -7
  331. claude_mpm/services/agents/deployment/pipeline/steps/base_step.py +7 -16
  332. claude_mpm/services/agents/deployment/pipeline/steps/configuration_step.py +4 -3
  333. claude_mpm/services/agents/deployment/pipeline/steps/target_directory_step.py +7 -5
  334. claude_mpm/services/agents/deployment/pipeline/steps/validation_step.py +6 -5
  335. claude_mpm/services/agents/deployment/refactored_agent_deployment_service.py +10 -10
  336. claude_mpm/services/agents/deployment/remote_agent_discovery_service.py +363 -0
  337. claude_mpm/services/agents/deployment/single_agent_deployer.py +2 -2
  338. claude_mpm/services/agents/deployment/system_instructions_deployer.py +168 -43
  339. claude_mpm/services/agents/deployment/validation/__init__.py +3 -1
  340. claude_mpm/services/agents/deployment/validation/deployment_validator.py +2 -2
  341. claude_mpm/services/agents/deployment/validation/template_validator.py +64 -44
  342. claude_mpm/services/agents/deployment/validation/validation_result.py +1 -9
  343. claude_mpm/services/agents/git_source_manager.py +629 -0
  344. claude_mpm/services/agents/loading/agent_profile_loader.py +10 -9
  345. claude_mpm/services/agents/loading/base_agent_manager.py +16 -6
  346. claude_mpm/services/agents/loading/framework_agent_loader.py +11 -14
  347. claude_mpm/services/agents/local_template_manager.py +784 -0
  348. claude_mpm/services/agents/management/agent_capabilities_generator.py +3 -2
  349. claude_mpm/services/agents/management/agent_management_service.py +5 -5
  350. claude_mpm/services/agents/memory/agent_memory_manager.py +32 -29
  351. claude_mpm/services/agents/memory/content_manager.py +17 -9
  352. claude_mpm/services/agents/memory/memory_categorization_service.py +4 -2
  353. claude_mpm/services/agents/memory/memory_file_service.py +32 -6
  354. claude_mpm/services/agents/memory/memory_format_service.py +6 -4
  355. claude_mpm/services/agents/memory/memory_limits_service.py +4 -2
  356. claude_mpm/services/agents/memory/template_generator.py +3 -3
  357. claude_mpm/services/agents/observers.py +547 -0
  358. claude_mpm/services/agents/recommender.py +615 -0
  359. claude_mpm/services/agents/registry/deployed_agent_discovery.py +3 -3
  360. claude_mpm/services/agents/registry/modification_tracker.py +30 -19
  361. claude_mpm/services/agents/single_tier_deployment_service.py +696 -0
  362. claude_mpm/services/agents/sources/__init__.py +13 -0
  363. claude_mpm/services/agents/sources/agent_sync_state.py +516 -0
  364. claude_mpm/services/agents/sources/git_source_sync_service.py +1087 -0
  365. claude_mpm/services/agents/startup_sync.py +239 -0
  366. claude_mpm/services/agents/toolchain_detector.py +474 -0
  367. claude_mpm/services/analysis/__init__.py +25 -0
  368. claude_mpm/services/analysis/postmortem_reporter.py +474 -0
  369. claude_mpm/services/analysis/postmortem_service.py +765 -0
  370. claude_mpm/services/async_session_logger.py +141 -98
  371. claude_mpm/services/claude_session_logger.py +82 -74
  372. claude_mpm/services/cli/agent_cleanup_service.py +5 -0
  373. claude_mpm/services/cli/agent_listing_service.py +5 -5
  374. claude_mpm/services/cli/agent_validation_service.py +3 -1
  375. claude_mpm/services/cli/memory_crud_service.py +12 -7
  376. claude_mpm/services/cli/memory_output_formatter.py +2 -2
  377. claude_mpm/services/cli/resume_service.py +617 -0
  378. claude_mpm/services/cli/session_manager.py +104 -13
  379. claude_mpm/services/cli/session_pause_manager.py +504 -0
  380. claude_mpm/services/cli/session_resume_helper.py +372 -0
  381. claude_mpm/services/cli/startup_checker.py +13 -10
  382. claude_mpm/services/cli/unified_dashboard_manager.py +439 -0
  383. claude_mpm/services/command_deployment_service.py +209 -13
  384. claude_mpm/services/command_handler_service.py +11 -5
  385. claude_mpm/services/core/__init__.py +33 -1
  386. claude_mpm/services/core/base.py +31 -11
  387. claude_mpm/services/core/interfaces/__init__.py +88 -3
  388. claude_mpm/services/core/interfaces/agent.py +184 -0
  389. claude_mpm/services/core/interfaces/health.py +169 -0
  390. claude_mpm/services/core/interfaces/model.py +281 -0
  391. claude_mpm/services/core/interfaces/process.py +372 -0
  392. claude_mpm/services/core/interfaces/project.py +121 -0
  393. claude_mpm/services/core/interfaces/restart.py +307 -0
  394. claude_mpm/services/core/interfaces/stability.py +260 -0
  395. claude_mpm/services/core/interfaces.py +56 -1
  396. claude_mpm/services/core/memory_manager.py +92 -47
  397. claude_mpm/services/core/models/__init__.py +70 -0
  398. claude_mpm/services/core/models/agent_config.py +384 -0
  399. claude_mpm/services/core/models/health.py +162 -0
  400. claude_mpm/services/core/models/process.py +239 -0
  401. claude_mpm/services/core/models/restart.py +302 -0
  402. claude_mpm/services/core/models/stability.py +264 -0
  403. claude_mpm/services/core/models/toolchain.py +306 -0
  404. claude_mpm/services/core/path_resolver.py +36 -14
  405. claude_mpm/services/diagnostics/__init__.py +2 -2
  406. claude_mpm/services/diagnostics/checks/__init__.py +8 -2
  407. claude_mpm/services/diagnostics/checks/agent_check.py +30 -34
  408. claude_mpm/services/diagnostics/checks/agent_sources_check.py +577 -0
  409. claude_mpm/services/diagnostics/checks/claude_code_check.py +270 -0
  410. claude_mpm/services/diagnostics/checks/common_issues_check.py +28 -27
  411. claude_mpm/services/diagnostics/checks/configuration_check.py +26 -25
  412. claude_mpm/services/diagnostics/checks/filesystem_check.py +18 -17
  413. claude_mpm/services/diagnostics/checks/installation_check.py +165 -60
  414. claude_mpm/services/diagnostics/checks/instructions_check.py +21 -21
  415. claude_mpm/services/diagnostics/checks/mcp_check.py +57 -44
  416. claude_mpm/services/diagnostics/checks/mcp_services_check.py +1058 -0
  417. claude_mpm/services/diagnostics/checks/monitor_check.py +24 -24
  418. claude_mpm/services/diagnostics/checks/skill_sources_check.py +587 -0
  419. claude_mpm/services/diagnostics/checks/startup_log_check.py +14 -11
  420. claude_mpm/services/diagnostics/diagnostic_runner.py +31 -13
  421. claude_mpm/services/diagnostics/doctor_reporter.py +305 -47
  422. claude_mpm/services/diagnostics/models.py +37 -21
  423. claude_mpm/services/event_aggregator.py +5 -3
  424. claude_mpm/services/event_bus/direct_relay.py +11 -7
  425. claude_mpm/services/event_bus/event_bus.py +51 -9
  426. claude_mpm/services/event_bus/relay.py +33 -14
  427. claude_mpm/services/events/consumers/dead_letter.py +7 -5
  428. claude_mpm/services/events/consumers/logging.py +1 -2
  429. claude_mpm/services/events/core.py +5 -6
  430. claude_mpm/services/events/producers/hook.py +6 -6
  431. claude_mpm/services/events/producers/system.py +8 -8
  432. claude_mpm/services/exceptions.py +5 -5
  433. claude_mpm/services/framework_claude_md_generator/__init__.py +1 -1
  434. claude_mpm/services/framework_claude_md_generator/content_assembler.py +5 -5
  435. claude_mpm/services/framework_claude_md_generator/content_validator.py +2 -2
  436. claude_mpm/services/framework_claude_md_generator/deployment_manager.py +3 -3
  437. claude_mpm/services/framework_claude_md_generator/section_generators/__init__.py +2 -2
  438. claude_mpm/services/framework_claude_md_generator/version_manager.py +1 -1
  439. claude_mpm/services/git/__init__.py +21 -0
  440. claude_mpm/services/git/git_operations_service.py +494 -0
  441. claude_mpm/services/github/__init__.py +21 -0
  442. claude_mpm/services/github/github_cli_service.py +397 -0
  443. claude_mpm/services/hook_installer_service.py +506 -0
  444. claude_mpm/services/hook_service.py +5 -6
  445. claude_mpm/services/infrastructure/context_preservation.py +13 -11
  446. claude_mpm/services/infrastructure/daemon_manager.py +9 -9
  447. claude_mpm/services/infrastructure/logging.py +2 -2
  448. claude_mpm/services/infrastructure/monitoring/__init__.py +2 -6
  449. claude_mpm/services/infrastructure/monitoring/aggregator.py +13 -18
  450. claude_mpm/services/infrastructure/monitoring/base.py +5 -13
  451. claude_mpm/services/infrastructure/monitoring/network.py +7 -6
  452. claude_mpm/services/infrastructure/monitoring/process.py +13 -12
  453. claude_mpm/services/infrastructure/monitoring/resources.py +8 -7
  454. claude_mpm/services/infrastructure/monitoring/service.py +16 -15
  455. claude_mpm/services/infrastructure/resume_log_generator.py +439 -0
  456. claude_mpm/services/instructions/__init__.py +9 -0
  457. claude_mpm/services/instructions/instruction_cache_service.py +374 -0
  458. claude_mpm/services/local_ops/__init__.py +155 -0
  459. claude_mpm/services/local_ops/crash_detector.py +257 -0
  460. claude_mpm/services/local_ops/health_checks/__init__.py +26 -0
  461. claude_mpm/services/local_ops/health_checks/http_check.py +224 -0
  462. claude_mpm/services/local_ops/health_checks/process_check.py +236 -0
  463. claude_mpm/services/local_ops/health_checks/resource_check.py +255 -0
  464. claude_mpm/services/local_ops/health_manager.py +427 -0
  465. claude_mpm/services/local_ops/log_monitor.py +396 -0
  466. claude_mpm/services/local_ops/memory_leak_detector.py +294 -0
  467. claude_mpm/services/local_ops/process_manager.py +595 -0
  468. claude_mpm/services/local_ops/resource_monitor.py +331 -0
  469. claude_mpm/services/local_ops/restart_manager.py +401 -0
  470. claude_mpm/services/local_ops/restart_policy.py +387 -0
  471. claude_mpm/services/local_ops/state_manager.py +372 -0
  472. claude_mpm/services/local_ops/unified_manager.py +600 -0
  473. claude_mpm/services/mcp_config_manager.py +1542 -0
  474. claude_mpm/services/mcp_gateway/__init__.py +97 -93
  475. claude_mpm/services/mcp_gateway/auto_configure.py +43 -38
  476. claude_mpm/services/mcp_gateway/config/config_loader.py +3 -3
  477. claude_mpm/services/mcp_gateway/config/configuration.py +24 -5
  478. claude_mpm/services/mcp_gateway/core/__init__.py +1 -2
  479. claude_mpm/services/mcp_gateway/core/base.py +20 -33
  480. claude_mpm/services/mcp_gateway/core/process_pool.py +591 -31
  481. claude_mpm/services/mcp_gateway/core/singleton_manager.py +2 -2
  482. claude_mpm/services/mcp_gateway/core/startup_verification.py +3 -3
  483. claude_mpm/services/mcp_gateway/main.py +90 -15
  484. claude_mpm/services/mcp_gateway/registry/service_registry.py +4 -2
  485. claude_mpm/services/mcp_gateway/registry/tool_registry.py +12 -9
  486. claude_mpm/services/mcp_gateway/server/mcp_gateway.py +5 -10
  487. claude_mpm/services/mcp_gateway/server/stdio_server.py +9 -17
  488. claude_mpm/services/mcp_gateway/tools/__init__.py +14 -2
  489. claude_mpm/services/mcp_gateway/tools/base_adapter.py +15 -15
  490. claude_mpm/services/mcp_gateway/tools/document_summarizer.py +10 -9
  491. claude_mpm/services/mcp_gateway/tools/external_mcp_services.py +654 -0
  492. claude_mpm/services/mcp_gateway/tools/health_check_tool.py +36 -34
  493. claude_mpm/services/mcp_gateway/tools/hello_world.py +8 -8
  494. claude_mpm/services/mcp_gateway/tools/kuzu_memory_service.py +555 -0
  495. claude_mpm/services/mcp_gateway/utils/__init__.py +14 -0
  496. claude_mpm/services/mcp_gateway/utils/package_version_checker.py +160 -0
  497. claude_mpm/services/mcp_gateway/utils/update_preferences.py +170 -0
  498. claude_mpm/services/mcp_service_verifier.py +732 -0
  499. claude_mpm/services/memory/builder.py +9 -8
  500. claude_mpm/services/memory/cache/shared_prompt_cache.py +2 -1
  501. claude_mpm/services/memory/cache/simple_cache.py +2 -2
  502. claude_mpm/services/memory/failure_tracker.py +578 -0
  503. claude_mpm/services/memory/indexed_memory.py +8 -8
  504. claude_mpm/services/memory/optimizer.py +8 -9
  505. claude_mpm/services/memory/router.py +3 -3
  506. claude_mpm/services/memory_hook_service.py +165 -4
  507. claude_mpm/services/model/__init__.py +147 -0
  508. claude_mpm/services/model/base_provider.py +365 -0
  509. claude_mpm/services/model/claude_provider.py +412 -0
  510. claude_mpm/services/model/model_router.py +452 -0
  511. claude_mpm/services/model/ollama_provider.py +415 -0
  512. claude_mpm/services/monitor/__init__.py +20 -0
  513. claude_mpm/services/monitor/daemon.py +691 -0
  514. claude_mpm/services/monitor/daemon_manager.py +1040 -0
  515. claude_mpm/services/monitor/event_emitter.py +350 -0
  516. claude_mpm/services/monitor/handlers/__init__.py +21 -0
  517. claude_mpm/services/monitor/handlers/code_analysis.py +332 -0
  518. claude_mpm/services/monitor/handlers/dashboard.py +299 -0
  519. claude_mpm/services/monitor/handlers/file.py +264 -0
  520. claude_mpm/services/monitor/handlers/hooks.py +512 -0
  521. claude_mpm/services/monitor/management/__init__.py +18 -0
  522. claude_mpm/services/monitor/management/health.py +124 -0
  523. claude_mpm/services/monitor/management/lifecycle.py +724 -0
  524. claude_mpm/services/monitor/server.py +817 -0
  525. claude_mpm/services/monitor_build_service.py +2 -2
  526. claude_mpm/services/native_agent_converter.py +356 -0
  527. claude_mpm/services/orphan_detection.py +786 -0
  528. claude_mpm/services/port_manager.py +3 -3
  529. claude_mpm/services/pr/__init__.py +14 -0
  530. claude_mpm/services/pr/pr_template_service.py +329 -0
  531. claude_mpm/services/project/__init__.py +23 -0
  532. claude_mpm/services/project/analyzer.py +3 -3
  533. claude_mpm/services/project/architecture_analyzer.py +5 -5
  534. claude_mpm/services/project/archive_manager.py +1045 -0
  535. claude_mpm/services/project/dependency_analyzer.py +4 -4
  536. claude_mpm/services/project/detection_strategies.py +719 -0
  537. claude_mpm/services/project/documentation_manager.py +554 -0
  538. claude_mpm/services/project/enhanced_analyzer.py +572 -0
  539. claude_mpm/services/project/metrics_collector.py +4 -4
  540. claude_mpm/services/project/project_organizer.py +1005 -0
  541. claude_mpm/services/project/registry.py +13 -7
  542. claude_mpm/services/project/toolchain_analyzer.py +583 -0
  543. claude_mpm/services/project_port_allocator.py +596 -0
  544. claude_mpm/services/response_tracker.py +21 -10
  545. claude_mpm/services/runner_configuration_service.py +17 -3
  546. claude_mpm/services/self_upgrade_service.py +500 -0
  547. claude_mpm/services/session_management_service.py +23 -9
  548. claude_mpm/services/session_manager.py +380 -0
  549. claude_mpm/services/shared/__init__.py +2 -1
  550. claude_mpm/services/shared/async_service_base.py +16 -27
  551. claude_mpm/services/shared/config_service_base.py +17 -14
  552. claude_mpm/services/shared/lifecycle_service_base.py +1 -14
  553. claude_mpm/services/shared/service_factory.py +8 -5
  554. claude_mpm/services/skills/__init__.py +18 -0
  555. claude_mpm/services/skills/git_skill_source_manager.py +1169 -0
  556. claude_mpm/services/skills/skill_discovery_service.py +568 -0
  557. claude_mpm/services/skills_config.py +547 -0
  558. claude_mpm/services/skills_deployer.py +955 -0
  559. claude_mpm/services/socketio/client_proxy.py +60 -5
  560. claude_mpm/services/socketio/dashboard_server.py +361 -0
  561. claude_mpm/services/socketio/event_normalizer.py +10 -6
  562. claude_mpm/services/socketio/handlers/__init__.py +5 -2
  563. claude_mpm/services/socketio/handlers/base.py +2 -2
  564. claude_mpm/services/socketio/handlers/code_analysis.py +90 -27
  565. claude_mpm/services/socketio/handlers/connection.py +22 -41
  566. claude_mpm/services/socketio/handlers/connection_handler.py +13 -10
  567. claude_mpm/services/socketio/handlers/file.py +46 -10
  568. claude_mpm/services/socketio/handlers/git.py +9 -9
  569. claude_mpm/services/socketio/handlers/hook.py +29 -17
  570. claude_mpm/services/socketio/handlers/registry.py +4 -2
  571. claude_mpm/services/socketio/monitor_client.py +364 -0
  572. claude_mpm/services/socketio/server/broadcaster.py +9 -7
  573. claude_mpm/services/socketio/server/connection_manager.py +2 -2
  574. claude_mpm/services/socketio/server/core.py +142 -8
  575. claude_mpm/services/socketio/server/eventbus_integration.py +20 -14
  576. claude_mpm/services/socketio/server/main.py +24 -24
  577. claude_mpm/services/socketio_client_manager.py +4 -4
  578. claude_mpm/services/subprocess_launcher_service.py +19 -15
  579. claude_mpm/services/system_instructions_service.py +3 -5
  580. claude_mpm/services/ticket_services/formatter_service.py +1 -1
  581. claude_mpm/services/ticket_services/validation_service.py +5 -5
  582. claude_mpm/services/unified/__init__.py +65 -0
  583. claude_mpm/services/unified/analyzer_strategies/__init__.py +44 -0
  584. claude_mpm/services/unified/analyzer_strategies/code_analyzer.py +518 -0
  585. claude_mpm/services/unified/analyzer_strategies/dependency_analyzer.py +680 -0
  586. claude_mpm/services/unified/analyzer_strategies/performance_analyzer.py +900 -0
  587. claude_mpm/services/unified/analyzer_strategies/security_analyzer.py +745 -0
  588. claude_mpm/services/unified/analyzer_strategies/structure_analyzer.py +733 -0
  589. claude_mpm/services/unified/config_strategies/__init__.py +175 -0
  590. claude_mpm/services/unified/config_strategies/config_schema.py +731 -0
  591. claude_mpm/services/unified/config_strategies/context_strategy.py +747 -0
  592. claude_mpm/services/unified/config_strategies/error_handling_strategy.py +1005 -0
  593. claude_mpm/services/unified/config_strategies/file_loader_strategy.py +881 -0
  594. claude_mpm/services/unified/config_strategies/unified_config_service.py +823 -0
  595. claude_mpm/services/unified/config_strategies/validation_strategy.py +1148 -0
  596. claude_mpm/services/unified/deployment_strategies/__init__.py +97 -0
  597. claude_mpm/services/unified/deployment_strategies/base.py +553 -0
  598. claude_mpm/services/unified/deployment_strategies/cloud_strategies.py +573 -0
  599. claude_mpm/services/unified/deployment_strategies/local.py +607 -0
  600. claude_mpm/services/unified/deployment_strategies/utils.py +667 -0
  601. claude_mpm/services/unified/deployment_strategies/vercel.py +471 -0
  602. claude_mpm/services/unified/interfaces.py +475 -0
  603. claude_mpm/services/unified/migration.py +509 -0
  604. claude_mpm/services/unified/strategies.py +534 -0
  605. claude_mpm/services/unified/unified_analyzer.py +542 -0
  606. claude_mpm/services/unified/unified_config.py +691 -0
  607. claude_mpm/services/unified/unified_deployment.py +466 -0
  608. claude_mpm/services/utility_service.py +6 -3
  609. claude_mpm/services/version_control/branch_strategy.py +2 -2
  610. claude_mpm/services/version_control/conflict_resolution.py +14 -8
  611. claude_mpm/services/version_control/git_operations.py +26 -24
  612. claude_mpm/services/version_control/semantic_versioning.py +14 -14
  613. claude_mpm/services/version_control/version_parser.py +14 -11
  614. claude_mpm/services/version_service.py +104 -1
  615. claude_mpm/services/visualization/__init__.py +1 -5
  616. claude_mpm/services/visualization/mermaid_generator.py +2 -3
  617. claude_mpm/skills/__init__.py +42 -0
  618. claude_mpm/skills/agent_skills_injector.py +324 -0
  619. claude_mpm/skills/bundled/LICENSE_ATTRIBUTIONS.md +79 -0
  620. claude_mpm/skills/bundled/__init__.py +6 -0
  621. claude_mpm/skills/bundled/api-documentation.md +393 -0
  622. claude_mpm/skills/bundled/async-testing.md +571 -0
  623. claude_mpm/skills/bundled/code-review.md +143 -0
  624. claude_mpm/skills/bundled/database-migration.md +199 -0
  625. claude_mpm/skills/bundled/docker-containerization.md +194 -0
  626. claude_mpm/skills/bundled/express-local-dev.md +1429 -0
  627. claude_mpm/skills/bundled/fastapi-local-dev.md +1199 -0
  628. claude_mpm/skills/bundled/git-workflow.md +414 -0
  629. claude_mpm/skills/bundled/imagemagick.md +204 -0
  630. claude_mpm/skills/bundled/infrastructure/env-manager/scripts/validate_env.py +576 -0
  631. claude_mpm/skills/bundled/json-data-handling.md +223 -0
  632. claude_mpm/skills/bundled/main/mcp-builder/scripts/connections.py +157 -0
  633. claude_mpm/skills/bundled/main/mcp-builder/scripts/evaluation.py +425 -0
  634. claude_mpm/skills/bundled/main/skill-creator/scripts/init_skill.py +303 -0
  635. claude_mpm/skills/bundled/main/skill-creator/scripts/package_skill.py +113 -0
  636. claude_mpm/skills/bundled/main/skill-creator/scripts/quick_validate.py +72 -0
  637. claude_mpm/skills/bundled/nextjs-local-dev.md +807 -0
  638. claude_mpm/skills/bundled/pdf.md +141 -0
  639. claude_mpm/skills/bundled/performance-profiling.md +573 -0
  640. claude_mpm/skills/bundled/refactoring-patterns.md +180 -0
  641. claude_mpm/skills/bundled/security-scanning.md +327 -0
  642. claude_mpm/skills/bundled/systematic-debugging.md +473 -0
  643. claude_mpm/skills/bundled/test-driven-development.md +378 -0
  644. claude_mpm/skills/bundled/testing/webapp-testing/examples/console_logging.py +35 -0
  645. claude_mpm/skills/bundled/testing/webapp-testing/examples/element_discovery.py +44 -0
  646. claude_mpm/skills/bundled/testing/webapp-testing/examples/static_html_automation.py +34 -0
  647. claude_mpm/skills/bundled/testing/webapp-testing/scripts/with_server.py +129 -0
  648. claude_mpm/skills/bundled/vite-local-dev.md +1061 -0
  649. claude_mpm/skills/bundled/web-performance-optimization.md +2305 -0
  650. claude_mpm/skills/bundled/xlsx.md +157 -0
  651. claude_mpm/skills/registry.py +286 -0
  652. claude_mpm/skills/skill_manager.py +310 -0
  653. claude_mpm/skills/skills_registry.py +347 -0
  654. claude_mpm/skills/skills_service.py +739 -0
  655. claude_mpm/storage/state_storage.py +31 -31
  656. claude_mpm/templates/questions/__init__.py +38 -0
  657. claude_mpm/templates/questions/base.py +193 -0
  658. claude_mpm/templates/questions/pr_strategy.py +311 -0
  659. claude_mpm/templates/questions/project_init.py +385 -0
  660. claude_mpm/templates/questions/ticket_mgmt.py +394 -0
  661. claude_mpm/tools/__main__.py +9 -9
  662. claude_mpm/tools/code_tree_analyzer/__init__.py +45 -0
  663. claude_mpm/tools/code_tree_analyzer/analysis.py +299 -0
  664. claude_mpm/tools/code_tree_analyzer/cache.py +131 -0
  665. claude_mpm/tools/code_tree_analyzer/core.py +380 -0
  666. claude_mpm/tools/code_tree_analyzer/discovery.py +403 -0
  667. claude_mpm/tools/code_tree_analyzer/events.py +168 -0
  668. claude_mpm/tools/code_tree_analyzer/gitignore.py +308 -0
  669. claude_mpm/tools/code_tree_analyzer/models.py +39 -0
  670. claude_mpm/tools/code_tree_analyzer/multilang_analyzer.py +224 -0
  671. claude_mpm/tools/code_tree_analyzer/python_analyzer.py +284 -0
  672. claude_mpm/tools/code_tree_builder.py +6 -6
  673. claude_mpm/tools/code_tree_events.py +14 -10
  674. claude_mpm/tools/socketio_debug.py +11 -11
  675. claude_mpm/utils/agent_dependency_loader.py +184 -36
  676. claude_mpm/utils/agent_filters.py +288 -0
  677. claude_mpm/utils/common.py +544 -0
  678. claude_mpm/utils/config_manager.py +12 -6
  679. claude_mpm/utils/database_connector.py +298 -0
  680. claude_mpm/utils/dependency_cache.py +5 -3
  681. claude_mpm/utils/dependency_strategies.py +15 -10
  682. claude_mpm/utils/display_helper.py +260 -0
  683. claude_mpm/utils/environment_context.py +4 -3
  684. claude_mpm/utils/error_handler.py +5 -3
  685. claude_mpm/utils/file_utils.py +13 -14
  686. claude_mpm/utils/git_analyzer.py +407 -0
  687. claude_mpm/utils/gitignore.py +241 -0
  688. claude_mpm/utils/log_cleanup.py +627 -0
  689. claude_mpm/utils/migration.py +372 -0
  690. claude_mpm/utils/path_operations.py +7 -4
  691. claude_mpm/utils/progress.py +387 -0
  692. claude_mpm/utils/robust_installer.py +131 -24
  693. claude_mpm/utils/session_logging.py +2 -2
  694. claude_mpm/utils/structured_questions.py +619 -0
  695. claude_mpm/utils/subprocess_utils.py +9 -8
  696. claude_mpm/validation/agent_validator.py +6 -6
  697. claude_mpm/validation/frontmatter_validator.py +6 -6
  698. claude_mpm-5.0.9.dist-info/METADATA +1028 -0
  699. claude_mpm-5.0.9.dist-info/RECORD +864 -0
  700. {claude_mpm-4.1.26.dist-info → claude_mpm-5.0.9.dist-info}/entry_points.txt +1 -0
  701. claude_mpm/agents/INSTRUCTIONS.md +0 -261
  702. claude_mpm/agents/templates/.claude-mpm/memories/README.md +0 -17
  703. claude_mpm/agents/templates/.claude-mpm/memories/engineer_memories.md +0 -3
  704. claude_mpm/agents/templates/agent-manager.json +0 -270
  705. claude_mpm/agents/templates/agent-manager.md +0 -619
  706. claude_mpm/agents/templates/agentic_coder_optimizer.json +0 -222
  707. claude_mpm/agents/templates/api_qa.json +0 -171
  708. claude_mpm/agents/templates/code_analyzer.json +0 -95
  709. claude_mpm/agents/templates/data_engineer.json +0 -152
  710. claude_mpm/agents/templates/documentation.json +0 -175
  711. claude_mpm/agents/templates/engineer.json +0 -176
  712. claude_mpm/agents/templates/imagemagick.json +0 -261
  713. claude_mpm/agents/templates/logs/prompts/agent_engineer_20250826_014258_728.md +0 -39
  714. claude_mpm/agents/templates/memory_manager.json +0 -155
  715. claude_mpm/agents/templates/ops.json +0 -175
  716. claude_mpm/agents/templates/project_organizer.json +0 -130
  717. claude_mpm/agents/templates/qa.json +0 -223
  718. claude_mpm/agents/templates/refactoring_engineer.json +0 -266
  719. claude_mpm/agents/templates/research.json +0 -163
  720. claude_mpm/agents/templates/security.json +0 -153
  721. claude_mpm/agents/templates/ticketing.json +0 -169
  722. claude_mpm/agents/templates/vercel_ops_agent.json +0 -281
  723. claude_mpm/agents/templates/version_control.json +0 -147
  724. claude_mpm/agents/templates/web_qa.json +0 -254
  725. claude_mpm/agents/templates/web_ui.json +0 -176
  726. claude_mpm/cli/commands/configure_tui.py +0 -1927
  727. claude_mpm/cli/commands/mpm_init.py +0 -594
  728. claude_mpm/cli/commands/socketio_monitor.py +0 -233
  729. claude_mpm/commands/mpm-agents.md +0 -12
  730. claude_mpm/commands/mpm-config.md +0 -18
  731. claude_mpm/commands/mpm-tickets.md +0 -102
  732. claude_mpm/dashboard/.claude-mpm/socketio-instances.json +0 -1
  733. claude_mpm/dashboard/static/built/components/activity-tree.js +0 -2
  734. claude_mpm/dashboard/static/built/components/agent-inference.js +0 -2
  735. claude_mpm/dashboard/static/built/components/code-tree.js +0 -2
  736. claude_mpm/dashboard/static/built/components/code-viewer.js +0 -2
  737. claude_mpm/dashboard/static/built/components/event-processor.js +0 -2
  738. claude_mpm/dashboard/static/built/components/event-viewer.js +0 -2
  739. claude_mpm/dashboard/static/built/components/export-manager.js +0 -2
  740. claude_mpm/dashboard/static/built/components/file-tool-tracker.js +0 -2
  741. claude_mpm/dashboard/static/built/components/hud-library-loader.js +0 -2
  742. claude_mpm/dashboard/static/built/components/hud-manager.js +0 -2
  743. claude_mpm/dashboard/static/built/components/hud-visualizer.js +0 -2
  744. claude_mpm/dashboard/static/built/components/module-viewer.js +0 -2
  745. claude_mpm/dashboard/static/built/components/session-manager.js +0 -2
  746. claude_mpm/dashboard/static/built/components/socket-manager.js +0 -2
  747. claude_mpm/dashboard/static/built/components/ui-state-manager.js +0 -2
  748. claude_mpm/dashboard/static/built/components/unified-data-viewer.js +0 -2
  749. claude_mpm/dashboard/static/built/components/working-directory.js +0 -2
  750. claude_mpm/dashboard/static/built/dashboard.js +0 -2
  751. claude_mpm/dashboard/static/built/socket-client.js +0 -2
  752. claude_mpm/dashboard/static/css/code-tree.css +0 -1408
  753. claude_mpm/dashboard/static/dist/components/activity-tree.js +0 -2
  754. claude_mpm/dashboard/static/dist/components/agent-inference.js +0 -2
  755. claude_mpm/dashboard/static/dist/components/code-tree.js +0 -2
  756. claude_mpm/dashboard/static/dist/components/code-viewer.js +0 -2
  757. claude_mpm/dashboard/static/dist/components/event-processor.js +0 -2
  758. claude_mpm/dashboard/static/dist/components/event-viewer.js +0 -2
  759. claude_mpm/dashboard/static/dist/components/export-manager.js +0 -2
  760. claude_mpm/dashboard/static/dist/components/file-tool-tracker.js +0 -2
  761. claude_mpm/dashboard/static/dist/components/hud-library-loader.js +0 -2
  762. claude_mpm/dashboard/static/dist/components/hud-manager.js +0 -2
  763. claude_mpm/dashboard/static/dist/components/hud-visualizer.js +0 -2
  764. claude_mpm/dashboard/static/dist/components/module-viewer.js +0 -2
  765. claude_mpm/dashboard/static/dist/components/session-manager.js +0 -2
  766. claude_mpm/dashboard/static/dist/components/socket-manager.js +0 -2
  767. claude_mpm/dashboard/static/dist/components/ui-state-manager.js +0 -2
  768. claude_mpm/dashboard/static/dist/components/unified-data-viewer.js +0 -2
  769. claude_mpm/dashboard/static/dist/components/working-directory.js +0 -2
  770. claude_mpm/dashboard/static/dist/dashboard.js +0 -2
  771. claude_mpm/dashboard/static/dist/socket-client.js +0 -2
  772. claude_mpm/dashboard/static/js/components/code-tree.js +0 -3220
  773. claude_mpm/dashboard/static/js/components/code-viewer.js +0 -480
  774. claude_mpm/hooks/claude_hooks/hook_handler_eventbus.py +0 -425
  775. claude_mpm/hooks/claude_hooks/hook_handler_original.py +0 -1040
  776. claude_mpm/hooks/claude_hooks/hook_handler_refactored.py +0 -347
  777. claude_mpm/scripts/socketio_daemon_hardened.py +0 -937
  778. claude_mpm/scripts/socketio_daemon_wrapper.py +0 -78
  779. claude_mpm/scripts/socketio_server_manager.py +0 -349
  780. claude_mpm/services/agents/deployment/agent_lifecycle_manager_refactored.py +0 -575
  781. claude_mpm/services/cli/dashboard_launcher.py +0 -423
  782. claude_mpm/services/cli/socketio_manager.py +0 -537
  783. claude_mpm/services/diagnostics/checks/claude_desktop_check.py +0 -286
  784. claude_mpm/services/mcp_gateway/tools/ticket_tools.py +0 -645
  785. claude_mpm/services/mcp_gateway/tools/unified_ticket_tool.py +0 -602
  786. claude_mpm/services/project/analyzer_refactored.py +0 -450
  787. claude_mpm/tools/code_tree_analyzer.py +0 -1693
  788. claude_mpm-4.1.26.dist-info/METADATA +0 -332
  789. claude_mpm-4.1.26.dist-info/RECORD +0 -606
  790. {claude_mpm-4.1.26.dist-info → claude_mpm-5.0.9.dist-info}/WHEEL +0 -0
  791. {claude_mpm-4.1.26.dist-info → claude_mpm-5.0.9.dist-info}/licenses/LICENSE +0 -0
  792. {claude_mpm-4.1.26.dist-info → claude_mpm-5.0.9.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,1040 @@
1
+ """
2
+ Unified Daemon Manager Service
3
+ ==============================
4
+
5
+ WHY: This service consolidates ALL daemon lifecycle operations into a single place,
6
+ eliminating duplicate code and race conditions from having daemon management logic
7
+ scattered across multiple files.
8
+
9
+ DESIGN DECISIONS:
10
+ - Single source of truth for all daemon operations
11
+ - Robust port cleanup with retry logic
12
+ - Thread-safe operations with proper locking
13
+ - Comprehensive error handling and recovery
14
+ - Supports both foreground and background/daemon modes
15
+ - Manages PID files, port conflicts, and process lifecycle
16
+
17
+ This replaces duplicate logic that was in:
18
+ - UnifiedMonitorDaemon._cleanup_port_conflicts()
19
+ - UnifiedDashboardManager._cleanup_port_conflicts()
20
+ - Various daemon startup/stop logic spread across files
21
+ """
22
+
23
+ import os
24
+ import signal
25
+ import socket
26
+ import subprocess
27
+ import sys
28
+ import tempfile
29
+ import threading
30
+ import time
31
+ from pathlib import Path
32
+ from typing import Optional, Tuple
33
+
34
+ from ...core.enums import OperationResult
35
+ from ...core.logging_config import get_logger
36
+
37
+
38
+ class DaemonManager:
39
+ """Centralized manager for all daemon lifecycle operations.
40
+
41
+ This is the SINGLE source of truth for:
42
+ - Port conflict resolution
43
+ - Process cleanup
44
+ - Daemon startup/stop
45
+ - PID file management
46
+ - Service detection
47
+ """
48
+
49
+ # Class-level lock for thread safety
50
+ _lock = threading.Lock()
51
+
52
+ def __init__(
53
+ self,
54
+ port: int = 8765,
55
+ host: str = "localhost",
56
+ pid_file: Optional[str] = None,
57
+ log_file: Optional[str] = None,
58
+ ):
59
+ """Initialize the daemon manager.
60
+
61
+ Args:
62
+ port: Port number for the daemon
63
+ host: Host to bind to
64
+ pid_file: Path to PID file (uses default if None)
65
+ log_file: Path to log file for daemon mode
66
+ """
67
+ self.port = port
68
+ self.host = host
69
+ self.logger = get_logger(__name__)
70
+
71
+ # Set up paths
72
+ if pid_file:
73
+ self.pid_file = Path(pid_file)
74
+ else:
75
+ self.pid_file = self._get_default_pid_file()
76
+
77
+ self.log_file = Path(log_file) if log_file else self._get_default_log_file()
78
+
79
+ # Startup status communication
80
+ self.startup_status_file = None
81
+
82
+ def _get_default_pid_file(self) -> Path:
83
+ """Get default PID file path with port number to support multiple daemons."""
84
+ project_root = Path.cwd()
85
+ claude_mpm_dir = project_root / ".claude-mpm"
86
+ claude_mpm_dir.mkdir(exist_ok=True)
87
+ # Include port in filename to support multiple daemon instances
88
+ return claude_mpm_dir / f"monitor-daemon-{self.port}.pid"
89
+
90
+ def _get_default_log_file(self) -> Path:
91
+ """Get default log file path with port number to support multiple daemons."""
92
+ project_root = Path.cwd()
93
+ claude_mpm_dir = project_root / ".claude-mpm"
94
+ claude_mpm_dir.mkdir(exist_ok=True)
95
+ # Include port in filename to support multiple daemon instances
96
+ return claude_mpm_dir / f"monitor-daemon-{self.port}.log"
97
+
98
+ def cleanup_port_conflicts(self, max_retries: int = 3) -> bool:
99
+ """Clean up any processes using the daemon port.
100
+
101
+ This is the SINGLE implementation for port cleanup, replacing
102
+ duplicate logic in multiple files.
103
+
104
+ Args:
105
+ max_retries: Maximum number of cleanup attempts
106
+
107
+ Returns:
108
+ True if port is available after cleanup, False otherwise
109
+ """
110
+ with self._lock:
111
+ for attempt in range(max_retries):
112
+ if attempt > 0:
113
+ self.logger.info(
114
+ f"Port cleanup attempt {attempt + 1}/{max_retries}"
115
+ )
116
+
117
+ # First check if port is actually in use
118
+ if self._is_port_available():
119
+ self.logger.debug(f"Port {self.port} is available")
120
+ return True
121
+
122
+ self.logger.info(f"Port {self.port} is in use, attempting cleanup")
123
+
124
+ # Try to find and kill processes using the port
125
+ if self._kill_processes_on_port():
126
+ # Wait for port to be released
127
+ time.sleep(2 if attempt == 0 else 3)
128
+
129
+ # Verify port is now free
130
+ if self._is_port_available():
131
+ self.logger.info(f"Port {self.port} successfully cleaned up")
132
+ return True
133
+
134
+ if attempt < max_retries - 1:
135
+ # Wait longer between attempts
136
+ time.sleep(3)
137
+
138
+ self.logger.error(
139
+ f"Failed to clean up port {self.port} after {max_retries} attempts"
140
+ )
141
+ return False
142
+
143
+ def _is_port_available(self) -> bool:
144
+ """Check if the port is available for binding.
145
+
146
+ Returns:
147
+ True if port is available, False otherwise
148
+ """
149
+ # Try to bind to the port using the same method as the actual server
150
+ # We only need to check if we can bind to at least one address family
151
+ try:
152
+ # Try IPv4 first (most common)
153
+ test_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
154
+ test_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
155
+
156
+ # Use 127.0.0.1 for localhost to match what the server does
157
+ bind_host = "127.0.0.1" if self.host == "localhost" else self.host
158
+ test_sock.bind((bind_host, self.port))
159
+ test_sock.close()
160
+ return True
161
+ except OSError:
162
+ # IPv4 failed, try IPv6
163
+ try:
164
+ test_sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
165
+ test_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
166
+ test_sock.bind(("::1", self.port))
167
+ test_sock.close()
168
+ return True
169
+ except Exception:
170
+ # Both IPv4 and IPv6 failed - port is in use
171
+ return False
172
+
173
+ def _kill_processes_on_port(self) -> bool:
174
+ """Kill processes using the daemon port.
175
+
176
+ Returns:
177
+ True if processes were killed or none found, False on error
178
+ """
179
+ try:
180
+ # Try using lsof first (most reliable)
181
+ if self._kill_using_lsof():
182
+ return True
183
+
184
+ # Fallback to checking our known PID file
185
+ if self._kill_using_pid_file():
186
+ return True
187
+
188
+ # Try to identify claude-mpm processes
189
+ return bool(self._kill_claude_mpm_processes())
190
+
191
+ except Exception as e:
192
+ self.logger.error(f"Error killing processes on port: {e}")
193
+ return False
194
+
195
+ def _kill_using_lsof(self) -> bool:
196
+ """Kill processes using lsof to find them.
197
+
198
+ Returns:
199
+ True if successful or lsof not available, False on error
200
+ """
201
+ try:
202
+ # Find processes using the port
203
+ result = subprocess.run(
204
+ ["lsof", "-ti", f":{self.port}"],
205
+ capture_output=True,
206
+ text=True,
207
+ check=False,
208
+ )
209
+
210
+ if result.returncode != 0 or not result.stdout.strip():
211
+ self.logger.debug(f"No processes found using port {self.port}")
212
+ return True
213
+
214
+ pids = result.stdout.strip().split("\n")
215
+ self.logger.info(f"Found processes using port {self.port}: {pids}")
216
+
217
+ # Kill each process
218
+ for pid_str in pids:
219
+ try:
220
+ pid = int(pid_str.strip())
221
+
222
+ # Check if it's a Python/Claude process
223
+ process_info = subprocess.run(
224
+ ["ps", "-p", str(pid), "-o", "comm="],
225
+ capture_output=True,
226
+ text=True,
227
+ check=False,
228
+ )
229
+
230
+ # Get full command to check if it's our monitor process
231
+ cmd_info = subprocess.run(
232
+ ["ps", "-p", str(pid), "-o", "command="],
233
+ capture_output=True,
234
+ text=True,
235
+ check=False,
236
+ )
237
+
238
+ if cmd_info.returncode != 0:
239
+ continue
240
+
241
+ full_command = cmd_info.stdout.strip().lower()
242
+ process_name = process_info.stdout.strip().lower()
243
+
244
+ # Check if this is our monitor/socketio process specifically
245
+ # Look for monitor, socketio, dashboard, or our specific port
246
+ is_monitor = any(
247
+ [
248
+ "monitor" in full_command,
249
+ "socketio" in full_command,
250
+ "dashboard" in full_command,
251
+ f"port={self.port}" in full_command,
252
+ f":{self.port}" in full_command,
253
+ "unified_monitor" in full_command,
254
+ ]
255
+ )
256
+
257
+ if is_monitor and "python" in process_name:
258
+ self.logger.info(
259
+ f"Killing monitor process {pid}: {full_command[:100]}"
260
+ )
261
+ os.kill(pid, signal.SIGTERM)
262
+
263
+ # Wait briefly for graceful shutdown
264
+ time.sleep(1)
265
+
266
+ # Check if still alive and force kill if needed
267
+ try:
268
+ os.kill(pid, 0) # Check if process exists
269
+ self.logger.warning(
270
+ f"Process {pid} didn't terminate, force killing"
271
+ )
272
+ os.kill(pid, signal.SIGKILL)
273
+ time.sleep(0.5)
274
+ except ProcessLookupError:
275
+ pass # Process already dead
276
+ else:
277
+ # Not a monitor process - log but don't fail
278
+ self.logger.info(
279
+ f"Skipping non-monitor process {pid} ({process_name})"
280
+ )
281
+ # Continue to next PID - don't return False
282
+ continue
283
+
284
+ except (ValueError, ProcessLookupError) as e:
285
+ self.logger.debug(f"Error handling PID {pid_str}: {e}")
286
+ continue
287
+
288
+ return True
289
+
290
+ except FileNotFoundError:
291
+ # lsof not available
292
+ self.logger.debug("lsof not available, using alternative methods")
293
+ return True
294
+ except Exception as e:
295
+ self.logger.error(f"Error using lsof: {e}")
296
+ return False
297
+
298
+ def _kill_using_pid_file(self) -> bool:
299
+ """Kill process using PID file.
300
+
301
+ Returns:
302
+ True if successful or no PID file, False on error
303
+ """
304
+ try:
305
+ if not self.pid_file.exists():
306
+ return True
307
+
308
+ with self.pid_file.open() as f:
309
+ pid = int(f.read().strip())
310
+
311
+ self.logger.info(f"Found PID {pid} in PID file")
312
+
313
+ # Kill the process
314
+ try:
315
+ os.kill(pid, signal.SIGTERM)
316
+ time.sleep(1)
317
+
318
+ # Check if still alive
319
+ try:
320
+ os.kill(pid, 0)
321
+ os.kill(pid, signal.SIGKILL)
322
+ time.sleep(0.5)
323
+ except ProcessLookupError:
324
+ pass
325
+
326
+ # Remove PID file
327
+ self.pid_file.unlink(missing_ok=True)
328
+ return True
329
+
330
+ except ProcessLookupError:
331
+ # Process doesn't exist, just remove PID file
332
+ self.pid_file.unlink(missing_ok=True)
333
+ return True
334
+
335
+ except Exception as e:
336
+ self.logger.error(f"Error killing process from PID file: {e}")
337
+ return False
338
+
339
+ def _kill_claude_mpm_processes(self) -> bool:
340
+ """Kill any claude-mpm monitor processes specifically.
341
+
342
+ This targets monitor/dashboard/socketio processes only,
343
+ NOT general Claude instances.
344
+
345
+ Returns:
346
+ True if successful, False on error
347
+ """
348
+ try:
349
+ # Look for monitor-specific processes
350
+ result = subprocess.run(
351
+ ["ps", "aux"], capture_output=True, text=True, check=False
352
+ )
353
+
354
+ if result.returncode != 0:
355
+ return False
356
+
357
+ lines = result.stdout.strip().split("\n")
358
+ killed_any = False
359
+
360
+ for line in lines:
361
+ line_lower = line.lower()
362
+ # Only target monitor/dashboard/socketio processes
363
+ if any(
364
+ [
365
+ "monitor" in line_lower and "claude" in line_lower,
366
+ "dashboard" in line_lower and "claude" in line_lower,
367
+ "socketio" in line_lower,
368
+ f":{self.port}" in line_lower and "python" in line_lower,
369
+ ]
370
+ ):
371
+ parts = line.split()
372
+ if len(parts) > 1:
373
+ try:
374
+ pid = int(parts[1])
375
+ self.logger.info(
376
+ f"Killing claude-mpm monitor process {pid}"
377
+ )
378
+ os.kill(pid, signal.SIGTERM)
379
+ killed_any = True
380
+ time.sleep(0.5)
381
+ except (ValueError, ProcessLookupError):
382
+ continue
383
+
384
+ if killed_any:
385
+ time.sleep(1) # Give processes time to exit
386
+
387
+ return True
388
+
389
+ except Exception as e:
390
+ self.logger.error(f"Error killing claude-mpm processes: {e}")
391
+ return False
392
+
393
+ def is_our_service(self) -> Tuple[bool, Optional[int]]:
394
+ """Check if the service on the port is our claude-mpm monitor.
395
+
396
+ Returns:
397
+ Tuple of (is_ours, pid) where is_ours is True if it's our service
398
+ """
399
+ try:
400
+ # First check PID file
401
+ if self.pid_file.exists():
402
+ try:
403
+ with self.pid_file.open() as f:
404
+ pid = int(f.read().strip())
405
+
406
+ # Verify process exists
407
+ os.kill(pid, 0)
408
+
409
+ # Check if it's a Python process
410
+ process_info = subprocess.run(
411
+ ["ps", "-p", str(pid), "-o", "comm="],
412
+ capture_output=True,
413
+ text=True,
414
+ check=False,
415
+ )
416
+
417
+ if "python" in process_info.stdout.lower():
418
+ return True, pid
419
+
420
+ except (ValueError, ProcessLookupError, subprocess.CalledProcessError):
421
+ # PID file exists but process doesn't or isn't Python
422
+ self.pid_file.unlink(missing_ok=True)
423
+
424
+ # Check if service responds to our health endpoint
425
+ try:
426
+ import requests
427
+
428
+ response = requests.get(
429
+ f"http://{self.host}:{self.port}/health", timeout=2
430
+ )
431
+
432
+ if response.status_code == 200:
433
+ # Try to get service info
434
+ try:
435
+ data = response.json()
436
+ if "claude" in str(data).lower() or "mpm" in str(data).lower():
437
+ # It's likely our service, try to find PID
438
+ pid = self._find_service_pid()
439
+ return True, pid
440
+ except Exception:
441
+ pass
442
+
443
+ except Exception:
444
+ pass
445
+
446
+ return False, None
447
+
448
+ except Exception as e:
449
+ self.logger.error(f"Error checking service ownership: {e}")
450
+ return False, None
451
+
452
+ def _find_service_pid(self) -> Optional[int]:
453
+ """Find PID of service on our port using lsof.
454
+
455
+ Returns:
456
+ PID if found, None otherwise
457
+ """
458
+ try:
459
+ result = subprocess.run(
460
+ ["lsof", "-ti", f":{self.port}"],
461
+ capture_output=True,
462
+ text=True,
463
+ check=False,
464
+ )
465
+
466
+ if result.returncode == 0 and result.stdout.strip():
467
+ pids = result.stdout.strip().split("\n")
468
+ if pids:
469
+ return int(pids[0].strip())
470
+
471
+ except Exception:
472
+ pass
473
+
474
+ return None
475
+
476
+ def _verify_daemon_health(self, max_attempts: int = 3) -> bool:
477
+ """Verify daemon is healthy by checking HTTP health endpoint.
478
+
479
+ Args:
480
+ max_attempts: Maximum number of connection attempts
481
+
482
+ Returns:
483
+ True if health check passes, False otherwise
484
+ """
485
+ try:
486
+ import requests
487
+
488
+ for attempt in range(max_attempts):
489
+ try:
490
+ # Try to connect to health endpoint
491
+ response = requests.get(
492
+ f"http://{self.host}:{self.port}/health", timeout=2
493
+ )
494
+
495
+ if response.status_code == 200:
496
+ self.logger.debug(
497
+ f"Health check passed on attempt {attempt + 1}/{max_attempts}"
498
+ )
499
+ return True
500
+
501
+ self.logger.debug(
502
+ f"Health check returned status {response.status_code} on attempt {attempt + 1}/{max_attempts}"
503
+ )
504
+
505
+ except requests.exceptions.RequestException as e:
506
+ self.logger.debug(
507
+ f"Health check attempt {attempt + 1}/{max_attempts} failed: {e}"
508
+ )
509
+
510
+ # Wait before retry (except on last attempt)
511
+ if attempt < max_attempts - 1:
512
+ time.sleep(1)
513
+
514
+ self.logger.debug(f"Health check failed after {max_attempts} attempts")
515
+ return False
516
+
517
+ except ImportError:
518
+ # requests not available, skip health check
519
+ self.logger.debug("requests library not available, skipping health check")
520
+ return True
521
+ except Exception as e:
522
+ self.logger.debug(f"Health check error: {e}")
523
+ return False
524
+
525
+ def start_daemon(self, force_restart: bool = False) -> bool:
526
+ """Start the daemon with automatic cleanup and retry.
527
+
528
+ Args:
529
+ force_restart: Force restart even if already running
530
+
531
+ Returns:
532
+ True if daemon started successfully
533
+ """
534
+ with self._lock:
535
+ # Check if already running
536
+ if self.is_running():
537
+ if not force_restart:
538
+ pid = self.get_pid()
539
+ self.logger.info(f"Daemon already running with PID {pid}")
540
+ return True
541
+
542
+ # Stop existing daemon
543
+ self.logger.info("Force restarting daemon")
544
+ if not self.stop_daemon():
545
+ self.logger.error("Failed to stop existing daemon")
546
+ return False
547
+
548
+ # Wait for cleanup
549
+ time.sleep(2)
550
+
551
+ # Clean up port conflicts
552
+ if not self.cleanup_port_conflicts():
553
+ self.logger.error(f"Cannot start daemon - port {self.port} is in use")
554
+ return False
555
+
556
+ # Use subprocess for clean daemon startup (v4.2.40)
557
+ # This avoids fork() issues with Python threading
558
+ if self.use_subprocess_daemon():
559
+ return self.start_daemon_subprocess()
560
+ # Fallback to traditional fork (kept for compatibility)
561
+ return self.daemonize()
562
+
563
+ def use_subprocess_daemon(self) -> bool:
564
+ """Check if we should use subprocess instead of fork for daemonization.
565
+
566
+ Returns:
567
+ True to use subprocess (safer), False to use traditional fork
568
+ """
569
+ # Check if we're already in a subprocess to prevent infinite recursion
570
+ if os.environ.get("CLAUDE_MPM_SUBPROCESS_DAEMON") == "1":
571
+ # We're already in a subprocess, use traditional fork
572
+ return False
573
+
574
+ # Otherwise, use subprocess for monitor daemon to avoid threading issues
575
+ return True
576
+
577
+ def start_daemon_subprocess(self) -> bool:
578
+ """Start daemon using subprocess.Popen for clean process isolation.
579
+
580
+ This avoids all the fork() + threading issues by starting the monitor
581
+ in a completely fresh process with no inherited threads or locks.
582
+
583
+ Returns:
584
+ True if daemon started successfully
585
+ """
586
+ try:
587
+ # Build command to run monitor in foreground mode in subprocess
588
+ import sys
589
+
590
+ python_exe = sys.executable
591
+
592
+ # Run 'claude-mpm monitor start' in subprocess with environment variable
593
+ # to indicate we're already in a subprocess (prevents infinite recursion)
594
+ cmd = [
595
+ python_exe,
596
+ "-m",
597
+ "claude_mpm.cli",
598
+ "monitor",
599
+ "start",
600
+ "--background", # Run as daemon
601
+ "--port",
602
+ str(self.port),
603
+ "--host",
604
+ self.host,
605
+ ]
606
+
607
+ # Set environment variable to prevent recursive subprocess creation
608
+ env = os.environ.copy()
609
+ env["CLAUDE_MPM_SUBPROCESS_DAEMON"] = "1"
610
+
611
+ self.logger.info(f"Starting monitor daemon via subprocess: {' '.join(cmd)}")
612
+
613
+ # Open log file for output redirection
614
+ log_file_handle = None
615
+ if self.log_file:
616
+ log_file_handle = Path(self.log_file).open("a")
617
+ log_file = log_file_handle
618
+ else:
619
+ log_file = subprocess.DEVNULL
620
+
621
+ try:
622
+ # Start the subprocess detached from parent
623
+ # Redirect stdout/stderr to log file to capture output
624
+ process = subprocess.Popen(
625
+ cmd,
626
+ stdin=subprocess.DEVNULL,
627
+ stdout=log_file,
628
+ stderr=subprocess.STDOUT if self.log_file else subprocess.DEVNULL,
629
+ start_new_session=True, # Create new process group
630
+ close_fds=(not self.log_file), # Keep log file open if redirecting
631
+ env=env, # Pass modified environment
632
+ )
633
+
634
+ # Close the log file handle now that subprocess has it
635
+ if log_file_handle:
636
+ log_file_handle.close()
637
+
638
+ # Get the process PID
639
+ pid = process.pid
640
+ self.logger.info(f"Monitor subprocess started with PID {pid}")
641
+
642
+ # Wait for the subprocess to write its PID file and bind to port
643
+ # The subprocess will write the PID file after it starts successfully
644
+ max_wait = 10 # seconds
645
+ start_time = time.time()
646
+ pid_file_found = False
647
+ port_bound = False
648
+
649
+ self.logger.debug(f"Waiting up to {max_wait}s for daemon to start...")
650
+
651
+ while time.time() - start_time < max_wait:
652
+ # Check if process is still running
653
+ returncode = process.poll()
654
+ if returncode is not None:
655
+ # Process exited - this is the bug we're fixing!
656
+ self.logger.error(
657
+ f"Monitor daemon subprocess exited prematurely with code {returncode}"
658
+ )
659
+ self.logger.error(
660
+ f"Port {self.port} daemon failed to start. Check {self.log_file} for details."
661
+ )
662
+ return False
663
+
664
+ # Check if PID file was written
665
+ if not pid_file_found and self.pid_file.exists():
666
+ try:
667
+ with self.pid_file.open() as f:
668
+ written_pid = int(f.read().strip())
669
+ if written_pid == pid:
670
+ pid_file_found = True
671
+ self.logger.debug(
672
+ f"PID file found with correct PID {pid}"
673
+ )
674
+ except Exception as e:
675
+ self.logger.debug(f"Error reading PID file: {e}")
676
+
677
+ # Check if port is bound (health check)
678
+ if not port_bound and not self._is_port_available():
679
+ # Port NOT available means it's in use (good!)
680
+ port_bound = True
681
+ self.logger.debug(f"Port {self.port} is now bound")
682
+
683
+ # Success criteria: both PID file exists and port is bound
684
+ if pid_file_found and port_bound:
685
+ self.logger.info(
686
+ f"Monitor daemon successfully started on port {self.port} (PID: {pid})"
687
+ )
688
+ # Additional health check: verify we can connect
689
+ if self._verify_daemon_health(max_attempts=3):
690
+ self.logger.info("Daemon health check passed")
691
+ return True
692
+ self.logger.warning(
693
+ "Daemon started but health check failed - may still be initializing"
694
+ )
695
+ return (
696
+ True # Return success anyway if PID file and port are good
697
+ )
698
+
699
+ time.sleep(0.5)
700
+
701
+ # Timeout waiting for daemon to start
702
+ self.logger.error("Timeout waiting for monitor daemon to start")
703
+ # Try to kill the process if it's still running
704
+ if process.poll() is None:
705
+ process.terminate()
706
+ time.sleep(1)
707
+ if process.poll() is None:
708
+ process.kill()
709
+ return False
710
+ finally:
711
+ # Clean up log file handle if still open
712
+ if log_file_handle and not log_file_handle.closed:
713
+ log_file_handle.close()
714
+
715
+ except Exception as e:
716
+ self.logger.error(f"Failed to start daemon via subprocess: {e}")
717
+ return False
718
+
719
+ def daemonize(self) -> bool:
720
+ """Daemonize the current process.
721
+
722
+ Returns:
723
+ True if successful (in parent), doesn't return in child
724
+ """
725
+ # Guard against re-entrant execution after fork
726
+ if hasattr(self, "_forking_in_progress"):
727
+ self.logger.error(
728
+ "CRITICAL: Detected re-entrant daemonize call after fork!"
729
+ )
730
+ return False
731
+
732
+ self._forking_in_progress = True
733
+
734
+ try:
735
+ # Clean up asyncio event loops before forking
736
+ self._cleanup_event_loops()
737
+
738
+ # Create status file for communication
739
+ with tempfile.NamedTemporaryFile(
740
+ mode="w", delete=False, suffix=".status"
741
+ ) as f:
742
+ self.startup_status_file = f.name
743
+ f.write("starting")
744
+
745
+ # First fork
746
+ pid = os.fork()
747
+ if pid > 0:
748
+ # Parent process - wait for child to confirm startup
749
+ del self._forking_in_progress # Clean up in parent
750
+ return self._parent_wait_for_startup(pid)
751
+
752
+ except OSError as e:
753
+ self.logger.error(f"First fork failed: {e}")
754
+ return False
755
+
756
+ # Child process continues...
757
+
758
+ # Decouple from parent
759
+ os.chdir("/")
760
+ os.setsid()
761
+ os.umask(0)
762
+
763
+ try:
764
+ # Second fork
765
+ pid = os.fork()
766
+ if pid > 0:
767
+ # First child exits
768
+ sys.exit(0)
769
+ except OSError as e:
770
+ self.logger.error(f"Second fork failed: {e}")
771
+ self._report_startup_error(f"Second fork failed: {e}")
772
+ sys.exit(1)
773
+
774
+ # Grandchild process - the actual daemon
775
+
776
+ # Write PID file
777
+ self.write_pid_file()
778
+
779
+ # Redirect streams
780
+ self._redirect_streams()
781
+
782
+ # Setup signal handlers
783
+ self._setup_signal_handlers()
784
+
785
+ self.logger.info(f"Daemon process started with PID {os.getpid()}")
786
+
787
+ # DO NOT report success here - let the caller report after starting the service
788
+ # This prevents race conditions where we report success before the server starts
789
+ # self._report_startup_success() # REMOVED - caller must report
790
+
791
+ # Note: Daemon process continues running
792
+ # Caller is responsible for running the actual service AND reporting status
793
+ return True
794
+
795
+ def stop_daemon(self, timeout: int = 10) -> bool:
796
+ """Stop the daemon process.
797
+
798
+ Args:
799
+ timeout: Maximum time to wait for daemon to stop
800
+
801
+ Returns:
802
+ True if stopped successfully
803
+ """
804
+ with self._lock:
805
+ try:
806
+ pid = self.get_pid()
807
+ if not pid:
808
+ self.logger.info("No daemon PID found")
809
+ # Still try to clean up port
810
+ self.cleanup_port_conflicts()
811
+ return True
812
+
813
+ self.logger.info(f"Stopping daemon with PID {pid}")
814
+
815
+ # Send SIGTERM for graceful shutdown
816
+ try:
817
+ os.kill(pid, signal.SIGTERM)
818
+ except ProcessLookupError:
819
+ # Process already dead
820
+ self.cleanup_pid_file()
821
+ return True
822
+
823
+ # Wait for process to exit
824
+ start_time = time.time()
825
+ while time.time() - start_time < timeout:
826
+ try:
827
+ os.kill(pid, 0) # Check if still alive
828
+ time.sleep(0.5)
829
+ except ProcessLookupError:
830
+ # Process exited
831
+ self.cleanup_pid_file()
832
+ return True
833
+
834
+ # Force kill if still running
835
+ self.logger.warning("Daemon didn't stop gracefully, force killing")
836
+ try:
837
+ os.kill(pid, signal.SIGKILL)
838
+ time.sleep(1)
839
+ except ProcessLookupError:
840
+ pass
841
+
842
+ self.cleanup_pid_file()
843
+ return True
844
+
845
+ except Exception as e:
846
+ self.logger.error(f"Error stopping daemon: {e}")
847
+ return False
848
+
849
+ def is_running(self) -> bool:
850
+ """Check if daemon is running.
851
+
852
+ Returns:
853
+ True if daemon is running
854
+ """
855
+ try:
856
+ pid = self.get_pid()
857
+ if not pid:
858
+ return False
859
+
860
+ # Check if process exists
861
+ os.kill(pid, 0)
862
+ return True
863
+
864
+ except ProcessLookupError:
865
+ # Process doesn't exist
866
+ self.cleanup_pid_file()
867
+ return False
868
+
869
+ def get_pid(self) -> Optional[int]:
870
+ """Get daemon PID from PID file.
871
+
872
+ Returns:
873
+ PID if found, None otherwise
874
+ """
875
+ try:
876
+ if not self.pid_file.exists():
877
+ return None
878
+
879
+ with self.pid_file.open() as f:
880
+ return int(f.read().strip())
881
+
882
+ except Exception as e:
883
+ self.logger.error(f"Error reading PID file: {e}")
884
+ return None
885
+
886
+ def write_pid_file(self):
887
+ """Write current PID to PID file."""
888
+ try:
889
+ self.pid_file.parent.mkdir(parents=True, exist_ok=True)
890
+ with self.pid_file.open("w") as f:
891
+ f.write(str(os.getpid()))
892
+ self.logger.debug(f"PID file written: {self.pid_file}")
893
+ except Exception as e:
894
+ self.logger.error(f"Error writing PID file: {e}")
895
+ raise
896
+
897
+ def cleanup_pid_file(self):
898
+ """Remove PID file."""
899
+ try:
900
+ self.pid_file.unlink(missing_ok=True)
901
+ self.logger.debug("PID file removed")
902
+ except Exception as e:
903
+ self.logger.error(f"Error removing PID file: {e}")
904
+
905
+ def _cleanup_event_loops(self):
906
+ """Clean up asyncio event loops before forking."""
907
+ try:
908
+ import asyncio
909
+
910
+ try:
911
+ loop = asyncio.get_event_loop()
912
+ if loop and not loop.is_closed():
913
+ # Cancel pending tasks
914
+ pending = asyncio.all_tasks(loop)
915
+ for task in pending:
916
+ task.cancel()
917
+
918
+ # Stop and close loop
919
+ if loop.is_running():
920
+ loop.stop()
921
+
922
+ asyncio.set_event_loop(None)
923
+ loop.close()
924
+
925
+ except RuntimeError:
926
+ # No event loop
927
+ pass
928
+
929
+ except Exception as e:
930
+ self.logger.debug(f"Error cleaning up event loops: {e}")
931
+
932
+ def _redirect_streams(self):
933
+ """Redirect standard streams for daemon mode."""
934
+ try:
935
+ sys.stdout.flush()
936
+ sys.stderr.flush()
937
+
938
+ # Redirect stdin to /dev/null
939
+ with Path("/dev/null").open() as null_in:
940
+ os.dup2(null_in.fileno(), sys.stdin.fileno())
941
+
942
+ # Redirect stdout and stderr to log file
943
+ self.log_file.parent.mkdir(parents=True, exist_ok=True)
944
+ with self.log_file.open("a") as log_out:
945
+ os.dup2(log_out.fileno(), sys.stdout.fileno())
946
+ os.dup2(log_out.fileno(), sys.stderr.fileno())
947
+
948
+ except Exception as e:
949
+ self.logger.error(f"Error redirecting streams: {e}")
950
+
951
+ def _setup_signal_handlers(self):
952
+ """Setup signal handlers for graceful shutdown."""
953
+
954
+ def signal_handler(signum, frame):
955
+ self.logger.info(f"Received signal {signum}, shutting down")
956
+ self.cleanup_pid_file()
957
+ sys.exit(0)
958
+
959
+ signal.signal(signal.SIGTERM, signal_handler)
960
+ signal.signal(signal.SIGINT, signal_handler)
961
+
962
+ def _parent_wait_for_startup(self, child_pid: int, timeout: float = 10.0) -> bool:
963
+ """Parent process waits for child to confirm startup.
964
+
965
+ Args:
966
+ child_pid: PID of child process
967
+ timeout: Maximum time to wait
968
+
969
+ Returns:
970
+ True if child started successfully
971
+ """
972
+ try:
973
+ start_time = time.time()
974
+
975
+ while time.time() - start_time < timeout:
976
+ if (
977
+ not self.startup_status_file
978
+ or not Path(self.startup_status_file).exists()
979
+ ):
980
+ time.sleep(0.1)
981
+ continue
982
+
983
+ try:
984
+ with self.startup_status_file.open() as f:
985
+ status = f.read().strip()
986
+
987
+ if status == OperationResult.SUCCESS:
988
+ # Cleanup status file
989
+ Path(self.startup_status_file).unlink(missing_ok=True)
990
+ return True
991
+
992
+ if status.startswith("error:"):
993
+ error_msg = status[6:]
994
+ self.logger.error(f"Daemon startup failed: {error_msg}")
995
+ Path(self.startup_status_file).unlink(missing_ok=True)
996
+ return False
997
+
998
+ except Exception:
999
+ pass
1000
+
1001
+ time.sleep(0.1)
1002
+
1003
+ self.logger.error("Daemon startup timed out")
1004
+ return False
1005
+
1006
+ except Exception as e:
1007
+ self.logger.error(f"Error waiting for daemon startup: {e}")
1008
+ return False
1009
+
1010
+ def _report_startup_success(self):
1011
+ """Report successful startup to parent process."""
1012
+ if self.startup_status_file:
1013
+ try:
1014
+ # Don't check if file exists - we need to write to it regardless
1015
+ # The parent created it and is waiting for us to update it
1016
+ with self.startup_status_file.open("w") as f:
1017
+ f.write(OperationResult.SUCCESS)
1018
+ f.flush() # Ensure it's written immediately
1019
+ os.fsync(f.fileno()) # Force write to disk
1020
+ except Exception:
1021
+ # Logging might not work in daemon process after fork
1022
+ pass
1023
+
1024
+ def _report_startup_error(self, error: str):
1025
+ """Report startup error to parent process."""
1026
+ if self.startup_status_file:
1027
+ try:
1028
+ # Don't check if file exists - we need to write to it regardless
1029
+ with self.startup_status_file.open("w") as f:
1030
+ f.write(f"error:{error}")
1031
+ f.flush() # Ensure it's written immediately
1032
+ os.fsync(f.fileno()) # Force write to disk
1033
+ except Exception as e:
1034
+ # Try to write error to a debug file since logging might not work
1035
+ try:
1036
+ with Path("/tmp/daemon_debug_error.txt").open("a") as debug:
1037
+ debug.write(f"Error reporting error: {e}\n")
1038
+ debug.write(f"Status file: {self.startup_status_file}\n")
1039
+ except Exception:
1040
+ pass