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
@@ -88,18 +88,21 @@ fi
88
88
  #
89
89
  # STRATEGY:
90
90
  # This function implements a fallback chain to find Python with claude-mpm dependencies:
91
- # 1. Project-specific virtual environments (venv, .venv)
92
- # 2. Currently active virtual environment ($VIRTUAL_ENV)
93
- # 3. System python3 (may lack dependencies)
94
- # 4. System python (last resort)
91
+ # 1. UV-managed projects (uv.lock detected) - uses "uv run python"
92
+ # 2. pipx installations - uses pipx venv Python
93
+ # 3. Project-specific virtual environments (venv, .venv)
94
+ # 4. Currently active virtual environment ($VIRTUAL_ENV)
95
+ # 5. System python3 (may lack dependencies)
96
+ # 6. System python (last resort)
95
97
  #
96
98
  # WHY THIS APPROACH:
97
99
  # - Claude MPM requires specific packages (socketio, eventlet) not in system Python
98
- # - Virtual environments ensure dependency isolation and availability
100
+ # - UV and virtual environments ensure dependency isolation and availability
99
101
  # - Multiple naming conventions supported (venv vs .venv)
100
102
  # - Graceful degradation to system Python if no venv found
101
103
  #
102
104
  # ACTIVATION STRATEGY:
105
+ # - UV projects: use "uv run python" to execute in UV-managed environment
103
106
  # - Sources activate script to set up environment variables
104
107
  # - Returns specific Python path for exec (not just 'python')
105
108
  # - Maintains environment in same shell process
@@ -110,10 +113,18 @@ fi
110
113
  # - Caches result in process environment
111
114
  #
112
115
  # RETURNS:
113
- # Absolute path to Python executable with claude-mpm dependencies
116
+ # Absolute path to Python executable with claude-mpm dependencies, or "uv run python" for UV projects
114
117
  #
115
118
  find_python_command() {
116
- # 1. Check if we're in a pipx installation first
119
+ # 1. Check for UV project first (uv.lock or pyproject.toml with uv)
120
+ if [ -f "$CLAUDE_MPM_ROOT/uv.lock" ]; then
121
+ if command -v uv &> /dev/null; then
122
+ echo "uv run python"
123
+ return
124
+ fi
125
+ fi
126
+
127
+ # 2. Check if we're in a pipx installation
117
128
  if [[ "$SCRIPT_DIR" == *"/.local/pipx/venvs/claude-mpm/"* ]]; then
118
129
  # pipx installation - use the pipx venv's Python directly
119
130
  if [ -f "$CLAUDE_MPM_ROOT/bin/python" ]; then
@@ -122,7 +133,7 @@ find_python_command() {
122
133
  fi
123
134
  fi
124
135
 
125
- # 2. Check for project-local virtual environment (common in development)
136
+ # 3. Check for project-local virtual environment (common in development)
126
137
  if [ -f "$CLAUDE_MPM_ROOT/venv/bin/activate" ]; then
127
138
  source "$CLAUDE_MPM_ROOT/venv/bin/activate"
128
139
  echo "$CLAUDE_MPM_ROOT/venv/bin/python"
@@ -173,15 +184,44 @@ fi
173
184
  # Set Socket.IO configuration for hook events
174
185
  export CLAUDE_MPM_SOCKETIO_PORT="${CLAUDE_MPM_SOCKETIO_PORT:-8765}"
175
186
 
176
- # Run the Python hook handler with all input
177
- # Use exec to replace the shell process with Python
178
- if ! exec "$PYTHON_CMD" -m claude_mpm.hooks.claude_hooks.hook_handler "$@" 2>/tmp/claude-mpm-hook-error.log; then
179
- # If the Python handler fails, always return continue to not block Claude
187
+ # Function for debug logging
188
+ log_debug() {
180
189
  if [ "${CLAUDE_MPM_HOOK_DEBUG}" = "true" ]; then
181
- echo "[$(date -u +%Y-%m-%dT%H:%M:%S.%3NZ)] Hook handler failed, see /tmp/claude-mpm-hook-error.log" >> /tmp/claude-mpm-hook.log
182
- echo "[$(date -u +%Y-%m-%dT%H:%M:%S.%3NZ)] Error: $(cat /tmp/claude-mpm-hook-error.log 2>/dev/null | head -5)" >> /tmp/claude-mpm-hook.log
190
+ echo "[$(date -u +%Y-%m-%dT%H:%M:%S.%3NZ)] $1" >> /tmp/claude-mpm-hook.log
191
+ fi
192
+ }
193
+
194
+ # Test Python works and module exists
195
+ # Handle UV's multi-word command specially
196
+ if [[ "$PYTHON_CMD" == "uv run python" ]]; then
197
+ if ! uv run python -c "import claude_mpm" 2>/dev/null; then
198
+ log_debug "claude_mpm module not available, continuing without hook"
199
+ echo '{"action": "continue"}'
200
+ exit 0
201
+ fi
202
+ else
203
+ if ! $PYTHON_CMD -c "import claude_mpm" 2>/dev/null; then
204
+ log_debug "claude_mpm module not available, continuing without hook"
205
+ echo '{"action": "continue"}'
206
+ exit 0
183
207
  fi
184
- # Return continue action to prevent blocking Claude Code
185
- echo '{"action": "continue"}'
186
- exit 0
187
- fi
208
+ fi
209
+
210
+ # Run the Python hook handler with all input
211
+ # Use exec to replace the shell process with Python
212
+ # Handle UV's multi-word command specially
213
+ if [[ "$PYTHON_CMD" == "uv run python" ]]; then
214
+ exec uv run python -m claude_mpm.hooks.claude_hooks.hook_handler "$@" 2>/tmp/claude-mpm-hook-error.log
215
+ else
216
+ exec "$PYTHON_CMD" -m claude_mpm.hooks.claude_hooks.hook_handler "$@" 2>/tmp/claude-mpm-hook-error.log
217
+ fi
218
+
219
+ # Note: exec replaces the shell process, so code below only runs if exec fails
220
+ # If we reach here, the Python handler failed
221
+ if [ "${CLAUDE_MPM_HOOK_DEBUG}" = "true" ]; then
222
+ echo "[$(date -u +%Y-%m-%dT%H:%M:%S.%3NZ)] Hook handler failed, see /tmp/claude-mpm-hook-error.log" >> /tmp/claude-mpm-hook.log
223
+ echo "[$(date -u +%Y-%m-%dT%H:%M:%S.%3NZ)] Error: $(cat /tmp/claude-mpm-hook-error.log 2>/dev/null | head -5)" >> /tmp/claude-mpm-hook.log
224
+ fi
225
+ # Return continue action to prevent blocking Claude Code
226
+ echo '{"action": "continue"}'
227
+ exit 0
@@ -7,6 +7,12 @@ dashboard, which includes both the Socket.IO server and web interface.
7
7
 
8
8
  WHY: Provides a simple command to start the monitoring dashboard that tracks
9
9
  Claude MPM events and agent activity in real-time.
10
+
11
+ SINGLE INSTANCE ENFORCEMENT:
12
+ - Only ONE monitor instance runs at a time on port 8765 (default)
13
+ - If monitor already running on default port: reuse existing, open browser
14
+ - If user specifies --port explicitly: use that port, fail if busy
15
+ - No auto-increment port selection (prevents multiple instances)
10
16
  """
11
17
 
12
18
  import argparse
@@ -15,12 +21,36 @@ import webbrowser
15
21
 
16
22
  from claude_mpm.core.logging_config import get_logger
17
23
  from claude_mpm.services.monitor.daemon import UnifiedMonitorDaemon
18
- from claude_mpm.services.port_manager import PortManager
24
+ from claude_mpm.services.monitor.daemon_manager import DaemonManager
19
25
 
20
26
  DEFAULT_PORT = 8765
21
27
  logger = get_logger(__name__)
22
28
 
23
29
 
30
+ def check_existing_monitor(host: str, port: int) -> bool:
31
+ """Check if monitor is already running on the specified port.
32
+
33
+ Args:
34
+ host: Host to check
35
+ port: Port to check
36
+
37
+ Returns:
38
+ True if monitor is running, False otherwise
39
+ """
40
+ try:
41
+ import requests
42
+
43
+ response = requests.get(f"http://{host}:{port}/health", timeout=2)
44
+ if response.status_code == 200:
45
+ data = response.json()
46
+ # Check if it's our claude-mpm-monitor service
47
+ if data.get("service") == "claude-mpm-monitor":
48
+ return True
49
+ except Exception:
50
+ pass
51
+ return False
52
+
53
+
24
54
  def main():
25
55
  """Main entry point for monitor launcher."""
26
56
  parser = argparse.ArgumentParser(
@@ -30,8 +60,8 @@ def main():
30
60
  parser.add_argument(
31
61
  "--port",
32
62
  type=int,
33
- default=DEFAULT_PORT,
34
- help=f"Port to run on (default: {DEFAULT_PORT})",
63
+ default=None, # Changed: None means use DEFAULT_PORT with single-instance check
64
+ help=f"Port to run on (default: {DEFAULT_PORT}). If specified, fails if port is busy.",
35
65
  )
36
66
 
37
67
  parser.add_argument(
@@ -46,20 +76,70 @@ def main():
46
76
  "--background", action="store_true", help="Run in background daemon mode"
47
77
  )
48
78
 
79
+ parser.add_argument(
80
+ "--dev",
81
+ action="store_true",
82
+ help="Enable development mode with hot reload for Svelte changes",
83
+ )
84
+
49
85
  args = parser.parse_args()
50
86
 
51
- # Find available port
52
- port_manager = PortManager()
53
- actual_port = port_manager.find_available_port(preferred_port=args.port)
87
+ # Determine target port
88
+ user_specified_port = args.port is not None
89
+ target_port = args.port if user_specified_port else DEFAULT_PORT
90
+
91
+ # SINGLE INSTANCE ENFORCEMENT:
92
+ # Check if monitor already running on target port
93
+ if check_existing_monitor(args.host, target_port):
94
+ logger.info(f"Monitor already running at http://{args.host}:{target_port}")
54
95
 
55
- if actual_port != args.port:
56
- logger.info(f"Port {args.port} is in use, using port {actual_port} instead")
96
+ # Open browser to existing instance if requested
97
+ if not args.no_browser:
98
+ url = f"http://{args.host}:{target_port}"
99
+ logger.info(f"Opening browser to existing instance: {url}")
100
+ webbrowser.open(url)
101
+
102
+ # Success - reusing existing instance
103
+ return
104
+
105
+ # Port selection logic:
106
+ # - If user specified --port: Use that exact port, fail if busy
107
+ # - If no --port: Use DEFAULT_PORT (8765), fail if busy
108
+ # - Never auto-increment to find free port
109
+
110
+ # Create daemon manager for port checking
111
+ daemon_manager = DaemonManager(port=target_port, host=args.host)
112
+
113
+ if not daemon_manager._is_port_available():
114
+ if user_specified_port:
115
+ # User explicitly requested a port - fail with clear message
116
+ logger.error(
117
+ f"Port {target_port} is already in use by another service. "
118
+ f"Please stop the existing service or choose a different port."
119
+ )
120
+ sys.exit(1)
121
+ else:
122
+ # Default port is busy - fail with helpful message
123
+ logger.error(
124
+ f"Default port {DEFAULT_PORT} is already in use by another service. "
125
+ f"Please stop the existing service with 'claude-mpm monitor stop' "
126
+ f"or specify a different port with --port."
127
+ )
128
+ sys.exit(1)
57
129
 
58
130
  # Start the monitor daemon
59
- logger.info(f"Starting Claude MPM monitor on {args.host}:{actual_port}")
131
+ if args.dev:
132
+ logger.info(
133
+ f"Starting Claude MPM monitor on {args.host}:{target_port} (DEV MODE - hot reload enabled)"
134
+ )
135
+ else:
136
+ logger.info(f"Starting Claude MPM monitor on {args.host}:{target_port}")
60
137
 
61
138
  daemon = UnifiedMonitorDaemon(
62
- host=args.host, port=actual_port, daemon_mode=args.background
139
+ host=args.host,
140
+ port=target_port,
141
+ daemon_mode=args.background,
142
+ enable_hot_reload=args.dev,
63
143
  )
64
144
 
65
145
  success = daemon.start()
@@ -67,14 +147,14 @@ def main():
67
147
  if success:
68
148
  # Open browser if requested
69
149
  if not args.no_browser:
70
- url = f"http://{args.host}:{actual_port}"
150
+ url = f"http://{args.host}:{target_port}"
71
151
  logger.info(f"Opening browser to {url}")
72
152
  webbrowser.open(url)
73
153
 
74
154
  if args.background:
75
- logger.info(f"Monitor daemon started in background on port {actual_port}")
155
+ logger.info(f"Monitor daemon started in background on port {target_port}")
76
156
  else:
77
- logger.info(f"Monitor running on port {actual_port}")
157
+ logger.info(f"Monitor running on port {target_port}")
78
158
  logger.info("Press Ctrl+C to stop")
79
159
  else:
80
160
  logger.error("Failed to start monitor")
File without changes
@@ -206,8 +206,8 @@ class AgentBuilderService:
206
206
  """
207
207
  errors = []
208
208
 
209
- # Required fields
210
- required_fields = ["id", "name", "prompt", "model"]
209
+ # Required fields (model is optional - defaults to sonnet if not specified)
210
+ required_fields = ["id", "name", "prompt"]
211
211
  for field in required_fields:
212
212
  if field not in config:
213
213
  errors.append(f"Missing required field: {field}")
@@ -219,7 +219,7 @@ class AgentBuilderService:
219
219
  except AgentDeploymentError as e:
220
220
  errors.append(str(e))
221
221
 
222
- # Validate model
222
+ # Validate model (only if present)
223
223
  if "model" in config:
224
224
  try:
225
225
  self._validate_model(config["model"])
@@ -0,0 +1,278 @@
1
+ """Agent Recommendation Service
2
+
3
+ WHY: Provides intelligent agent recommendations based on toolchain detection
4
+ and always-recommended core agents. Helps users discover and install the
5
+ most relevant agents for their project without manual selection.
6
+
7
+ DESIGN DECISION: Uses toolchain analysis to map detected languages/frameworks
8
+ to specific engineer agents, plus always includes core agents.
9
+
10
+ Architecture:
11
+ - Toolchain-based recommendations: Python → python-engineer, etc.
12
+ - Core agents (always recommended): engineer, qa-agent, memory-manager-agent, local-ops-agent,
13
+ research-agent, documentation-agent, security-agent
14
+ - Confidence-based filtering: Only recommend high-confidence detections
15
+ """
16
+
17
+ from pathlib import Path
18
+ from typing import Dict, List, Optional, Set
19
+
20
+ from ...services.project.toolchain_analyzer import ToolchainAnalyzerService
21
+
22
+
23
+ class AgentRecommendationService:
24
+ """Service for recommending agents based on project toolchain.
25
+
26
+ WHY: Users shouldn't have to manually figure out which agents to install.
27
+ This service provides intelligent recommendations based on detected stack.
28
+
29
+ DESIGN DECISION: Separated from configure.py for reusability and testability.
30
+ Can be used by CLI, API, or future auto-configuration features.
31
+ """
32
+
33
+ # Core agents always included - matches ToolchainDetector.CORE_AGENTS
34
+ # Uses exact agent IDs from repository for consistency
35
+ CORE_AGENTS = {
36
+ "engineer",
37
+ "qa-agent",
38
+ "memory-manager-agent",
39
+ "local-ops-agent",
40
+ "research-agent",
41
+ "documentation-agent",
42
+ "security-agent",
43
+ }
44
+
45
+ # Map detected languages to recommended engineer agents
46
+ LANGUAGE_TO_AGENTS: Dict[str, List[str]] = {
47
+ "python": [
48
+ "engineer/backend/python-engineer",
49
+ "qa/api-qa",
50
+ ],
51
+ "javascript": [
52
+ "engineer/backend/javascript-engineer",
53
+ "engineer/data/typescript-engineer",
54
+ ],
55
+ "typescript": [
56
+ "engineer/data/typescript-engineer",
57
+ "engineer/backend/javascript-engineer",
58
+ ],
59
+ "rust": [
60
+ "engineer/backend/rust-engineer",
61
+ ],
62
+ "go": [
63
+ "engineer/backend/golang-engineer",
64
+ ],
65
+ "java": [
66
+ "engineer/backend/java-engineer",
67
+ ],
68
+ "dart": [
69
+ "engineer/mobile/dart-engineer",
70
+ ],
71
+ "php": [
72
+ "engineer/backend/php-engineer",
73
+ ],
74
+ "ruby": [
75
+ "engineer/backend/ruby-engineer",
76
+ ],
77
+ "swift": [
78
+ "engineer/mobile/swift-engineer",
79
+ ],
80
+ "kotlin": [
81
+ "engineer/mobile/kotlin-engineer",
82
+ ],
83
+ }
84
+
85
+ # Map detected frameworks to recommended agents
86
+ FRAMEWORK_TO_AGENTS: Dict[str, List[str]] = {
87
+ # Frontend frameworks
88
+ "react": [
89
+ "engineer/frontend/react-engineer",
90
+ "qa/web-qa",
91
+ ],
92
+ "nextjs": [
93
+ "engineer/frontend/nextjs-engineer",
94
+ "engineer/frontend/react-engineer",
95
+ "ops/platform/vercel-ops",
96
+ ],
97
+ "vue": [
98
+ "engineer/frontend/vue-engineer",
99
+ ],
100
+ "angular": [
101
+ "engineer/frontend/angular-engineer",
102
+ ],
103
+ # Backend frameworks
104
+ "fastapi": [
105
+ "engineer/backend/python-engineer",
106
+ "qa/api-qa",
107
+ ],
108
+ "django": [
109
+ "engineer/backend/python-engineer",
110
+ "qa/api-qa",
111
+ ],
112
+ "flask": [
113
+ "engineer/backend/python-engineer",
114
+ "qa/api-qa",
115
+ ],
116
+ "express": [
117
+ "engineer/backend/javascript-engineer",
118
+ "qa/api-qa",
119
+ ],
120
+ "nest": [
121
+ "engineer/backend/javascript-engineer",
122
+ "qa/api-qa",
123
+ ],
124
+ # Mobile frameworks
125
+ "flutter": [
126
+ "engineer/mobile/dart-engineer",
127
+ ],
128
+ "react-native": [
129
+ "engineer/frontend/react-engineer",
130
+ "engineer/mobile/react-native-engineer",
131
+ ],
132
+ # Desktop frameworks
133
+ "tauri": [
134
+ "engineer/mobile/tauri-engineer",
135
+ "engineer/backend/rust-engineer",
136
+ ],
137
+ "electron": [
138
+ "engineer/backend/javascript-engineer",
139
+ ],
140
+ }
141
+
142
+ def __init__(self, toolchain_analyzer: Optional[ToolchainAnalyzerService] = None):
143
+ """Initialize agent recommendation service.
144
+
145
+ Args:
146
+ toolchain_analyzer: Optional pre-initialized toolchain analyzer.
147
+ If None, creates a new instance.
148
+ """
149
+ self.toolchain_analyzer = toolchain_analyzer or ToolchainAnalyzerService()
150
+
151
+ def get_recommended_agents(
152
+ self,
153
+ project_path: Optional[str] = None,
154
+ confidence_threshold: float = 0.5,
155
+ ) -> Set[str]:
156
+ """Get recommended agents for a project.
157
+
158
+ Args:
159
+ project_path: Path to project directory. Defaults to cwd.
160
+ confidence_threshold: Minimum confidence for recommendations (0.0-1.0).
161
+ Only include detected components above this threshold.
162
+
163
+ Returns:
164
+ Set of recommended agent IDs (e.g., {"qa-agent", "research-agent", ...})
165
+
166
+ Example:
167
+ >>> service = AgentRecommendationService()
168
+ >>> recommended = service.get_recommended_agents()
169
+ >>> "qa-agent" in recommended
170
+ True
171
+ >>> # For Python project:
172
+ >>> "engineer/backend/python-engineer" in recommended
173
+ True
174
+ """
175
+ # Start with core agents (always recommended)
176
+ recommended = self.CORE_AGENTS.copy()
177
+
178
+ # Analyze project toolchain
179
+ if project_path is None:
180
+ project_path = str(Path.cwd())
181
+
182
+ try:
183
+ # ToolchainAnalyzerService.analyze_toolchain takes Path, not str
184
+ analysis = self.toolchain_analyzer.analyze_toolchain(Path(project_path))
185
+ except Exception as e:
186
+ # If analysis fails, just return core agents
187
+ print(f"Warning: Toolchain analysis failed: {e}")
188
+ return recommended
189
+
190
+ # Add language-specific agents
191
+ # ToolchainAnalysis has a single LanguageDetection object, not a list
192
+ if analysis.language_detection:
193
+ # Check primary language
194
+ primary_lang = analysis.language_detection.primary_language.lower()
195
+ if primary_lang in self.LANGUAGE_TO_AGENTS:
196
+ recommended.update(self.LANGUAGE_TO_AGENTS[primary_lang])
197
+
198
+ # Check secondary languages
199
+ for lang_component in analysis.language_detection.secondary_languages:
200
+ lang = lang_component.name.lower()
201
+ if lang in self.LANGUAGE_TO_AGENTS:
202
+ recommended.update(self.LANGUAGE_TO_AGENTS[lang])
203
+
204
+ # Add framework-specific agents
205
+ for framework in analysis.frameworks:
206
+ fw_name = framework.name.lower()
207
+ if fw_name in self.FRAMEWORK_TO_AGENTS:
208
+ recommended.update(self.FRAMEWORK_TO_AGENTS[fw_name])
209
+
210
+ return recommended
211
+
212
+ def get_detection_summary(self, project_path: Optional[str] = None) -> Dict:
213
+ """Get human-readable summary of detected toolchain.
214
+
215
+ Args:
216
+ project_path: Path to project directory. Defaults to cwd.
217
+
218
+ Returns:
219
+ Dict with keys:
220
+ - detected_languages: List of detected language names
221
+ - detected_frameworks: List of detected framework names
222
+ - recommended_count: Number of recommended agents
223
+ - detection_quality: "high", "medium", "low", or "none"
224
+
225
+ Example:
226
+ >>> summary = service.get_detection_summary()
227
+ >>> summary['detected_languages']
228
+ ['Python', 'JavaScript']
229
+ >>> summary['recommended_count']
230
+ 15
231
+ """
232
+ if project_path is None:
233
+ project_path = str(Path.cwd())
234
+
235
+ try:
236
+ analysis = self.toolchain_analyzer.analyze_toolchain(Path(project_path))
237
+
238
+ # Extract languages from LanguageDetection object
239
+ languages = []
240
+ if analysis.language_detection:
241
+ languages.append(analysis.language_detection.primary_language)
242
+ languages.extend(
243
+ comp.name
244
+ for comp in analysis.language_detection.secondary_languages
245
+ )
246
+
247
+ # Extract frameworks
248
+ frameworks = [fw.name for fw in analysis.frameworks]
249
+
250
+ # Get recommended agents
251
+ recommended = self.get_recommended_agents(project_path)
252
+
253
+ # Determine detection quality from overall_confidence
254
+ confidence_map = {
255
+ "high": "high",
256
+ "medium": "medium",
257
+ "low": "low",
258
+ "very_low": "low",
259
+ }
260
+ quality = confidence_map.get(
261
+ str(analysis.overall_confidence).lower(), "unknown"
262
+ )
263
+
264
+ return {
265
+ "detected_languages": languages,
266
+ "detected_frameworks": frameworks,
267
+ "recommended_count": len(recommended),
268
+ "detection_quality": quality,
269
+ }
270
+ except Exception as e:
271
+ # Log the error for debugging
272
+ print(f"Warning: Toolchain analysis failed: {e}")
273
+ return {
274
+ "detected_languages": [],
275
+ "detected_frameworks": [],
276
+ "recommended_count": len(self.CORE_AGENTS),
277
+ "detection_quality": "none",
278
+ }