claude-mpm 3.3.0__py3-none-any.whl → 3.3.2__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/agents/templates/data_engineer.json +1 -1
- claude_mpm/agents/templates/documentation.json +1 -1
- claude_mpm/agents/templates/engineer.json +1 -1
- claude_mpm/agents/templates/ops.json +1 -1
- claude_mpm/agents/templates/pm.json +1 -1
- claude_mpm/agents/templates/qa.json +1 -1
- claude_mpm/agents/templates/research.json +1 -1
- claude_mpm/agents/templates/security.json +1 -1
- claude_mpm/agents/templates/test_integration.json +112 -0
- claude_mpm/agents/templates/version_control.json +1 -1
- claude_mpm/cli/commands/memory.py +575 -25
- claude_mpm/cli/commands/run.py +115 -14
- claude_mpm/cli/parser.py +76 -0
- claude_mpm/constants.py +5 -0
- claude_mpm/core/claude_runner.py +13 -11
- claude_mpm/core/session_manager.py +46 -0
- claude_mpm/core/simple_runner.py +13 -11
- claude_mpm/hooks/claude_hooks/hook_handler.py +2 -26
- claude_mpm/services/agent_memory_manager.py +264 -23
- claude_mpm/services/memory_builder.py +491 -0
- claude_mpm/services/memory_optimizer.py +619 -0
- claude_mpm/services/memory_router.py +445 -0
- claude_mpm/services/socketio_server.py +184 -20
- claude_mpm-3.3.2.dist-info/METADATA +159 -0
- {claude_mpm-3.3.0.dist-info → claude_mpm-3.3.2.dist-info}/RECORD +29 -28
- claude_mpm/agents/templates/test-integration-agent.md +0 -34
- claude_mpm/core/websocket_handler.py +0 -233
- claude_mpm/services/websocket_server.py +0 -376
- claude_mpm-3.3.0.dist-info/METADATA +0 -432
- {claude_mpm-3.3.0.dist-info → claude_mpm-3.3.2.dist-info}/WHEEL +0 -0
- {claude_mpm-3.3.0.dist-info → claude_mpm-3.3.2.dist-info}/entry_points.txt +0 -0
- {claude_mpm-3.3.0.dist-info → claude_mpm-3.3.2.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-3.3.0.dist-info → claude_mpm-3.3.2.dist-info}/top_level.txt +0 -0
claude_mpm/cli/commands/run.py
CHANGED
|
@@ -11,12 +11,13 @@ import sys
|
|
|
11
11
|
import time
|
|
12
12
|
import webbrowser
|
|
13
13
|
from pathlib import Path
|
|
14
|
+
from datetime import datetime
|
|
14
15
|
|
|
15
16
|
from ...core.logger import get_logger
|
|
16
17
|
from ...constants import LogLevel
|
|
17
18
|
from ..utils import get_user_input, list_agent_versions_at_startup
|
|
18
19
|
from ...utils.dependency_manager import ensure_socketio_dependencies
|
|
19
|
-
from ...deployment_paths import
|
|
20
|
+
from ...deployment_paths import get_scripts_dir, get_package_root
|
|
20
21
|
|
|
21
22
|
|
|
22
23
|
def filter_claude_mpm_args(claude_args):
|
|
@@ -80,10 +81,19 @@ def filter_claude_mpm_args(claude_args):
|
|
|
80
81
|
# Also skip the next argument if this flag expects a value
|
|
81
82
|
value_expecting_flags = {
|
|
82
83
|
'--websocket-port', '--launch-method', '--logging', '--log-dir',
|
|
83
|
-
'--framework-path', '--agents-dir', '-i', '--input'
|
|
84
|
+
'--framework-path', '--agents-dir', '-i', '--input'
|
|
84
85
|
}
|
|
86
|
+
optional_value_flags = {
|
|
87
|
+
'--resume' # These flags can have optional values (nargs="?")
|
|
88
|
+
}
|
|
89
|
+
|
|
85
90
|
if arg in value_expecting_flags and i < len(claude_args):
|
|
86
91
|
i += 1 # Skip the value too
|
|
92
|
+
elif arg in optional_value_flags and i < len(claude_args):
|
|
93
|
+
# For optional value flags, only skip next arg if it doesn't start with --
|
|
94
|
+
next_arg = claude_args[i]
|
|
95
|
+
if not next_arg.startswith('--'):
|
|
96
|
+
i += 1 # Skip the value
|
|
87
97
|
else:
|
|
88
98
|
# This is not a claude-mpm flag, keep it
|
|
89
99
|
filtered_args.append(arg)
|
|
@@ -92,6 +102,58 @@ def filter_claude_mpm_args(claude_args):
|
|
|
92
102
|
return filtered_args
|
|
93
103
|
|
|
94
104
|
|
|
105
|
+
def create_session_context(session_id, session_manager):
|
|
106
|
+
"""
|
|
107
|
+
Create enhanced context for resumed sessions.
|
|
108
|
+
|
|
109
|
+
WHY: When resuming a session, we want to provide Claude with context about
|
|
110
|
+
the previous session including what agents were used and when it was created.
|
|
111
|
+
This helps maintain continuity across session boundaries.
|
|
112
|
+
|
|
113
|
+
Args:
|
|
114
|
+
session_id: Session ID being resumed
|
|
115
|
+
session_manager: SessionManager instance
|
|
116
|
+
|
|
117
|
+
Returns:
|
|
118
|
+
Enhanced context string with session information
|
|
119
|
+
"""
|
|
120
|
+
try:
|
|
121
|
+
from ...core.claude_runner import create_simple_context
|
|
122
|
+
except ImportError:
|
|
123
|
+
from claude_mpm.core.claude_runner import create_simple_context
|
|
124
|
+
|
|
125
|
+
base_context = create_simple_context()
|
|
126
|
+
|
|
127
|
+
session_data = session_manager.get_session_by_id(session_id)
|
|
128
|
+
if not session_data:
|
|
129
|
+
return base_context
|
|
130
|
+
|
|
131
|
+
# Add session resumption information
|
|
132
|
+
session_info = f"""
|
|
133
|
+
|
|
134
|
+
# Session Resumption
|
|
135
|
+
|
|
136
|
+
You are resuming session {session_id[:8]}... which was:
|
|
137
|
+
- Created: {session_data.get('created_at', 'unknown')}
|
|
138
|
+
- Last used: {session_data.get('last_used', 'unknown')}
|
|
139
|
+
- Context: {session_data.get('context', 'default')}
|
|
140
|
+
- Use count: {session_data.get('use_count', 0)}
|
|
141
|
+
"""
|
|
142
|
+
|
|
143
|
+
# Add information about agents previously run in this session
|
|
144
|
+
agents_run = session_data.get('agents_run', [])
|
|
145
|
+
if agents_run:
|
|
146
|
+
session_info += "\n- Previous agent activity:\n"
|
|
147
|
+
for agent_info in agents_run[-5:]: # Show last 5 agents
|
|
148
|
+
session_info += f" • {agent_info.get('agent', 'unknown')}: {agent_info.get('task', 'no description')[:50]}...\n"
|
|
149
|
+
if len(agents_run) > 5:
|
|
150
|
+
session_info += f" (and {len(agents_run) - 5} other agent interactions)\n"
|
|
151
|
+
|
|
152
|
+
session_info += "\nContinue from where you left off in this session."
|
|
153
|
+
|
|
154
|
+
return base_context + session_info
|
|
155
|
+
|
|
156
|
+
|
|
95
157
|
def run_session(args):
|
|
96
158
|
"""
|
|
97
159
|
Run a simplified Claude session.
|
|
@@ -112,8 +174,44 @@ def run_session(args):
|
|
|
112
174
|
|
|
113
175
|
try:
|
|
114
176
|
from ...core.claude_runner import ClaudeRunner, create_simple_context
|
|
177
|
+
from ...core.session_manager import SessionManager
|
|
115
178
|
except ImportError:
|
|
116
179
|
from claude_mpm.core.claude_runner import ClaudeRunner, create_simple_context
|
|
180
|
+
from claude_mpm.core.session_manager import SessionManager
|
|
181
|
+
|
|
182
|
+
# Handle session resumption
|
|
183
|
+
session_manager = SessionManager()
|
|
184
|
+
resume_session_id = None
|
|
185
|
+
resume_context = None
|
|
186
|
+
|
|
187
|
+
if hasattr(args, 'resume') and args.resume:
|
|
188
|
+
if args.resume == "last":
|
|
189
|
+
# Resume the last interactive session
|
|
190
|
+
resume_session_id = session_manager.get_last_interactive_session()
|
|
191
|
+
if resume_session_id:
|
|
192
|
+
session_data = session_manager.get_session_by_id(resume_session_id)
|
|
193
|
+
if session_data:
|
|
194
|
+
resume_context = session_data.get("context", "default")
|
|
195
|
+
logger.info(f"Resuming session {resume_session_id} (context: {resume_context})")
|
|
196
|
+
print(f"🔄 Resuming session {resume_session_id[:8]}... (created: {session_data.get('created_at', 'unknown')})")
|
|
197
|
+
else:
|
|
198
|
+
logger.warning(f"Session {resume_session_id} not found")
|
|
199
|
+
else:
|
|
200
|
+
logger.info("No recent interactive sessions found")
|
|
201
|
+
print("ℹ️ No recent interactive sessions found to resume")
|
|
202
|
+
else:
|
|
203
|
+
# Resume specific session by ID
|
|
204
|
+
resume_session_id = args.resume
|
|
205
|
+
session_data = session_manager.get_session_by_id(resume_session_id)
|
|
206
|
+
if session_data:
|
|
207
|
+
resume_context = session_data.get("context", "default")
|
|
208
|
+
logger.info(f"Resuming session {resume_session_id} (context: {resume_context})")
|
|
209
|
+
print(f"🔄 Resuming session {resume_session_id[:8]}... (context: {resume_context})")
|
|
210
|
+
else:
|
|
211
|
+
logger.error(f"Session {resume_session_id} not found")
|
|
212
|
+
print(f"❌ Session {resume_session_id} not found")
|
|
213
|
+
print("💡 Use 'claude-mpm sessions' to list available sessions")
|
|
214
|
+
return
|
|
117
215
|
|
|
118
216
|
# Skip native agents if disabled
|
|
119
217
|
if getattr(args, 'no_native_agents', False):
|
|
@@ -189,8 +287,19 @@ def run_session(args):
|
|
|
189
287
|
# Pass information about whether we already opened the browser in run.py
|
|
190
288
|
runner._browser_opened_by_cli = getattr(args, '_browser_opened_by_cli', False)
|
|
191
289
|
|
|
192
|
-
# Create
|
|
193
|
-
|
|
290
|
+
# Create context - use resumed session context if available
|
|
291
|
+
if resume_session_id and resume_context:
|
|
292
|
+
# For resumed sessions, create enhanced context with session information
|
|
293
|
+
context = create_session_context(resume_session_id, session_manager)
|
|
294
|
+
# Update session usage
|
|
295
|
+
session_manager.active_sessions[resume_session_id]["last_used"] = datetime.now().isoformat()
|
|
296
|
+
session_manager.active_sessions[resume_session_id]["use_count"] += 1
|
|
297
|
+
session_manager._save_sessions()
|
|
298
|
+
else:
|
|
299
|
+
# Create a new session for tracking
|
|
300
|
+
new_session_id = session_manager.create_session("default")
|
|
301
|
+
context = create_simple_context()
|
|
302
|
+
logger.info(f"Created new session {new_session_id}")
|
|
194
303
|
|
|
195
304
|
# For monitor mode, we handled everything in launch_socketio_monitor
|
|
196
305
|
# No need for ClaudeRunner browser delegation
|
|
@@ -260,16 +369,8 @@ def launch_socketio_monitor(port, logger):
|
|
|
260
369
|
|
|
261
370
|
socketio_port = port
|
|
262
371
|
|
|
263
|
-
#
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
if not html_file_path.exists():
|
|
267
|
-
logger.error(f"Monitor HTML file not found: {html_file_path}")
|
|
268
|
-
print(f"❌ Monitor HTML file not found: {html_file_path}")
|
|
269
|
-
return False, False
|
|
270
|
-
|
|
271
|
-
# Create file:// URL with port parameter
|
|
272
|
-
dashboard_url = f'file://{html_file_path.absolute()}?port={socketio_port}'
|
|
372
|
+
# Use HTTP URL to access dashboard from Socket.IO server
|
|
373
|
+
dashboard_url = f'http://localhost:{socketio_port}'
|
|
273
374
|
|
|
274
375
|
# Check if Socket.IO server is already running
|
|
275
376
|
server_running = _check_socketio_server_running(socketio_port, logger)
|
claude_mpm/cli/parser.py
CHANGED
|
@@ -119,6 +119,13 @@ def add_run_arguments(parser: argparse.ArgumentParser) -> None:
|
|
|
119
119
|
default=8765,
|
|
120
120
|
help="WebSocket server port (default: 8765)"
|
|
121
121
|
)
|
|
122
|
+
run_group.add_argument(
|
|
123
|
+
"--resume",
|
|
124
|
+
type=str,
|
|
125
|
+
nargs="?",
|
|
126
|
+
const="last",
|
|
127
|
+
help="Resume a session (last session if no ID specified, or specific session ID)"
|
|
128
|
+
)
|
|
122
129
|
|
|
123
130
|
# Input/output options
|
|
124
131
|
io_group = parser.add_argument_group('input/output options')
|
|
@@ -214,6 +221,13 @@ def create_parser(prog_name: str = "claude-mpm", version: str = "0.0.0") -> argp
|
|
|
214
221
|
default=8765,
|
|
215
222
|
help="WebSocket server port (default: 8765)"
|
|
216
223
|
)
|
|
224
|
+
run_group.add_argument(
|
|
225
|
+
"--resume",
|
|
226
|
+
type=str,
|
|
227
|
+
nargs="?",
|
|
228
|
+
const="last",
|
|
229
|
+
help="Resume a session (last session if no ID specified, or specific session ID)"
|
|
230
|
+
)
|
|
217
231
|
|
|
218
232
|
# Input/output options
|
|
219
233
|
io_group = parser.add_argument_group('input/output options (when no command specified)')
|
|
@@ -393,6 +407,68 @@ def create_parser(prog_name: str = "claude-mpm", version: str = "0.0.0") -> argp
|
|
|
393
407
|
help="Clean up old/unused memory files"
|
|
394
408
|
)
|
|
395
409
|
|
|
410
|
+
# Optimize command
|
|
411
|
+
optimize_parser = memory_subparsers.add_parser(
|
|
412
|
+
MemoryCommands.OPTIMIZE.value,
|
|
413
|
+
help="Optimize memory files by removing duplicates and consolidating similar items"
|
|
414
|
+
)
|
|
415
|
+
optimize_parser.add_argument(
|
|
416
|
+
"agent_id",
|
|
417
|
+
nargs="?",
|
|
418
|
+
help="Agent ID to optimize (optimize all if not specified)"
|
|
419
|
+
)
|
|
420
|
+
|
|
421
|
+
# Build command
|
|
422
|
+
build_parser = memory_subparsers.add_parser(
|
|
423
|
+
MemoryCommands.BUILD.value,
|
|
424
|
+
help="Build agent memories from project documentation"
|
|
425
|
+
)
|
|
426
|
+
build_parser.add_argument(
|
|
427
|
+
"--force-rebuild",
|
|
428
|
+
action="store_true",
|
|
429
|
+
help="Force rebuild even if docs haven't changed"
|
|
430
|
+
)
|
|
431
|
+
|
|
432
|
+
# Cross-reference command
|
|
433
|
+
cross_ref_parser = memory_subparsers.add_parser(
|
|
434
|
+
MemoryCommands.CROSS_REF.value,
|
|
435
|
+
help="Find cross-references and common patterns across agent memories"
|
|
436
|
+
)
|
|
437
|
+
cross_ref_parser.add_argument(
|
|
438
|
+
"--query",
|
|
439
|
+
type=str,
|
|
440
|
+
help="Optional search query to filter cross-references"
|
|
441
|
+
)
|
|
442
|
+
|
|
443
|
+
# Route command
|
|
444
|
+
route_parser = memory_subparsers.add_parser(
|
|
445
|
+
MemoryCommands.ROUTE.value,
|
|
446
|
+
help="Test memory command routing logic"
|
|
447
|
+
)
|
|
448
|
+
route_parser.add_argument(
|
|
449
|
+
"--content",
|
|
450
|
+
type=str,
|
|
451
|
+
required=True,
|
|
452
|
+
help="Content to analyze for routing"
|
|
453
|
+
)
|
|
454
|
+
|
|
455
|
+
# Show command
|
|
456
|
+
show_parser = memory_subparsers.add_parser(
|
|
457
|
+
MemoryCommands.SHOW.value,
|
|
458
|
+
help="Show agent memories in user-friendly format with cross-references"
|
|
459
|
+
)
|
|
460
|
+
show_parser.add_argument(
|
|
461
|
+
"agent_id",
|
|
462
|
+
nargs="?",
|
|
463
|
+
help="Agent ID to show memory for (show all if not specified)"
|
|
464
|
+
)
|
|
465
|
+
show_parser.add_argument(
|
|
466
|
+
"--format",
|
|
467
|
+
choices=["summary", "detailed", "full"],
|
|
468
|
+
default="summary",
|
|
469
|
+
help="Display format: summary (default), detailed, or full"
|
|
470
|
+
)
|
|
471
|
+
|
|
396
472
|
return parser
|
|
397
473
|
|
|
398
474
|
|
claude_mpm/constants.py
CHANGED
claude_mpm/core/claude_runner.py
CHANGED
|
@@ -95,7 +95,7 @@ class ClaudeRunner:
|
|
|
95
95
|
except Exception as e:
|
|
96
96
|
self.logger.debug(f"Failed to create session log file: {e}")
|
|
97
97
|
|
|
98
|
-
# Initialize
|
|
98
|
+
# Initialize Socket.IO server reference
|
|
99
99
|
self.websocket_server = None
|
|
100
100
|
|
|
101
101
|
def setup_agents(self) -> bool:
|
|
@@ -152,13 +152,14 @@ class ClaudeRunner:
|
|
|
152
152
|
|
|
153
153
|
def run_interactive(self, initial_context: Optional[str] = None):
|
|
154
154
|
"""Run Claude in interactive mode."""
|
|
155
|
-
#
|
|
155
|
+
# Connect to Socket.IO server if enabled
|
|
156
156
|
if self.enable_websocket:
|
|
157
157
|
try:
|
|
158
|
-
#
|
|
159
|
-
from claude_mpm.services.
|
|
160
|
-
self.websocket_server =
|
|
158
|
+
# Use Socket.IO client proxy to connect to monitoring server
|
|
159
|
+
from claude_mpm.services.socketio_server import SocketIOClientProxy
|
|
160
|
+
self.websocket_server = SocketIOClientProxy(port=self.websocket_port)
|
|
161
161
|
self.websocket_server.start()
|
|
162
|
+
self.logger.info("Connected to Socket.IO monitoring server")
|
|
162
163
|
|
|
163
164
|
# Generate session ID
|
|
164
165
|
session_id = str(uuid.uuid4())
|
|
@@ -171,7 +172,7 @@ class ClaudeRunner:
|
|
|
171
172
|
working_dir=working_dir
|
|
172
173
|
)
|
|
173
174
|
except Exception as e:
|
|
174
|
-
self.logger.warning(f"Failed to
|
|
175
|
+
self.logger.warning(f"Failed to connect to Socket.IO server: {e}")
|
|
175
176
|
self.websocket_server = None
|
|
176
177
|
|
|
177
178
|
# Get version
|
|
@@ -329,13 +330,14 @@ class ClaudeRunner:
|
|
|
329
330
|
"""Run Claude with a single prompt and return success status."""
|
|
330
331
|
start_time = time.time()
|
|
331
332
|
|
|
332
|
-
#
|
|
333
|
+
# Connect to Socket.IO server if enabled
|
|
333
334
|
if self.enable_websocket:
|
|
334
335
|
try:
|
|
335
|
-
#
|
|
336
|
-
from claude_mpm.services.
|
|
337
|
-
self.websocket_server =
|
|
336
|
+
# Use Socket.IO client proxy to connect to monitoring server
|
|
337
|
+
from claude_mpm.services.socketio_server import SocketIOClientProxy
|
|
338
|
+
self.websocket_server = SocketIOClientProxy(port=self.websocket_port)
|
|
338
339
|
self.websocket_server.start()
|
|
340
|
+
self.logger.info("Connected to Socket.IO monitoring server")
|
|
339
341
|
|
|
340
342
|
# Generate session ID
|
|
341
343
|
session_id = str(uuid.uuid4())
|
|
@@ -348,7 +350,7 @@ class ClaudeRunner:
|
|
|
348
350
|
working_dir=working_dir
|
|
349
351
|
)
|
|
350
352
|
except Exception as e:
|
|
351
|
-
self.logger.warning(f"Failed to
|
|
353
|
+
self.logger.warning(f"Failed to connect to Socket.IO server: {e}")
|
|
352
354
|
self.websocket_server = None
|
|
353
355
|
|
|
354
356
|
# Check for /mpm: commands
|
|
@@ -117,6 +117,52 @@ class SessionManager:
|
|
|
117
117
|
if expired:
|
|
118
118
|
self._save_sessions()
|
|
119
119
|
|
|
120
|
+
def get_recent_sessions(self, limit: int = 10, context: Optional[str] = None) -> list:
|
|
121
|
+
"""Get recent sessions sorted by last used time.
|
|
122
|
+
|
|
123
|
+
Args:
|
|
124
|
+
limit: Maximum number of sessions to return
|
|
125
|
+
context: Filter by context (optional)
|
|
126
|
+
|
|
127
|
+
Returns:
|
|
128
|
+
List of session data dictionaries sorted by last_used descending
|
|
129
|
+
"""
|
|
130
|
+
sessions = list(self.active_sessions.values())
|
|
131
|
+
|
|
132
|
+
# Filter by context if specified
|
|
133
|
+
if context:
|
|
134
|
+
sessions = [s for s in sessions if s.get("context") == context]
|
|
135
|
+
|
|
136
|
+
# Sort by last_used descending (most recent first)
|
|
137
|
+
sessions.sort(key=lambda s: datetime.fromisoformat(s["last_used"]), reverse=True)
|
|
138
|
+
|
|
139
|
+
return sessions[:limit]
|
|
140
|
+
|
|
141
|
+
def get_session_by_id(self, session_id: str) -> Optional[Dict[str, Any]]:
|
|
142
|
+
"""Get session data by ID.
|
|
143
|
+
|
|
144
|
+
Args:
|
|
145
|
+
session_id: Session ID to look up
|
|
146
|
+
|
|
147
|
+
Returns:
|
|
148
|
+
Session data dictionary or None if not found
|
|
149
|
+
"""
|
|
150
|
+
return self.active_sessions.get(session_id)
|
|
151
|
+
|
|
152
|
+
def get_last_interactive_session(self) -> Optional[str]:
|
|
153
|
+
"""Get the most recently used interactive session ID.
|
|
154
|
+
|
|
155
|
+
WHY: For --resume without arguments, we want to resume the last
|
|
156
|
+
interactive session (context="default" for regular Claude runs).
|
|
157
|
+
|
|
158
|
+
Returns:
|
|
159
|
+
Session ID of most recent interactive session, or None if none found
|
|
160
|
+
"""
|
|
161
|
+
recent_sessions = self.get_recent_sessions(limit=1, context="default")
|
|
162
|
+
if recent_sessions:
|
|
163
|
+
return recent_sessions[0]["id"]
|
|
164
|
+
return None
|
|
165
|
+
|
|
120
166
|
def _save_sessions(self):
|
|
121
167
|
"""Save sessions to disk."""
|
|
122
168
|
session_file = self.session_dir / "active_sessions.json"
|
claude_mpm/core/simple_runner.py
CHANGED
|
@@ -95,7 +95,7 @@ class ClaudeRunner:
|
|
|
95
95
|
except Exception as e:
|
|
96
96
|
self.logger.debug(f"Failed to create session log file: {e}")
|
|
97
97
|
|
|
98
|
-
# Initialize
|
|
98
|
+
# Initialize Socket.IO server reference
|
|
99
99
|
self.websocket_server = None
|
|
100
100
|
|
|
101
101
|
def setup_agents(self) -> bool:
|
|
@@ -152,13 +152,14 @@ class ClaudeRunner:
|
|
|
152
152
|
|
|
153
153
|
def run_interactive(self, initial_context: Optional[str] = None):
|
|
154
154
|
"""Run Claude in interactive mode."""
|
|
155
|
-
#
|
|
155
|
+
# Connect to Socket.IO server if enabled
|
|
156
156
|
if self.enable_websocket:
|
|
157
157
|
try:
|
|
158
|
-
#
|
|
159
|
-
from claude_mpm.services.
|
|
160
|
-
self.websocket_server =
|
|
158
|
+
# Use Socket.IO client proxy to connect to monitoring server
|
|
159
|
+
from claude_mpm.services.socketio_server import SocketIOClientProxy
|
|
160
|
+
self.websocket_server = SocketIOClientProxy(port=self.websocket_port)
|
|
161
161
|
self.websocket_server.start()
|
|
162
|
+
self.logger.info("Connected to Socket.IO monitoring server")
|
|
162
163
|
|
|
163
164
|
# Generate session ID
|
|
164
165
|
session_id = str(uuid.uuid4())
|
|
@@ -171,7 +172,7 @@ class ClaudeRunner:
|
|
|
171
172
|
working_dir=working_dir
|
|
172
173
|
)
|
|
173
174
|
except Exception as e:
|
|
174
|
-
self.logger.warning(f"Failed to
|
|
175
|
+
self.logger.warning(f"Failed to connect to Socket.IO server: {e}")
|
|
175
176
|
self.websocket_server = None
|
|
176
177
|
|
|
177
178
|
# Get version
|
|
@@ -329,13 +330,14 @@ class ClaudeRunner:
|
|
|
329
330
|
"""Run Claude with a single prompt and return success status."""
|
|
330
331
|
start_time = time.time()
|
|
331
332
|
|
|
332
|
-
#
|
|
333
|
+
# Connect to Socket.IO server if enabled
|
|
333
334
|
if self.enable_websocket:
|
|
334
335
|
try:
|
|
335
|
-
#
|
|
336
|
-
from claude_mpm.services.
|
|
337
|
-
self.websocket_server =
|
|
336
|
+
# Use Socket.IO client proxy to connect to monitoring server
|
|
337
|
+
from claude_mpm.services.socketio_server import SocketIOClientProxy
|
|
338
|
+
self.websocket_server = SocketIOClientProxy(port=self.websocket_port)
|
|
338
339
|
self.websocket_server.start()
|
|
340
|
+
self.logger.info("Connected to Socket.IO monitoring server")
|
|
339
341
|
|
|
340
342
|
# Generate session ID
|
|
341
343
|
session_id = str(uuid.uuid4())
|
|
@@ -348,7 +350,7 @@ class ClaudeRunner:
|
|
|
348
350
|
working_dir=working_dir
|
|
349
351
|
)
|
|
350
352
|
except Exception as e:
|
|
351
|
-
self.logger.warning(f"Failed to
|
|
353
|
+
self.logger.warning(f"Failed to connect to Socket.IO server: {e}")
|
|
352
354
|
self.websocket_server = None
|
|
353
355
|
|
|
354
356
|
# Check for /mpm: commands
|
|
@@ -31,13 +31,7 @@ except ImportError:
|
|
|
31
31
|
SOCKETIO_AVAILABLE = False
|
|
32
32
|
socketio = None
|
|
33
33
|
|
|
34
|
-
#
|
|
35
|
-
try:
|
|
36
|
-
from ...services.websocket_server import get_server_instance
|
|
37
|
-
SERVER_AVAILABLE = True
|
|
38
|
-
except ImportError:
|
|
39
|
-
SERVER_AVAILABLE = False
|
|
40
|
-
get_server_instance = None
|
|
34
|
+
# No fallback needed - we only use Socket.IO now
|
|
41
35
|
|
|
42
36
|
|
|
43
37
|
class ClaudeHookHandler:
|
|
@@ -65,14 +59,7 @@ class ClaudeHookHandler:
|
|
|
65
59
|
self._git_branch_cache = {}
|
|
66
60
|
self._git_branch_cache_time = {}
|
|
67
61
|
|
|
68
|
-
#
|
|
69
|
-
if SERVER_AVAILABLE:
|
|
70
|
-
try:
|
|
71
|
-
self.websocket_server = get_server_instance()
|
|
72
|
-
except:
|
|
73
|
-
self.websocket_server = None
|
|
74
|
-
else:
|
|
75
|
-
self.websocket_server = None
|
|
62
|
+
# No fallback server needed - we only use Socket.IO now
|
|
76
63
|
|
|
77
64
|
def _track_delegation(self, session_id: str, agent_type: str):
|
|
78
65
|
"""Track a new agent delegation."""
|
|
@@ -291,17 +278,6 @@ class ClaudeHookHandler:
|
|
|
291
278
|
print(f"Socket.IO emit failed: {e}", file=sys.stderr)
|
|
292
279
|
# Mark as disconnected so next call will reconnect
|
|
293
280
|
self.sio_connected = False
|
|
294
|
-
|
|
295
|
-
# Fallback to legacy WebSocket server
|
|
296
|
-
elif hasattr(self, 'websocket_server') and self.websocket_server:
|
|
297
|
-
try:
|
|
298
|
-
# Map to legacy event format
|
|
299
|
-
legacy_event = f"hook.{event}"
|
|
300
|
-
self.websocket_server.broadcast_event(legacy_event, data)
|
|
301
|
-
if DEBUG:
|
|
302
|
-
print(f"Emitted legacy event: {legacy_event}", file=sys.stderr)
|
|
303
|
-
except:
|
|
304
|
-
pass # Silent failure
|
|
305
281
|
|
|
306
282
|
def _handle_user_prompt_fast(self, event):
|
|
307
283
|
"""Handle user prompt with comprehensive data capture.
|