claude-mpm 5.1.9__py3-none-any.whl → 5.4.22__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.
- claude_mpm/VERSION +1 -1
- claude_mpm/__init__.py +4 -0
- claude_mpm/agents/CLAUDE_MPM_TEACHER_OUTPUT_STYLE.md +1 -1
- claude_mpm/agents/PM_INSTRUCTIONS.md +290 -34
- claude_mpm/agents/agent_loader.py +13 -44
- claude_mpm/agents/templates/circuit-breakers.md +138 -1
- claude_mpm/cli/__main__.py +4 -0
- claude_mpm/cli/chrome_devtools_installer.py +175 -0
- claude_mpm/cli/commands/agent_state_manager.py +8 -17
- claude_mpm/cli/commands/agents.py +0 -31
- claude_mpm/cli/commands/auto_configure.py +210 -25
- claude_mpm/cli/commands/config.py +88 -2
- claude_mpm/cli/commands/configure.py +1097 -158
- claude_mpm/cli/commands/configure_agent_display.py +15 -6
- claude_mpm/cli/commands/mpm_init/core.py +160 -46
- claude_mpm/cli/commands/mpm_init/knowledge_extractor.py +481 -0
- claude_mpm/cli/commands/mpm_init/prompts.py +280 -0
- claude_mpm/cli/commands/skills.py +214 -189
- claude_mpm/cli/commands/summarize.py +413 -0
- claude_mpm/cli/executor.py +11 -3
- claude_mpm/cli/parsers/agents_parser.py +0 -9
- claude_mpm/cli/parsers/auto_configure_parser.py +0 -138
- claude_mpm/cli/parsers/base_parser.py +5 -0
- claude_mpm/cli/parsers/config_parser.py +153 -83
- claude_mpm/cli/parsers/skills_parser.py +3 -2
- claude_mpm/cli/startup.py +550 -94
- claude_mpm/commands/mpm-config.md +265 -0
- claude_mpm/commands/mpm-help.md +14 -95
- claude_mpm/commands/mpm-organize.md +500 -0
- claude_mpm/config/agent_sources.py +27 -0
- claude_mpm/core/framework/formatters/content_formatter.py +3 -13
- claude_mpm/core/framework/loaders/agent_loader.py +8 -5
- claude_mpm/core/framework_loader.py +4 -2
- claude_mpm/core/logger.py +13 -0
- claude_mpm/core/socketio_pool.py +3 -3
- claude_mpm/core/unified_agent_registry.py +5 -15
- claude_mpm/hooks/claude_hooks/correlation_manager.py +60 -0
- claude_mpm/hooks/claude_hooks/event_handlers.py +211 -78
- claude_mpm/hooks/claude_hooks/hook_handler.py +6 -0
- claude_mpm/hooks/claude_hooks/installer.py +33 -10
- claude_mpm/hooks/claude_hooks/memory_integration.py +26 -9
- claude_mpm/hooks/claude_hooks/response_tracking.py +2 -3
- claude_mpm/hooks/claude_hooks/services/connection_manager.py +4 -0
- claude_mpm/hooks/memory_integration_hook.py +46 -1
- claude_mpm/init.py +0 -19
- claude_mpm/scripts/claude-hook-handler.sh +58 -18
- claude_mpm/scripts/launch_monitor.py +93 -13
- claude_mpm/scripts/start_activity_logging.py +0 -0
- claude_mpm/services/agents/agent_recommendation_service.py +278 -0
- claude_mpm/services/agents/agent_review_service.py +280 -0
- claude_mpm/services/agents/deployment/agent_discovery_service.py +2 -3
- claude_mpm/services/agents/deployment/agent_template_builder.py +4 -2
- claude_mpm/services/agents/deployment/multi_source_deployment_service.py +78 -9
- claude_mpm/services/agents/deployment/remote_agent_discovery_service.py +335 -53
- claude_mpm/services/agents/git_source_manager.py +34 -0
- claude_mpm/services/agents/loading/base_agent_manager.py +1 -13
- claude_mpm/services/agents/sources/git_source_sync_service.py +8 -1
- claude_mpm/services/agents/toolchain_detector.py +10 -6
- claude_mpm/services/analysis/__init__.py +11 -1
- claude_mpm/services/analysis/clone_detector.py +1030 -0
- claude_mpm/services/command_deployment_service.py +81 -10
- claude_mpm/services/event_bus/config.py +3 -1
- claude_mpm/services/git/git_operations_service.py +93 -8
- claude_mpm/services/monitor/daemon.py +9 -2
- claude_mpm/services/monitor/daemon_manager.py +39 -3
- claude_mpm/services/monitor/server.py +225 -19
- claude_mpm/services/self_upgrade_service.py +120 -12
- claude_mpm/services/skills/__init__.py +3 -0
- claude_mpm/services/skills/git_skill_source_manager.py +32 -2
- claude_mpm/services/skills/selective_skill_deployer.py +704 -0
- claude_mpm/services/skills/skill_to_agent_mapper.py +406 -0
- claude_mpm/services/skills_deployer.py +126 -9
- claude_mpm/services/socketio/event_normalizer.py +15 -1
- claude_mpm/services/socketio/server/core.py +160 -21
- claude_mpm/services/version_control/git_operations.py +103 -0
- claude_mpm/utils/agent_filters.py +17 -44
- {claude_mpm-5.1.9.dist-info → claude_mpm-5.4.22.dist-info}/METADATA +47 -84
- {claude_mpm-5.1.9.dist-info → claude_mpm-5.4.22.dist-info}/RECORD +82 -161
- claude_mpm-5.4.22.dist-info/entry_points.txt +5 -0
- claude_mpm-5.4.22.dist-info/licenses/LICENSE +94 -0
- claude_mpm-5.4.22.dist-info/licenses/LICENSE-FAQ.md +153 -0
- claude_mpm/agents/BASE_AGENT_TEMPLATE.md +0 -292
- claude_mpm/agents/BASE_DOCUMENTATION.md +0 -53
- claude_mpm/agents/BASE_ENGINEER.md +0 -658
- claude_mpm/agents/BASE_OPS.md +0 -219
- claude_mpm/agents/BASE_PM.md +0 -480
- claude_mpm/agents/BASE_PROMPT_ENGINEER.md +0 -787
- claude_mpm/agents/BASE_QA.md +0 -167
- claude_mpm/agents/BASE_RESEARCH.md +0 -53
- claude_mpm/agents/base_agent.json +0 -31
- claude_mpm/agents/base_agent_loader.py +0 -601
- claude_mpm/cli/commands/agents_detect.py +0 -380
- claude_mpm/cli/commands/agents_recommend.py +0 -309
- claude_mpm/cli/ticket_cli.py +0 -35
- claude_mpm/commands/mpm-agents-auto-configure.md +0 -278
- claude_mpm/commands/mpm-agents-detect.md +0 -177
- claude_mpm/commands/mpm-agents-list.md +0 -131
- claude_mpm/commands/mpm-agents-recommend.md +0 -223
- claude_mpm/commands/mpm-config-view.md +0 -150
- claude_mpm/commands/mpm-ticket-organize.md +0 -304
- claude_mpm/dashboard/analysis_runner.py +0 -455
- claude_mpm/dashboard/index.html +0 -13
- claude_mpm/dashboard/open_dashboard.py +0 -66
- claude_mpm/dashboard/static/css/activity.css +0 -1958
- claude_mpm/dashboard/static/css/connection-status.css +0 -370
- claude_mpm/dashboard/static/css/dashboard.css +0 -4701
- claude_mpm/dashboard/static/js/components/activity-tree.js +0 -1871
- claude_mpm/dashboard/static/js/components/agent-hierarchy.js +0 -777
- claude_mpm/dashboard/static/js/components/agent-inference.js +0 -956
- claude_mpm/dashboard/static/js/components/build-tracker.js +0 -333
- claude_mpm/dashboard/static/js/components/code-simple.js +0 -857
- claude_mpm/dashboard/static/js/components/connection-debug.js +0 -654
- claude_mpm/dashboard/static/js/components/diff-viewer.js +0 -891
- claude_mpm/dashboard/static/js/components/event-processor.js +0 -542
- claude_mpm/dashboard/static/js/components/event-viewer.js +0 -1155
- claude_mpm/dashboard/static/js/components/export-manager.js +0 -368
- claude_mpm/dashboard/static/js/components/file-change-tracker.js +0 -443
- claude_mpm/dashboard/static/js/components/file-change-viewer.js +0 -690
- claude_mpm/dashboard/static/js/components/file-tool-tracker.js +0 -724
- claude_mpm/dashboard/static/js/components/file-viewer.js +0 -580
- claude_mpm/dashboard/static/js/components/hud-library-loader.js +0 -211
- claude_mpm/dashboard/static/js/components/hud-manager.js +0 -671
- claude_mpm/dashboard/static/js/components/hud-visualizer.js +0 -1718
- claude_mpm/dashboard/static/js/components/module-viewer.js +0 -2764
- claude_mpm/dashboard/static/js/components/session-manager.js +0 -579
- claude_mpm/dashboard/static/js/components/socket-manager.js +0 -368
- claude_mpm/dashboard/static/js/components/ui-state-manager.js +0 -749
- claude_mpm/dashboard/static/js/components/unified-data-viewer.js +0 -1824
- claude_mpm/dashboard/static/js/components/working-directory.js +0 -920
- claude_mpm/dashboard/static/js/connection-manager.js +0 -536
- claude_mpm/dashboard/static/js/dashboard.js +0 -1914
- claude_mpm/dashboard/static/js/extension-error-handler.js +0 -164
- claude_mpm/dashboard/static/js/socket-client.js +0 -1474
- claude_mpm/dashboard/static/js/tab-isolation-fix.js +0 -185
- claude_mpm/dashboard/static/socket.io.min.js +0 -7
- claude_mpm/dashboard/static/socket.io.v4.8.1.backup.js +0 -7
- claude_mpm/dashboard/templates/code_simple.html +0 -153
- claude_mpm/dashboard/templates/index.html +0 -606
- claude_mpm/dashboard/test_dashboard.html +0 -372
- claude_mpm/scripts/mcp_server.py +0 -75
- claude_mpm/scripts/mcp_wrapper.py +0 -39
- claude_mpm/services/mcp_gateway/__init__.py +0 -159
- claude_mpm/services/mcp_gateway/auto_configure.py +0 -369
- claude_mpm/services/mcp_gateway/config/__init__.py +0 -17
- claude_mpm/services/mcp_gateway/config/config_loader.py +0 -296
- claude_mpm/services/mcp_gateway/config/config_schema.py +0 -243
- claude_mpm/services/mcp_gateway/config/configuration.py +0 -429
- claude_mpm/services/mcp_gateway/core/__init__.py +0 -43
- claude_mpm/services/mcp_gateway/core/base.py +0 -312
- claude_mpm/services/mcp_gateway/core/exceptions.py +0 -253
- claude_mpm/services/mcp_gateway/core/interfaces.py +0 -443
- claude_mpm/services/mcp_gateway/core/process_pool.py +0 -977
- claude_mpm/services/mcp_gateway/core/singleton_manager.py +0 -315
- claude_mpm/services/mcp_gateway/core/startup_verification.py +0 -316
- claude_mpm/services/mcp_gateway/main.py +0 -589
- claude_mpm/services/mcp_gateway/registry/__init__.py +0 -12
- claude_mpm/services/mcp_gateway/registry/service_registry.py +0 -412
- claude_mpm/services/mcp_gateway/registry/tool_registry.py +0 -489
- claude_mpm/services/mcp_gateway/server/__init__.py +0 -15
- claude_mpm/services/mcp_gateway/server/mcp_gateway.py +0 -414
- claude_mpm/services/mcp_gateway/server/stdio_handler.py +0 -372
- claude_mpm/services/mcp_gateway/server/stdio_server.py +0 -712
- claude_mpm/services/mcp_gateway/tools/__init__.py +0 -36
- claude_mpm/services/mcp_gateway/tools/base_adapter.py +0 -485
- claude_mpm/services/mcp_gateway/tools/document_summarizer.py +0 -789
- claude_mpm/services/mcp_gateway/tools/external_mcp_services.py +0 -654
- claude_mpm/services/mcp_gateway/tools/health_check_tool.py +0 -456
- claude_mpm/services/mcp_gateway/tools/hello_world.py +0 -551
- claude_mpm/services/mcp_gateway/tools/kuzu_memory_service.py +0 -555
- claude_mpm/services/mcp_gateway/utils/__init__.py +0 -14
- claude_mpm/services/mcp_gateway/utils/package_version_checker.py +0 -160
- claude_mpm/services/mcp_gateway/utils/update_preferences.py +0 -170
- claude_mpm-5.1.9.dist-info/entry_points.txt +0 -10
- claude_mpm-5.1.9.dist-info/licenses/LICENSE +0 -21
- {claude_mpm-5.1.9.dist-info → claude_mpm-5.4.22.dist-info}/WHEEL +0 -0
- {claude_mpm-5.1.9.dist-info → claude_mpm-5.4.22.dist-info}/top_level.txt +0 -0
|
@@ -18,8 +18,7 @@ DEBUG = os.environ.get("CLAUDE_MPM_HOOK_DEBUG", "true").lower() != "false"
|
|
|
18
18
|
|
|
19
19
|
# Response tracking integration
|
|
20
20
|
# NOTE: ResponseTracker import moved to _initialize_response_tracking() for lazy loading
|
|
21
|
-
# This prevents unnecessary import of
|
|
22
|
-
# when hooks don't need response tracking
|
|
21
|
+
# This prevents unnecessary import of heavy dependencies when hooks don't need response tracking
|
|
23
22
|
RESPONSE_TRACKING_AVAILABLE = (
|
|
24
23
|
True # Assume available, will check on actual initialization
|
|
25
24
|
)
|
|
@@ -51,7 +50,7 @@ class ResponseTrackingManager:
|
|
|
51
50
|
response tracking without code changes.
|
|
52
51
|
|
|
53
52
|
NOTE: ResponseTracker is imported lazily here to avoid loading
|
|
54
|
-
|
|
53
|
+
heavy dependencies unless actually needed.
|
|
55
54
|
"""
|
|
56
55
|
try:
|
|
57
56
|
# Lazy import of ResponseTracker to avoid unnecessary dependency loading
|
|
@@ -115,6 +115,9 @@ class ConnectionManagerService:
|
|
|
115
115
|
- Fallback: HTTP POST for reliability when direct connection fails
|
|
116
116
|
- Eliminates duplicate events from multiple emission paths
|
|
117
117
|
"""
|
|
118
|
+
# Extract tool_call_id from data if present for correlation
|
|
119
|
+
tool_call_id = data.get("tool_call_id")
|
|
120
|
+
|
|
118
121
|
# Create event data for normalization
|
|
119
122
|
raw_event = {
|
|
120
123
|
"type": "hook",
|
|
@@ -123,6 +126,7 @@ class ConnectionManagerService:
|
|
|
123
126
|
"data": data,
|
|
124
127
|
"source": "claude_hooks", # Identify the source
|
|
125
128
|
"session_id": data.get("sessionId"), # Include session if available
|
|
129
|
+
"correlation_id": tool_call_id, # Set from tool_call_id for event correlation
|
|
126
130
|
}
|
|
127
131
|
|
|
128
132
|
# Normalize the event using EventNormalizer for consistent schema
|
|
@@ -13,6 +13,7 @@ agent outputs because:
|
|
|
13
13
|
"""
|
|
14
14
|
|
|
15
15
|
import re
|
|
16
|
+
from datetime import datetime
|
|
16
17
|
from typing import Dict, List
|
|
17
18
|
|
|
18
19
|
from claude_mpm.core.config import Config
|
|
@@ -47,6 +48,16 @@ except ImportError as e:
|
|
|
47
48
|
SOCKETIO_AVAILABLE = False
|
|
48
49
|
get_socketio_server = None
|
|
49
50
|
|
|
51
|
+
# Try to import event bus with fallback handling
|
|
52
|
+
try:
|
|
53
|
+
from claude_mpm.services.event_bus.event_bus import EventBus
|
|
54
|
+
|
|
55
|
+
EVENT_BUS_AVAILABLE = True
|
|
56
|
+
except ImportError as e:
|
|
57
|
+
logger.debug(f"EventBus not available: {e}")
|
|
58
|
+
EVENT_BUS_AVAILABLE = False
|
|
59
|
+
EventBus = None
|
|
60
|
+
|
|
50
61
|
|
|
51
62
|
class MemoryPreDelegationHook(PreDelegationHook):
|
|
52
63
|
"""Inject agent memory into delegation context.
|
|
@@ -83,6 +94,16 @@ class MemoryPreDelegationHook(PreDelegationHook):
|
|
|
83
94
|
logger.info("Memory manager not available - hook will be inactive")
|
|
84
95
|
self.memory_manager = None
|
|
85
96
|
|
|
97
|
+
# Initialize event bus for observability
|
|
98
|
+
if EVENT_BUS_AVAILABLE and EventBus:
|
|
99
|
+
try:
|
|
100
|
+
self.event_bus = EventBus.get_instance()
|
|
101
|
+
except Exception as e:
|
|
102
|
+
logger.debug(f"Failed to get EventBus instance: {e}")
|
|
103
|
+
self.event_bus = None
|
|
104
|
+
else:
|
|
105
|
+
self.event_bus = None
|
|
106
|
+
|
|
86
107
|
def execute(self, context: HookContext) -> HookResult:
|
|
87
108
|
"""Add agent memory to delegation context.
|
|
88
109
|
|
|
@@ -137,7 +158,31 @@ INSTRUCTIONS: Review your memory above before proceeding. Apply learned patterns
|
|
|
137
158
|
|
|
138
159
|
logger.info(f"Injected memory for agent '{agent_id}'")
|
|
139
160
|
|
|
140
|
-
#
|
|
161
|
+
# Calculate memory size for observability
|
|
162
|
+
memory_size = len(memory_content)
|
|
163
|
+
|
|
164
|
+
# Emit event bus event for observability
|
|
165
|
+
if self.event_bus:
|
|
166
|
+
try:
|
|
167
|
+
# Determine memory source (project or user level)
|
|
168
|
+
# This is inferred from the memory manager's behavior
|
|
169
|
+
memory_source = (
|
|
170
|
+
"runtime" # Runtime loading from memory manager
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
self.event_bus.publish(
|
|
174
|
+
"agent.memory.loaded",
|
|
175
|
+
{
|
|
176
|
+
"agent_id": agent_id,
|
|
177
|
+
"memory_source": memory_source,
|
|
178
|
+
"memory_size": memory_size,
|
|
179
|
+
"timestamp": datetime.now(datetime.UTC).isoformat(),
|
|
180
|
+
},
|
|
181
|
+
)
|
|
182
|
+
except Exception as event_error:
|
|
183
|
+
logger.debug(f"EventBus publish failed: {event_error}")
|
|
184
|
+
|
|
185
|
+
# Emit Socket.IO event for memory injected (legacy compatibility)
|
|
141
186
|
try:
|
|
142
187
|
socketio_server = get_socketio_server()
|
|
143
188
|
# Calculate size of injected content
|
claude_mpm/init.py
CHANGED
|
@@ -141,25 +141,6 @@ class ProjectInitializer:
|
|
|
141
141
|
if not gitignore.exists():
|
|
142
142
|
gitignore.write_text("logs/\n*.log\n*.pyc\n__pycache__/\n")
|
|
143
143
|
|
|
144
|
-
# Also ensure MCP directories are in main project .gitignore
|
|
145
|
-
try:
|
|
146
|
-
from claude_mpm.services.project.project_organizer import (
|
|
147
|
-
ProjectOrganizer,
|
|
148
|
-
)
|
|
149
|
-
|
|
150
|
-
# Check if we're in a git repository
|
|
151
|
-
if (project_root / ".git").exists():
|
|
152
|
-
organizer = ProjectOrganizer(project_root)
|
|
153
|
-
# This will add MCP directories and other standard patterns
|
|
154
|
-
organizer.update_gitignore()
|
|
155
|
-
self.logger.debug(
|
|
156
|
-
"Updated project .gitignore with MCP and standard patterns"
|
|
157
|
-
)
|
|
158
|
-
except Exception as e:
|
|
159
|
-
self.logger.debug(
|
|
160
|
-
f"Could not update project gitignore with MCP patterns: {e}"
|
|
161
|
-
)
|
|
162
|
-
|
|
163
144
|
# Log successful creation with details
|
|
164
145
|
self.logger.info(f"Initialized project directory at {self.project_dir}")
|
|
165
146
|
self.logger.debug("Created directories: agents, config, responses, logs")
|
|
@@ -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.
|
|
92
|
-
# 2.
|
|
93
|
-
# 3.
|
|
94
|
-
# 4.
|
|
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
|
-
# -
|
|
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
|
|
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
|
-
#
|
|
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
|
-
#
|
|
177
|
-
|
|
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)]
|
|
182
|
-
|
|
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
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
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.
|
|
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
|
-
#
|
|
52
|
-
|
|
53
|
-
|
|
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
|
-
|
|
56
|
-
|
|
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
|
-
|
|
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,
|
|
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}:{
|
|
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 {
|
|
155
|
+
logger.info(f"Monitor daemon started in background on port {target_port}")
|
|
76
156
|
else:
|
|
77
|
-
logger.info(f"Monitor running on 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
|