claude-mpm 4.2.39__py3-none-any.whl → 4.2.40__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/__init__.py +3 -5
- claude_mpm/services/cli/unified_dashboard_manager.py +1 -1
- claude_mpm/services/monitor/daemon.py +69 -36
- claude_mpm/services/monitor/daemon_manager.py +186 -29
- claude_mpm/services/monitor/server.py +5 -1
- {claude_mpm-4.2.39.dist-info → claude_mpm-4.2.40.dist-info}/METADATA +1 -1
- {claude_mpm-4.2.39.dist-info → claude_mpm-4.2.40.dist-info}/RECORD +12 -12
- {claude_mpm-4.2.39.dist-info → claude_mpm-4.2.40.dist-info}/WHEEL +0 -0
- {claude_mpm-4.2.39.dist-info → claude_mpm-4.2.40.dist-info}/entry_points.txt +0 -0
- {claude_mpm-4.2.39.dist-info → claude_mpm-4.2.40.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-4.2.39.dist-info → claude_mpm-4.2.40.dist-info}/top_level.txt +0 -0
claude_mpm/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
4.2.
|
1
|
+
4.2.40
|
claude_mpm/cli/__init__.py
CHANGED
@@ -106,11 +106,9 @@ def main(argv: Optional[list] = None):
|
|
106
106
|
# Check for MCP auto-configuration (pipx installations)
|
107
107
|
_check_mcp_auto_configuration()
|
108
108
|
|
109
|
-
#
|
110
|
-
# The
|
111
|
-
|
112
|
-
# Accepting the 11.9s delay on first MCP use is better than a broken monitor.
|
113
|
-
# _verify_mcp_gateway_startup()
|
109
|
+
# Re-enabled: MCP pre-warming is safe with subprocess daemon (v4.2.40)
|
110
|
+
# The subprocess approach avoids fork() issues entirely
|
111
|
+
_verify_mcp_gateway_startup()
|
114
112
|
|
115
113
|
# Set up logging
|
116
114
|
# Special case: For MCP start command, we need minimal logging to avoid stdout interference
|
@@ -133,7 +133,7 @@ class UnifiedDashboardManager(IUnifiedDashboardManager):
|
|
133
133
|
if background:
|
134
134
|
# The daemon.start() method will handle cleanup when force_restart=True
|
135
135
|
# We don't need pre-emptive cleanup here as it causes race conditions
|
136
|
-
|
136
|
+
|
137
137
|
# Try to start daemon with retry on port conflicts
|
138
138
|
max_retries = 3
|
139
139
|
retry_count = 0
|
@@ -157,35 +157,67 @@ class UnifiedMonitorDaemon:
|
|
157
157
|
)
|
158
158
|
return False
|
159
159
|
|
160
|
-
#
|
161
|
-
|
162
|
-
|
163
|
-
#
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
160
|
+
# Use subprocess approach for clean daemon startup (v4.2.40)
|
161
|
+
# This avoids all fork() + threading issues by starting in a fresh process
|
162
|
+
# The daemon_manager.use_subprocess_daemon() now checks for CLAUDE_MPM_SUBPROCESS_DAEMON
|
163
|
+
# environment variable to prevent infinite recursion
|
164
|
+
if self.daemon_manager.use_subprocess_daemon():
|
165
|
+
# Start using subprocess - this returns immediately in parent
|
166
|
+
success = self.daemon_manager.start_daemon_subprocess()
|
167
|
+
return success
|
168
|
+
|
169
|
+
# Check if we're in subprocess mode (environment variable set)
|
170
|
+
if os.environ.get("CLAUDE_MPM_SUBPROCESS_DAEMON") == "1":
|
171
|
+
# We're in a subprocess started by start_daemon_subprocess
|
172
|
+
# We need to write the PID file ourselves since parent didn't
|
173
|
+
self.logger.info("Running in subprocess daemon mode, writing PID file")
|
174
|
+
self.daemon_manager.write_pid_file()
|
175
|
+
|
176
|
+
# Setup signal handlers for graceful shutdown
|
177
|
+
self._setup_signal_handlers()
|
178
|
+
|
179
|
+
# Start the server (this will run until shutdown)
|
180
|
+
try:
|
181
|
+
result = self._run_server()
|
182
|
+
if not result:
|
183
|
+
self.logger.error("Failed to start server in subprocess mode")
|
184
|
+
return result
|
185
|
+
except Exception as e:
|
186
|
+
self.logger.error(f"Server startup exception in subprocess: {e}")
|
187
|
+
raise
|
188
|
+
else:
|
189
|
+
# Legacy fork approach (kept for compatibility but not used by default)
|
190
|
+
# Wait for any pre-warming threads to complete before forking
|
191
|
+
self._wait_for_prewarm_completion()
|
192
|
+
|
193
|
+
# Use daemon manager's daemonize which includes cleanup
|
194
|
+
# DO NOT reset startup_status_file - it's needed for parent-child communication!
|
195
|
+
# self.daemon_manager.startup_status_file = None # BUG: This breaks communication
|
196
|
+
success = self.daemon_manager.daemonize()
|
197
|
+
if not success:
|
198
|
+
return False
|
169
199
|
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
200
|
+
# We're now in the daemon process
|
201
|
+
# Update our PID references and status file
|
202
|
+
self.lifecycle.pid_file = self.daemon_manager.pid_file
|
203
|
+
self.lifecycle.startup_status_file = self.daemon_manager.startup_status_file
|
174
204
|
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
205
|
+
# Start the server in daemon mode
|
206
|
+
try:
|
207
|
+
result = self._run_server()
|
208
|
+
if not result:
|
209
|
+
# Report failure before exiting
|
210
|
+
self.daemon_manager._report_startup_error("Failed to start server")
|
211
|
+
else:
|
212
|
+
# Report success
|
213
|
+
self.daemon_manager._report_startup_success()
|
214
|
+
return result
|
215
|
+
except Exception as e:
|
216
|
+
# Report any exceptions during startup
|
217
|
+
self.daemon_manager._report_startup_error(
|
218
|
+
f"Server startup exception: {e}"
|
219
|
+
)
|
220
|
+
raise
|
189
221
|
|
190
222
|
def _start_foreground(self, force_restart: bool = False) -> bool:
|
191
223
|
"""Start in foreground mode.
|
@@ -602,29 +634,30 @@ class UnifiedMonitorDaemon:
|
|
602
634
|
self.logger.info(
|
603
635
|
f"Waiting for {len(active_threads)} background threads to complete before forking"
|
604
636
|
)
|
605
|
-
|
637
|
+
|
606
638
|
# List thread names for debugging
|
607
639
|
thread_names = [t.name for t in active_threads]
|
608
640
|
self.logger.debug(f"Active threads: {thread_names}")
|
609
641
|
|
610
642
|
# Wait for threads to complete or timeout
|
611
643
|
while time.time() - start_time < timeout:
|
612
|
-
remaining_threads = [
|
613
|
-
t for t in active_threads if t.is_alive()
|
614
|
-
]
|
644
|
+
remaining_threads = [t for t in active_threads if t.is_alive()]
|
615
645
|
if not remaining_threads:
|
616
646
|
self.logger.debug("All threads completed")
|
617
647
|
break
|
618
|
-
|
648
|
+
|
619
649
|
# Log remaining threads periodically
|
620
650
|
if int(time.time() - start_time) % 1 == 0:
|
621
|
-
self.logger.debug(
|
622
|
-
|
651
|
+
self.logger.debug(
|
652
|
+
f"{len(remaining_threads)} threads still active"
|
653
|
+
)
|
654
|
+
|
623
655
|
time.sleep(0.1)
|
624
|
-
|
656
|
+
|
625
657
|
# Final check
|
626
658
|
final_threads = [
|
627
|
-
t
|
659
|
+
t
|
660
|
+
for t in threading.enumerate()
|
628
661
|
if t.is_alive() and t != threading.current_thread()
|
629
662
|
]
|
630
663
|
if final_threads:
|
@@ -149,7 +149,7 @@ class DaemonManager:
|
|
149
149
|
# Try IPv4 first (most common)
|
150
150
|
test_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
151
151
|
test_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
152
|
-
|
152
|
+
|
153
153
|
# Use 127.0.0.1 for localhost to match what the server does
|
154
154
|
bind_host = "127.0.0.1" if self.host == "localhost" else self.host
|
155
155
|
test_sock.bind((bind_host, self.port))
|
@@ -201,7 +201,10 @@ class DaemonManager:
|
|
201
201
|
try:
|
202
202
|
# Find processes using the port
|
203
203
|
result = subprocess.run(
|
204
|
-
["lsof", "-ti", f":{self.port}"],
|
204
|
+
["lsof", "-ti", f":{self.port}"],
|
205
|
+
capture_output=True,
|
206
|
+
text=True,
|
207
|
+
check=False,
|
205
208
|
)
|
206
209
|
|
207
210
|
if result.returncode != 0 or not result.stdout.strip():
|
@@ -220,35 +223,41 @@ class DaemonManager:
|
|
220
223
|
process_info = subprocess.run(
|
221
224
|
["ps", "-p", str(pid), "-o", "comm="],
|
222
225
|
capture_output=True,
|
223
|
-
text=True,
|
226
|
+
text=True,
|
227
|
+
check=False,
|
224
228
|
)
|
225
229
|
|
226
230
|
# Get full command to check if it's our monitor process
|
227
231
|
cmd_info = subprocess.run(
|
228
232
|
["ps", "-p", str(pid), "-o", "command="],
|
229
233
|
capture_output=True,
|
230
|
-
text=True,
|
234
|
+
text=True,
|
235
|
+
check=False,
|
231
236
|
)
|
232
|
-
|
237
|
+
|
233
238
|
if cmd_info.returncode != 0:
|
234
239
|
continue
|
235
|
-
|
240
|
+
|
236
241
|
full_command = cmd_info.stdout.strip().lower()
|
237
242
|
process_name = process_info.stdout.strip().lower()
|
238
|
-
|
243
|
+
|
239
244
|
# Check if this is our monitor/socketio process specifically
|
240
245
|
# Look for monitor, socketio, dashboard, or our specific port
|
241
|
-
is_monitor = any(
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
246
|
+
is_monitor = any(
|
247
|
+
[
|
248
|
+
"monitor" in full_command,
|
249
|
+
"socketio" in full_command,
|
250
|
+
"dashboard" in full_command,
|
251
|
+
f"port={self.port}" in full_command,
|
252
|
+
f":{self.port}" in full_command,
|
253
|
+
"unified_monitor" in full_command,
|
254
|
+
]
|
255
|
+
)
|
256
|
+
|
250
257
|
if is_monitor and "python" in process_name:
|
251
|
-
self.logger.info(
|
258
|
+
self.logger.info(
|
259
|
+
f"Killing monitor process {pid}: {full_command[:100]}"
|
260
|
+
)
|
252
261
|
os.kill(pid, signal.SIGTERM)
|
253
262
|
|
254
263
|
# Wait briefly for graceful shutdown
|
@@ -329,7 +338,7 @@ class DaemonManager:
|
|
329
338
|
|
330
339
|
def _kill_claude_mpm_processes(self) -> bool:
|
331
340
|
"""Kill any claude-mpm monitor processes specifically.
|
332
|
-
|
341
|
+
|
333
342
|
This targets monitor/dashboard/socketio processes only,
|
334
343
|
NOT general Claude instances.
|
335
344
|
|
@@ -338,7 +347,9 @@ class DaemonManager:
|
|
338
347
|
"""
|
339
348
|
try:
|
340
349
|
# Look for monitor-specific processes
|
341
|
-
result = subprocess.run(
|
350
|
+
result = subprocess.run(
|
351
|
+
["ps", "aux"], capture_output=True, text=True, check=False
|
352
|
+
)
|
342
353
|
|
343
354
|
if result.returncode != 0:
|
344
355
|
return False
|
@@ -349,10 +360,14 @@ class DaemonManager:
|
|
349
360
|
for line in lines:
|
350
361
|
line_lower = line.lower()
|
351
362
|
# Only target monitor/dashboard/socketio processes
|
352
|
-
if any(
|
363
|
+
if any(
|
364
|
+
[
|
365
|
+
"monitor" in line_lower and "claude" in line_lower,
|
353
366
|
"dashboard" in line_lower and "claude" in line_lower,
|
354
367
|
"socketio" in line_lower,
|
355
|
-
f":{self.port}" in line_lower and "python" in line_lower
|
368
|
+
f":{self.port}" in line_lower and "python" in line_lower,
|
369
|
+
]
|
370
|
+
):
|
356
371
|
parts = line.split()
|
357
372
|
if len(parts) > 1:
|
358
373
|
try:
|
@@ -395,7 +410,8 @@ class DaemonManager:
|
|
395
410
|
process_info = subprocess.run(
|
396
411
|
["ps", "-p", str(pid), "-o", "comm="],
|
397
412
|
capture_output=True,
|
398
|
-
text=True,
|
413
|
+
text=True,
|
414
|
+
check=False,
|
399
415
|
)
|
400
416
|
|
401
417
|
if "python" in process_info.stdout.lower():
|
@@ -441,7 +457,10 @@ class DaemonManager:
|
|
441
457
|
"""
|
442
458
|
try:
|
443
459
|
result = subprocess.run(
|
444
|
-
["lsof", "-ti", f":{self.port}"],
|
460
|
+
["lsof", "-ti", f":{self.port}"],
|
461
|
+
capture_output=True,
|
462
|
+
text=True,
|
463
|
+
check=False,
|
445
464
|
)
|
446
465
|
|
447
466
|
if result.returncode == 0 and result.stdout.strip():
|
@@ -485,9 +504,145 @@ class DaemonManager:
|
|
485
504
|
self.logger.error(f"Cannot start daemon - port {self.port} is in use")
|
486
505
|
return False
|
487
506
|
|
488
|
-
#
|
507
|
+
# Use subprocess for clean daemon startup (v4.2.40)
|
508
|
+
# This avoids fork() issues with Python threading
|
509
|
+
if self.use_subprocess_daemon():
|
510
|
+
return self.start_daemon_subprocess()
|
511
|
+
# Fallback to traditional fork (kept for compatibility)
|
489
512
|
return self.daemonize()
|
490
513
|
|
514
|
+
def use_subprocess_daemon(self) -> bool:
|
515
|
+
"""Check if we should use subprocess instead of fork for daemonization.
|
516
|
+
|
517
|
+
Returns:
|
518
|
+
True to use subprocess (safer), False to use traditional fork
|
519
|
+
"""
|
520
|
+
# Check if we're already in a subprocess to prevent infinite recursion
|
521
|
+
if os.environ.get("CLAUDE_MPM_SUBPROCESS_DAEMON") == "1":
|
522
|
+
# We're already in a subprocess, use traditional fork
|
523
|
+
return False
|
524
|
+
|
525
|
+
# Otherwise, use subprocess for monitor daemon to avoid threading issues
|
526
|
+
return True
|
527
|
+
|
528
|
+
def start_daemon_subprocess(self) -> bool:
|
529
|
+
"""Start daemon using subprocess.Popen for clean process isolation.
|
530
|
+
|
531
|
+
This avoids all the fork() + threading issues by starting the monitor
|
532
|
+
in a completely fresh process with no inherited threads or locks.
|
533
|
+
|
534
|
+
Returns:
|
535
|
+
True if daemon started successfully
|
536
|
+
"""
|
537
|
+
try:
|
538
|
+
# Build command to run monitor in foreground mode in subprocess
|
539
|
+
import sys
|
540
|
+
|
541
|
+
python_exe = sys.executable
|
542
|
+
|
543
|
+
# Run 'claude-mpm monitor start' in subprocess with environment variable
|
544
|
+
# to indicate we're already in a subprocess (prevents infinite recursion)
|
545
|
+
cmd = [
|
546
|
+
python_exe,
|
547
|
+
"-m",
|
548
|
+
"claude_mpm.cli",
|
549
|
+
"monitor",
|
550
|
+
"start",
|
551
|
+
"--background", # Run as daemon
|
552
|
+
"--port",
|
553
|
+
str(self.port),
|
554
|
+
"--host",
|
555
|
+
self.host,
|
556
|
+
]
|
557
|
+
|
558
|
+
# Set environment variable to prevent recursive subprocess creation
|
559
|
+
env = os.environ.copy()
|
560
|
+
env["CLAUDE_MPM_SUBPROCESS_DAEMON"] = "1"
|
561
|
+
|
562
|
+
self.logger.info(f"Starting monitor daemon via subprocess: {' '.join(cmd)}")
|
563
|
+
|
564
|
+
# Open log file for output redirection
|
565
|
+
log_file_handle = None
|
566
|
+
if self.log_file:
|
567
|
+
log_file_handle = open(self.log_file, "a")
|
568
|
+
log_file = log_file_handle
|
569
|
+
else:
|
570
|
+
log_file = subprocess.DEVNULL
|
571
|
+
|
572
|
+
try:
|
573
|
+
# Start the subprocess detached from parent
|
574
|
+
# Redirect stdout/stderr to log file to capture output
|
575
|
+
process = subprocess.Popen(
|
576
|
+
cmd,
|
577
|
+
stdin=subprocess.DEVNULL,
|
578
|
+
stdout=log_file,
|
579
|
+
stderr=subprocess.STDOUT if self.log_file else subprocess.DEVNULL,
|
580
|
+
start_new_session=True, # Create new process group
|
581
|
+
close_fds=(
|
582
|
+
False if self.log_file else True
|
583
|
+
), # Keep log file open if redirecting
|
584
|
+
env=env, # Pass modified environment
|
585
|
+
)
|
586
|
+
|
587
|
+
# Close the log file handle now that subprocess has it
|
588
|
+
if log_file_handle:
|
589
|
+
log_file_handle.close()
|
590
|
+
|
591
|
+
# Get the process PID
|
592
|
+
pid = process.pid
|
593
|
+
self.logger.info(f"Monitor subprocess started with PID {pid}")
|
594
|
+
|
595
|
+
# Wait for the subprocess to write its PID file
|
596
|
+
# The subprocess will write the PID file after it starts successfully
|
597
|
+
max_wait = 10 # seconds
|
598
|
+
start_time = time.time()
|
599
|
+
|
600
|
+
while time.time() - start_time < max_wait:
|
601
|
+
# Check if process is still running
|
602
|
+
if process.poll() is not None:
|
603
|
+
# Process exited
|
604
|
+
self.logger.error(
|
605
|
+
f"Monitor daemon exited with code {process.returncode}"
|
606
|
+
)
|
607
|
+
return False
|
608
|
+
|
609
|
+
# Check if PID file was written
|
610
|
+
if self.pid_file.exists():
|
611
|
+
try:
|
612
|
+
with open(self.pid_file) as f:
|
613
|
+
written_pid = int(f.read().strip())
|
614
|
+
if written_pid == pid:
|
615
|
+
# PID file written correctly, check port
|
616
|
+
if (
|
617
|
+
not self._is_port_available()
|
618
|
+
): # Port NOT available means it's in use (good!)
|
619
|
+
self.logger.info(
|
620
|
+
f"Monitor daemon successfully started on port {self.port}"
|
621
|
+
)
|
622
|
+
return True
|
623
|
+
except:
|
624
|
+
pass # PID file not ready yet
|
625
|
+
|
626
|
+
time.sleep(0.5)
|
627
|
+
|
628
|
+
# Timeout waiting for daemon to start
|
629
|
+
self.logger.error("Timeout waiting for monitor daemon to start")
|
630
|
+
# Try to kill the process if it's still running
|
631
|
+
if process.poll() is None:
|
632
|
+
process.terminate()
|
633
|
+
time.sleep(1)
|
634
|
+
if process.poll() is None:
|
635
|
+
process.kill()
|
636
|
+
return False
|
637
|
+
finally:
|
638
|
+
# Clean up log file handle if still open
|
639
|
+
if log_file_handle and not log_file_handle.closed:
|
640
|
+
log_file_handle.close()
|
641
|
+
|
642
|
+
except Exception as e:
|
643
|
+
self.logger.error(f"Failed to start daemon via subprocess: {e}")
|
644
|
+
return False
|
645
|
+
|
491
646
|
def daemonize(self) -> bool:
|
492
647
|
"""Daemonize the current process.
|
493
648
|
|
@@ -495,12 +650,14 @@ class DaemonManager:
|
|
495
650
|
True if successful (in parent), doesn't return in child
|
496
651
|
"""
|
497
652
|
# Guard against re-entrant execution after fork
|
498
|
-
if hasattr(self,
|
499
|
-
self.logger.error(
|
653
|
+
if hasattr(self, "_forking_in_progress"):
|
654
|
+
self.logger.error(
|
655
|
+
"CRITICAL: Detected re-entrant daemonize call after fork!"
|
656
|
+
)
|
500
657
|
return False
|
501
|
-
|
658
|
+
|
502
659
|
self._forking_in_progress = True
|
503
|
-
|
660
|
+
|
504
661
|
try:
|
505
662
|
# Clean up asyncio event loops before forking
|
506
663
|
self._cleanup_event_loops()
|
@@ -787,7 +944,7 @@ class DaemonManager:
|
|
787
944
|
f.write("success")
|
788
945
|
f.flush() # Ensure it's written immediately
|
789
946
|
os.fsync(f.fileno()) # Force write to disk
|
790
|
-
except Exception
|
947
|
+
except Exception:
|
791
948
|
# Logging might not work in daemon process after fork
|
792
949
|
pass
|
793
950
|
|
@@ -239,7 +239,11 @@ class UnifiedMonitorServer:
|
|
239
239
|
except OSError as e:
|
240
240
|
# Port binding error - make sure it's reported clearly
|
241
241
|
# Check for common port binding errors
|
242
|
-
if
|
242
|
+
if (
|
243
|
+
"Address already in use" in str(e)
|
244
|
+
or "[Errno 48]" in str(e)
|
245
|
+
or "[Errno 98]" in str(e)
|
246
|
+
):
|
243
247
|
error_msg = f"Port {self.port} is already in use. Another process may be using this port."
|
244
248
|
self.logger.error(error_msg)
|
245
249
|
self.startup_error = error_msg
|
@@ -1,5 +1,5 @@
|
|
1
1
|
claude_mpm/BUILD_NUMBER,sha256=toytnNjkIKPgQaGwDqQdC1rpNTAdSEc6Vja50d7Ovug,4
|
2
|
-
claude_mpm/VERSION,sha256=
|
2
|
+
claude_mpm/VERSION,sha256=Ytc6ZB74poWqcejFEJHapI9Tv44jcmBlZEH-GuEoUwY,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
|
@@ -56,7 +56,7 @@ claude_mpm/agents/templates/.claude-mpm/memories/README.md,sha256=vEiG7cPjHRZfwX
|
|
56
56
|
claude_mpm/agents/templates/.claude-mpm/memories/engineer_memories.md,sha256=KMZSJrQi-wHOwfl2C0m3A4PpC4QuBtDolAtVybGahKc,77
|
57
57
|
claude_mpm/agents/templates/logs/prompts/agent_engineer_20250826_014258_728.md,sha256=UBm4BycXtdaa-_l1VCh0alTGGOUSsnCbpKwbFuI-mUY,2219
|
58
58
|
claude_mpm/agents/templates/logs/prompts/agent_engineer_20250901_010124_142.md,sha256=oPvFSYFnmJ4TkbTe4AZnNHWaJMJ-xqZP2WM6scUKQKo,13089
|
59
|
-
claude_mpm/cli/__init__.py,sha256=
|
59
|
+
claude_mpm/cli/__init__.py,sha256=hbDc2PSEJHNRMhAYVAk1qPNCL5bTDOvU808VYBJEJgs,16670
|
60
60
|
claude_mpm/cli/__main__.py,sha256=WnVGBwe10InxuZjJRFdwuMF6Gh16aXox6zFgxr0sRXk,847
|
61
61
|
claude_mpm/cli/parser.py,sha256=Vqx9n-6Xo1uNhXR4rThmgWpZXTr0nOtkgDf3oMS9b0g,5855
|
62
62
|
claude_mpm/cli/startup_logging.py,sha256=CWu43ecTJLLT-YHRL94c783XljpR8GKydycGJ1JFuXI,23462
|
@@ -436,7 +436,7 @@ claude_mpm/services/cli/memory_crud_service.py,sha256=ciN9Pl_12iDAqF9zPBWOzu-iXi
|
|
436
436
|
claude_mpm/services/cli/memory_output_formatter.py,sha256=nbf7VsjGvH4e9fLv9c7PzjuO9COZhbK5P2fNZ79055w,24783
|
437
437
|
claude_mpm/services/cli/session_manager.py,sha256=rla_Stbcvt93wa9G9MCMu9UqB3FLGqlPt_eN5lQb3Gg,16599
|
438
438
|
claude_mpm/services/cli/startup_checker.py,sha256=efhuvu8ns5G16jcQ0nQZKVddmD2AktUEdlvjNcXjAuk,12232
|
439
|
-
claude_mpm/services/cli/unified_dashboard_manager.py,sha256=
|
439
|
+
claude_mpm/services/cli/unified_dashboard_manager.py,sha256=XOXC7mlfmqwKrNDDbxu3q8qPn0oAUvPkQY7e_SwTTIA,15433
|
440
440
|
claude_mpm/services/communication/__init__.py,sha256=b4qc7_Rqy4DE9q7BAUlfUZjoYG4uimAyUnE0irPcXyU,560
|
441
441
|
claude_mpm/services/core/__init__.py,sha256=evEayLlBqJvxMZhrhuK6aagXmNrKGSj8Jm9OOxKzqvU,2195
|
442
442
|
claude_mpm/services/core/base.py,sha256=iA-F7DgGp-FJIMvQTiHQ68RkG_k-AtUWlArJPMw6ZPk,7297
|
@@ -552,10 +552,10 @@ claude_mpm/services/memory/cache/__init__.py,sha256=6M6-P8ParyxX8vOgp_IxHgLMvacr
|
|
552
552
|
claude_mpm/services/memory/cache/shared_prompt_cache.py,sha256=crnYPUT8zcS7TvoE1vW7pyaf4T77N5rJ1wUf_YQ2vvo,28704
|
553
553
|
claude_mpm/services/memory/cache/simple_cache.py,sha256=qsTjbcsPxj-kNfaod9VN_uE5NioIwpfkUin_mMVUJCg,10218
|
554
554
|
claude_mpm/services/monitor/__init__.py,sha256=X7gxSLUm9Fg_zEsX6LtCHP2ipF0qj6Emkun20h2So7g,745
|
555
|
-
claude_mpm/services/monitor/daemon.py,sha256=
|
556
|
-
claude_mpm/services/monitor/daemon_manager.py,sha256=
|
555
|
+
claude_mpm/services/monitor/daemon.py,sha256=ZeFZPoLKd8PGShNA-mRpFkQkQBbsP7iaDDlNJGCbFe0,25964
|
556
|
+
claude_mpm/services/monitor/daemon_manager.py,sha256=fAcrRh-zBAHXP5_9s5WKCZ-V5EhTEijHPvrSJWwU7Yc,34238
|
557
557
|
claude_mpm/services/monitor/event_emitter.py,sha256=JzRLNg8PUJ5s3ulNnq_D4yqCPItvidJzu8DmFxriieQ,12224
|
558
|
-
claude_mpm/services/monitor/server.py,sha256=
|
558
|
+
claude_mpm/services/monitor/server.py,sha256=A8gBPqna_Yx5WT06_8OjHQuchgsWRXhfIOBYYtOTsI8,28659
|
559
559
|
claude_mpm/services/monitor/handlers/__init__.py,sha256=jgPIf4IJVERm_tAeD9834tfx9IcxtlHj5r9rhEWpkfM,701
|
560
560
|
claude_mpm/services/monitor/handlers/code_analysis.py,sha256=mHyI27Wp6WVmUBc0m0i991ogyFZBTvkrfR7Kf3EAk5U,11474
|
561
561
|
claude_mpm/services/monitor/handlers/dashboard.py,sha256=uGBhb-6RG6u4WLipUXgdx7RCW-vb_qek5dIfHIwAC7o,9805
|
@@ -644,9 +644,9 @@ claude_mpm/utils/subprocess_utils.py,sha256=zgiwLqh_17WxHpySvUPH65pb4bzIeUGOAYUJ
|
|
644
644
|
claude_mpm/validation/__init__.py,sha256=YZhwE3mhit-lslvRLuwfX82xJ_k4haZeKmh4IWaVwtk,156
|
645
645
|
claude_mpm/validation/agent_validator.py,sha256=3Lo6LK-Mw9IdnL_bd3zl_R6FkgSVDYKUUM7EeVVD3jc,20865
|
646
646
|
claude_mpm/validation/frontmatter_validator.py,sha256=u8g4Eyd_9O6ugj7Un47oSGh3kqv4wMkuks2i_CtWRvM,7028
|
647
|
-
claude_mpm-4.2.
|
648
|
-
claude_mpm-4.2.
|
649
|
-
claude_mpm-4.2.
|
650
|
-
claude_mpm-4.2.
|
651
|
-
claude_mpm-4.2.
|
652
|
-
claude_mpm-4.2.
|
647
|
+
claude_mpm-4.2.40.dist-info/licenses/LICENSE,sha256=lpaivOlPuBZW1ds05uQLJJswy8Rp_HMNieJEbFlqvLk,1072
|
648
|
+
claude_mpm-4.2.40.dist-info/METADATA,sha256=vAL5h1M9CdSo3Po_0evvbLYZixbc_un0p70-FZqLD4g,14451
|
649
|
+
claude_mpm-4.2.40.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
650
|
+
claude_mpm-4.2.40.dist-info/entry_points.txt,sha256=FDPZgz8JOvD-6iuXY2l9Zbo9zYVRuE4uz4Qr0vLeGOk,471
|
651
|
+
claude_mpm-4.2.40.dist-info/top_level.txt,sha256=1nUg3FEaBySgm8t-s54jK5zoPnu3_eY6EP6IOlekyHA,11
|
652
|
+
claude_mpm-4.2.40.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|