claude-mpm 4.2.24__py3-none-any.whl → 4.2.26__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 +10 -0
- claude_mpm/cli/commands/monitor.py +9 -7
- claude_mpm/cli/commands/uninstall.py +178 -0
- claude_mpm/cli/parsers/base_parser.py +8 -0
- claude_mpm/scripts/claude-hook-handler.sh +33 -7
- claude_mpm/services/cli/unified_dashboard_manager.py +14 -7
- claude_mpm/services/hook_installer_service.py +507 -0
- claude_mpm/services/monitor/daemon.py +172 -29
- claude_mpm/services/monitor/management/lifecycle.py +132 -90
- claude_mpm/services/monitor/server.py +11 -8
- {claude_mpm-4.2.24.dist-info → claude_mpm-4.2.26.dist-info}/METADATA +1 -1
- {claude_mpm-4.2.24.dist-info → claude_mpm-4.2.26.dist-info}/RECORD +17 -15
- {claude_mpm-4.2.24.dist-info → claude_mpm-4.2.26.dist-info}/WHEEL +0 -0
- {claude_mpm-4.2.24.dist-info → claude_mpm-4.2.26.dist-info}/entry_points.txt +0 -0
- {claude_mpm-4.2.24.dist-info → claude_mpm-4.2.26.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-4.2.24.dist-info → claude_mpm-4.2.26.dist-info}/top_level.txt +0 -0
@@ -13,6 +13,7 @@ DESIGN DECISIONS:
|
|
13
13
|
- Log file redirection for daemon mode
|
14
14
|
"""
|
15
15
|
|
16
|
+
import json
|
16
17
|
import os
|
17
18
|
import signal
|
18
19
|
import socket
|
@@ -21,7 +22,6 @@ import tempfile
|
|
21
22
|
import time
|
22
23
|
from pathlib import Path
|
23
24
|
from typing import Optional, Tuple
|
24
|
-
import json
|
25
25
|
|
26
26
|
from ....core.logging_config import get_logger
|
27
27
|
|
@@ -57,9 +57,11 @@ class DaemonLifecycle:
|
|
57
57
|
try:
|
58
58
|
# Clean up any existing asyncio event loops before forking
|
59
59
|
self._cleanup_event_loops()
|
60
|
-
|
60
|
+
|
61
61
|
# Create a temporary file for startup status communication
|
62
|
-
with tempfile.NamedTemporaryFile(
|
62
|
+
with tempfile.NamedTemporaryFile(
|
63
|
+
mode="w", delete=False, suffix=".status"
|
64
|
+
) as f:
|
63
65
|
self.startup_status_file = f.name
|
64
66
|
f.write("starting")
|
65
67
|
|
@@ -91,7 +93,7 @@ class DaemonLifecycle:
|
|
91
93
|
|
92
94
|
# Set up error logging before redirecting streams
|
93
95
|
self._setup_early_error_logging()
|
94
|
-
|
96
|
+
|
95
97
|
# Write PID file first (before stream redirection)
|
96
98
|
try:
|
97
99
|
self.write_pid_file()
|
@@ -106,7 +108,7 @@ class DaemonLifecycle:
|
|
106
108
|
self._setup_signal_handlers()
|
107
109
|
|
108
110
|
self.logger.info(f"Daemon process started with PID {os.getpid()}")
|
109
|
-
|
111
|
+
|
110
112
|
# Report successful startup (after basic setup but before server start)
|
111
113
|
self._report_startup_success()
|
112
114
|
return True
|
@@ -366,83 +368,93 @@ class DaemonLifecycle:
|
|
366
368
|
|
367
369
|
def _parent_wait_for_startup(self, child_pid: int, timeout: float = 10.0) -> bool:
|
368
370
|
"""Parent process waits for child daemon to report startup status.
|
369
|
-
|
371
|
+
|
370
372
|
Args:
|
371
373
|
child_pid: PID of the child process
|
372
374
|
timeout: Maximum time to wait for startup
|
373
|
-
|
375
|
+
|
374
376
|
Returns:
|
375
377
|
True if child started successfully, False otherwise
|
376
378
|
"""
|
377
379
|
import time
|
380
|
+
|
378
381
|
start_time = time.time()
|
379
|
-
|
382
|
+
|
380
383
|
# Wait for child to update status file
|
381
384
|
while time.time() - start_time < timeout:
|
382
385
|
try:
|
383
386
|
# Check if status file exists and read it
|
384
387
|
if self.startup_status_file and Path(self.startup_status_file).exists():
|
385
|
-
with open(self.startup_status_file
|
388
|
+
with open(self.startup_status_file) as f:
|
386
389
|
status = f.read().strip()
|
387
|
-
|
390
|
+
|
388
391
|
if status == "success":
|
389
392
|
# Child started successfully
|
390
393
|
self._cleanup_status_file()
|
391
394
|
return True
|
392
|
-
|
395
|
+
if status.startswith("error:"):
|
393
396
|
# Child reported an error
|
394
397
|
error_msg = status[6:] # Remove "error:" prefix
|
395
398
|
self.logger.error(f"Daemon startup failed: {error_msg}")
|
396
|
-
print(
|
399
|
+
print(
|
400
|
+
f"Error: Failed to start monitor daemon: {error_msg}",
|
401
|
+
file=sys.stderr,
|
402
|
+
)
|
397
403
|
self._cleanup_status_file()
|
398
404
|
return False
|
399
|
-
|
405
|
+
if status == "starting":
|
400
406
|
# Still starting, continue waiting
|
401
407
|
pass
|
402
|
-
|
408
|
+
|
403
409
|
# Also check if child process is still alive
|
404
410
|
try:
|
405
411
|
os.kill(child_pid, 0) # Check if process exists
|
406
412
|
except ProcessLookupError:
|
407
413
|
# Child process died
|
408
414
|
self.logger.error("Child daemon process died during startup")
|
409
|
-
print(
|
415
|
+
print(
|
416
|
+
"Error: Monitor daemon process died during startup",
|
417
|
+
file=sys.stderr,
|
418
|
+
)
|
410
419
|
self._cleanup_status_file()
|
411
420
|
return False
|
412
|
-
|
421
|
+
|
413
422
|
except Exception as e:
|
414
423
|
self.logger.debug(f"Error checking startup status: {e}")
|
415
|
-
|
424
|
+
|
416
425
|
time.sleep(0.1) # Check every 100ms
|
417
|
-
|
426
|
+
|
418
427
|
# Timeout reached
|
419
428
|
self.logger.error(f"Daemon startup timed out after {timeout} seconds")
|
420
|
-
print(
|
429
|
+
print(
|
430
|
+
f"Error: Monitor daemon startup timed out after {timeout} seconds",
|
431
|
+
file=sys.stderr,
|
432
|
+
)
|
421
433
|
self._cleanup_status_file()
|
422
434
|
return False
|
423
|
-
|
435
|
+
|
424
436
|
def _report_startup_success(self):
|
425
437
|
"""Report successful startup to parent process."""
|
426
438
|
if self.startup_status_file:
|
427
439
|
try:
|
428
|
-
with open(self.startup_status_file,
|
440
|
+
with open(self.startup_status_file, "w") as f:
|
429
441
|
f.write("success")
|
430
442
|
except Exception as e:
|
431
443
|
self.logger.error(f"Failed to report startup success: {e}")
|
432
|
-
|
444
|
+
|
433
445
|
def _report_startup_error(self, error_msg: str):
|
434
446
|
"""Report startup error to parent process.
|
435
|
-
|
447
|
+
|
436
448
|
Args:
|
437
449
|
error_msg: Error message to report
|
438
450
|
"""
|
439
451
|
if self.startup_status_file:
|
440
452
|
try:
|
441
|
-
with open(self.startup_status_file,
|
453
|
+
with open(self.startup_status_file, "w") as f:
|
442
454
|
f.write(f"error:{error_msg}")
|
443
455
|
except Exception:
|
444
456
|
pass # Can't report if file write fails
|
445
|
-
|
457
|
+
|
446
458
|
def _cleanup_status_file(self):
|
447
459
|
"""Clean up the temporary status file."""
|
448
460
|
if self.startup_status_file:
|
@@ -452,10 +464,10 @@ class DaemonLifecycle:
|
|
452
464
|
pass # Ignore cleanup errors
|
453
465
|
finally:
|
454
466
|
self.startup_status_file = None
|
455
|
-
|
467
|
+
|
456
468
|
def _setup_early_error_logging(self):
|
457
469
|
"""Set up error logging before stream redirection.
|
458
|
-
|
470
|
+
|
459
471
|
This ensures we can capture and report errors that occur during
|
460
472
|
daemon initialization, especially port binding errors.
|
461
473
|
"""
|
@@ -465,27 +477,30 @@ class DaemonLifecycle:
|
|
465
477
|
default_log = Path.home() / ".claude-mpm" / "monitor-daemon.log"
|
466
478
|
default_log.parent.mkdir(parents=True, exist_ok=True)
|
467
479
|
self.log_file = default_log
|
468
|
-
|
480
|
+
|
469
481
|
# Configure logger to write to file immediately
|
470
482
|
import logging
|
483
|
+
|
471
484
|
file_handler = logging.FileHandler(self.log_file)
|
472
485
|
file_handler.setLevel(logging.DEBUG)
|
473
486
|
formatter = logging.Formatter(
|
474
|
-
|
487
|
+
"%(asctime)s - %(name)s - %(levelname)s - %(message)s"
|
475
488
|
)
|
476
489
|
file_handler.setFormatter(formatter)
|
477
490
|
self.logger.addHandler(file_handler)
|
478
|
-
|
491
|
+
|
479
492
|
except Exception as e:
|
480
493
|
# If we can't set up logging, at least try to report the error
|
481
494
|
self._report_startup_error(f"Failed to setup error logging: {e}")
|
482
|
-
|
483
|
-
def verify_port_available(
|
495
|
+
|
496
|
+
def verify_port_available(
|
497
|
+
self, host: str = "localhost"
|
498
|
+
) -> Tuple[bool, Optional[str]]:
|
484
499
|
"""Verify that the port is available for binding.
|
485
|
-
|
500
|
+
|
486
501
|
Args:
|
487
502
|
host: Host to check port on
|
488
|
-
|
503
|
+
|
489
504
|
Returns:
|
490
505
|
Tuple of (is_available, error_message)
|
491
506
|
"""
|
@@ -498,57 +513,61 @@ class DaemonLifecycle:
|
|
498
513
|
except OSError as e:
|
499
514
|
error_msg = f"Port {self.port} is already in use or cannot be bound: {e}"
|
500
515
|
return False, error_msg
|
501
|
-
|
516
|
+
|
502
517
|
def is_our_service(self, host: str = "localhost") -> Tuple[bool, Optional[int]]:
|
503
518
|
"""Check if the service on the port is our Socket.IO service.
|
504
|
-
|
519
|
+
|
505
520
|
This uses multiple detection methods:
|
506
521
|
1. Check health endpoint for service signature
|
507
522
|
2. Check Socket.IO namespace availability
|
508
523
|
3. Check process ownership if PID file exists
|
509
|
-
|
524
|
+
|
510
525
|
Args:
|
511
526
|
host: Host to check
|
512
|
-
|
527
|
+
|
513
528
|
Returns:
|
514
529
|
Tuple of (is_ours, pid_if_found)
|
515
530
|
"""
|
516
531
|
self.logger.debug(f"Checking if service on {host}:{self.port} is ours")
|
517
|
-
|
532
|
+
|
518
533
|
try:
|
519
534
|
# Method 1: Check health endpoint
|
520
|
-
import urllib.request
|
521
535
|
import urllib.error
|
522
|
-
|
536
|
+
import urllib.request
|
537
|
+
|
523
538
|
health_url = f"http://{host}:{self.port}/health"
|
524
539
|
self.logger.debug(f"Checking health endpoint: {health_url}")
|
525
|
-
|
540
|
+
|
526
541
|
try:
|
527
542
|
req = urllib.request.Request(health_url)
|
528
|
-
req.add_header(
|
529
|
-
|
543
|
+
req.add_header("User-Agent", "claude-mpm-monitor")
|
544
|
+
|
530
545
|
with urllib.request.urlopen(req, timeout=3) as response:
|
531
546
|
if response.status == 200:
|
532
547
|
data = json.loads(response.read().decode())
|
533
548
|
self.logger.debug(f"Health endpoint response: {data}")
|
534
|
-
|
549
|
+
|
535
550
|
# Check for our service signature
|
536
551
|
service_name = data.get("service")
|
537
552
|
if service_name == "claude-mpm-monitor":
|
538
553
|
# Try to get PID from response
|
539
554
|
pid = data.get("pid")
|
540
555
|
if pid:
|
541
|
-
self.logger.info(
|
556
|
+
self.logger.info(
|
557
|
+
f"Found our claude-mpm-monitor service via health endpoint, PID: {pid}"
|
558
|
+
)
|
542
559
|
return True, pid
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
|
547
|
-
|
548
|
-
|
549
|
-
|
550
|
-
|
551
|
-
|
560
|
+
# Service is ours but no PID in response
|
561
|
+
# Try to get from PID file
|
562
|
+
file_pid = self.get_pid()
|
563
|
+
self.logger.info(
|
564
|
+
f"Found our claude-mpm-monitor service via health endpoint, PID from file: {file_pid}"
|
565
|
+
)
|
566
|
+
return True, file_pid
|
567
|
+
self.logger.debug(
|
568
|
+
f"Service name '{service_name}' does not match 'claude-mpm-monitor'"
|
569
|
+
)
|
570
|
+
|
552
571
|
except urllib.error.URLError as e:
|
553
572
|
self.logger.debug(f"Health endpoint not accessible: {e}")
|
554
573
|
except urllib.error.HTTPError as e:
|
@@ -557,7 +576,7 @@ class DaemonLifecycle:
|
|
557
576
|
self.logger.debug(f"Health endpoint invalid JSON: {e}")
|
558
577
|
except Exception as e:
|
559
578
|
self.logger.debug(f"Health endpoint check failed: {e}")
|
560
|
-
|
579
|
+
|
561
580
|
# Method 2: Check if PID file exists and process matches
|
562
581
|
pid = self.get_pid()
|
563
582
|
if pid:
|
@@ -566,116 +585,139 @@ class DaemonLifecycle:
|
|
566
585
|
# Check if process exists
|
567
586
|
os.kill(pid, 0)
|
568
587
|
self.logger.debug(f"Process {pid} exists")
|
569
|
-
|
588
|
+
|
570
589
|
# Process exists, check if it's using our port
|
571
590
|
# This requires psutil for accurate port checking
|
572
591
|
try:
|
573
592
|
import psutil
|
593
|
+
|
574
594
|
process = psutil.Process(pid)
|
575
|
-
|
595
|
+
|
576
596
|
# Check process command line for our service
|
577
|
-
cmdline =
|
578
|
-
if
|
579
|
-
if
|
580
|
-
self.logger.info(
|
597
|
+
cmdline = " ".join(process.cmdline())
|
598
|
+
if "claude_mpm" in cmdline or "claude-mpm" in cmdline:
|
599
|
+
if "monitor" in cmdline:
|
600
|
+
self.logger.info(
|
601
|
+
f"Found our claude-mpm monitor process via PID file, PID: {pid}"
|
602
|
+
)
|
581
603
|
return True, pid
|
582
|
-
|
604
|
+
|
583
605
|
# Also check if it's listening on our port
|
584
606
|
connections = process.connections()
|
585
607
|
for conn in connections:
|
586
|
-
if conn.laddr.port == self.port and conn.status ==
|
587
|
-
self.logger.info(
|
608
|
+
if conn.laddr.port == self.port and conn.status == "LISTEN":
|
609
|
+
self.logger.info(
|
610
|
+
f"Found process {pid} listening on our port {self.port}"
|
611
|
+
)
|
588
612
|
# Double-check it's a Python process (likely ours)
|
589
|
-
if
|
590
|
-
self.logger.info(
|
613
|
+
if "python" in process.name().lower():
|
614
|
+
self.logger.info(
|
615
|
+
"Confirmed as Python process, assuming it's our service"
|
616
|
+
)
|
591
617
|
return True, pid
|
592
|
-
|
618
|
+
|
593
619
|
except ImportError:
|
594
620
|
# psutil not available, but we have a PID file and process exists
|
595
621
|
# Assume it's ours since we manage the PID file
|
596
|
-
self.logger.info(
|
622
|
+
self.logger.info(
|
623
|
+
f"Found process with our PID file: {pid}, assuming it's ours (psutil not available)"
|
624
|
+
)
|
597
625
|
return True, pid
|
598
626
|
except psutil.NoSuchProcess:
|
599
627
|
self.logger.debug(f"Process {pid} no longer exists")
|
600
628
|
except psutil.AccessDenied:
|
601
629
|
# Can't access process info, but it exists - likely ours
|
602
|
-
self.logger.info(
|
630
|
+
self.logger.info(
|
631
|
+
f"Process {pid} exists but access denied, assuming it's ours"
|
632
|
+
)
|
603
633
|
return True, pid
|
604
634
|
except Exception as e:
|
605
635
|
self.logger.debug(f"Error checking process {pid}: {e}")
|
606
|
-
|
636
|
+
|
607
637
|
except (OSError, ProcessLookupError):
|
608
638
|
# Process doesn't exist
|
609
639
|
self.logger.debug(f"Process {pid} does not exist")
|
610
640
|
self._cleanup_stale_pid_file()
|
611
|
-
|
641
|
+
|
612
642
|
# Method 3: Try Socket.IO connection to check namespace
|
613
643
|
try:
|
614
644
|
import socketio
|
645
|
+
|
615
646
|
sio_client = socketio.Client()
|
616
|
-
|
647
|
+
|
617
648
|
# Try to connect with a short timeout
|
618
649
|
connected = False
|
650
|
+
|
619
651
|
def on_connect():
|
620
652
|
nonlocal connected
|
621
653
|
connected = True
|
622
|
-
|
623
|
-
sio_client.on(
|
624
|
-
|
654
|
+
|
655
|
+
sio_client.on("connect", on_connect)
|
656
|
+
|
625
657
|
try:
|
626
|
-
sio_client.connect(f
|
658
|
+
sio_client.connect(f"http://{host}:{self.port}", wait_timeout=2)
|
627
659
|
if connected:
|
628
660
|
# Successfully connected to Socket.IO
|
629
661
|
sio_client.disconnect()
|
630
|
-
|
662
|
+
|
631
663
|
# Check for orphaned process (no PID file but service running)
|
632
664
|
try:
|
633
665
|
# Try to find process using the port
|
634
666
|
import psutil
|
635
|
-
|
667
|
+
|
668
|
+
for proc in psutil.process_iter(["pid", "name"]):
|
636
669
|
try:
|
637
670
|
for conn in proc.connections():
|
638
|
-
if
|
671
|
+
if (
|
672
|
+
conn.laddr.port == self.port
|
673
|
+
and conn.status == "LISTEN"
|
674
|
+
):
|
639
675
|
# Found process listening on our port
|
640
|
-
if
|
641
|
-
self.logger.debug(
|
676
|
+
if "python" in proc.name().lower():
|
677
|
+
self.logger.debug(
|
678
|
+
f"Found likely orphaned claude-mpm service on port {self.port}, PID: {proc.pid}"
|
679
|
+
)
|
642
680
|
return True, proc.pid
|
643
681
|
except (psutil.NoSuchProcess, psutil.AccessDenied):
|
644
682
|
continue
|
645
683
|
except ImportError:
|
646
684
|
pass
|
647
|
-
|
685
|
+
|
648
686
|
# Socket.IO service exists but can't determine if it's ours
|
649
|
-
self.logger.debug(
|
687
|
+
self.logger.debug(
|
688
|
+
f"Found Socket.IO service on port {self.port}, but cannot confirm ownership"
|
689
|
+
)
|
650
690
|
return False, None
|
651
|
-
|
691
|
+
|
652
692
|
except Exception:
|
653
693
|
pass
|
654
694
|
finally:
|
655
695
|
if sio_client.connected:
|
656
696
|
sio_client.disconnect()
|
657
|
-
|
697
|
+
|
658
698
|
except ImportError:
|
659
699
|
# socketio not available
|
660
700
|
pass
|
661
701
|
except Exception as e:
|
662
702
|
self.logger.debug(f"Error checking Socket.IO connection: {e}")
|
663
|
-
|
703
|
+
|
664
704
|
# Method 4: Final fallback - if we have a PID file and can't definitively say it's NOT ours
|
665
705
|
# This handles edge cases where the health endpoint might be temporarily unavailable
|
666
706
|
if pid and self.pid_file.exists():
|
667
707
|
try:
|
668
708
|
# One more check - see if process exists
|
669
709
|
os.kill(pid, 0)
|
670
|
-
self.logger.info(
|
710
|
+
self.logger.info(
|
711
|
+
f"PID file exists with valid process {pid}, assuming it's our stale service"
|
712
|
+
)
|
671
713
|
return True, pid
|
672
714
|
except (OSError, ProcessLookupError):
|
673
715
|
pass
|
674
|
-
|
716
|
+
|
675
717
|
# No service detected or not ours
|
676
718
|
self.logger.debug("Service not detected as ours")
|
677
719
|
return False, None
|
678
|
-
|
720
|
+
|
679
721
|
except Exception as e:
|
680
722
|
self.logger.error(f"Error checking if service is ours: {e}", exc_info=True)
|
681
723
|
return False, None
|
@@ -115,7 +115,9 @@ class UnifiedMonitorServer:
|
|
115
115
|
time.sleep(0.1)
|
116
116
|
|
117
117
|
if not self.running:
|
118
|
-
error_msg =
|
118
|
+
error_msg = (
|
119
|
+
self.startup_error or "Server failed to start within timeout"
|
120
|
+
)
|
119
121
|
self.logger.error(error_msg)
|
120
122
|
return False
|
121
123
|
|
@@ -231,7 +233,7 @@ class UnifiedMonitorServer:
|
|
231
233
|
try:
|
232
234
|
self.site = web.TCPSite(self.runner, self.host, self.port)
|
233
235
|
await self.site.start()
|
234
|
-
|
236
|
+
|
235
237
|
self.running = True
|
236
238
|
self.logger.info(f"Server running on http://{self.host}:{self.port}")
|
237
239
|
except OSError as e:
|
@@ -241,10 +243,9 @@ class UnifiedMonitorServer:
|
|
241
243
|
self.logger.error(error_msg)
|
242
244
|
self.startup_error = error_msg
|
243
245
|
raise OSError(error_msg) from e
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
raise
|
246
|
+
self.logger.error(f"Failed to bind to {self.host}:{self.port}: {e}")
|
247
|
+
self.startup_error = str(e)
|
248
|
+
raise
|
248
249
|
|
249
250
|
# Keep the server running
|
250
251
|
while self.running:
|
@@ -312,12 +313,14 @@ class UnifiedMonitorServer:
|
|
312
313
|
# Get version from VERSION file
|
313
314
|
version = "1.0.0"
|
314
315
|
try:
|
315
|
-
version_file =
|
316
|
+
version_file = (
|
317
|
+
Path(__file__).parent.parent.parent.parent.parent / "VERSION"
|
318
|
+
)
|
316
319
|
if version_file.exists():
|
317
320
|
version = version_file.read_text().strip()
|
318
321
|
except Exception:
|
319
322
|
pass
|
320
|
-
|
323
|
+
|
321
324
|
return web.json_response(
|
322
325
|
{
|
323
326
|
"status": "healthy",
|
@@ -1,5 +1,5 @@
|
|
1
1
|
claude_mpm/BUILD_NUMBER,sha256=toytnNjkIKPgQaGwDqQdC1rpNTAdSEc6Vja50d7Ovug,4
|
2
|
-
claude_mpm/VERSION,sha256=
|
2
|
+
claude_mpm/VERSION,sha256=SQgB6WkCfQ-PDwm6SAsbMLZRb40PTP081FhoVTi1sfg,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
|
@@ -54,7 +54,7 @@ claude_mpm/agents/templates/.claude-mpm/memories/README.md,sha256=vEiG7cPjHRZfwX
|
|
54
54
|
claude_mpm/agents/templates/.claude-mpm/memories/engineer_memories.md,sha256=KMZSJrQi-wHOwfl2C0m3A4PpC4QuBtDolAtVybGahKc,77
|
55
55
|
claude_mpm/agents/templates/logs/prompts/agent_engineer_20250826_014258_728.md,sha256=UBm4BycXtdaa-_l1VCh0alTGGOUSsnCbpKwbFuI-mUY,2219
|
56
56
|
claude_mpm/agents/templates/logs/prompts/agent_engineer_20250901_010124_142.md,sha256=oPvFSYFnmJ4TkbTe4AZnNHWaJMJ-xqZP2WM6scUKQKo,13089
|
57
|
-
claude_mpm/cli/__init__.py,sha256=
|
57
|
+
claude_mpm/cli/__init__.py,sha256=yOUdP3WeyXyBL8eqp5YgcfFjUpUZx9PYpBc3FYFZeqs,16600
|
58
58
|
claude_mpm/cli/__main__.py,sha256=WnVGBwe10InxuZjJRFdwuMF6Gh16aXox6zFgxr0sRXk,847
|
59
59
|
claude_mpm/cli/parser.py,sha256=Vqx9n-6Xo1uNhXR4rThmgWpZXTr0nOtkgDf3oMS9b0g,5855
|
60
60
|
claude_mpm/cli/startup_logging.py,sha256=CWu43ecTJLLT-YHRL94c783XljpR8GKydycGJ1JFuXI,23462
|
@@ -83,11 +83,12 @@ 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=Y4on91KW6Ye5NIglHnMVG8hhzw8ZRslBm1oMm6KC48s,9556
|
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
|
90
90
|
claude_mpm/cli/commands/tickets.py,sha256=kl2dklTBnG3Y4jUUJ_PcEVsTx4CtVJfkGWboWBx_mQM,21234
|
91
|
+
claude_mpm/cli/commands/uninstall.py,sha256=l2FOgzQMJRVRSdg3npApkyKkZ7fNE5LgpAa2THyVVV8,5970
|
91
92
|
claude_mpm/cli/interactive/__init__.py,sha256=vQqUCgPFvLYA1Vkq-5pnY7Ow3A-IgdM0SByfNL1ZLTk,433
|
92
93
|
claude_mpm/cli/interactive/agent_wizard.py,sha256=ZaCIToc-IGT4gsBDWtDHzZU9QOd3VlSUkJgewoZmBF8,36199
|
93
94
|
claude_mpm/cli/parsers/__init__.py,sha256=f0Fm1DDXorlVOZPLxUpjC-GIvLh01G-FZOK7TEV1L3I,1005
|
@@ -95,7 +96,7 @@ claude_mpm/cli/parsers/agent_manager_parser.py,sha256=E2BHHb9FusktHjcXo3qb13d8EH
|
|
95
96
|
claude_mpm/cli/parsers/agents_parser.py,sha256=leyq8ugOZikNH_YWRolR7fTwJF1f3a_xPniGSzeqkSI,9065
|
96
97
|
claude_mpm/cli/parsers/analyze_code_parser.py,sha256=cpJSMFbc3mqB4qrMBIEZiikzPekC2IQX-cjt9U2fHW4,5356
|
97
98
|
claude_mpm/cli/parsers/analyze_parser.py,sha256=E00Ao0zwzbJPchs_AJt-aoQ7LQEtJPXRCNQ6Piivb4o,3908
|
98
|
-
claude_mpm/cli/parsers/base_parser.py,sha256=
|
99
|
+
claude_mpm/cli/parsers/base_parser.py,sha256=3BAUzITN1bUvJcXYP110zlzt-oURL1pxFzbTMQFuNRU,13647
|
99
100
|
claude_mpm/cli/parsers/config_parser.py,sha256=wp6NbV8_p9txP28MXFcQrri0JDIfGFM7u4aJbYJXcYQ,2699
|
100
101
|
claude_mpm/cli/parsers/configure_parser.py,sha256=cg3VXrnSqi9QLhMihJXeKDjtp1sS5jSHZNM_prgm0S4,4598
|
101
102
|
claude_mpm/cli/parsers/dashboard_parser.py,sha256=JBCM6v_iZhADr_Fwtk_d3up9AOod1avMab-vkNE61gE,3460
|
@@ -298,7 +299,7 @@ claude_mpm/models/agent_definition.py,sha256=LC7EwihixF2Gw4QqOxiCNchsEzzyQJPR6Ag
|
|
298
299
|
claude_mpm/models/agent_session.py,sha256=7YU9oklwqEDyH3PTKUQ52yh6N9C9eJX8GJKhxDCpDj0,19988
|
299
300
|
claude_mpm/schemas/__init__.py,sha256=2SLpkojJq34KnwPkVxrsVmw_cEI66872i75QBT1C2To,446
|
300
301
|
claude_mpm/scripts/__init__.py,sha256=IffMdVD99Pxyw85yluRa0VDPi4dRQecIWce764pcfZE,553
|
301
|
-
claude_mpm/scripts/claude-hook-handler.sh,sha256=
|
302
|
+
claude_mpm/scripts/claude-hook-handler.sh,sha256=xe6dKubrjK1JDO0SJdc1-tA6C7YSb5YazhwKhOYBQvw,7905
|
302
303
|
claude_mpm/scripts/launch_monitor.py,sha256=Q7hN4Wurw45veLWPSXk0WfvkKxQ1Snz7TjZsV_pNWQc,2418
|
303
304
|
claude_mpm/scripts/mcp_server.py,sha256=_i9ydtI7AcO-Eb7gzbIDbcJY4PKRQRYNobB8eMailI4,2259
|
304
305
|
claude_mpm/scripts/mcp_wrapper.py,sha256=PvfHJShcsQHGJZD-RN3RnwLOzemAKYZ2kW_QfTkGzkk,1105
|
@@ -313,6 +314,7 @@ claude_mpm/services/command_deployment_service.py,sha256=FxrHWuhvEaYL6PmjCN7Y0Tl
|
|
313
314
|
claude_mpm/services/command_handler_service.py,sha256=LdKnujKUgrCYrvKvmCXaUMk7JGFJsyNeiKnDFdR8ox8,7031
|
314
315
|
claude_mpm/services/event_aggregator.py,sha256=859FzkvOKL558Np42A8diVu8Hpa66AhKyhdQcEbBXoI,20201
|
315
316
|
claude_mpm/services/exceptions.py,sha256=6l5lXX8j_JhZAgFVs7QoETvsEdzEbByvtAIDbr_JeNY,26439
|
317
|
+
claude_mpm/services/hook_installer_service.py,sha256=UoB3rdN3RTK6VHftEM-x4RtCxs55TBvpDf-auXeLceI,19710
|
316
318
|
claude_mpm/services/hook_service.py,sha256=rZnMn_4qxX5g9KAn0IQdoG50WmySNfsTmfG0XHuRHXk,15737
|
317
319
|
claude_mpm/services/memory_hook_service.py,sha256=pRlTClkRcw30Jhwbha4BC8IMdzKZxF8aWqf52JlntgY,11600
|
318
320
|
claude_mpm/services/monitor_build_service.py,sha256=ggtkHcR7eKOMZjoVEA-1GzNdXhI-sh8z83PJXlozgZM,12141
|
@@ -432,7 +434,7 @@ claude_mpm/services/cli/memory_crud_service.py,sha256=ciN9Pl_12iDAqF9zPBWOzu-iXi
|
|
432
434
|
claude_mpm/services/cli/memory_output_formatter.py,sha256=nbf7VsjGvH4e9fLv9c7PzjuO9COZhbK5P2fNZ79055w,24783
|
433
435
|
claude_mpm/services/cli/session_manager.py,sha256=rla_Stbcvt93wa9G9MCMu9UqB3FLGqlPt_eN5lQb3Gg,16599
|
434
436
|
claude_mpm/services/cli/startup_checker.py,sha256=efhuvu8ns5G16jcQ0nQZKVddmD2AktUEdlvjNcXjAuk,12232
|
435
|
-
claude_mpm/services/cli/unified_dashboard_manager.py,sha256=
|
437
|
+
claude_mpm/services/cli/unified_dashboard_manager.py,sha256=4p8ubQhAXMezCVjd2sj_Q7D_AsDXtJJDQFD9BuvW838,12682
|
436
438
|
claude_mpm/services/communication/__init__.py,sha256=b4qc7_Rqy4DE9q7BAUlfUZjoYG4uimAyUnE0irPcXyU,560
|
437
439
|
claude_mpm/services/core/__init__.py,sha256=evEayLlBqJvxMZhrhuK6aagXmNrKGSj8Jm9OOxKzqvU,2195
|
438
440
|
claude_mpm/services/core/base.py,sha256=iA-F7DgGp-FJIMvQTiHQ68RkG_k-AtUWlArJPMw6ZPk,7297
|
@@ -548,9 +550,9 @@ claude_mpm/services/memory/cache/__init__.py,sha256=6M6-P8ParyxX8vOgp_IxHgLMvacr
|
|
548
550
|
claude_mpm/services/memory/cache/shared_prompt_cache.py,sha256=crnYPUT8zcS7TvoE1vW7pyaf4T77N5rJ1wUf_YQ2vvo,28704
|
549
551
|
claude_mpm/services/memory/cache/simple_cache.py,sha256=qsTjbcsPxj-kNfaod9VN_uE5NioIwpfkUin_mMVUJCg,10218
|
550
552
|
claude_mpm/services/monitor/__init__.py,sha256=X7gxSLUm9Fg_zEsX6LtCHP2ipF0qj6Emkun20h2So7g,745
|
551
|
-
claude_mpm/services/monitor/daemon.py,sha256=
|
553
|
+
claude_mpm/services/monitor/daemon.py,sha256=pZqn_-1PBMf9gfIq8Zo55gXoXInB0gKNyOZpWGkdrxw,27731
|
552
554
|
claude_mpm/services/monitor/event_emitter.py,sha256=JzRLNg8PUJ5s3ulNnq_D4yqCPItvidJzu8DmFxriieQ,12224
|
553
|
-
claude_mpm/services/monitor/server.py,sha256=
|
555
|
+
claude_mpm/services/monitor/server.py,sha256=m98Eyv9caxRywJ4JtAdOuv5EB__z7vd2hYRZPwcqFLg,28498
|
554
556
|
claude_mpm/services/monitor/handlers/__init__.py,sha256=jgPIf4IJVERm_tAeD9834tfx9IcxtlHj5r9rhEWpkfM,701
|
555
557
|
claude_mpm/services/monitor/handlers/code_analysis.py,sha256=mHyI27Wp6WVmUBc0m0i991ogyFZBTvkrfR7Kf3EAk5U,11474
|
556
558
|
claude_mpm/services/monitor/handlers/dashboard.py,sha256=uGBhb-6RG6u4WLipUXgdx7RCW-vb_qek5dIfHIwAC7o,9805
|
@@ -558,7 +560,7 @@ claude_mpm/services/monitor/handlers/file.py,sha256=p3C4wffl0GIcN00b-KkrmZ8F-Amd
|
|
558
560
|
claude_mpm/services/monitor/handlers/hooks.py,sha256=dlrmyFu8WChlvn6-sND9DLjSbm5nrMfNZrAgoWN-2No,17582
|
559
561
|
claude_mpm/services/monitor/management/__init__.py,sha256=mxaEFRgvvgV85gUpXu_DsnHtywihdP14EisvISAVZuQ,525
|
560
562
|
claude_mpm/services/monitor/management/health.py,sha256=Wm92Cli_4cWD6B89KX_CdpAvvevuEaGB8Ah59ILhFww,3772
|
561
|
-
claude_mpm/services/monitor/management/lifecycle.py,sha256=
|
563
|
+
claude_mpm/services/monitor/management/lifecycle.py,sha256=WVs231EOEkMOx2ofWDdE8GdWazFS8NZ6xwi185szapI,27340
|
562
564
|
claude_mpm/services/project/__init__.py,sha256=IUclN1L7ChHCNya7PJiVxu4nttxsrj3WRIpwyA1A_hw,512
|
563
565
|
claude_mpm/services/project/analyzer.py,sha256=VHlLrP8-S5gr12w4Yzs7-6d7LWdJKISHPCFSG7SDiQU,38434
|
564
566
|
claude_mpm/services/project/analyzer_refactored.py,sha256=USYEdPAhSoGPqZCpaT89Dw6ElFW_L1yXSURheQjAhLA,18243
|
@@ -639,9 +641,9 @@ claude_mpm/utils/subprocess_utils.py,sha256=zgiwLqh_17WxHpySvUPH65pb4bzIeUGOAYUJ
|
|
639
641
|
claude_mpm/validation/__init__.py,sha256=YZhwE3mhit-lslvRLuwfX82xJ_k4haZeKmh4IWaVwtk,156
|
640
642
|
claude_mpm/validation/agent_validator.py,sha256=3Lo6LK-Mw9IdnL_bd3zl_R6FkgSVDYKUUM7EeVVD3jc,20865
|
641
643
|
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.
|
644
|
+
claude_mpm-4.2.26.dist-info/licenses/LICENSE,sha256=lpaivOlPuBZW1ds05uQLJJswy8Rp_HMNieJEbFlqvLk,1072
|
645
|
+
claude_mpm-4.2.26.dist-info/METADATA,sha256=_oxZGDtNFYz4VX0vwQhEKz5cQFyw1bl3gcpiYXohypI,14451
|
646
|
+
claude_mpm-4.2.26.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
647
|
+
claude_mpm-4.2.26.dist-info/entry_points.txt,sha256=FDPZgz8JOvD-6iuXY2l9Zbo9zYVRuE4uz4Qr0vLeGOk,471
|
648
|
+
claude_mpm-4.2.26.dist-info/top_level.txt,sha256=1nUg3FEaBySgm8t-s54jK5zoPnu3_eY6EP6IOlekyHA,11
|
649
|
+
claude_mpm-4.2.26.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|