claude-mpm 4.2.21__py3-none-any.whl → 4.2.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.
- claude_mpm/VERSION +1 -1
- claude_mpm/cli/commands/monitor.py +34 -2
- claude_mpm/services/monitor/daemon.py +38 -6
- claude_mpm/services/monitor/management/lifecycle.py +173 -12
- claude_mpm/services/monitor/server.py +33 -6
- {claude_mpm-4.2.21.dist-info → claude_mpm-4.2.22.dist-info}/METADATA +8 -1
- {claude_mpm-4.2.21.dist-info → claude_mpm-4.2.22.dist-info}/RECORD +11 -11
- {claude_mpm-4.2.21.dist-info → claude_mpm-4.2.22.dist-info}/WHEEL +0 -0
- {claude_mpm-4.2.21.dist-info → claude_mpm-4.2.22.dist-info}/entry_points.txt +0 -0
- {claude_mpm-4.2.21.dist-info → claude_mpm-4.2.22.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-4.2.21.dist-info → claude_mpm-4.2.22.dist-info}/top_level.txt +0 -0
claude_mpm/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
4.2.
|
1
|
+
4.2.22
|
@@ -105,12 +105,44 @@ class MonitorCommand(BaseCommand):
|
|
105
105
|
|
106
106
|
# Start the daemon
|
107
107
|
if self.daemon.start():
|
108
|
-
|
108
|
+
# For daemon mode, verify it actually started
|
109
|
+
if daemon_mode:
|
110
|
+
# Give it a moment to fully initialize
|
111
|
+
import time
|
112
|
+
time.sleep(0.5)
|
113
|
+
|
114
|
+
# Check if it's actually running
|
115
|
+
if not self.daemon.lifecycle.is_running():
|
116
|
+
return CommandResult.error_result(
|
117
|
+
"Monitor daemon failed to start. Check ~/.claude-mpm/monitor-daemon.log for details."
|
118
|
+
)
|
119
|
+
|
120
|
+
# Get the actual PID
|
121
|
+
actual_pid = self.daemon.lifecycle.get_pid()
|
122
|
+
mode_info = f" in background (PID: {actual_pid})"
|
123
|
+
else:
|
124
|
+
mode_info = " in foreground"
|
125
|
+
|
109
126
|
return CommandResult.success_result(
|
110
127
|
f"Unified monitor daemon started on {host}:{port}{mode_info}",
|
111
128
|
data={"url": f"http://{host}:{port}", "port": port, "mode": mode_str},
|
112
129
|
)
|
113
|
-
|
130
|
+
|
131
|
+
# Check if error was due to port already in use
|
132
|
+
import socket
|
133
|
+
try:
|
134
|
+
test_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
135
|
+
test_sock.connect((host, port))
|
136
|
+
test_sock.close()
|
137
|
+
return CommandResult.error_result(
|
138
|
+
f"Port {port} is already in use. Try 'claude-mpm monitor stop' first or use a different port."
|
139
|
+
)
|
140
|
+
except:
|
141
|
+
pass
|
142
|
+
|
143
|
+
return CommandResult.error_result(
|
144
|
+
"Failed to start unified monitor daemon. Check ~/.claude-mpm/monitor-daemon.log for details."
|
145
|
+
)
|
114
146
|
|
115
147
|
def _stop_monitor(self, args) -> CommandResult:
|
116
148
|
"""Stop the unified monitor daemon."""
|
@@ -57,9 +57,11 @@ class UnifiedMonitorDaemon:
|
|
57
57
|
self.daemon_mode = daemon_mode
|
58
58
|
self.logger = get_logger(__name__)
|
59
59
|
|
60
|
-
# Daemon management
|
60
|
+
# Daemon management with port for verification
|
61
61
|
self.lifecycle = DaemonLifecycle(
|
62
|
-
pid_file=pid_file or self._get_default_pid_file(),
|
62
|
+
pid_file=pid_file or self._get_default_pid_file(),
|
63
|
+
log_file=log_file,
|
64
|
+
port=port
|
63
65
|
)
|
64
66
|
|
65
67
|
# Core server
|
@@ -102,6 +104,13 @@ class UnifiedMonitorDaemon:
|
|
102
104
|
existing_pid = self.lifecycle.get_pid()
|
103
105
|
self.logger.warning(f"Daemon already running with PID {existing_pid}")
|
104
106
|
return False
|
107
|
+
|
108
|
+
# Verify port is available before forking
|
109
|
+
port_available, error_msg = self.lifecycle.verify_port_available(self.host)
|
110
|
+
if not port_available:
|
111
|
+
self.logger.error(error_msg)
|
112
|
+
print(f"Error: {error_msg}", file=sys.stderr)
|
113
|
+
return False
|
105
114
|
|
106
115
|
# Wait for any pre-warming threads to complete before forking
|
107
116
|
self._wait_for_prewarm_completion()
|
@@ -112,7 +121,17 @@ class UnifiedMonitorDaemon:
|
|
112
121
|
return False
|
113
122
|
|
114
123
|
# Start the server in daemon mode
|
115
|
-
|
124
|
+
# This will run in the child process
|
125
|
+
try:
|
126
|
+
result = self._run_server()
|
127
|
+
if not result:
|
128
|
+
# Report failure before exiting
|
129
|
+
self.lifecycle._report_startup_error("Failed to start server")
|
130
|
+
return result
|
131
|
+
except Exception as e:
|
132
|
+
# Report any exceptions during startup
|
133
|
+
self.lifecycle._report_startup_error(f"Server startup exception: {e}")
|
134
|
+
raise
|
116
135
|
|
117
136
|
def _start_foreground(self) -> bool:
|
118
137
|
"""Start in foreground mode."""
|
@@ -140,11 +159,17 @@ class UnifiedMonitorDaemon:
|
|
140
159
|
try:
|
141
160
|
# Ensure components exist before starting
|
142
161
|
if not self.health_monitor:
|
143
|
-
|
162
|
+
error_msg = "Health monitor not initialized"
|
163
|
+
self.logger.error(error_msg)
|
164
|
+
if self.daemon_mode:
|
165
|
+
self.lifecycle._report_startup_error(error_msg)
|
144
166
|
return False
|
145
167
|
|
146
168
|
if not self.server:
|
147
|
-
|
169
|
+
error_msg = "Server not initialized"
|
170
|
+
self.logger.error(error_msg)
|
171
|
+
if self.daemon_mode:
|
172
|
+
self.lifecycle._report_startup_error(error_msg)
|
148
173
|
return False
|
149
174
|
|
150
175
|
# Start health monitoring
|
@@ -153,11 +178,18 @@ class UnifiedMonitorDaemon:
|
|
153
178
|
# Start the unified server
|
154
179
|
success = self.server.start()
|
155
180
|
if not success:
|
156
|
-
|
181
|
+
error_msg = "Failed to start unified monitor server"
|
182
|
+
self.logger.error(error_msg)
|
183
|
+
if self.daemon_mode:
|
184
|
+
self.lifecycle._report_startup_error(error_msg)
|
157
185
|
return False
|
158
186
|
|
159
187
|
self.running = True
|
160
188
|
self.logger.info("Unified monitor daemon started successfully")
|
189
|
+
|
190
|
+
# Report successful startup to parent (for daemon mode)
|
191
|
+
if self.daemon_mode:
|
192
|
+
self.lifecycle._report_startup_success()
|
161
193
|
|
162
194
|
# Keep running until shutdown
|
163
195
|
if self.daemon_mode:
|
@@ -15,10 +15,12 @@ DESIGN DECISIONS:
|
|
15
15
|
|
16
16
|
import os
|
17
17
|
import signal
|
18
|
+
import socket
|
18
19
|
import sys
|
20
|
+
import tempfile
|
19
21
|
import time
|
20
22
|
from pathlib import Path
|
21
|
-
from typing import Optional
|
23
|
+
from typing import Optional, Tuple
|
22
24
|
|
23
25
|
from ....core.logging_config import get_logger
|
24
26
|
|
@@ -30,16 +32,20 @@ class DaemonLifecycle:
|
|
30
32
|
handling, and graceful shutdown capabilities.
|
31
33
|
"""
|
32
34
|
|
33
|
-
def __init__(self, pid_file: str, log_file: Optional[str] = None):
|
35
|
+
def __init__(self, pid_file: str, log_file: Optional[str] = None, port: int = 8765):
|
34
36
|
"""Initialize daemon lifecycle manager.
|
35
37
|
|
36
38
|
Args:
|
37
39
|
pid_file: Path to PID file
|
38
40
|
log_file: Path to log file for daemon mode
|
41
|
+
port: Port number for startup verification
|
39
42
|
"""
|
40
43
|
self.pid_file = Path(pid_file)
|
41
44
|
self.log_file = Path(log_file) if log_file else None
|
45
|
+
self.port = port
|
42
46
|
self.logger = get_logger(__name__)
|
47
|
+
# Create a temporary file for startup status communication
|
48
|
+
self.startup_status_file = None
|
43
49
|
|
44
50
|
def daemonize(self) -> bool:
|
45
51
|
"""Daemonize the current process.
|
@@ -50,14 +56,20 @@ class DaemonLifecycle:
|
|
50
56
|
try:
|
51
57
|
# Clean up any existing asyncio event loops before forking
|
52
58
|
self._cleanup_event_loops()
|
59
|
+
|
60
|
+
# Create a temporary file for startup status communication
|
61
|
+
with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.status') as f:
|
62
|
+
self.startup_status_file = f.name
|
63
|
+
f.write("starting")
|
53
64
|
|
54
65
|
# First fork
|
55
66
|
pid = os.fork()
|
56
67
|
if pid > 0:
|
57
|
-
# Parent process
|
58
|
-
|
68
|
+
# Parent process - wait for child to confirm startup
|
69
|
+
return self._parent_wait_for_startup(pid)
|
59
70
|
except OSError as e:
|
60
71
|
self.logger.error(f"First fork failed: {e}")
|
72
|
+
self._report_startup_error(f"First fork failed: {e}")
|
61
73
|
return False
|
62
74
|
|
63
75
|
# Decouple from parent environment
|
@@ -69,22 +81,33 @@ class DaemonLifecycle:
|
|
69
81
|
# Second fork
|
70
82
|
pid = os.fork()
|
71
83
|
if pid > 0:
|
72
|
-
#
|
84
|
+
# First child process exits
|
73
85
|
sys.exit(0)
|
74
86
|
except OSError as e:
|
75
87
|
self.logger.error(f"Second fork failed: {e}")
|
88
|
+
self._report_startup_error(f"Second fork failed: {e}")
|
89
|
+
return False
|
90
|
+
|
91
|
+
# Set up error logging before redirecting streams
|
92
|
+
self._setup_early_error_logging()
|
93
|
+
|
94
|
+
# Write PID file first (before stream redirection)
|
95
|
+
try:
|
96
|
+
self.write_pid_file()
|
97
|
+
except Exception as e:
|
98
|
+
self._report_startup_error(f"Failed to write PID file: {e}")
|
76
99
|
return False
|
77
100
|
|
78
101
|
# Redirect standard file descriptors
|
79
102
|
self._redirect_streams()
|
80
103
|
|
81
|
-
# Write PID file
|
82
|
-
self.write_pid_file()
|
83
|
-
|
84
104
|
# Setup signal handlers
|
85
105
|
self._setup_signal_handlers()
|
86
106
|
|
87
107
|
self.logger.info(f"Daemon process started with PID {os.getpid()}")
|
108
|
+
|
109
|
+
# Report successful startup (after basic setup but before server start)
|
110
|
+
self._report_startup_success()
|
88
111
|
return True
|
89
112
|
|
90
113
|
def _redirect_streams(self):
|
@@ -105,13 +128,16 @@ class DaemonLifecycle:
|
|
105
128
|
os.dup2(log_out.fileno(), sys.stdout.fileno())
|
106
129
|
os.dup2(log_out.fileno(), sys.stderr.fileno())
|
107
130
|
else:
|
108
|
-
#
|
109
|
-
|
110
|
-
|
111
|
-
|
131
|
+
# Default to a daemon log file instead of /dev/null for errors
|
132
|
+
default_log = Path.home() / ".claude-mpm" / "monitor-daemon.log"
|
133
|
+
default_log.parent.mkdir(parents=True, exist_ok=True)
|
134
|
+
with open(default_log, "a") as log_out:
|
135
|
+
os.dup2(log_out.fileno(), sys.stdout.fileno())
|
136
|
+
os.dup2(log_out.fileno(), sys.stderr.fileno())
|
112
137
|
|
113
138
|
except Exception as e:
|
114
139
|
self.logger.error(f"Error redirecting streams: {e}")
|
140
|
+
self._report_startup_error(f"Failed to redirect streams: {e}")
|
115
141
|
|
116
142
|
def write_pid_file(self):
|
117
143
|
"""Write PID to PID file."""
|
@@ -336,3 +362,138 @@ class DaemonLifecycle:
|
|
336
362
|
self.logger.debug(f"Error getting process info: {e}")
|
337
363
|
|
338
364
|
return status
|
365
|
+
|
366
|
+
def _parent_wait_for_startup(self, child_pid: int, timeout: float = 10.0) -> bool:
|
367
|
+
"""Parent process waits for child daemon to report startup status.
|
368
|
+
|
369
|
+
Args:
|
370
|
+
child_pid: PID of the child process
|
371
|
+
timeout: Maximum time to wait for startup
|
372
|
+
|
373
|
+
Returns:
|
374
|
+
True if child started successfully, False otherwise
|
375
|
+
"""
|
376
|
+
import time
|
377
|
+
start_time = time.time()
|
378
|
+
|
379
|
+
# Wait for child to update status file
|
380
|
+
while time.time() - start_time < timeout:
|
381
|
+
try:
|
382
|
+
# Check if status file exists and read it
|
383
|
+
if self.startup_status_file and Path(self.startup_status_file).exists():
|
384
|
+
with open(self.startup_status_file, 'r') as f:
|
385
|
+
status = f.read().strip()
|
386
|
+
|
387
|
+
if status == "success":
|
388
|
+
# Child started successfully
|
389
|
+
self._cleanup_status_file()
|
390
|
+
return True
|
391
|
+
elif status.startswith("error:"):
|
392
|
+
# Child reported an error
|
393
|
+
error_msg = status[6:] # Remove "error:" prefix
|
394
|
+
self.logger.error(f"Daemon startup failed: {error_msg}")
|
395
|
+
print(f"Error: Failed to start monitor daemon: {error_msg}", file=sys.stderr)
|
396
|
+
self._cleanup_status_file()
|
397
|
+
return False
|
398
|
+
elif status == "starting":
|
399
|
+
# Still starting, continue waiting
|
400
|
+
pass
|
401
|
+
|
402
|
+
# Also check if child process is still alive
|
403
|
+
try:
|
404
|
+
os.kill(child_pid, 0) # Check if process exists
|
405
|
+
except ProcessLookupError:
|
406
|
+
# Child process died
|
407
|
+
self.logger.error("Child daemon process died during startup")
|
408
|
+
print("Error: Monitor daemon process died during startup", file=sys.stderr)
|
409
|
+
self._cleanup_status_file()
|
410
|
+
return False
|
411
|
+
|
412
|
+
except Exception as e:
|
413
|
+
self.logger.debug(f"Error checking startup status: {e}")
|
414
|
+
|
415
|
+
time.sleep(0.1) # Check every 100ms
|
416
|
+
|
417
|
+
# Timeout reached
|
418
|
+
self.logger.error(f"Daemon startup timed out after {timeout} seconds")
|
419
|
+
print(f"Error: Monitor daemon startup timed out after {timeout} seconds", file=sys.stderr)
|
420
|
+
self._cleanup_status_file()
|
421
|
+
return False
|
422
|
+
|
423
|
+
def _report_startup_success(self):
|
424
|
+
"""Report successful startup to parent process."""
|
425
|
+
if self.startup_status_file:
|
426
|
+
try:
|
427
|
+
with open(self.startup_status_file, 'w') as f:
|
428
|
+
f.write("success")
|
429
|
+
except Exception as e:
|
430
|
+
self.logger.error(f"Failed to report startup success: {e}")
|
431
|
+
|
432
|
+
def _report_startup_error(self, error_msg: str):
|
433
|
+
"""Report startup error to parent process.
|
434
|
+
|
435
|
+
Args:
|
436
|
+
error_msg: Error message to report
|
437
|
+
"""
|
438
|
+
if self.startup_status_file:
|
439
|
+
try:
|
440
|
+
with open(self.startup_status_file, 'w') as f:
|
441
|
+
f.write(f"error:{error_msg}")
|
442
|
+
except Exception:
|
443
|
+
pass # Can't report if file write fails
|
444
|
+
|
445
|
+
def _cleanup_status_file(self):
|
446
|
+
"""Clean up the temporary status file."""
|
447
|
+
if self.startup_status_file:
|
448
|
+
try:
|
449
|
+
Path(self.startup_status_file).unlink(missing_ok=True)
|
450
|
+
except Exception:
|
451
|
+
pass # Ignore cleanup errors
|
452
|
+
finally:
|
453
|
+
self.startup_status_file = None
|
454
|
+
|
455
|
+
def _setup_early_error_logging(self):
|
456
|
+
"""Set up error logging before stream redirection.
|
457
|
+
|
458
|
+
This ensures we can capture and report errors that occur during
|
459
|
+
daemon initialization, especially port binding errors.
|
460
|
+
"""
|
461
|
+
try:
|
462
|
+
# If no log file specified, create a default one
|
463
|
+
if not self.log_file:
|
464
|
+
default_log = Path.home() / ".claude-mpm" / "monitor-daemon.log"
|
465
|
+
default_log.parent.mkdir(parents=True, exist_ok=True)
|
466
|
+
self.log_file = default_log
|
467
|
+
|
468
|
+
# Configure logger to write to file immediately
|
469
|
+
import logging
|
470
|
+
file_handler = logging.FileHandler(self.log_file)
|
471
|
+
file_handler.setLevel(logging.DEBUG)
|
472
|
+
formatter = logging.Formatter(
|
473
|
+
'%(asctime)s - %(name)s - %(levelname)s - %(message)s'
|
474
|
+
)
|
475
|
+
file_handler.setFormatter(formatter)
|
476
|
+
self.logger.addHandler(file_handler)
|
477
|
+
|
478
|
+
except Exception as e:
|
479
|
+
# If we can't set up logging, at least try to report the error
|
480
|
+
self._report_startup_error(f"Failed to setup error logging: {e}")
|
481
|
+
|
482
|
+
def verify_port_available(self, host: str = "localhost") -> Tuple[bool, Optional[str]]:
|
483
|
+
"""Verify that the port is available for binding.
|
484
|
+
|
485
|
+
Args:
|
486
|
+
host: Host to check port on
|
487
|
+
|
488
|
+
Returns:
|
489
|
+
Tuple of (is_available, error_message)
|
490
|
+
"""
|
491
|
+
try:
|
492
|
+
test_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
493
|
+
test_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
494
|
+
test_sock.bind((host, self.port))
|
495
|
+
test_sock.close()
|
496
|
+
return True, None
|
497
|
+
except OSError as e:
|
498
|
+
error_msg = f"Port {self.port} is already in use or cannot be bound: {e}"
|
499
|
+
return False, error_msg
|
@@ -79,6 +79,7 @@ class UnifiedMonitorServer:
|
|
79
79
|
self.running = False
|
80
80
|
self.loop = None
|
81
81
|
self.server_thread = None
|
82
|
+
self.startup_error = None # Track startup errors
|
82
83
|
|
83
84
|
# Heartbeat tracking
|
84
85
|
self.heartbeat_task: Optional[asyncio.Task] = None
|
@@ -106,10 +107,15 @@ class UnifiedMonitorServer:
|
|
106
107
|
for _ in range(50): # Wait up to 5 seconds
|
107
108
|
if self.running:
|
108
109
|
break
|
110
|
+
if self.startup_error:
|
111
|
+
# Server thread reported an error
|
112
|
+
self.logger.error(f"Server startup failed: {self.startup_error}")
|
113
|
+
return False
|
109
114
|
time.sleep(0.1)
|
110
115
|
|
111
116
|
if not self.running:
|
112
|
-
self.
|
117
|
+
error_msg = self.startup_error or "Server failed to start within timeout"
|
118
|
+
self.logger.error(error_msg)
|
113
119
|
return False
|
114
120
|
|
115
121
|
self.logger.info("Unified monitor server started successfully")
|
@@ -131,8 +137,17 @@ class UnifiedMonitorServer:
|
|
131
137
|
# Run the async server
|
132
138
|
loop.run_until_complete(self._start_async_server())
|
133
139
|
|
140
|
+
except OSError as e:
|
141
|
+
# Specific handling for port binding errors
|
142
|
+
if "Address already in use" in str(e) or "[Errno 48]" in str(e):
|
143
|
+
self.logger.error(f"Port {self.port} is already in use: {e}")
|
144
|
+
self.startup_error = f"Port {self.port} is already in use"
|
145
|
+
else:
|
146
|
+
self.logger.error(f"OS error in server thread: {e}")
|
147
|
+
self.startup_error = str(e)
|
134
148
|
except Exception as e:
|
135
149
|
self.logger.error(f"Error in server thread: {e}")
|
150
|
+
self.startup_error = str(e)
|
136
151
|
finally:
|
137
152
|
# Always ensure loop cleanup happens
|
138
153
|
if loop is not None:
|
@@ -212,11 +227,23 @@ class UnifiedMonitorServer:
|
|
212
227
|
self.runner = web.AppRunner(self.app)
|
213
228
|
await self.runner.setup()
|
214
229
|
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
230
|
+
try:
|
231
|
+
self.site = web.TCPSite(self.runner, self.host, self.port)
|
232
|
+
await self.site.start()
|
233
|
+
|
234
|
+
self.running = True
|
235
|
+
self.logger.info(f"Server running on http://{self.host}:{self.port}")
|
236
|
+
except OSError as e:
|
237
|
+
# Port binding error - make sure it's reported clearly
|
238
|
+
if "Address already in use" in str(e) or "[Errno 48]" in str(e):
|
239
|
+
error_msg = f"Port {self.port} is already in use. Another process may be using this port."
|
240
|
+
self.logger.error(error_msg)
|
241
|
+
self.startup_error = error_msg
|
242
|
+
raise OSError(error_msg) from e
|
243
|
+
else:
|
244
|
+
self.logger.error(f"Failed to bind to {self.host}:{self.port}: {e}")
|
245
|
+
self.startup_error = str(e)
|
246
|
+
raise
|
220
247
|
|
221
248
|
# Keep the server running
|
222
249
|
while self.running:
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: claude-mpm
|
3
|
-
Version: 4.2.
|
3
|
+
Version: 4.2.22
|
4
4
|
Summary: Claude Multi-Agent Project Manager - Orchestrate Claude with agent delegation and ticket tracking
|
5
5
|
Author-email: Bob Matsuoka <bob@matsuoka.com>
|
6
6
|
Maintainer: Claude MPM Team
|
@@ -149,11 +149,18 @@ pip install claude-mpm
|
|
149
149
|
|
150
150
|
Or with pipx (recommended for isolated installation):
|
151
151
|
```bash
|
152
|
+
# Install with monitor support (recommended)
|
153
|
+
pipx install "claude-mpm[monitor]"
|
154
|
+
|
155
|
+
# Basic installation without monitor
|
152
156
|
pipx install claude-mpm
|
157
|
+
|
153
158
|
# Configure MCP for pipx users:
|
154
159
|
claude-mpm mcp-pipx-config
|
155
160
|
```
|
156
161
|
|
162
|
+
**💡 Pipx Tip**: Use `"claude-mpm[monitor]"` to get full monitoring dashboard functionality! The `[monitor]` optional dependency includes Socket.IO and async web server components needed for real-time agent monitoring.
|
163
|
+
|
157
164
|
**🎉 Pipx Support Now Fully Functional!** Recent improvements ensure complete compatibility:
|
158
165
|
- ✅ Socket.IO daemon script path resolution (fixed)
|
159
166
|
- ✅ Commands directory access (fixed)
|
@@ -1,5 +1,5 @@
|
|
1
1
|
claude_mpm/BUILD_NUMBER,sha256=toytnNjkIKPgQaGwDqQdC1rpNTAdSEc6Vja50d7Ovug,4
|
2
|
-
claude_mpm/VERSION,sha256=
|
2
|
+
claude_mpm/VERSION,sha256=Ot17YPeSmtj6ZTyrKo7TK2xXA1paIoGEVSS9MGsMyHA,7
|
3
3
|
claude_mpm/__init__.py,sha256=lyTZAYGH4DTaFGLRNWJKk5Q5oTjzN5I6AXmfVX-Jff0,1512
|
4
4
|
claude_mpm/__main__.py,sha256=Ro5UBWBoQaSAIoSqWAr7zkbLyvi4sSy28WShqAhKJG0,723
|
5
5
|
claude_mpm/constants.py,sha256=I946iCQzIIPRZVVJ8aO7lA4euiyDnNw2IX7EelAOkIE,5915
|
@@ -83,7 +83,7 @@ claude_mpm/cli/commands/mcp_pipx_config.py,sha256=sE62VD6Q1CcO2k1nlbIhHMfAJFQTZf
|
|
83
83
|
claude_mpm/cli/commands/mcp_server_commands.py,sha256=-1G_2Y5ScTvzDd-kY8fTAao2H6FH7DnsLimleF1rVqQ,6197
|
84
84
|
claude_mpm/cli/commands/mcp_tool_commands.py,sha256=q17GzlFT3JiLTrDqwPO2tz1-fKmPO5QU449syTnKTz4,1283
|
85
85
|
claude_mpm/cli/commands/memory.py,sha256=Yzfs3_oiKciv3sfOoDm2lJL4M9idG7ARV3-sNw1ge_g,26186
|
86
|
-
claude_mpm/cli/commands/monitor.py,sha256=
|
86
|
+
claude_mpm/cli/commands/monitor.py,sha256=WeUprVOIbuZuxmw4GJo54RPTnYIIJ5LgFonAymeTW7k,9442
|
87
87
|
claude_mpm/cli/commands/mpm_init.py,sha256=lO7N91ZHn_n18XbchUUcYoyme7L5NLcXVnhWm5F_Gq8,22367
|
88
88
|
claude_mpm/cli/commands/mpm_init_handler.py,sha256=-pCB0XL3KipqGtnta8CC7Lg5TPMwstEhMFBcgF4aaa4,2919
|
89
89
|
claude_mpm/cli/commands/run.py,sha256=qS3eolLiDrE8EXLQJioB6kL1ONr_l0c3OE3qMUJCqbA,43489
|
@@ -548,9 +548,9 @@ claude_mpm/services/memory/cache/__init__.py,sha256=6M6-P8ParyxX8vOgp_IxHgLMvacr
|
|
548
548
|
claude_mpm/services/memory/cache/shared_prompt_cache.py,sha256=crnYPUT8zcS7TvoE1vW7pyaf4T77N5rJ1wUf_YQ2vvo,28704
|
549
549
|
claude_mpm/services/memory/cache/simple_cache.py,sha256=qsTjbcsPxj-kNfaod9VN_uE5NioIwpfkUin_mMVUJCg,10218
|
550
550
|
claude_mpm/services/monitor/__init__.py,sha256=X7gxSLUm9Fg_zEsX6LtCHP2ipF0qj6Emkun20h2So7g,745
|
551
|
-
claude_mpm/services/monitor/daemon.py,sha256=
|
551
|
+
claude_mpm/services/monitor/daemon.py,sha256=oFMDnMZABAqKEuuj62W2gmDSxUSr0jHflzddMVfXz8k,16739
|
552
552
|
claude_mpm/services/monitor/event_emitter.py,sha256=JzRLNg8PUJ5s3ulNnq_D4yqCPItvidJzu8DmFxriieQ,12224
|
553
|
-
claude_mpm/services/monitor/server.py,sha256=
|
553
|
+
claude_mpm/services/monitor/server.py,sha256=ELo1CLucGAiXLh7iSkwM82nCCwE_ANgqd5sMG0TwUY0,27899
|
554
554
|
claude_mpm/services/monitor/handlers/__init__.py,sha256=jgPIf4IJVERm_tAeD9834tfx9IcxtlHj5r9rhEWpkfM,701
|
555
555
|
claude_mpm/services/monitor/handlers/code_analysis.py,sha256=mHyI27Wp6WVmUBc0m0i991ogyFZBTvkrfR7Kf3EAk5U,11474
|
556
556
|
claude_mpm/services/monitor/handlers/dashboard.py,sha256=uGBhb-6RG6u4WLipUXgdx7RCW-vb_qek5dIfHIwAC7o,9805
|
@@ -558,7 +558,7 @@ claude_mpm/services/monitor/handlers/file.py,sha256=p3C4wffl0GIcN00b-KkrmZ8F-Amd
|
|
558
558
|
claude_mpm/services/monitor/handlers/hooks.py,sha256=dlrmyFu8WChlvn6-sND9DLjSbm5nrMfNZrAgoWN-2No,17582
|
559
559
|
claude_mpm/services/monitor/management/__init__.py,sha256=mxaEFRgvvgV85gUpXu_DsnHtywihdP14EisvISAVZuQ,525
|
560
560
|
claude_mpm/services/monitor/management/health.py,sha256=Wm92Cli_4cWD6B89KX_CdpAvvevuEaGB8Ah59ILhFww,3772
|
561
|
-
claude_mpm/services/monitor/management/lifecycle.py,sha256
|
561
|
+
claude_mpm/services/monitor/management/lifecycle.py,sha256=-KuTXEkpoZQmqVvYE2q7EmwpezY4ZM5Yvp71cWYD93Q,17667
|
562
562
|
claude_mpm/services/project/__init__.py,sha256=IUclN1L7ChHCNya7PJiVxu4nttxsrj3WRIpwyA1A_hw,512
|
563
563
|
claude_mpm/services/project/analyzer.py,sha256=VHlLrP8-S5gr12w4Yzs7-6d7LWdJKISHPCFSG7SDiQU,38434
|
564
564
|
claude_mpm/services/project/analyzer_refactored.py,sha256=USYEdPAhSoGPqZCpaT89Dw6ElFW_L1yXSURheQjAhLA,18243
|
@@ -639,9 +639,9 @@ claude_mpm/utils/subprocess_utils.py,sha256=zgiwLqh_17WxHpySvUPH65pb4bzIeUGOAYUJ
|
|
639
639
|
claude_mpm/validation/__init__.py,sha256=YZhwE3mhit-lslvRLuwfX82xJ_k4haZeKmh4IWaVwtk,156
|
640
640
|
claude_mpm/validation/agent_validator.py,sha256=3Lo6LK-Mw9IdnL_bd3zl_R6FkgSVDYKUUM7EeVVD3jc,20865
|
641
641
|
claude_mpm/validation/frontmatter_validator.py,sha256=u8g4Eyd_9O6ugj7Un47oSGh3kqv4wMkuks2i_CtWRvM,7028
|
642
|
-
claude_mpm-4.2.
|
643
|
-
claude_mpm-4.2.
|
644
|
-
claude_mpm-4.2.
|
645
|
-
claude_mpm-4.2.
|
646
|
-
claude_mpm-4.2.
|
647
|
-
claude_mpm-4.2.
|
642
|
+
claude_mpm-4.2.22.dist-info/licenses/LICENSE,sha256=lpaivOlPuBZW1ds05uQLJJswy8Rp_HMNieJEbFlqvLk,1072
|
643
|
+
claude_mpm-4.2.22.dist-info/METADATA,sha256=hPxeNHmoQ1VlgLWWOaox9vJ5hTTpODq2O67IhCUbt5g,14451
|
644
|
+
claude_mpm-4.2.22.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
645
|
+
claude_mpm-4.2.22.dist-info/entry_points.txt,sha256=FDPZgz8JOvD-6iuXY2l9Zbo9zYVRuE4uz4Qr0vLeGOk,471
|
646
|
+
claude_mpm-4.2.22.dist-info/top_level.txt,sha256=1nUg3FEaBySgm8t-s54jK5zoPnu3_eY6EP6IOlekyHA,11
|
647
|
+
claude_mpm-4.2.22.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|