claude-mpm 5.1.9__py3-none-any.whl → 5.4.48__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 (248) 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/CLAUDE_MPM_TEACHER_OUTPUT_STYLE.md +1 -1
  5. claude_mpm/agents/MEMORY.md +1 -1
  6. claude_mpm/agents/PM_INSTRUCTIONS.md +843 -900
  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 +2 -2
  11. claude_mpm/agents/templates/circuit-breakers.md +138 -1
  12. claude_mpm/cli/__main__.py +4 -0
  13. claude_mpm/cli/chrome_devtools_installer.py +175 -0
  14. claude_mpm/cli/commands/agent_state_manager.py +18 -27
  15. claude_mpm/cli/commands/agents.py +9 -40
  16. claude_mpm/cli/commands/auto_configure.py +210 -25
  17. claude_mpm/cli/commands/config.py +88 -2
  18. claude_mpm/cli/commands/configure.py +1098 -159
  19. claude_mpm/cli/commands/configure_agent_display.py +25 -6
  20. claude_mpm/cli/commands/mpm_init/core.py +225 -46
  21. claude_mpm/cli/commands/mpm_init/knowledge_extractor.py +481 -0
  22. claude_mpm/cli/commands/mpm_init/prompts.py +280 -0
  23. claude_mpm/cli/commands/postmortem.py +1 -1
  24. claude_mpm/cli/commands/profile.py +277 -0
  25. claude_mpm/cli/commands/skills.py +218 -197
  26. claude_mpm/cli/commands/summarize.py +413 -0
  27. claude_mpm/cli/executor.py +21 -3
  28. claude_mpm/cli/interactive/agent_wizard.py +2 -2
  29. claude_mpm/cli/parsers/agents_parser.py +0 -9
  30. claude_mpm/cli/parsers/auto_configure_parser.py +0 -138
  31. claude_mpm/cli/parsers/base_parser.py +12 -0
  32. claude_mpm/cli/parsers/config_parser.py +153 -83
  33. claude_mpm/cli/parsers/profile_parser.py +148 -0
  34. claude_mpm/cli/parsers/skills_parser.py +0 -5
  35. claude_mpm/cli/startup.py +876 -149
  36. claude_mpm/commands/mpm-config.md +28 -0
  37. claude_mpm/commands/mpm-doctor.md +9 -22
  38. claude_mpm/commands/mpm-help.md +5 -287
  39. claude_mpm/commands/mpm-init.md +81 -507
  40. claude_mpm/commands/mpm-monitor.md +15 -402
  41. claude_mpm/commands/mpm-organize.md +120 -0
  42. claude_mpm/commands/mpm-postmortem.md +6 -108
  43. claude_mpm/commands/mpm-session-resume.md +12 -363
  44. claude_mpm/commands/mpm-status.md +5 -69
  45. claude_mpm/commands/mpm-ticket-view.md +52 -495
  46. claude_mpm/commands/mpm-version.md +5 -107
  47. claude_mpm/config/agent_sources.py +27 -0
  48. claude_mpm/core/config.py +2 -4
  49. claude_mpm/core/framework/formatters/content_formatter.py +3 -13
  50. claude_mpm/core/framework/loaders/agent_loader.py +8 -5
  51. claude_mpm/core/framework/loaders/instruction_loader.py +52 -11
  52. claude_mpm/core/framework_loader.py +4 -2
  53. claude_mpm/core/logger.py +13 -0
  54. claude_mpm/core/optimized_startup.py +59 -0
  55. claude_mpm/core/shared/config_loader.py +1 -1
  56. claude_mpm/core/socketio_pool.py +3 -3
  57. claude_mpm/core/unified_agent_registry.py +5 -15
  58. claude_mpm/dashboard/static/svelte-build/_app/env.js +1 -0
  59. claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/0.B_FtCwCQ.css +1 -0
  60. claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/2.Cl_eSA4x.css +1 -0
  61. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BgChzWQ1.js +1 -0
  62. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CIXEwuWe.js +1 -0
  63. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CWc5urbQ.js +1 -0
  64. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DMkZpdF2.js +2 -0
  65. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DjhvlsAc.js +1 -0
  66. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/N4qtv3Hx.js +2 -0
  67. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/uj46x2Wr.js +1 -0
  68. claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/app.DTL5mJO-.js +2 -0
  69. claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/start.DzuEhzqh.js +1 -0
  70. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/0.CAGBuiOw.js +1 -0
  71. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/1.DFLC8jdE.js +1 -0
  72. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/2.DPvEihJJ.js +10 -0
  73. claude_mpm/dashboard/static/svelte-build/_app/version.json +1 -0
  74. claude_mpm/dashboard/static/svelte-build/favicon.svg +7 -0
  75. claude_mpm/dashboard/static/svelte-build/index.html +36 -0
  76. claude_mpm/hooks/claude_hooks/__pycache__/__init__.cpython-311.pyc +0 -0
  77. claude_mpm/hooks/claude_hooks/__pycache__/correlation_manager.cpython-311.pyc +0 -0
  78. claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-311.pyc +0 -0
  79. claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-311.pyc +0 -0
  80. claude_mpm/hooks/claude_hooks/__pycache__/installer.cpython-311.pyc +0 -0
  81. claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-311.pyc +0 -0
  82. claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-311.pyc +0 -0
  83. claude_mpm/hooks/claude_hooks/__pycache__/tool_analysis.cpython-311.pyc +0 -0
  84. claude_mpm/hooks/claude_hooks/correlation_manager.py +60 -0
  85. claude_mpm/hooks/claude_hooks/event_handlers.py +211 -78
  86. claude_mpm/hooks/claude_hooks/hook_handler.py +155 -1
  87. claude_mpm/hooks/claude_hooks/installer.py +33 -10
  88. claude_mpm/hooks/claude_hooks/memory_integration.py +26 -9
  89. claude_mpm/hooks/claude_hooks/response_tracking.py +2 -3
  90. claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-311.pyc +0 -0
  91. claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager.cpython-311.pyc +0 -0
  92. claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-311.pyc +0 -0
  93. claude_mpm/hooks/claude_hooks/services/__pycache__/duplicate_detector.cpython-311.pyc +0 -0
  94. claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-311.pyc +0 -0
  95. claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-311.pyc +0 -0
  96. claude_mpm/hooks/claude_hooks/services/connection_manager.py +30 -6
  97. claude_mpm/hooks/kuzu_memory_hook.py +5 -5
  98. claude_mpm/hooks/memory_integration_hook.py +46 -1
  99. claude_mpm/init.py +63 -19
  100. claude_mpm/models/git_repository.py +3 -3
  101. claude_mpm/scripts/claude-hook-handler.sh +58 -18
  102. claude_mpm/scripts/launch_monitor.py +93 -13
  103. claude_mpm/services/agents/agent_builder.py +3 -3
  104. claude_mpm/services/agents/agent_recommendation_service.py +278 -0
  105. claude_mpm/services/agents/agent_review_service.py +280 -0
  106. claude_mpm/services/agents/cache_git_manager.py +6 -6
  107. claude_mpm/services/agents/deployment/agent_deployment.py +29 -7
  108. claude_mpm/services/agents/deployment/agent_discovery_service.py +4 -5
  109. claude_mpm/services/agents/deployment/agent_format_converter.py +23 -13
  110. claude_mpm/services/agents/deployment/agent_template_builder.py +32 -20
  111. claude_mpm/services/agents/deployment/agents_directory_resolver.py +2 -2
  112. claude_mpm/services/agents/deployment/async_agent_deployment.py +31 -27
  113. claude_mpm/services/agents/deployment/local_template_deployment.py +3 -1
  114. claude_mpm/services/agents/deployment/multi_source_deployment_service.py +247 -35
  115. claude_mpm/services/agents/deployment/remote_agent_discovery_service.py +392 -87
  116. claude_mpm/services/agents/git_source_manager.py +53 -4
  117. claude_mpm/services/agents/loading/base_agent_manager.py +1 -13
  118. claude_mpm/services/agents/recommender.py +5 -3
  119. claude_mpm/services/agents/single_tier_deployment_service.py +2 -2
  120. claude_mpm/services/agents/sources/git_source_sync_service.py +120 -7
  121. claude_mpm/services/agents/startup_sync.py +22 -2
  122. claude_mpm/services/agents/toolchain_detector.py +10 -6
  123. claude_mpm/services/analysis/__init__.py +11 -1
  124. claude_mpm/services/analysis/clone_detector.py +1030 -0
  125. claude_mpm/services/command_deployment_service.py +81 -10
  126. claude_mpm/services/diagnostics/checks/agent_check.py +2 -2
  127. claude_mpm/services/diagnostics/checks/agent_sources_check.py +1 -1
  128. claude_mpm/services/event_bus/config.py +3 -1
  129. claude_mpm/services/git/git_operations_service.py +101 -16
  130. claude_mpm/services/monitor/daemon.py +9 -2
  131. claude_mpm/services/monitor/daemon_manager.py +39 -3
  132. claude_mpm/services/monitor/management/lifecycle.py +8 -1
  133. claude_mpm/services/monitor/server.py +698 -22
  134. claude_mpm/services/pm_skills_deployer.py +711 -0
  135. claude_mpm/services/profile_manager.py +331 -0
  136. claude_mpm/services/self_upgrade_service.py +120 -12
  137. claude_mpm/services/skills/__init__.py +3 -0
  138. claude_mpm/services/skills/git_skill_source_manager.py +130 -2
  139. claude_mpm/services/skills/selective_skill_deployer.py +704 -0
  140. claude_mpm/services/skills/skill_to_agent_mapper.py +406 -0
  141. claude_mpm/services/skills_deployer.py +127 -9
  142. claude_mpm/services/socketio/dashboard_server.py +1 -0
  143. claude_mpm/services/socketio/event_normalizer.py +51 -6
  144. claude_mpm/services/socketio/server/core.py +386 -108
  145. claude_mpm/services/version_control/git_operations.py +103 -0
  146. claude_mpm/skills/skill_manager.py +92 -3
  147. claude_mpm/utils/agent_dependency_loader.py +14 -2
  148. claude_mpm/utils/agent_filters.py +17 -44
  149. claude_mpm/utils/migration.py +4 -4
  150. claude_mpm/utils/robust_installer.py +47 -3
  151. {claude_mpm-5.1.9.dist-info → claude_mpm-5.4.48.dist-info}/METADATA +53 -87
  152. {claude_mpm-5.1.9.dist-info → claude_mpm-5.4.48.dist-info}/RECORD +157 -197
  153. claude_mpm-5.4.48.dist-info/entry_points.txt +5 -0
  154. claude_mpm-5.4.48.dist-info/licenses/LICENSE +94 -0
  155. claude_mpm-5.4.48.dist-info/licenses/LICENSE-FAQ.md +153 -0
  156. claude_mpm/agents/BASE_AGENT_TEMPLATE.md +0 -292
  157. claude_mpm/agents/BASE_DOCUMENTATION.md +0 -53
  158. claude_mpm/agents/BASE_OPS.md +0 -219
  159. claude_mpm/agents/BASE_PM.md +0 -480
  160. claude_mpm/agents/BASE_PROMPT_ENGINEER.md +0 -787
  161. claude_mpm/agents/BASE_QA.md +0 -167
  162. claude_mpm/agents/BASE_RESEARCH.md +0 -53
  163. claude_mpm/agents/base_agent_loader.py +0 -601
  164. claude_mpm/cli/commands/agents_detect.py +0 -380
  165. claude_mpm/cli/commands/agents_recommend.py +0 -309
  166. claude_mpm/cli/ticket_cli.py +0 -35
  167. claude_mpm/commands/mpm-agents-auto-configure.md +0 -278
  168. claude_mpm/commands/mpm-agents-detect.md +0 -177
  169. claude_mpm/commands/mpm-agents-list.md +0 -131
  170. claude_mpm/commands/mpm-agents-recommend.md +0 -223
  171. claude_mpm/commands/mpm-config-view.md +0 -150
  172. claude_mpm/commands/mpm-ticket-organize.md +0 -304
  173. claude_mpm/dashboard/analysis_runner.py +0 -455
  174. claude_mpm/dashboard/index.html +0 -13
  175. claude_mpm/dashboard/open_dashboard.py +0 -66
  176. claude_mpm/dashboard/static/css/activity.css +0 -1958
  177. claude_mpm/dashboard/static/css/connection-status.css +0 -370
  178. claude_mpm/dashboard/static/css/dashboard.css +0 -4701
  179. claude_mpm/dashboard/static/js/components/activity-tree.js +0 -1871
  180. claude_mpm/dashboard/static/js/components/agent-hierarchy.js +0 -777
  181. claude_mpm/dashboard/static/js/components/agent-inference.js +0 -956
  182. claude_mpm/dashboard/static/js/components/build-tracker.js +0 -333
  183. claude_mpm/dashboard/static/js/components/code-simple.js +0 -857
  184. claude_mpm/dashboard/static/js/components/connection-debug.js +0 -654
  185. claude_mpm/dashboard/static/js/components/diff-viewer.js +0 -891
  186. claude_mpm/dashboard/static/js/components/event-processor.js +0 -542
  187. claude_mpm/dashboard/static/js/components/event-viewer.js +0 -1155
  188. claude_mpm/dashboard/static/js/components/export-manager.js +0 -368
  189. claude_mpm/dashboard/static/js/components/file-change-tracker.js +0 -443
  190. claude_mpm/dashboard/static/js/components/file-change-viewer.js +0 -690
  191. claude_mpm/dashboard/static/js/components/file-tool-tracker.js +0 -724
  192. claude_mpm/dashboard/static/js/components/file-viewer.js +0 -580
  193. claude_mpm/dashboard/static/js/components/hud-library-loader.js +0 -211
  194. claude_mpm/dashboard/static/js/components/hud-manager.js +0 -671
  195. claude_mpm/dashboard/static/js/components/hud-visualizer.js +0 -1718
  196. claude_mpm/dashboard/static/js/components/module-viewer.js +0 -2764
  197. claude_mpm/dashboard/static/js/components/session-manager.js +0 -579
  198. claude_mpm/dashboard/static/js/components/socket-manager.js +0 -368
  199. claude_mpm/dashboard/static/js/components/ui-state-manager.js +0 -749
  200. claude_mpm/dashboard/static/js/components/unified-data-viewer.js +0 -1824
  201. claude_mpm/dashboard/static/js/components/working-directory.js +0 -920
  202. claude_mpm/dashboard/static/js/connection-manager.js +0 -536
  203. claude_mpm/dashboard/static/js/dashboard.js +0 -1914
  204. claude_mpm/dashboard/static/js/extension-error-handler.js +0 -164
  205. claude_mpm/dashboard/static/js/socket-client.js +0 -1474
  206. claude_mpm/dashboard/static/js/tab-isolation-fix.js +0 -185
  207. claude_mpm/dashboard/static/socket.io.min.js +0 -7
  208. claude_mpm/dashboard/static/socket.io.v4.8.1.backup.js +0 -7
  209. claude_mpm/dashboard/templates/code_simple.html +0 -153
  210. claude_mpm/dashboard/templates/index.html +0 -606
  211. claude_mpm/dashboard/test_dashboard.html +0 -372
  212. claude_mpm/scripts/mcp_server.py +0 -75
  213. claude_mpm/scripts/mcp_wrapper.py +0 -39
  214. claude_mpm/services/mcp_gateway/__init__.py +0 -159
  215. claude_mpm/services/mcp_gateway/auto_configure.py +0 -369
  216. claude_mpm/services/mcp_gateway/config/__init__.py +0 -17
  217. claude_mpm/services/mcp_gateway/config/config_loader.py +0 -296
  218. claude_mpm/services/mcp_gateway/config/config_schema.py +0 -243
  219. claude_mpm/services/mcp_gateway/config/configuration.py +0 -429
  220. claude_mpm/services/mcp_gateway/core/__init__.py +0 -43
  221. claude_mpm/services/mcp_gateway/core/base.py +0 -312
  222. claude_mpm/services/mcp_gateway/core/exceptions.py +0 -253
  223. claude_mpm/services/mcp_gateway/core/interfaces.py +0 -443
  224. claude_mpm/services/mcp_gateway/core/process_pool.py +0 -977
  225. claude_mpm/services/mcp_gateway/core/singleton_manager.py +0 -315
  226. claude_mpm/services/mcp_gateway/core/startup_verification.py +0 -316
  227. claude_mpm/services/mcp_gateway/main.py +0 -589
  228. claude_mpm/services/mcp_gateway/registry/__init__.py +0 -12
  229. claude_mpm/services/mcp_gateway/registry/service_registry.py +0 -412
  230. claude_mpm/services/mcp_gateway/registry/tool_registry.py +0 -489
  231. claude_mpm/services/mcp_gateway/server/__init__.py +0 -15
  232. claude_mpm/services/mcp_gateway/server/mcp_gateway.py +0 -414
  233. claude_mpm/services/mcp_gateway/server/stdio_handler.py +0 -372
  234. claude_mpm/services/mcp_gateway/server/stdio_server.py +0 -712
  235. claude_mpm/services/mcp_gateway/tools/__init__.py +0 -36
  236. claude_mpm/services/mcp_gateway/tools/base_adapter.py +0 -485
  237. claude_mpm/services/mcp_gateway/tools/document_summarizer.py +0 -789
  238. claude_mpm/services/mcp_gateway/tools/external_mcp_services.py +0 -654
  239. claude_mpm/services/mcp_gateway/tools/health_check_tool.py +0 -456
  240. claude_mpm/services/mcp_gateway/tools/hello_world.py +0 -551
  241. claude_mpm/services/mcp_gateway/tools/kuzu_memory_service.py +0 -555
  242. claude_mpm/services/mcp_gateway/utils/__init__.py +0 -14
  243. claude_mpm/services/mcp_gateway/utils/package_version_checker.py +0 -160
  244. claude_mpm/services/mcp_gateway/utils/update_preferences.py +0 -170
  245. claude_mpm-5.1.9.dist-info/entry_points.txt +0 -10
  246. claude_mpm-5.1.9.dist-info/licenses/LICENSE +0 -21
  247. {claude_mpm-5.1.9.dist-info → claude_mpm-5.4.48.dist-info}/WHEEL +0 -0
  248. {claude_mpm-5.1.9.dist-info → claude_mpm-5.4.48.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"