claude-mpm 4.1.4__py3-none-any.whl → 4.1.6__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.
- claude_mpm/VERSION +1 -1
- claude_mpm/agents/templates/research.json +39 -13
- claude_mpm/cli/__init__.py +2 -0
- claude_mpm/cli/commands/__init__.py +2 -0
- claude_mpm/cli/commands/configure.py +1221 -0
- claude_mpm/cli/commands/configure_tui.py +1921 -0
- claude_mpm/cli/commands/tickets.py +365 -784
- claude_mpm/cli/parsers/base_parser.py +7 -0
- claude_mpm/cli/parsers/configure_parser.py +119 -0
- claude_mpm/cli/startup_logging.py +39 -12
- claude_mpm/constants.py +1 -0
- claude_mpm/core/output_style_manager.py +24 -0
- claude_mpm/core/socketio_pool.py +35 -3
- claude_mpm/core/unified_agent_registry.py +46 -15
- claude_mpm/dashboard/static/css/connection-status.css +370 -0
- claude_mpm/dashboard/static/js/components/connection-debug.js +654 -0
- claude_mpm/dashboard/static/js/connection-manager.js +536 -0
- claude_mpm/dashboard/templates/index.html +11 -0
- claude_mpm/hooks/claude_hooks/services/__init__.py +3 -1
- claude_mpm/hooks/claude_hooks/services/connection_manager_http.py +190 -0
- claude_mpm/services/agents/deployment/agent_discovery_service.py +12 -3
- claude_mpm/services/agents/deployment/agent_lifecycle_manager.py +172 -233
- claude_mpm/services/agents/deployment/agent_lifecycle_manager_refactored.py +575 -0
- claude_mpm/services/agents/deployment/agent_operation_service.py +573 -0
- claude_mpm/services/agents/deployment/agent_record_service.py +419 -0
- claude_mpm/services/agents/deployment/agent_state_service.py +381 -0
- claude_mpm/services/agents/deployment/multi_source_deployment_service.py +4 -2
- claude_mpm/services/diagnostics/checks/__init__.py +2 -0
- claude_mpm/services/diagnostics/checks/instructions_check.py +418 -0
- claude_mpm/services/diagnostics/diagnostic_runner.py +15 -2
- claude_mpm/services/event_bus/direct_relay.py +173 -0
- claude_mpm/services/infrastructure/__init__.py +31 -5
- claude_mpm/services/infrastructure/monitoring/__init__.py +43 -0
- claude_mpm/services/infrastructure/monitoring/aggregator.py +437 -0
- claude_mpm/services/infrastructure/monitoring/base.py +130 -0
- claude_mpm/services/infrastructure/monitoring/legacy.py +203 -0
- claude_mpm/services/infrastructure/monitoring/network.py +218 -0
- claude_mpm/services/infrastructure/monitoring/process.py +342 -0
- claude_mpm/services/infrastructure/monitoring/resources.py +243 -0
- claude_mpm/services/infrastructure/monitoring/service.py +367 -0
- claude_mpm/services/infrastructure/monitoring.py +67 -1030
- claude_mpm/services/project/analyzer.py +13 -4
- claude_mpm/services/project/analyzer_refactored.py +450 -0
- claude_mpm/services/project/analyzer_v2.py +566 -0
- claude_mpm/services/project/architecture_analyzer.py +461 -0
- claude_mpm/services/project/dependency_analyzer.py +462 -0
- claude_mpm/services/project/language_analyzer.py +265 -0
- claude_mpm/services/project/metrics_collector.py +410 -0
- claude_mpm/services/socketio/handlers/connection_handler.py +345 -0
- claude_mpm/services/socketio/server/broadcaster.py +32 -1
- claude_mpm/services/socketio/server/connection_manager.py +516 -0
- claude_mpm/services/socketio/server/core.py +63 -0
- claude_mpm/services/socketio/server/eventbus_integration.py +20 -9
- claude_mpm/services/socketio/server/main.py +27 -1
- claude_mpm/services/ticket_manager.py +5 -1
- claude_mpm/services/ticket_services/__init__.py +26 -0
- claude_mpm/services/ticket_services/crud_service.py +328 -0
- claude_mpm/services/ticket_services/formatter_service.py +290 -0
- claude_mpm/services/ticket_services/search_service.py +324 -0
- claude_mpm/services/ticket_services/validation_service.py +303 -0
- claude_mpm/services/ticket_services/workflow_service.py +244 -0
- {claude_mpm-4.1.4.dist-info → claude_mpm-4.1.6.dist-info}/METADATA +3 -1
- {claude_mpm-4.1.4.dist-info → claude_mpm-4.1.6.dist-info}/RECORD +67 -46
- claude_mpm/agents/OUTPUT_STYLE.md +0 -73
- claude_mpm/agents/backups/INSTRUCTIONS.md +0 -352
- claude_mpm/agents/templates/OPTIMIZATION_REPORT.md +0 -156
- claude_mpm/agents/templates/backup/data_engineer_agent_20250726_234551.json +0 -79
- claude_mpm/agents/templates/backup/documentation_agent_20250726_234551.json +0 -68
- claude_mpm/agents/templates/backup/engineer_agent_20250726_234551.json +0 -77
- claude_mpm/agents/templates/backup/ops_agent_20250726_234551.json +0 -78
- claude_mpm/agents/templates/backup/qa_agent_20250726_234551.json +0 -67
- claude_mpm/agents/templates/backup/research_agent_2025011_234551.json +0 -88
- claude_mpm/agents/templates/backup/research_agent_20250726_234551.json +0 -72
- claude_mpm/agents/templates/backup/research_memory_efficient.json +0 -88
- claude_mpm/agents/templates/backup/security_agent_20250726_234551.json +0 -78
- claude_mpm/agents/templates/backup/version_control_agent_20250726_234551.json +0 -62
- claude_mpm/agents/templates/vercel_ops_instructions.md +0 -582
- {claude_mpm-4.1.4.dist-info → claude_mpm-4.1.6.dist-info}/WHEEL +0 -0
- {claude_mpm-4.1.4.dist-info → claude_mpm-4.1.6.dist-info}/entry_points.txt +0 -0
- {claude_mpm-4.1.4.dist-info → claude_mpm-4.1.6.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-4.1.4.dist-info → claude_mpm-4.1.6.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
"""HTTP-based connection management service for Claude hook handler.
|
|
2
|
+
|
|
3
|
+
This service manages:
|
|
4
|
+
- HTTP POST event emission for ephemeral hook processes
|
|
5
|
+
- EventBus initialization (optional)
|
|
6
|
+
- Event emission through both channels
|
|
7
|
+
|
|
8
|
+
DESIGN DECISION: Use stateless HTTP POST instead of persistent SocketIO
|
|
9
|
+
connections because hook handlers are ephemeral processes (< 1 second lifetime).
|
|
10
|
+
This eliminates disconnection issues and matches the process lifecycle.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
import os
|
|
14
|
+
import sys
|
|
15
|
+
from datetime import datetime
|
|
16
|
+
|
|
17
|
+
# Debug mode is enabled by default for better visibility into hook processing
|
|
18
|
+
DEBUG = os.environ.get("CLAUDE_MPM_HOOK_DEBUG", "true").lower() != "false"
|
|
19
|
+
|
|
20
|
+
# Import requests for HTTP POST communication
|
|
21
|
+
try:
|
|
22
|
+
import requests
|
|
23
|
+
|
|
24
|
+
REQUESTS_AVAILABLE = True
|
|
25
|
+
except ImportError:
|
|
26
|
+
REQUESTS_AVAILABLE = False
|
|
27
|
+
requests = None
|
|
28
|
+
|
|
29
|
+
# Import EventNormalizer for consistent event formatting
|
|
30
|
+
try:
|
|
31
|
+
from claude_mpm.services.socketio.event_normalizer import EventNormalizer
|
|
32
|
+
except ImportError:
|
|
33
|
+
# Create a simple fallback EventNormalizer if import fails
|
|
34
|
+
class EventNormalizer:
|
|
35
|
+
def normalize(self, event_data, source="hook"):
|
|
36
|
+
"""Simple fallback normalizer that returns event as-is."""
|
|
37
|
+
return type(
|
|
38
|
+
"NormalizedEvent",
|
|
39
|
+
(),
|
|
40
|
+
{
|
|
41
|
+
"to_dict": lambda: {
|
|
42
|
+
"event": "claude_event",
|
|
43
|
+
"type": event_data.get("type", "unknown"),
|
|
44
|
+
"subtype": event_data.get("subtype", "generic"),
|
|
45
|
+
"timestamp": event_data.get(
|
|
46
|
+
"timestamp", datetime.now().isoformat()
|
|
47
|
+
),
|
|
48
|
+
"data": event_data.get("data", event_data),
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
# Import EventBus for decoupled event distribution
|
|
55
|
+
try:
|
|
56
|
+
from claude_mpm.services.event_bus import EventBus
|
|
57
|
+
|
|
58
|
+
EVENTBUS_AVAILABLE = True
|
|
59
|
+
except ImportError:
|
|
60
|
+
EVENTBUS_AVAILABLE = False
|
|
61
|
+
EventBus = None
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class ConnectionManagerService:
|
|
65
|
+
"""Manages connections for the Claude hook handler using HTTP POST."""
|
|
66
|
+
|
|
67
|
+
def __init__(self):
|
|
68
|
+
"""Initialize connection management service."""
|
|
69
|
+
# Event normalizer for consistent event schema
|
|
70
|
+
self.event_normalizer = EventNormalizer()
|
|
71
|
+
|
|
72
|
+
# Server configuration for HTTP POST
|
|
73
|
+
self.server_host = os.environ.get("CLAUDE_MPM_SERVER_HOST", "localhost")
|
|
74
|
+
self.server_port = int(os.environ.get("CLAUDE_MPM_SERVER_PORT", "8765"))
|
|
75
|
+
self.http_endpoint = f"http://{self.server_host}:{self.server_port}/api/events"
|
|
76
|
+
|
|
77
|
+
# Initialize EventBus for in-process event distribution (optional)
|
|
78
|
+
self.event_bus = None
|
|
79
|
+
self._initialize_eventbus()
|
|
80
|
+
|
|
81
|
+
# For backward compatibility with tests
|
|
82
|
+
self.connection_pool = None # No longer used
|
|
83
|
+
|
|
84
|
+
if DEBUG:
|
|
85
|
+
print(
|
|
86
|
+
f"✅ HTTP connection manager initialized - endpoint: {self.http_endpoint}",
|
|
87
|
+
file=sys.stderr,
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
def _initialize_eventbus(self):
|
|
91
|
+
"""Initialize the EventBus for in-process distribution."""
|
|
92
|
+
if EVENTBUS_AVAILABLE:
|
|
93
|
+
try:
|
|
94
|
+
self.event_bus = EventBus.get_instance()
|
|
95
|
+
if DEBUG:
|
|
96
|
+
print("✅ EventBus initialized for hook handler", file=sys.stderr)
|
|
97
|
+
except Exception as e:
|
|
98
|
+
if DEBUG:
|
|
99
|
+
print(f"⚠️ Failed to initialize EventBus: {e}", file=sys.stderr)
|
|
100
|
+
self.event_bus = None
|
|
101
|
+
|
|
102
|
+
def emit_event(self, namespace: str, event: str, data: dict):
|
|
103
|
+
"""Emit event using HTTP POST and optionally EventBus.
|
|
104
|
+
|
|
105
|
+
WHY HTTP POST approach:
|
|
106
|
+
- Stateless: Perfect for ephemeral hook processes
|
|
107
|
+
- Fire-and-forget: No connection management needed
|
|
108
|
+
- Fast: Minimal overhead, no handshake
|
|
109
|
+
- Reliable: Server handles buffering and retries
|
|
110
|
+
"""
|
|
111
|
+
# Create event data for normalization
|
|
112
|
+
raw_event = {
|
|
113
|
+
"type": "hook",
|
|
114
|
+
"subtype": event, # e.g., "user_prompt", "pre_tool", "subagent_stop"
|
|
115
|
+
"timestamp": datetime.now().isoformat(),
|
|
116
|
+
"data": data,
|
|
117
|
+
"source": "claude_hooks", # Identify the source
|
|
118
|
+
"session_id": data.get("sessionId"), # Include session if available
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
# Normalize the event using EventNormalizer for consistent schema
|
|
122
|
+
normalized_event = self.event_normalizer.normalize(raw_event, source="hook")
|
|
123
|
+
claude_event_data = normalized_event.to_dict()
|
|
124
|
+
|
|
125
|
+
# Log important events for debugging
|
|
126
|
+
if DEBUG and event in ["subagent_stop", "pre_tool"]:
|
|
127
|
+
if event == "subagent_stop":
|
|
128
|
+
agent_type = data.get("agent_type", "unknown")
|
|
129
|
+
print(
|
|
130
|
+
f"Hook handler: Publishing SubagentStop for agent '{agent_type}'",
|
|
131
|
+
file=sys.stderr,
|
|
132
|
+
)
|
|
133
|
+
elif event == "pre_tool" and data.get("tool_name") == "Task":
|
|
134
|
+
delegation = data.get("delegation_details", {})
|
|
135
|
+
agent_type = delegation.get("agent_type", "unknown")
|
|
136
|
+
print(
|
|
137
|
+
f"Hook handler: Publishing Task delegation to agent '{agent_type}'",
|
|
138
|
+
file=sys.stderr,
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
# Primary method: HTTP POST to server
|
|
142
|
+
# This is fire-and-forget with a short timeout
|
|
143
|
+
if REQUESTS_AVAILABLE:
|
|
144
|
+
try:
|
|
145
|
+
# Send HTTP POST with short timeout (fire-and-forget pattern)
|
|
146
|
+
response = requests.post(
|
|
147
|
+
self.http_endpoint,
|
|
148
|
+
json=claude_event_data,
|
|
149
|
+
timeout=0.5, # 500ms timeout - don't wait long
|
|
150
|
+
headers={"Content-Type": "application/json"},
|
|
151
|
+
)
|
|
152
|
+
if DEBUG and response.status_code == 204:
|
|
153
|
+
print(f"✅ Emitted via HTTP POST: {event}", file=sys.stderr)
|
|
154
|
+
elif DEBUG and response.status_code != 204:
|
|
155
|
+
print(
|
|
156
|
+
f"⚠️ HTTP POST returned status {response.status_code} for: {event}",
|
|
157
|
+
file=sys.stderr,
|
|
158
|
+
)
|
|
159
|
+
except requests.exceptions.Timeout:
|
|
160
|
+
# Timeout is expected for fire-and-forget pattern
|
|
161
|
+
if DEBUG:
|
|
162
|
+
print(f"✅ HTTP POST sent (timeout OK): {event}", file=sys.stderr)
|
|
163
|
+
except requests.exceptions.ConnectionError:
|
|
164
|
+
# Server might not be running - this is OK
|
|
165
|
+
if DEBUG:
|
|
166
|
+
print(f"⚠️ Server not available for: {event}", file=sys.stderr)
|
|
167
|
+
except Exception as e:
|
|
168
|
+
if DEBUG:
|
|
169
|
+
print(f"⚠️ Failed to emit via HTTP POST: {e}", file=sys.stderr)
|
|
170
|
+
elif DEBUG:
|
|
171
|
+
print(
|
|
172
|
+
"⚠️ requests module not available - cannot emit via HTTP",
|
|
173
|
+
file=sys.stderr,
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
# Also publish to EventBus for any in-process subscribers
|
|
177
|
+
if self.event_bus and EVENTBUS_AVAILABLE:
|
|
178
|
+
try:
|
|
179
|
+
# Publish to EventBus with topic format: hook.{event}
|
|
180
|
+
topic = f"hook.{event}"
|
|
181
|
+
self.event_bus.publish(topic, claude_event_data)
|
|
182
|
+
if DEBUG:
|
|
183
|
+
print(f"✅ Published to EventBus: {topic}", file=sys.stderr)
|
|
184
|
+
except Exception as e:
|
|
185
|
+
if DEBUG:
|
|
186
|
+
print(f"⚠️ Failed to publish to EventBus: {e}", file=sys.stderr)
|
|
187
|
+
|
|
188
|
+
def cleanup(self):
|
|
189
|
+
"""Cleanup connections on service destruction."""
|
|
190
|
+
# Nothing to cleanup for HTTP POST approach
|
|
@@ -34,10 +34,14 @@ class AgentDiscoveryService:
|
|
|
34
34
|
self.logger = get_logger(__name__)
|
|
35
35
|
self.templates_dir = templates_dir
|
|
36
36
|
|
|
37
|
-
def list_available_agents(self) -> List[Dict[str, Any]]:
|
|
37
|
+
def list_available_agents(self, log_discovery: bool = True) -> List[Dict[str, Any]]:
|
|
38
38
|
"""
|
|
39
39
|
List all available agent templates with their metadata.
|
|
40
40
|
|
|
41
|
+
Args:
|
|
42
|
+
log_discovery: Whether to log discovery results (default: True).
|
|
43
|
+
Set to False when called from multi-source discovery to avoid duplicate logs.
|
|
44
|
+
|
|
41
45
|
Returns:
|
|
42
46
|
List of agent information dictionaries containing:
|
|
43
47
|
- name: Agent name
|
|
@@ -73,7 +77,11 @@ class AgentDiscoveryService:
|
|
|
73
77
|
# Sort by agent name for consistent ordering
|
|
74
78
|
agents.sort(key=lambda x: x.get("name", ""))
|
|
75
79
|
|
|
76
|
-
|
|
80
|
+
# Only log if requested (to avoid duplicate logging from multi-source discovery)
|
|
81
|
+
if log_discovery:
|
|
82
|
+
self.logger.info(
|
|
83
|
+
f"Discovered {len(agents)} available agent templates from {self.templates_dir.name}"
|
|
84
|
+
)
|
|
77
85
|
return agents
|
|
78
86
|
|
|
79
87
|
def get_filtered_templates(
|
|
@@ -153,7 +161,8 @@ class AgentDiscoveryService:
|
|
|
153
161
|
Dictionary mapping categories to lists of agent names
|
|
154
162
|
"""
|
|
155
163
|
categories = {}
|
|
156
|
-
|
|
164
|
+
# Don't log discovery when called internally
|
|
165
|
+
agents = self.list_available_agents(log_discovery=False)
|
|
157
166
|
|
|
158
167
|
for agent in agents:
|
|
159
168
|
agent_name = agent.get("name", "unknown")
|