claude-mpm 4.2.23__py3-none-any.whl → 4.2.24__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 CHANGED
@@ -1 +1 @@
1
- 4.2.23
1
+ 4.2.24
@@ -112,9 +112,11 @@ class UnifiedMonitorDaemon:
112
112
 
113
113
  if force_restart:
114
114
  # Check if it's our service
115
- is_ours, pid = self.lifecycle.is_our_service(self.host)
115
+ self.logger.debug(f"Checking if existing daemon (PID: {existing_pid}) is our service...")
116
+ is_ours, detected_pid = self.lifecycle.is_our_service(self.host)
117
+
116
118
  if is_ours:
117
- self.logger.info(f"Force restarting our existing daemon (PID: {pid or existing_pid})")
119
+ self.logger.info(f"Force restarting our existing claude-mpm monitor daemon (PID: {detected_pid or existing_pid})")
118
120
  # Stop the existing daemon
119
121
  if self.lifecycle.stop_daemon():
120
122
  # Wait a moment for port to be released
@@ -123,7 +125,8 @@ class UnifiedMonitorDaemon:
123
125
  self.logger.error("Failed to stop existing daemon for restart")
124
126
  return False
125
127
  else:
126
- self.logger.warning(f"Daemon already running with PID {existing_pid}, but it's not our service")
128
+ self.logger.warning(f"Port {self.port} is in use by another service (PID: {existing_pid}). Cannot force restart.")
129
+ self.logger.info("To restart the claude-mpm monitor, first stop the other service or use a different port.")
127
130
  return False
128
131
  else:
129
132
  self.logger.warning(f"Daemon already running with PID {existing_pid}")
@@ -131,9 +134,10 @@ class UnifiedMonitorDaemon:
131
134
 
132
135
  # Check for orphaned processes (service running but no PID file)
133
136
  elif force_restart:
137
+ self.logger.debug("No PID file found, checking for orphaned claude-mpm service...")
134
138
  is_ours, pid = self.lifecycle.is_our_service(self.host)
135
139
  if is_ours and pid:
136
- self.logger.info(f"Found orphaned claude-mpm service (PID: {pid}), force restarting")
140
+ self.logger.info(f"Found orphaned claude-mpm monitor service (PID: {pid}), force restarting")
137
141
  # Try to kill the orphaned process
138
142
  try:
139
143
  os.kill(pid, signal.SIGTERM)
@@ -194,9 +198,11 @@ class UnifiedMonitorDaemon:
194
198
 
195
199
  if force_restart:
196
200
  # Check if it's our service
197
- is_ours, pid = self.lifecycle.is_our_service(self.host)
201
+ self.logger.debug(f"Checking if existing daemon (PID: {existing_pid}) is our service...")
202
+ is_ours, detected_pid = self.lifecycle.is_our_service(self.host)
203
+
198
204
  if is_ours:
199
- self.logger.info(f"Force restarting our existing daemon (PID: {pid or existing_pid})")
205
+ self.logger.info(f"Force restarting our existing claude-mpm monitor daemon (PID: {detected_pid or existing_pid})")
200
206
  # Stop the existing daemon
201
207
  if self.lifecycle.stop_daemon():
202
208
  # Wait a moment for port to be released
@@ -205,7 +211,8 @@ class UnifiedMonitorDaemon:
205
211
  self.logger.error("Failed to stop existing daemon for restart")
206
212
  return False
207
213
  else:
208
- self.logger.warning(f"Monitor daemon already running with PID {existing_pid}, but it's not our service")
214
+ self.logger.warning(f"Port {self.port} is in use by another service (PID: {existing_pid}). Cannot force restart.")
215
+ self.logger.info("To restart the claude-mpm monitor, first stop the other service or use a different port.")
209
216
  return False
210
217
  else:
211
218
  self.logger.warning(
@@ -215,9 +222,10 @@ class UnifiedMonitorDaemon:
215
222
 
216
223
  # Check for orphaned processes (service running but no PID file)
217
224
  elif force_restart:
225
+ self.logger.debug("No PID file found, checking for orphaned claude-mpm service...")
218
226
  is_ours, pid = self.lifecycle.is_our_service(self.host)
219
227
  if is_ours and pid:
220
- self.logger.info(f"Found orphaned claude-mpm service (PID: {pid}), force restarting")
228
+ self.logger.info(f"Found orphaned claude-mpm monitor service (PID: {pid}), force restarting")
221
229
  # Try to kill the orphaned process
222
230
  try:
223
231
  os.kill(pid, signal.SIGTERM)
@@ -513,60 +513,101 @@ class DaemonLifecycle:
513
513
  Returns:
514
514
  Tuple of (is_ours, pid_if_found)
515
515
  """
516
+ self.logger.debug(f"Checking if service on {host}:{self.port} is ours")
517
+
516
518
  try:
517
519
  # Method 1: Check health endpoint
518
520
  import urllib.request
519
521
  import urllib.error
520
522
 
521
523
  health_url = f"http://{host}:{self.port}/health"
524
+ self.logger.debug(f"Checking health endpoint: {health_url}")
525
+
522
526
  try:
523
- with urllib.request.urlopen(health_url, timeout=2) as response:
527
+ req = urllib.request.Request(health_url)
528
+ req.add_header('User-Agent', 'claude-mpm-monitor')
529
+
530
+ with urllib.request.urlopen(req, timeout=3) as response:
524
531
  if response.status == 200:
525
532
  data = json.loads(response.read().decode())
533
+ self.logger.debug(f"Health endpoint response: {data}")
534
+
526
535
  # Check for our service signature
527
- if data.get("service") == "claude-mpm-monitor":
536
+ service_name = data.get("service")
537
+ if service_name == "claude-mpm-monitor":
528
538
  # Try to get PID from response
529
539
  pid = data.get("pid")
530
540
  if pid:
531
- self.logger.debug(f"Found our service via health endpoint, PID: {pid}")
541
+ self.logger.info(f"Found our claude-mpm-monitor service via health endpoint, PID: {pid}")
532
542
  return True, pid
533
543
  else:
534
544
  # Service is ours but no PID in response
535
545
  # Try to get from PID file
536
546
  file_pid = self.get_pid()
537
- self.logger.debug(f"Found our service via health endpoint, PID from file: {file_pid}")
547
+ self.logger.info(f"Found our claude-mpm-monitor service via health endpoint, PID from file: {file_pid}")
538
548
  return True, file_pid
539
- except (urllib.error.URLError, urllib.error.HTTPError, json.JSONDecodeError):
540
- # Health endpoint not accessible or invalid response
541
- pass
549
+ else:
550
+ self.logger.debug(f"Service name '{service_name}' does not match 'claude-mpm-monitor'")
551
+
552
+ except urllib.error.URLError as e:
553
+ self.logger.debug(f"Health endpoint not accessible: {e}")
554
+ except urllib.error.HTTPError as e:
555
+ self.logger.debug(f"Health endpoint HTTP error: {e}")
556
+ except json.JSONDecodeError as e:
557
+ self.logger.debug(f"Health endpoint invalid JSON: {e}")
558
+ except Exception as e:
559
+ self.logger.debug(f"Health endpoint check failed: {e}")
542
560
 
543
561
  # Method 2: Check if PID file exists and process matches
544
562
  pid = self.get_pid()
545
563
  if pid:
564
+ self.logger.debug(f"Checking PID from file: {pid}")
546
565
  try:
547
566
  # Check if process exists
548
567
  os.kill(pid, 0)
568
+ self.logger.debug(f"Process {pid} exists")
549
569
 
550
570
  # Process exists, check if it's using our port
551
571
  # This requires psutil for accurate port checking
552
572
  try:
553
573
  import psutil
554
574
  process = psutil.Process(pid)
575
+
576
+ # Check process command line for our service
577
+ cmdline = ' '.join(process.cmdline())
578
+ if 'claude_mpm' in cmdline or 'claude-mpm' in cmdline:
579
+ if 'monitor' in cmdline:
580
+ self.logger.info(f"Found our claude-mpm monitor process via PID file, PID: {pid}")
581
+ return True, pid
582
+
583
+ # Also check if it's listening on our port
555
584
  connections = process.connections()
556
585
  for conn in connections:
557
- if conn.laddr.port == self.port:
558
- self.logger.debug(f"Found our service via PID file, PID: {pid}")
559
- return True, pid
586
+ if conn.laddr.port == self.port and conn.status == 'LISTEN':
587
+ self.logger.info(f"Found process {pid} listening on our port {self.port}")
588
+ # Double-check it's a Python process (likely ours)
589
+ if 'python' in process.name().lower():
590
+ self.logger.info(f"Confirmed as Python process, assuming it's our service")
591
+ return True, pid
592
+
560
593
  except ImportError:
561
- # psutil not available, assume it's ours if PID matches
562
- self.logger.debug(f"Found process with our PID file: {pid}, assuming it's ours")
594
+ # psutil not available, but we have a PID file and process exists
595
+ # Assume it's ours since we manage the PID file
596
+ self.logger.info(f"Found process with our PID file: {pid}, assuming it's ours (psutil not available)")
563
597
  return True, pid
564
- except Exception:
565
- pass
598
+ except psutil.NoSuchProcess:
599
+ self.logger.debug(f"Process {pid} no longer exists")
600
+ except psutil.AccessDenied:
601
+ # Can't access process info, but it exists - likely ours
602
+ self.logger.info(f"Process {pid} exists but access denied, assuming it's ours")
603
+ return True, pid
604
+ except Exception as e:
605
+ self.logger.debug(f"Error checking process {pid}: {e}")
566
606
 
567
607
  except (OSError, ProcessLookupError):
568
608
  # Process doesn't exist
569
- pass
609
+ self.logger.debug(f"Process {pid} does not exist")
610
+ self._cleanup_stale_pid_file()
570
611
 
571
612
  # Method 3: Try Socket.IO connection to check namespace
572
613
  try:
@@ -620,9 +661,21 @@ class DaemonLifecycle:
620
661
  except Exception as e:
621
662
  self.logger.debug(f"Error checking Socket.IO connection: {e}")
622
663
 
664
+ # Method 4: Final fallback - if we have a PID file and can't definitively say it's NOT ours
665
+ # This handles edge cases where the health endpoint might be temporarily unavailable
666
+ if pid and self.pid_file.exists():
667
+ try:
668
+ # One more check - see if process exists
669
+ os.kill(pid, 0)
670
+ self.logger.info(f"PID file exists with valid process {pid}, assuming it's our stale service")
671
+ return True, pid
672
+ except (OSError, ProcessLookupError):
673
+ pass
674
+
623
675
  # No service detected or not ours
676
+ self.logger.debug("Service not detected as ours")
624
677
  return False, None
625
678
 
626
679
  except Exception as e:
627
- self.logger.error(f"Error checking if service is ours: {e}")
680
+ self.logger.error(f"Error checking if service is ours: {e}", exc_info=True)
628
681
  return False, None
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: claude-mpm
3
- Version: 4.2.23
3
+ Version: 4.2.24
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
@@ -1,5 +1,5 @@
1
1
  claude_mpm/BUILD_NUMBER,sha256=toytnNjkIKPgQaGwDqQdC1rpNTAdSEc6Vja50d7Ovug,4
2
- claude_mpm/VERSION,sha256=bLmVrJAUXnac417S8JQgDFhX08Y4MIxjz0i-TIhW9zA,7
2
+ claude_mpm/VERSION,sha256=U4vIe-sZ-5Iumq-KW3abswP9PknmnEd-RswDftmED1g,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
@@ -548,7 +548,7 @@ 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=g7g7g7kZTzCowM5iNJHhkE2UYDRms_7C34Net2CpdjQ,21015
551
+ claude_mpm/services/monitor/daemon.py,sha256=9Cllm-jtVA85_qX1Z3TF-OwB5QZDzSfIvAcKhVQ65G8,21829
552
552
  claude_mpm/services/monitor/event_emitter.py,sha256=JzRLNg8PUJ5s3ulNnq_D4yqCPItvidJzu8DmFxriieQ,12224
553
553
  claude_mpm/services/monitor/server.py,sha256=2-xLo14qFBZf5MDYwBxBhTrFCNnbu3tOS3eEu8vyddc,28476
554
554
  claude_mpm/services/monitor/handlers/__init__.py,sha256=jgPIf4IJVERm_tAeD9834tfx9IcxtlHj5r9rhEWpkfM,701
@@ -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=DO3UYLWN2tW1P72--kTbWCpWZBNxK1Ok2GeI_PdOgHc,23713
561
+ claude_mpm/services/monitor/management/lifecycle.py,sha256=EClf8CK-kCLVb2iRo9uiGI2CAHFrii1Qe1i8xuWnhG4,27037
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.23.dist-info/licenses/LICENSE,sha256=lpaivOlPuBZW1ds05uQLJJswy8Rp_HMNieJEbFlqvLk,1072
643
- claude_mpm-4.2.23.dist-info/METADATA,sha256=5lkYIMEIiPKrYRkOrljAj3wfsRqrBzIip_Vh9sb4eZE,14451
644
- claude_mpm-4.2.23.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
645
- claude_mpm-4.2.23.dist-info/entry_points.txt,sha256=FDPZgz8JOvD-6iuXY2l9Zbo9zYVRuE4uz4Qr0vLeGOk,471
646
- claude_mpm-4.2.23.dist-info/top_level.txt,sha256=1nUg3FEaBySgm8t-s54jK5zoPnu3_eY6EP6IOlekyHA,11
647
- claude_mpm-4.2.23.dist-info/RECORD,,
642
+ claude_mpm-4.2.24.dist-info/licenses/LICENSE,sha256=lpaivOlPuBZW1ds05uQLJJswy8Rp_HMNieJEbFlqvLk,1072
643
+ claude_mpm-4.2.24.dist-info/METADATA,sha256=gBePVzfdnH07tGtxu8dnaQwimTu90EqPI0hcpdIZnjM,14451
644
+ claude_mpm-4.2.24.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
645
+ claude_mpm-4.2.24.dist-info/entry_points.txt,sha256=FDPZgz8JOvD-6iuXY2l9Zbo9zYVRuE4uz4Qr0vLeGOk,471
646
+ claude_mpm-4.2.24.dist-info/top_level.txt,sha256=1nUg3FEaBySgm8t-s54jK5zoPnu3_eY6EP6IOlekyHA,11
647
+ claude_mpm-4.2.24.dist-info/RECORD,,