claude-mpm 5.0.9__py3-none-any.whl → 5.4.41__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.

Potentially problematic release.


This version of claude-mpm might be problematic. Click here for more details.

Files changed (263) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/__init__.py +4 -0
  3. claude_mpm/agents/BASE_AGENT.md +164 -0
  4. claude_mpm/agents/{PM_INSTRUCTIONS_TEACH.md → CLAUDE_MPM_TEACHER_OUTPUT_STYLE.md} +721 -41
  5. claude_mpm/agents/MEMORY.md +1 -1
  6. claude_mpm/agents/PM_INSTRUCTIONS.md +468 -468
  7. claude_mpm/agents/WORKFLOW.md +5 -254
  8. claude_mpm/agents/agent_loader.py +13 -44
  9. claude_mpm/agents/base_agent.json +1 -1
  10. claude_mpm/agents/frontmatter_validator.py +70 -2
  11. claude_mpm/agents/templates/circuit-breakers.md +431 -45
  12. claude_mpm/cli/__init__.py +0 -1
  13. claude_mpm/cli/__main__.py +4 -0
  14. claude_mpm/cli/chrome_devtools_installer.py +175 -0
  15. claude_mpm/cli/commands/agent_state_manager.py +18 -27
  16. claude_mpm/cli/commands/agents.py +175 -37
  17. claude_mpm/cli/commands/auto_configure.py +723 -236
  18. claude_mpm/cli/commands/config.py +88 -2
  19. claude_mpm/cli/commands/configure.py +1262 -157
  20. claude_mpm/cli/commands/configure_agent_display.py +25 -6
  21. claude_mpm/cli/commands/mpm_init/core.py +225 -46
  22. claude_mpm/cli/commands/mpm_init/knowledge_extractor.py +481 -0
  23. claude_mpm/cli/commands/mpm_init/prompts.py +280 -0
  24. claude_mpm/cli/commands/postmortem.py +1 -1
  25. claude_mpm/cli/commands/profile.py +277 -0
  26. claude_mpm/cli/commands/skills.py +214 -189
  27. claude_mpm/cli/commands/summarize.py +413 -0
  28. claude_mpm/cli/executor.py +21 -3
  29. claude_mpm/cli/interactive/agent_wizard.py +85 -10
  30. claude_mpm/cli/parsers/agents_parser.py +54 -9
  31. claude_mpm/cli/parsers/auto_configure_parser.py +13 -138
  32. claude_mpm/cli/parsers/base_parser.py +12 -0
  33. claude_mpm/cli/parsers/config_parser.py +153 -83
  34. claude_mpm/cli/parsers/profile_parser.py +148 -0
  35. claude_mpm/cli/parsers/skills_parser.py +3 -2
  36. claude_mpm/cli/startup.py +879 -149
  37. claude_mpm/commands/mpm-config.md +28 -0
  38. claude_mpm/commands/mpm-doctor.md +9 -22
  39. claude_mpm/commands/mpm-help.md +5 -287
  40. claude_mpm/commands/mpm-init.md +81 -507
  41. claude_mpm/commands/mpm-monitor.md +15 -402
  42. claude_mpm/commands/mpm-organize.md +120 -0
  43. claude_mpm/commands/mpm-postmortem.md +6 -108
  44. claude_mpm/commands/mpm-session-resume.md +12 -363
  45. claude_mpm/commands/mpm-status.md +5 -69
  46. claude_mpm/commands/mpm-ticket-view.md +52 -495
  47. claude_mpm/commands/mpm-version.md +5 -107
  48. claude_mpm/config/agent_sources.py +27 -0
  49. claude_mpm/core/config.py +2 -4
  50. claude_mpm/core/framework/formatters/content_formatter.py +3 -13
  51. claude_mpm/core/framework/loaders/agent_loader.py +8 -5
  52. claude_mpm/core/framework/loaders/instruction_loader.py +52 -11
  53. claude_mpm/core/framework_loader.py +4 -2
  54. claude_mpm/core/logger.py +13 -0
  55. claude_mpm/core/optimized_startup.py +59 -0
  56. claude_mpm/core/output_style_manager.py +173 -43
  57. claude_mpm/core/shared/config_loader.py +1 -1
  58. claude_mpm/core/socketio_pool.py +3 -3
  59. claude_mpm/core/unified_agent_registry.py +134 -16
  60. claude_mpm/core/unified_config.py +22 -0
  61. claude_mpm/dashboard/static/svelte-build/_app/env.js +1 -0
  62. claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/0.B_FtCwCQ.css +1 -0
  63. claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/2.Cl_eSA4x.css +1 -0
  64. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BgChzWQ1.js +1 -0
  65. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CIXEwuWe.js +1 -0
  66. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CWc5urbQ.js +1 -0
  67. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DMkZpdF2.js +2 -0
  68. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DjhvlsAc.js +1 -0
  69. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/N4qtv3Hx.js +2 -0
  70. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/uj46x2Wr.js +1 -0
  71. claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/app.DTL5mJO-.js +2 -0
  72. claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/start.DzuEhzqh.js +1 -0
  73. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/0.CAGBuiOw.js +1 -0
  74. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/1.DFLC8jdE.js +1 -0
  75. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/2.DPvEihJJ.js +10 -0
  76. claude_mpm/dashboard/static/svelte-build/_app/version.json +1 -0
  77. claude_mpm/dashboard/static/svelte-build/favicon.svg +7 -0
  78. claude_mpm/dashboard/static/svelte-build/index.html +36 -0
  79. claude_mpm/hooks/claude_hooks/__pycache__/__init__.cpython-311.pyc +0 -0
  80. claude_mpm/hooks/claude_hooks/__pycache__/correlation_manager.cpython-311.pyc +0 -0
  81. claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-311.pyc +0 -0
  82. claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-311.pyc +0 -0
  83. claude_mpm/hooks/claude_hooks/__pycache__/installer.cpython-311.pyc +0 -0
  84. claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-311.pyc +0 -0
  85. claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-311.pyc +0 -0
  86. claude_mpm/hooks/claude_hooks/__pycache__/tool_analysis.cpython-311.pyc +0 -0
  87. claude_mpm/hooks/claude_hooks/correlation_manager.py +60 -0
  88. claude_mpm/hooks/claude_hooks/event_handlers.py +211 -78
  89. claude_mpm/hooks/claude_hooks/hook_handler.py +155 -1
  90. claude_mpm/hooks/claude_hooks/installer.py +33 -10
  91. claude_mpm/hooks/claude_hooks/memory_integration.py +28 -0
  92. claude_mpm/hooks/claude_hooks/response_tracking.py +2 -3
  93. claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-311.pyc +0 -0
  94. claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager.cpython-311.pyc +0 -0
  95. claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-311.pyc +0 -0
  96. claude_mpm/hooks/claude_hooks/services/__pycache__/duplicate_detector.cpython-311.pyc +0 -0
  97. claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-311.pyc +0 -0
  98. claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-311.pyc +0 -0
  99. claude_mpm/hooks/claude_hooks/services/connection_manager.py +30 -6
  100. claude_mpm/hooks/memory_integration_hook.py +46 -1
  101. claude_mpm/init.py +63 -19
  102. claude_mpm/models/agent_definition.py +7 -0
  103. claude_mpm/models/git_repository.py +3 -3
  104. claude_mpm/scripts/claude-hook-handler.sh +58 -18
  105. claude_mpm/scripts/launch_monitor.py +93 -13
  106. claude_mpm/scripts/start_activity_logging.py +0 -0
  107. claude_mpm/services/agents/agent_builder.py +3 -3
  108. claude_mpm/services/agents/agent_recommendation_service.py +278 -0
  109. claude_mpm/services/agents/agent_review_service.py +280 -0
  110. claude_mpm/services/agents/cache_git_manager.py +6 -6
  111. claude_mpm/services/agents/deployment/agent_deployment.py +29 -7
  112. claude_mpm/services/agents/deployment/agent_discovery_service.py +4 -5
  113. claude_mpm/services/agents/deployment/agent_template_builder.py +5 -3
  114. claude_mpm/services/agents/deployment/agents_directory_resolver.py +2 -2
  115. claude_mpm/services/agents/deployment/multi_source_deployment_service.py +320 -29
  116. claude_mpm/services/agents/deployment/remote_agent_discovery_service.py +546 -68
  117. claude_mpm/services/agents/git_source_manager.py +36 -2
  118. claude_mpm/services/agents/loading/base_agent_manager.py +1 -13
  119. claude_mpm/services/agents/recommender.py +5 -3
  120. claude_mpm/services/agents/single_tier_deployment_service.py +2 -2
  121. claude_mpm/services/agents/sources/git_source_sync_service.py +13 -6
  122. claude_mpm/services/agents/startup_sync.py +22 -2
  123. claude_mpm/services/agents/toolchain_detector.py +10 -6
  124. claude_mpm/services/analysis/__init__.py +11 -1
  125. claude_mpm/services/analysis/clone_detector.py +1030 -0
  126. claude_mpm/services/command_deployment_service.py +81 -10
  127. claude_mpm/services/diagnostics/checks/agent_check.py +2 -2
  128. claude_mpm/services/diagnostics/checks/agent_sources_check.py +1 -1
  129. claude_mpm/services/event_bus/config.py +3 -1
  130. claude_mpm/services/git/git_operations_service.py +101 -16
  131. claude_mpm/services/monitor/daemon.py +9 -2
  132. claude_mpm/services/monitor/daemon_manager.py +39 -3
  133. claude_mpm/services/monitor/management/lifecycle.py +8 -1
  134. claude_mpm/services/monitor/server.py +698 -22
  135. claude_mpm/services/pm_skills_deployer.py +676 -0
  136. claude_mpm/services/profile_manager.py +331 -0
  137. claude_mpm/services/project/project_organizer.py +4 -0
  138. claude_mpm/services/self_upgrade_service.py +120 -12
  139. claude_mpm/services/skills/__init__.py +3 -0
  140. claude_mpm/services/skills/git_skill_source_manager.py +130 -2
  141. claude_mpm/services/skills/selective_skill_deployer.py +704 -0
  142. claude_mpm/services/skills/skill_to_agent_mapper.py +406 -0
  143. claude_mpm/services/skills_deployer.py +126 -9
  144. claude_mpm/services/socketio/dashboard_server.py +1 -0
  145. claude_mpm/services/socketio/event_normalizer.py +51 -6
  146. claude_mpm/services/socketio/server/core.py +386 -108
  147. claude_mpm/services/version_control/git_operations.py +103 -0
  148. claude_mpm/skills/skill_manager.py +92 -3
  149. claude_mpm/utils/agent_dependency_loader.py +14 -2
  150. claude_mpm/utils/agent_filters.py +17 -44
  151. claude_mpm/utils/gitignore.py +3 -0
  152. claude_mpm/utils/migration.py +4 -4
  153. claude_mpm/utils/robust_installer.py +47 -3
  154. {claude_mpm-5.0.9.dist-info → claude_mpm-5.4.41.dist-info}/METADATA +57 -87
  155. {claude_mpm-5.0.9.dist-info → claude_mpm-5.4.41.dist-info}/RECORD +160 -211
  156. claude_mpm-5.4.41.dist-info/entry_points.txt +5 -0
  157. claude_mpm-5.4.41.dist-info/licenses/LICENSE +94 -0
  158. claude_mpm-5.4.41.dist-info/licenses/LICENSE-FAQ.md +153 -0
  159. claude_mpm/agents/BASE_AGENT_TEMPLATE.md +0 -292
  160. claude_mpm/agents/BASE_DOCUMENTATION.md +0 -53
  161. claude_mpm/agents/BASE_OPS.md +0 -219
  162. claude_mpm/agents/BASE_PM.md +0 -480
  163. claude_mpm/agents/BASE_PROMPT_ENGINEER.md +0 -787
  164. claude_mpm/agents/BASE_QA.md +0 -167
  165. claude_mpm/agents/BASE_RESEARCH.md +0 -53
  166. claude_mpm/agents/base_agent_loader.py +0 -601
  167. claude_mpm/cli/commands/agents_detect.py +0 -380
  168. claude_mpm/cli/commands/agents_recommend.py +0 -309
  169. claude_mpm/cli/ticket_cli.py +0 -35
  170. claude_mpm/commands/mpm-agents-auto-configure.md +0 -278
  171. claude_mpm/commands/mpm-agents-detect.md +0 -177
  172. claude_mpm/commands/mpm-agents-list.md +0 -131
  173. claude_mpm/commands/mpm-agents-recommend.md +0 -223
  174. claude_mpm/commands/mpm-config-view.md +0 -150
  175. claude_mpm/commands/mpm-ticket-organize.md +0 -304
  176. claude_mpm/dashboard/analysis_runner.py +0 -455
  177. claude_mpm/dashboard/index.html +0 -13
  178. claude_mpm/dashboard/open_dashboard.py +0 -66
  179. claude_mpm/dashboard/static/css/activity.css +0 -1958
  180. claude_mpm/dashboard/static/css/connection-status.css +0 -370
  181. claude_mpm/dashboard/static/css/dashboard.css +0 -4701
  182. claude_mpm/dashboard/static/js/components/activity-tree.js +0 -1871
  183. claude_mpm/dashboard/static/js/components/agent-hierarchy.js +0 -777
  184. claude_mpm/dashboard/static/js/components/agent-inference.js +0 -956
  185. claude_mpm/dashboard/static/js/components/build-tracker.js +0 -333
  186. claude_mpm/dashboard/static/js/components/code-simple.js +0 -857
  187. claude_mpm/dashboard/static/js/components/connection-debug.js +0 -654
  188. claude_mpm/dashboard/static/js/components/diff-viewer.js +0 -891
  189. claude_mpm/dashboard/static/js/components/event-processor.js +0 -542
  190. claude_mpm/dashboard/static/js/components/event-viewer.js +0 -1155
  191. claude_mpm/dashboard/static/js/components/export-manager.js +0 -368
  192. claude_mpm/dashboard/static/js/components/file-change-tracker.js +0 -443
  193. claude_mpm/dashboard/static/js/components/file-change-viewer.js +0 -690
  194. claude_mpm/dashboard/static/js/components/file-tool-tracker.js +0 -724
  195. claude_mpm/dashboard/static/js/components/file-viewer.js +0 -580
  196. claude_mpm/dashboard/static/js/components/hud-library-loader.js +0 -211
  197. claude_mpm/dashboard/static/js/components/hud-manager.js +0 -671
  198. claude_mpm/dashboard/static/js/components/hud-visualizer.js +0 -1718
  199. claude_mpm/dashboard/static/js/components/module-viewer.js +0 -2764
  200. claude_mpm/dashboard/static/js/components/session-manager.js +0 -579
  201. claude_mpm/dashboard/static/js/components/socket-manager.js +0 -368
  202. claude_mpm/dashboard/static/js/components/ui-state-manager.js +0 -749
  203. claude_mpm/dashboard/static/js/components/unified-data-viewer.js +0 -1824
  204. claude_mpm/dashboard/static/js/components/working-directory.js +0 -920
  205. claude_mpm/dashboard/static/js/connection-manager.js +0 -536
  206. claude_mpm/dashboard/static/js/dashboard.js +0 -1914
  207. claude_mpm/dashboard/static/js/extension-error-handler.js +0 -164
  208. claude_mpm/dashboard/static/js/socket-client.js +0 -1474
  209. claude_mpm/dashboard/static/js/tab-isolation-fix.js +0 -185
  210. claude_mpm/dashboard/static/socket.io.min.js +0 -7
  211. claude_mpm/dashboard/static/socket.io.v4.8.1.backup.js +0 -7
  212. claude_mpm/dashboard/templates/code_simple.html +0 -153
  213. claude_mpm/dashboard/templates/index.html +0 -606
  214. claude_mpm/dashboard/test_dashboard.html +0 -372
  215. claude_mpm/hooks/claude_hooks/__pycache__/__init__.cpython-313.pyc +0 -0
  216. claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-313.pyc +0 -0
  217. claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-313.pyc +0 -0
  218. claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-313.pyc +0 -0
  219. claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-313.pyc +0 -0
  220. claude_mpm/hooks/claude_hooks/__pycache__/tool_analysis.cpython-313.pyc +0 -0
  221. claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-313.pyc +0 -0
  222. claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-313.pyc +0 -0
  223. claude_mpm/hooks/claude_hooks/services/__pycache__/duplicate_detector.cpython-313.pyc +0 -0
  224. claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-313.pyc +0 -0
  225. claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-313.pyc +0 -0
  226. claude_mpm/scripts/mcp_server.py +0 -75
  227. claude_mpm/scripts/mcp_wrapper.py +0 -39
  228. claude_mpm/services/mcp_gateway/__init__.py +0 -159
  229. claude_mpm/services/mcp_gateway/auto_configure.py +0 -369
  230. claude_mpm/services/mcp_gateway/config/__init__.py +0 -17
  231. claude_mpm/services/mcp_gateway/config/config_loader.py +0 -296
  232. claude_mpm/services/mcp_gateway/config/config_schema.py +0 -243
  233. claude_mpm/services/mcp_gateway/config/configuration.py +0 -429
  234. claude_mpm/services/mcp_gateway/core/__init__.py +0 -43
  235. claude_mpm/services/mcp_gateway/core/base.py +0 -312
  236. claude_mpm/services/mcp_gateway/core/exceptions.py +0 -253
  237. claude_mpm/services/mcp_gateway/core/interfaces.py +0 -443
  238. claude_mpm/services/mcp_gateway/core/process_pool.py +0 -977
  239. claude_mpm/services/mcp_gateway/core/singleton_manager.py +0 -315
  240. claude_mpm/services/mcp_gateway/core/startup_verification.py +0 -316
  241. claude_mpm/services/mcp_gateway/main.py +0 -589
  242. claude_mpm/services/mcp_gateway/registry/__init__.py +0 -12
  243. claude_mpm/services/mcp_gateway/registry/service_registry.py +0 -412
  244. claude_mpm/services/mcp_gateway/registry/tool_registry.py +0 -489
  245. claude_mpm/services/mcp_gateway/server/__init__.py +0 -15
  246. claude_mpm/services/mcp_gateway/server/mcp_gateway.py +0 -414
  247. claude_mpm/services/mcp_gateway/server/stdio_handler.py +0 -372
  248. claude_mpm/services/mcp_gateway/server/stdio_server.py +0 -712
  249. claude_mpm/services/mcp_gateway/tools/__init__.py +0 -36
  250. claude_mpm/services/mcp_gateway/tools/base_adapter.py +0 -485
  251. claude_mpm/services/mcp_gateway/tools/document_summarizer.py +0 -789
  252. claude_mpm/services/mcp_gateway/tools/external_mcp_services.py +0 -654
  253. claude_mpm/services/mcp_gateway/tools/health_check_tool.py +0 -456
  254. claude_mpm/services/mcp_gateway/tools/hello_world.py +0 -551
  255. claude_mpm/services/mcp_gateway/tools/kuzu_memory_service.py +0 -555
  256. claude_mpm/services/mcp_gateway/utils/__init__.py +0 -14
  257. claude_mpm/services/mcp_gateway/utils/package_version_checker.py +0 -160
  258. claude_mpm/services/mcp_gateway/utils/update_preferences.py +0 -170
  259. claude_mpm-5.0.9.dist-info/entry_points.txt +0 -10
  260. claude_mpm-5.0.9.dist-info/licenses/LICENSE +0 -21
  261. /claude_mpm/agents/{OUTPUT_STYLE.md → CLAUDE_MPM_OUTPUT_STYLE.md} +0 -0
  262. {claude_mpm-5.0.9.dist-info → claude_mpm-5.4.41.dist-info}/WHEEL +0 -0
  263. {claude_mpm-5.0.9.dist-info → claude_mpm-5.4.41.dist-info}/top_level.txt +0 -0
@@ -23,10 +23,14 @@ class CommandDeploymentService(BaseService):
23
23
  DEPRECATED_COMMANDS = [
24
24
  "mpm-agents.md", # Replaced by mpm-agents-list.md
25
25
  "mpm-auto-configure.md", # Replaced by mpm-agents-auto-configure.md
26
- "mpm-config.md", # Replaced by mpm-config-view.md
27
- "mpm-organize.md", # Replaced by mpm-ticket-organize.md
26
+ "mpm-config-view.md", # Replaced by mpm-config.md
28
27
  "mpm-resume.md", # Replaced by mpm-session-resume.md
29
28
  "mpm-ticket.md", # Replaced by mpm-ticket-view.md
29
+ # Removed - consolidated into /mpm-configure
30
+ "mpm-agents-list.md", # Consolidated into /mpm-configure
31
+ "mpm-agents-detect.md", # Consolidated into /mpm-configure
32
+ "mpm-agents-auto-configure.md", # Consolidated into /mpm-configure
33
+ "mpm-agents-recommend.md", # Consolidated into /mpm-configure
30
34
  ]
31
35
 
32
36
  def __init__(self):
@@ -315,10 +319,14 @@ class CommandDeploymentService(BaseService):
315
319
  replacement_map = {
316
320
  "mpm-agents.md": "mpm-agents-list.md",
317
321
  "mpm-auto-configure.md": "mpm-agents-auto-configure.md",
318
- "mpm-config.md": "mpm-config-view.md",
319
- "mpm-organize.md": "mpm-ticket-organize.md",
322
+ "mpm-config-view.md": "mpm-config.md",
320
323
  "mpm-resume.md": "mpm-session-resume.md",
321
324
  "mpm-ticket.md": "mpm-ticket-view.md",
325
+ # Removed commands - consolidated into /mpm-configure
326
+ "mpm-agents-list.md": "mpm-configure (use /mpm-configure)",
327
+ "mpm-agents-detect.md": "mpm-configure (use /mpm-configure)",
328
+ "mpm-agents-auto-configure.md": "mpm-configure (use /mpm-configure)",
329
+ "mpm-agents-recommend.md": "mpm-configure (use /mpm-configure)",
322
330
  }
323
331
 
324
332
  for deprecated_cmd in self.DEPRECATED_COMMANDS:
@@ -345,13 +353,71 @@ class CommandDeploymentService(BaseService):
345
353
 
346
354
  return removed
347
355
 
356
+ def remove_stale_commands(self) -> int:
357
+ """Remove stale MPM commands that no longer exist in source.
358
+
359
+ This method cleans up deployed commands that have been deleted or renamed
360
+ in the source directory. It's called automatically on startup to ensure
361
+ deployed commands stay in sync with source.
362
+
363
+ Returns:
364
+ Number of stale files removed
365
+ """
366
+ if not self.target_dir.exists():
367
+ self.logger.debug(
368
+ f"Target directory does not exist: {self.target_dir}, skipping stale command cleanup"
369
+ )
370
+ return 0
371
+
372
+ if not self.source_dir.exists():
373
+ self.logger.warning(
374
+ f"Source directory does not exist: {self.source_dir}, cannot detect stale commands"
375
+ )
376
+ return 0
377
+
378
+ # Get current source commands (ground truth)
379
+ source_commands = {f.name for f in self.source_dir.glob("mpm*.md")}
380
+
381
+ # Get deployed commands
382
+ deployed_commands = {f.name for f in self.target_dir.glob("mpm*.md")}
383
+
384
+ # Find stale commands (deployed but not in source, excluding deprecated)
385
+ stale_commands = (
386
+ deployed_commands - source_commands - set(self.DEPRECATED_COMMANDS)
387
+ )
388
+
389
+ if not stale_commands:
390
+ self.logger.debug("No stale commands found to remove")
391
+ return 0
392
+
393
+ removed = 0
394
+ self.logger.info(f"Cleaning up {len(stale_commands)} stale command(s)...")
395
+
396
+ for stale_cmd in stale_commands:
397
+ stale_file = self.target_dir / stale_cmd
398
+ try:
399
+ stale_file.unlink()
400
+ self.logger.info(
401
+ f"Removed stale command: {stale_cmd} (no longer in source)"
402
+ )
403
+ removed += 1
404
+ except Exception as e:
405
+ # Log error but don't fail startup - this is non-critical
406
+ self.logger.warning(f"Failed to remove stale command {stale_cmd}: {e}")
407
+
408
+ if removed > 0:
409
+ self.logger.info(f"Removed {removed} stale command(s)")
410
+
411
+ return removed
412
+
348
413
 
349
414
  def deploy_commands_on_startup(force: bool = False) -> None:
350
415
  """Convenience function to deploy commands during startup.
351
416
 
352
417
  This function:
353
418
  1. Removes deprecated commands that have been replaced
354
- 2. Deploys current command files
419
+ 2. Removes stale commands that no longer exist in source
420
+ 3. Deploys current command files
355
421
 
356
422
  Args:
357
423
  force: Force deployment even if files exist
@@ -359,12 +425,17 @@ def deploy_commands_on_startup(force: bool = False) -> None:
359
425
  service = CommandDeploymentService()
360
426
  logger = get_logger("startup")
361
427
 
362
- # Clean up deprecated commands BEFORE deploying new ones
363
- removed_count = service.remove_deprecated_commands()
364
- if removed_count > 0:
365
- logger.info(f"Cleaned up {removed_count} deprecated command(s)")
428
+ # Clean up deprecated commands FIRST (known old commands)
429
+ deprecated_count = service.remove_deprecated_commands()
430
+ if deprecated_count > 0:
431
+ logger.info(f"Cleaned up {deprecated_count} deprecated command(s)")
432
+
433
+ # Clean up stale commands SECOND (deployed but not in source anymore)
434
+ stale_count = service.remove_stale_commands()
435
+ if stale_count > 0:
436
+ logger.info(f"Cleaned up {stale_count} stale command(s)")
366
437
 
367
- # Deploy current commands
438
+ # Deploy current commands LAST
368
439
  result = service.deploy_commands(force=force)
369
440
 
370
441
  if result["deployed"]:
@@ -66,9 +66,9 @@ class AgentCheck(BaseDiagnosticCheck):
66
66
 
67
67
  if deployed_count == 0:
68
68
  status = ValidationSeverity.ERROR
69
- message = f"No agents deployed (0/{available_count} available)"
69
+ message = f"No agents deployed (0/{available_count} cached)"
70
70
  fix_command = "claude-mpm agents deploy"
71
- fix_description = "Deploy all available agents"
71
+ fix_description = "Deploy all cached agents"
72
72
  elif deployed_count < available_count:
73
73
  status = ValidationSeverity.WARNING
74
74
  message = f"{deployed_count}/{available_count} agents deployed"
@@ -432,7 +432,7 @@ class AgentSourcesCheck(BaseDiagnosticCheck):
432
432
 
433
433
  def _check_cache_directory(self) -> DiagnosticResult:
434
434
  """Check cache directory health."""
435
- cache_dir = Path.home() / ".claude-mpm" / "cache" / "remote-agents"
435
+ cache_dir = Path.home() / ".claude-mpm" / "cache" / "agents"
436
436
 
437
437
  if not cache_dir.exists():
438
438
  return DiagnosticResult(
@@ -52,9 +52,11 @@ class EventBusConfig:
52
52
  )
53
53
 
54
54
  # Relay configuration
55
+ # DirectSocketIORelay disabled by default - events already emit via direct sio.emit()
56
+ # Enable with CLAUDE_MPM_RELAY_ENABLED=true if needed for external consumers
55
57
  relay_enabled: bool = field(
56
58
  default_factory=lambda: os.environ.get(
57
- "CLAUDE_MPM_RELAY_ENABLED", "true"
59
+ "CLAUDE_MPM_RELAY_ENABLED", "false"
58
60
  ).lower()
59
61
  == "true"
60
62
  )
@@ -12,16 +12,19 @@ Design Decisions:
12
12
 
13
13
  Example:
14
14
  >>> service = GitOperationsService()
15
- >>> success = service.create_branch(Path("~/.claude-mpm/cache/remote-agents"), "improve/research-memory")
15
+ >>> success = service.create_branch(Path("~/.claude-mpm/cache/agents"), "improve/research-memory")
16
16
  >>> if success:
17
- ... service.stage_files(Path("~/.claude-mpm/cache/remote-agents"), ["agents/research.md"])
18
- ... service.commit(Path("~/.claude-mpm/cache/remote-agents"), "feat: improve research agent memory handling")
17
+ ... service.stage_files(Path("~/.claude-mpm/cache/agents"), ["agents/research.md"])
18
+ ... service.commit(Path("~/.claude-mpm/cache/agents"), "feat: improve research agent memory handling")
19
19
  """
20
20
 
21
+ import logging
21
22
  import subprocess
22
23
  from pathlib import Path
23
24
  from typing import List, Optional, Tuple
24
25
 
26
+ logger = logging.getLogger(__name__)
27
+
25
28
 
26
29
  # Custom Exceptions
27
30
  class GitOperationError(Exception):
@@ -65,7 +68,7 @@ class GitOperationsService:
65
68
 
66
69
  Example:
67
70
  >>> service = GitOperationsService()
68
- >>> service.is_git_repo(Path("~/.claude-mpm/cache/remote-agents"))
71
+ >>> service.is_git_repo(Path("~/.claude-mpm/cache/agents"))
69
72
  True
70
73
  """
71
74
  try:
@@ -147,7 +150,7 @@ class GitOperationsService:
147
150
  Example:
148
151
  >>> service = GitOperationsService()
149
152
  >>> service.create_and_checkout_branch(
150
- ... Path("~/.claude-mpm/cache/remote-agents"),
153
+ ... Path("~/.claude-mpm/cache/agents"),
151
154
  ... "improve/research-memory",
152
155
  ... "main"
153
156
  ... )
@@ -242,7 +245,7 @@ class GitOperationsService:
242
245
  Example:
243
246
  >>> service = GitOperationsService()
244
247
  >>> service.commit(
245
- ... Path("~/.claude-mpm/cache/remote-agents"),
248
+ ... Path("~/.claude-mpm/cache/agents"),
246
249
  ... "feat(agent): improve research agent memory handling\\n\\n- Add hard limit of 5 files"
247
250
  ... )
248
251
  True
@@ -286,7 +289,7 @@ class GitOperationsService:
286
289
 
287
290
  Example:
288
291
  >>> service = GitOperationsService()
289
- >>> service.push(Path("~/.claude-mpm/cache/remote-agents"), "improve/research-memory")
292
+ >>> service.push(Path("~/.claude-mpm/cache/agents"), "improve/research-memory")
290
293
  True
291
294
  """
292
295
  self._validate_repo(repo_path)
@@ -314,7 +317,12 @@ class GitOperationsService:
314
317
 
315
318
  def pull(self, repo_path: Path, branch: str = "main") -> bool:
316
319
  """
317
- Pull latest changes from remote.
320
+ Pull latest changes from remote with automatic divergent branch recovery.
321
+
322
+ For cache/sync repositories, automatically handles divergent branches by:
323
+ 1. First attempting git pull --rebase (cleaner history)
324
+ 2. If rebase fails or conflicts exist, hard reset to match remote exactly
325
+ (cache repos should mirror remote state, local changes are discarded)
318
326
 
319
327
  Args:
320
328
  repo_path: Repository path
@@ -325,21 +333,98 @@ class GitOperationsService:
325
333
 
326
334
  Raises:
327
335
  GitOperationError: If pull fails
328
- GitConflictError: If merge conflicts detected
336
+ GitConflictError: If merge conflicts detected and cannot be auto-resolved
329
337
  """
330
338
  self._validate_repo(repo_path)
331
339
 
340
+ # First, try standard pull
332
341
  returncode, _stdout, stderr = self._run_git_command(
333
342
  ["git", "pull", "origin", branch], cwd=repo_path
334
343
  )
335
344
 
336
- if returncode != 0:
337
- # Check for merge conflicts
338
- if "conflict" in stderr.lower():
339
- raise GitConflictError(
340
- f"Merge conflicts detected when pulling {branch}: {stderr}"
345
+ if returncode == 0:
346
+ return True
347
+
348
+ # Check if this is a divergent branch situation
349
+ is_divergent = any(
350
+ phrase in stderr.lower()
351
+ for phrase in [
352
+ "divergent branches",
353
+ "need to specify how to reconcile",
354
+ "have diverged",
355
+ ]
356
+ )
357
+
358
+ if is_divergent:
359
+ logger.warning(
360
+ f"Divergent branches detected in cache repository at {repo_path}. "
361
+ "Attempting automatic recovery with rebase..."
362
+ )
363
+
364
+ # Strategy 1: Try rebase for cleaner history
365
+ returncode, _stdout, stderr = self._run_git_command(
366
+ ["git", "pull", "--rebase", "origin", branch], cwd=repo_path
367
+ )
368
+
369
+ if returncode == 0:
370
+ logger.info(
371
+ f"Successfully resolved divergent branches with rebase for {branch}"
372
+ )
373
+ return True
374
+
375
+ # Check if rebase had conflicts
376
+ has_rebase_conflict = any(
377
+ phrase in stderr.lower()
378
+ for phrase in ["conflict", "rebase in progress"]
379
+ )
380
+
381
+ if has_rebase_conflict:
382
+ logger.warning(
383
+ "Rebase conflicts detected. Aborting rebase and resetting to remote state..."
341
384
  )
342
- raise GitOperationError(f"Failed to pull {branch}: {stderr}")
385
+ # Abort the rebase
386
+ self._run_git_command(["git", "rebase", "--abort"], cwd=repo_path)
387
+
388
+ # Strategy 2: Hard reset to match remote exactly (cache repos should mirror remote)
389
+ logger.warning(
390
+ f"Discarding local changes and resetting to origin/{branch} "
391
+ "(cache repositories should match remote exactly)"
392
+ )
393
+
394
+ # Fetch latest to ensure we have the remote state
395
+ returncode, _stdout, stderr = self._run_git_command(
396
+ ["git", "fetch", "origin", branch], cwd=repo_path
397
+ )
398
+
399
+ if returncode != 0:
400
+ raise GitOperationError(
401
+ f"Failed to fetch from remote during divergent branch recovery: {stderr}"
402
+ )
403
+
404
+ # Hard reset to remote branch
405
+ returncode, _stdout, stderr = self._run_git_command(
406
+ ["git", "reset", "--hard", f"origin/{branch}"], cwd=repo_path
407
+ )
408
+
409
+ if returncode != 0:
410
+ raise GitOperationError(
411
+ f"Failed to reset to origin/{branch} during divergent branch recovery: {stderr}"
412
+ )
413
+
414
+ logger.info(
415
+ f"Successfully reset cache repository to origin/{branch} "
416
+ "(local changes discarded)"
417
+ )
418
+ return True
419
+
420
+ # Check for merge conflicts (non-divergent case)
421
+ if "conflict" in stderr.lower():
422
+ raise GitConflictError(
423
+ f"Merge conflicts detected when pulling {branch}: {stderr}"
424
+ )
425
+
426
+ # Other pull errors
427
+ raise GitOperationError(f"Failed to pull {branch}: {stderr}")
343
428
 
344
429
  return True
345
430
 
@@ -404,7 +489,7 @@ class GitOperationsService:
404
489
 
405
490
  Example:
406
491
  >>> service = GitOperationsService()
407
- >>> valid, msg = service.validate_repo(Path("~/.claude-mpm/cache/remote-agents"))
492
+ >>> valid, msg = service.validate_repo(Path("~/.claude-mpm/cache/agents"))
408
493
  >>> if not valid:
409
494
  ... print(f"Repository invalid: {msg}")
410
495
  """
@@ -44,6 +44,7 @@ class UnifiedMonitorDaemon:
44
44
  daemon_mode: bool = False,
45
45
  pid_file: Optional[str] = None,
46
46
  log_file: Optional[str] = None,
47
+ enable_hot_reload: bool = False,
47
48
  ):
48
49
  """Initialize the unified monitor daemon.
49
50
 
@@ -53,10 +54,12 @@ class UnifiedMonitorDaemon:
53
54
  daemon_mode: Whether to run as background daemon
54
55
  pid_file: Path to PID file for daemon mode
55
56
  log_file: Path to log file for daemon mode
57
+ enable_hot_reload: Enable file watching and hot reload for development
56
58
  """
57
59
  self.host = host
58
60
  self.port = port
59
61
  self.daemon_mode = daemon_mode
62
+ self.enable_hot_reload = enable_hot_reload
60
63
  self.logger = get_logger(__name__)
61
64
 
62
65
  # Use new consolidated DaemonManager for all daemon operations
@@ -75,7 +78,9 @@ class UnifiedMonitorDaemon:
75
78
  )
76
79
 
77
80
  # Core server
78
- self.server = UnifiedMonitorServer(host=host, port=port)
81
+ self.server = UnifiedMonitorServer(
82
+ host=host, port=port, enable_hot_reload=enable_hot_reload
83
+ )
79
84
 
80
85
  # Health monitoring
81
86
  self.health_monitor = HealthMonitor(port=port)
@@ -510,7 +515,9 @@ class UnifiedMonitorDaemon:
510
515
 
511
516
  # Recreate the server and health monitor after stop() sets them to None
512
517
  self.logger.info(f"Recreating server components for {self.host}:{self.port}")
513
- self.server = UnifiedMonitorServer(host=self.host, port=self.port)
518
+ self.server = UnifiedMonitorServer(
519
+ host=self.host, port=self.port, enable_hot_reload=self.enable_hot_reload
520
+ )
514
521
  self.health_monitor = HealthMonitor(port=self.port)
515
522
 
516
523
  # Reset the shutdown event for the new run
@@ -34,6 +34,11 @@ from typing import Optional, Tuple
34
34
  from ...core.enums import OperationResult
35
35
  from ...core.logging_config import get_logger
36
36
 
37
+ # Exit code constants for signal handling
38
+ EXIT_NORMAL = 0
39
+ EXIT_SIGKILL = 137 # 128 + SIGKILL(9) - forced termination
40
+ EXIT_SIGTERM = 143 # 128 + SIGTERM(15) - graceful shutdown
41
+
37
42
 
38
43
  class DaemonManager:
39
44
  """Centralized manager for all daemon lifecycle operations.
@@ -556,7 +561,7 @@ class DaemonManager:
556
561
  # Use subprocess for clean daemon startup (v4.2.40)
557
562
  # This avoids fork() issues with Python threading
558
563
  if self.use_subprocess_daemon():
559
- return self.start_daemon_subprocess()
564
+ return self.start_daemon_subprocess(force_restart=force_restart)
560
565
  # Fallback to traditional fork (kept for compatibility)
561
566
  return self.daemonize()
562
567
 
@@ -574,12 +579,15 @@ class DaemonManager:
574
579
  # Otherwise, use subprocess for monitor daemon to avoid threading issues
575
580
  return True
576
581
 
577
- def start_daemon_subprocess(self) -> bool:
582
+ def start_daemon_subprocess(self, force_restart: bool = False) -> bool:
578
583
  """Start daemon using subprocess.Popen for clean process isolation.
579
584
 
580
585
  This avoids all the fork() + threading issues by starting the monitor
581
586
  in a completely fresh process with no inherited threads or locks.
582
587
 
588
+ Args:
589
+ force_restart: Whether this is a force restart (helps interpret exit codes)
590
+
583
591
  Returns:
584
592
  True if daemon started successfully
585
593
  """
@@ -652,7 +660,35 @@ class DaemonManager:
652
660
  # Check if process is still running
653
661
  returncode = process.poll()
654
662
  if returncode is not None:
655
- # Process exited - this is the bug we're fixing!
663
+ # Process exited - interpret exit code with context
664
+ # Exit codes 137 (SIGKILL) and 143 (SIGTERM) are common during daemon replacement
665
+ if returncode == EXIT_SIGKILL:
666
+ # SIGKILL - process was forcefully terminated
667
+ if force_restart:
668
+ # This is expected during force restart - old daemon was killed
669
+ self.logger.info(
670
+ f"Previous monitor instance replaced (exit {EXIT_SIGKILL}: SIGKILL during force restart)"
671
+ )
672
+ else:
673
+ # Unexpected SIGKILL - something else killed our new daemon
674
+ self.logger.warning(
675
+ f"Monitor subprocess terminated unexpectedly (exit {EXIT_SIGKILL}: SIGKILL). "
676
+ f"Check {self.log_file} for details."
677
+ )
678
+ return False
679
+ if returncode == EXIT_SIGTERM:
680
+ # SIGTERM - graceful shutdown requested
681
+ self.logger.info(
682
+ f"Monitor subprocess cleanly terminated (exit {EXIT_SIGTERM}: SIGTERM, graceful shutdown)"
683
+ )
684
+ return False
685
+ if returncode == EXIT_NORMAL:
686
+ # Normal exit
687
+ self.logger.info(
688
+ f"Monitor subprocess exited normally (exit code {EXIT_NORMAL})"
689
+ )
690
+ return False
691
+ # Unexpected exit code - this IS an error
656
692
  self.logger.error(
657
693
  f"Monitor daemon subprocess exited prematurely with code {returncode}"
658
694
  )
@@ -14,6 +14,7 @@ DESIGN DECISIONS:
14
14
  """
15
15
 
16
16
  import json
17
+ import logging.handlers
17
18
  import os
18
19
  import signal
19
20
  import socket
@@ -482,7 +483,13 @@ class DaemonLifecycle:
482
483
  # Configure logger to write to file immediately
483
484
  import logging
484
485
 
485
- file_handler = logging.FileHandler(self.log_file)
486
+ # Use RotatingFileHandler for automatic log rotation
487
+ # 5MB max size, 5 backup files (consistent with project logging standards)
488
+ file_handler = logging.handlers.RotatingFileHandler(
489
+ self.log_file,
490
+ maxBytes=5 * 1024 * 1024, # 5MB
491
+ backupCount=5,
492
+ )
486
493
  file_handler.setLevel(logging.DEBUG)
487
494
  formatter = logging.Formatter(
488
495
  "%(asctime)s - %(name)s - %(levelname)s - %(message)s"