claude-mpm 3.4.0__py3-none-any.whl → 3.4.3__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.
@@ -31,6 +31,12 @@ try:
31
31
  except ImportError:
32
32
  REQUESTS_AVAILABLE = False
33
33
 
34
+ try:
35
+ import psutil
36
+ PSUTIL_AVAILABLE = True
37
+ except ImportError:
38
+ PSUTIL_AVAILABLE = False
39
+
34
40
 
35
41
  class ServerManager:
36
42
  """Manages Socket.IO server instances across different deployment modes."""
@@ -40,30 +46,49 @@ class ServerManager:
40
46
  self.max_instances = 5
41
47
  self.script_dir = Path(__file__).parent
42
48
  self.project_root = self.script_dir.parent
49
+ # Daemon PID file location (used by socketio_daemon.py)
50
+ self.daemon_pidfile_path = Path.home() / ".claude-mpm" / "socketio-server.pid"
51
+ # Standalone server PID file location pattern
52
+ self.standalone_pidfile_pattern = "/tmp/claude_mpm_socketio_{port}.pid"
43
53
 
44
54
  def get_server_info(self, port: int) -> Optional[Dict]:
45
- """Get server information from a running instance."""
55
+ """Get server information from a running instance with daemon compatibility."""
46
56
  if not REQUESTS_AVAILABLE:
47
- return None
57
+ return self._check_daemon_fallback(port)
48
58
 
49
59
  try:
50
60
  response = requests.get(f"http://localhost:{port}/health", timeout=2.0)
51
61
  if response.status_code == 200:
52
- return response.json()
53
- except Exception:
54
- pass
62
+ data = response.json()
63
+ # Check if this is a daemon-style response (no 'pid' field)
64
+ if 'pid' not in data and 'status' in data:
65
+ # Try to get PID from daemon PID file
66
+ daemon_pid = self._get_daemon_pid()
67
+ if daemon_pid:
68
+ data['pid'] = daemon_pid
69
+ data['management_style'] = 'daemon'
70
+ return data
71
+ except Exception as e:
72
+ # If HTTP fails, try daemon fallback
73
+ return self._check_daemon_fallback(port)
55
74
  return None
56
75
 
57
76
  def list_running_servers(self) -> List[Dict]:
58
- """List all running Socket.IO servers."""
77
+ """List all running Socket.IO servers including daemon-style servers."""
59
78
  running_servers = []
60
79
 
80
+ # Check standard port range
61
81
  for port in range(self.base_port, self.base_port + self.max_instances):
62
82
  server_info = self.get_server_info(port)
63
83
  if server_info:
64
84
  server_info['port'] = port
65
85
  running_servers.append(server_info)
66
86
 
87
+ # Also check for daemon-style server specifically
88
+ daemon_info = self._get_daemon_server_info()
89
+ if daemon_info and not any(s['port'] == daemon_info.get('port', self.base_port) for s in running_servers):
90
+ running_servers.append(daemon_info)
91
+
67
92
  return running_servers
68
93
 
69
94
  def find_available_port(self, start_port: int = None) -> int:
@@ -78,7 +103,7 @@ class ServerManager:
78
103
 
79
104
  def start_server(self, port: int = None, server_id: str = None,
80
105
  host: str = "localhost") -> bool:
81
- """Start a standalone Socket.IO server."""
106
+ """Start a standalone Socket.IO server with conflict detection."""
82
107
 
83
108
  # Find available port if not specified
84
109
  if port is None:
@@ -89,10 +114,26 @@ class ServerManager:
89
114
  return False
90
115
 
91
116
  # Check if server is already running on this port
92
- if self.get_server_info(port):
93
- print(f"Server already running on port {port}")
117
+ existing_server = self.get_server_info(port)
118
+ if existing_server:
119
+ management_style = existing_server.get('management_style', 'http')
120
+ server_id_existing = existing_server.get('server_id', 'unknown')
121
+
122
+ print(f"❌ Server already running on port {port}")
123
+ print(f" Existing server: {server_id_existing} ({management_style}-managed)")
124
+
125
+ if management_style == 'daemon':
126
+ print(f"💡 To stop daemon server: {self.project_root / 'src' / 'claude_mpm' / 'scripts' / 'socketio_daemon.py'} stop")
127
+ else:
128
+ print(f"💡 To stop server: {sys.executable} {__file__} stop --port {port}")
129
+
94
130
  return False
95
131
 
132
+ # Warn if daemon server exists on default port but we're starting on different port
133
+ if port != self.base_port and self._get_daemon_pid():
134
+ print(f"⚠️ Warning: Daemon server is running on port {self.base_port}, you're starting on port {port}")
135
+ print(f" This may cause conflicts. Consider stopping daemon first.")
136
+
96
137
  # Try different ways to start the server based on deployment
97
138
  success = False
98
139
 
@@ -158,13 +199,20 @@ class ServerManager:
158
199
 
159
200
  if success:
160
201
  print(f"✅ Server started successfully on {host}:{port}")
202
+ print(f"💡 Management commands:")
203
+ print(f" Status: {sys.executable} {__file__} status")
204
+ print(f" Stop: {sys.executable} {__file__} stop --port {port}")
161
205
  return True
162
206
  else:
163
207
  print(f"❌ Failed to start server on {host}:{port}")
208
+ print(f"💡 Troubleshooting:")
209
+ print(f" • Check if port {port} is already in use: lsof -i :{port}")
210
+ print(f" • Check server status: {sys.executable} {__file__} status")
211
+ print(f" • Try different port: {sys.executable} {__file__} start --port {port + 1}")
164
212
  return False
165
213
 
166
214
  def stop_server(self, port: int = None, server_id: str = None) -> bool:
167
- """Stop a running Socket.IO server."""
215
+ """Stop a running Socket.IO server with daemon compatibility."""
168
216
 
169
217
  if port is None and server_id is None:
170
218
  print("Must specify either port or server_id")
@@ -185,35 +233,56 @@ class ServerManager:
185
233
  # Get server info
186
234
  server_info = self.get_server_info(port)
187
235
  if not server_info:
188
- print(f"No server running on port {port}")
189
- return False
236
+ # Try daemon-specific stop as fallback
237
+ return self._try_daemon_stop(port)
238
+
239
+ # Determine management style
240
+ management_style = server_info.get('management_style', 'http')
190
241
 
191
- # Try to get PID and send termination signal
242
+ # Try HTTP-based stop first
192
243
  pid = server_info.get('pid')
193
244
  if pid:
194
245
  try:
195
- os.kill(pid, signal.SIGTERM)
196
- print(f"✅ Sent termination signal to server (PID: {pid})")
197
-
198
- # Wait for server to stop
199
- for _ in range(10):
200
- time.sleep(1)
201
- if not self.get_server_info(port):
202
- print(f"✅ Server stopped successfully")
203
- return True
204
-
205
- # Force kill if still running
206
- try:
207
- os.kill(pid, signal.SIGKILL)
208
- print(f"⚠️ Force killed server (PID: {pid})")
209
- return True
210
- except:
211
- pass
246
+ # Validate PID before attempting to kill
247
+ if self._validate_pid(pid):
248
+ os.kill(pid, signal.SIGTERM)
249
+ print(f"✅ Sent termination signal to server (PID: {pid})")
250
+
251
+ # Wait for server to stop
252
+ for i in range(10):
253
+ time.sleep(1)
254
+ if not self.get_server_info(port):
255
+ print(f"✅ Server stopped successfully")
256
+ return True
257
+ if i == 5: # After 5 seconds, show progress
258
+ print(f"⏳ Waiting for server to stop...")
259
+
260
+ # Force kill if still running
261
+ try:
262
+ if self._validate_pid(pid):
263
+ os.kill(pid, signal.SIGKILL)
264
+ print(f"⚠️ Force killed server (PID: {pid})")
265
+ return True
266
+ except OSError:
267
+ pass
268
+
269
+ else:
270
+ print(f"⚠️ PID {pid} is no longer valid, trying daemon stop...")
271
+ return self._try_daemon_stop(port)
212
272
 
213
273
  except OSError as e:
214
- print(f"Error stopping server: {e}")
274
+ print(f"Error stopping server via PID {pid}: {e}")
275
+ if management_style == 'daemon':
276
+ print("🔄 Trying daemon-style stop...")
277
+ return self._try_daemon_stop(port)
278
+
279
+ # If HTTP method failed, try daemon stop
280
+ if management_style == 'daemon' or not pid:
281
+ print("🔄 Attempting daemon-style stop...")
282
+ return self._try_daemon_stop(port)
215
283
 
216
284
  print(f"❌ Failed to stop server on port {port}")
285
+ print(f"💡 Try using the socketio_daemon.py stop command if this is a daemon-managed server")
217
286
  return False
218
287
 
219
288
  def restart_server(self, port: int = None, server_id: str = None) -> bool:
@@ -232,11 +301,15 @@ class ServerManager:
232
301
  return False
233
302
 
234
303
  def status(self, verbose: bool = False) -> None:
235
- """Show status of all Socket.IO servers."""
304
+ """Show status of all Socket.IO servers with management style info."""
236
305
  running_servers = self.list_running_servers()
237
306
 
238
307
  if not running_servers:
239
308
  print("No Socket.IO servers currently running")
309
+ print()
310
+ print("💡 Management options:")
311
+ print(f" • Start with manager: {sys.executable} {__file__} start")
312
+ print(f" • Start with daemon: {self.project_root / 'src' / 'claude_mpm' / 'scripts' / 'socketio_daemon.py'} start")
240
313
  return
241
314
 
242
315
  print(f"Found {len(running_servers)} running server(s):")
@@ -248,10 +321,15 @@ class ServerManager:
248
321
  version = server.get('server_version', 'unknown')
249
322
  uptime = server.get('uptime_seconds', 0)
250
323
  clients = server.get('clients_connected', 0)
324
+ management_style = server.get('management_style', 'http')
325
+
326
+ # Different icons based on management style
327
+ icon = "🖥️" if management_style == 'http' else "🔧"
251
328
 
252
- print(f"🖥️ Server ID: {server_id}")
329
+ print(f"{icon} Server ID: {server_id}")
253
330
  print(f" Port: {port}")
254
331
  print(f" Version: {version}")
332
+ print(f" Management: {management_style}")
255
333
  print(f" Uptime: {self._format_uptime(uptime)}")
256
334
  print(f" Clients: {clients}")
257
335
 
@@ -259,34 +337,61 @@ class ServerManager:
259
337
  print(f" PID: {server.get('pid', 'unknown')}")
260
338
  print(f" Host: {server.get('host', 'unknown')}")
261
339
 
262
- # Get additional stats
263
- stats = self._get_server_stats(port)
264
- if stats:
265
- events_processed = stats.get('events', {}).get('total_processed', 0)
266
- clients_served = stats.get('connections', {}).get('total_served', 0)
267
- print(f" Events processed: {events_processed}")
268
- print(f" Total clients served: {clients_served}")
340
+ # Show appropriate stop command
341
+ if management_style == 'daemon':
342
+ print(f" Stop command: {self.project_root / 'src' / 'claude_mpm' / 'scripts' / 'socketio_daemon.py'} stop")
343
+ else:
344
+ print(f" Stop command: {sys.executable} {__file__} stop --port {port}")
345
+
346
+ # Get additional stats (only for HTTP-style servers)
347
+ if management_style == 'http':
348
+ stats = self._get_server_stats(port)
349
+ if stats:
350
+ events_processed = stats.get('events', {}).get('total_processed', 0)
351
+ clients_served = stats.get('connections', {}).get('total_served', 0)
352
+ print(f" Events processed: {events_processed}")
353
+ print(f" Total clients served: {clients_served}")
269
354
 
270
355
  print()
271
356
 
272
357
  def health_check(self, port: int = None) -> bool:
273
- """Perform health check on server(s)."""
358
+ """Perform health check on server(s) with management style awareness."""
274
359
 
275
360
  if port:
276
361
  # Check specific server
277
362
  server_info = self.get_server_info(port)
278
363
  if server_info:
279
364
  status = server_info.get('status', 'unknown')
280
- print(f"Server on port {port}: {status}")
281
- return status == 'healthy'
365
+ management_style = server_info.get('management_style', 'http')
366
+ server_id = server_info.get('server_id', 'unknown')
367
+
368
+ print(f"Server {server_id} on port {port}: {status} ({management_style}-managed)")
369
+
370
+ # Additional health info for daemon servers
371
+ if management_style == 'daemon':
372
+ pid = server_info.get('pid')
373
+ if pid and self._validate_pid(pid):
374
+ print(f" ✅ Process {pid} is running")
375
+ else:
376
+ print(f" ❌ Process {pid} is not running")
377
+ return False
378
+
379
+ return status in ['healthy', 'running']
282
380
  else:
283
381
  print(f"No server found on port {port}")
382
+ # Try daemon fallback for default port
383
+ if port == self.base_port:
384
+ daemon_info = self._get_daemon_server_info()
385
+ if daemon_info:
386
+ print(f" Found daemon server: {daemon_info['server_id']}")
387
+ return True
284
388
  return False
285
389
  else:
286
390
  # Check all servers
287
391
  running_servers = self.list_running_servers()
288
392
  if not running_servers:
289
393
  print("No servers running")
394
+ print(f"💡 Start a server with: {sys.executable} {__file__} start")
290
395
  return False
291
396
 
292
397
  all_healthy = True
@@ -294,8 +399,14 @@ class ServerManager:
294
399
  port = server['port']
295
400
  status = server.get('status', 'unknown')
296
401
  server_id = server.get('server_id', 'unknown')
297
- print(f"Server {server_id} (port {port}): {status}")
298
- if status != 'healthy':
402
+ management_style = server.get('management_style', 'http')
403
+
404
+ health_status = status in ['healthy', 'running']
405
+ icon = "✅" if health_status else "❌"
406
+
407
+ print(f"{icon} Server {server_id} (port {port}): {status} ({management_style}-managed)")
408
+
409
+ if not health_status:
299
410
  all_healthy = False
300
411
 
301
412
  return all_healthy
@@ -342,6 +453,213 @@ class ServerManager:
342
453
  except Exception:
343
454
  pass
344
455
  return None
456
+
457
+ def _check_daemon_fallback(self, port: int) -> Optional[Dict]:
458
+ """Check for daemon-style server when HTTP fails."""
459
+ if port == self.base_port: # Only check daemon for default port
460
+ return self._get_daemon_server_info()
461
+ return None
462
+
463
+ def _get_daemon_pid(self) -> Optional[int]:
464
+ """Get PID from daemon PID file."""
465
+ try:
466
+ if self.daemon_pidfile_path.exists():
467
+ with open(self.daemon_pidfile_path, 'r') as f:
468
+ content = f.read().strip()
469
+ if content.isdigit():
470
+ pid = int(content)
471
+ # Validate the PID exists
472
+ if self._validate_pid(pid):
473
+ return pid
474
+ except Exception:
475
+ pass
476
+ return None
477
+
478
+ def _get_daemon_server_info(self) -> Optional[Dict]:
479
+ """Get server info for daemon-style server."""
480
+ daemon_pid = self._get_daemon_pid()
481
+ if daemon_pid:
482
+ # Basic server info for daemon
483
+ info = {
484
+ 'pid': daemon_pid,
485
+ 'server_id': 'daemon-socketio',
486
+ 'management_style': 'daemon',
487
+ 'status': 'running',
488
+ 'port': self.base_port,
489
+ 'server_version': 'daemon-managed'
490
+ }
491
+
492
+ # Try to get additional process info if psutil is available
493
+ if PSUTIL_AVAILABLE:
494
+ try:
495
+ process = psutil.Process(daemon_pid)
496
+ info.update({
497
+ 'uptime_seconds': time.time() - process.create_time(),
498
+ 'host': 'localhost',
499
+ 'process_name': process.name()
500
+ })
501
+ except:
502
+ pass
503
+
504
+ return info
505
+ return None
506
+
507
+ def _validate_pid(self, pid: int) -> bool:
508
+ """Validate that a PID represents a running process."""
509
+ try:
510
+ # Check if process exists
511
+ os.kill(pid, 0)
512
+ return True
513
+ except OSError:
514
+ return False
515
+
516
+ def _try_daemon_stop(self, port: int) -> bool:
517
+ """Try to stop daemon-style server."""
518
+ if port != self.base_port:
519
+ print(f"⚠️ Daemon management only supports default port {self.base_port}, not {port}")
520
+ return False
521
+
522
+ daemon_pid = self._get_daemon_pid()
523
+ if not daemon_pid:
524
+ print(f"❌ No daemon server found (no PID file at {self.daemon_pidfile_path})")
525
+ return False
526
+
527
+ try:
528
+ print(f"🔄 Stopping daemon server (PID: {daemon_pid})...")
529
+ os.kill(daemon_pid, signal.SIGTERM)
530
+
531
+ # Wait for daemon to stop
532
+ for i in range(10):
533
+ time.sleep(1)
534
+ if not self._validate_pid(daemon_pid):
535
+ print(f"✅ Daemon server stopped successfully")
536
+ # Clean up PID file
537
+ try:
538
+ self.daemon_pidfile_path.unlink(missing_ok=True)
539
+ except:
540
+ pass
541
+ return True
542
+ if i == 5:
543
+ print(f"⏳ Waiting for daemon to stop...")
544
+
545
+ # Force kill if still running
546
+ if self._validate_pid(daemon_pid):
547
+ print(f"⚠️ Force killing daemon server...")
548
+ os.kill(daemon_pid, signal.SIGKILL)
549
+ time.sleep(1)
550
+ if not self._validate_pid(daemon_pid):
551
+ print(f"✅ Daemon server force stopped")
552
+ try:
553
+ self.daemon_pidfile_path.unlink(missing_ok=True)
554
+ except:
555
+ pass
556
+ return True
557
+
558
+ except OSError as e:
559
+ print(f"❌ Error stopping daemon server: {e}")
560
+ return False
561
+
562
+ print(f"❌ Failed to stop daemon server")
563
+ return False
564
+
565
+ def diagnose_conflicts(self, port: int = None) -> None:
566
+ """Diagnose server management conflicts and suggest resolutions."""
567
+ if port is None:
568
+ port = self.base_port
569
+
570
+ print(f"🔍 Diagnosing Socket.IO server management on port {port}")
571
+ print("=" * 60)
572
+
573
+ # Check HTTP-managed server
574
+ http_server = None
575
+ daemon_server = None
576
+
577
+ try:
578
+ if REQUESTS_AVAILABLE:
579
+ response = requests.get(f"http://localhost:{port}/health", timeout=2.0)
580
+ if response.status_code == 200:
581
+ data = response.json()
582
+ if 'pid' in data:
583
+ http_server = data
584
+ except:
585
+ pass
586
+
587
+ # Check daemon-managed server
588
+ daemon_pid = self._get_daemon_pid()
589
+ if daemon_pid and port == self.base_port:
590
+ daemon_server = self._get_daemon_server_info()
591
+
592
+ # Analysis
593
+ print("📊 Server Analysis:")
594
+
595
+ if http_server and daemon_server:
596
+ print("⚠️ CONFLICT DETECTED: Both HTTP and daemon servers found!")
597
+ print(f" HTTP server: PID {http_server.get('pid')}, ID {http_server.get('server_id')}")
598
+ print(f" Daemon server: PID {daemon_server.get('pid')}, ID {daemon_server.get('server_id')}")
599
+ print()
600
+ print("🔧 Resolution Steps:")
601
+ print(" 1. Choose one management approach:")
602
+ print(f" • Keep HTTP: {sys.executable} {__file__} stop --port {port} (stops daemon)")
603
+ print(f" • Keep daemon: Stop HTTP server first, then use daemon commands")
604
+ print()
605
+
606
+ elif http_server:
607
+ print(f"✅ HTTP-managed server found")
608
+ print(f" Server ID: {http_server.get('server_id')}")
609
+ print(f" PID: {http_server.get('pid')}")
610
+ print(f" Status: {http_server.get('status')}")
611
+ print()
612
+ print("🔧 Management Commands:")
613
+ print(f" • Stop: {sys.executable} {__file__} stop --port {port}")
614
+ print(f" • Status: {sys.executable} {__file__} status")
615
+ print()
616
+
617
+ elif daemon_server:
618
+ print(f"✅ Daemon-managed server found")
619
+ print(f" PID: {daemon_server.get('pid')}")
620
+ print(f" PID file: {self.daemon_pidfile_path}")
621
+ print()
622
+ print("🔧 Management Commands:")
623
+ daemon_script = self.project_root / "src" / "claude_mpm" / "scripts" / "socketio_daemon.py"
624
+ print(f" • Stop: {daemon_script} stop")
625
+ print(f" • Status: {daemon_script} status")
626
+ print(f" • Restart: {daemon_script} restart")
627
+ print()
628
+
629
+ else:
630
+ print("❌ No servers found on specified port")
631
+ print()
632
+ print("🔧 Start a server:")
633
+ print(f" • HTTP-managed: {sys.executable} {__file__} start --port {port}")
634
+ daemon_script = self.project_root / "src" / "claude_mpm" / "scripts" / "socketio_daemon.py"
635
+ if port == self.base_port:
636
+ print(f" • Daemon-managed: {daemon_script} start")
637
+ print()
638
+
639
+ # Port conflict check
640
+ try:
641
+ import socket
642
+ with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
643
+ result = s.connect_ex(('localhost', port))
644
+ if result == 0:
645
+ print("🌐 Port Status: IN USE")
646
+ if not http_server and not daemon_server:
647
+ print(f" ⚠️ Port {port} is occupied by unknown process")
648
+ if PSUTIL_AVAILABLE:
649
+ print(" 🔍 Use 'lsof -i :{port}' or 'netstat -tulpn | grep {port}' to identify")
650
+ else:
651
+ print("🌐 Port Status: AVAILABLE")
652
+ except:
653
+ print("🌐 Port Status: UNKNOWN")
654
+
655
+ print()
656
+ print("📚 Management Style Comparison:")
657
+ print(" HTTP-managed:")
658
+ print(" • Pros: Full API, stats, multi-instance support")
659
+ print(" • Cons: More complex, requires HTTP client")
660
+ print(" Daemon-managed:")
661
+ print(" • Pros: Simple, lightweight, traditional daemon")
662
+ print(" • Cons: Single instance, basic management")
345
663
 
346
664
 
347
665
  def main():
@@ -379,6 +697,10 @@ def main():
379
697
  # List command
380
698
  subparsers.add_parser('list', help='List running servers')
381
699
 
700
+ # Diagnose command
701
+ diagnose_parser = subparsers.add_parser('diagnose', help='Diagnose server management conflicts')
702
+ diagnose_parser.add_argument('--port', type=int, default=8765, help='Port to diagnose')
703
+
382
704
  args = parser.parse_args()
383
705
 
384
706
  if not args.command:
@@ -422,6 +744,9 @@ def main():
422
744
 
423
745
  elif args.command == 'list':
424
746
  manager.status(verbose=False)
747
+
748
+ elif args.command == 'diagnose':
749
+ manager.diagnose_conflicts(port=args.port)
425
750
 
426
751
 
427
752
  if __name__ == "__main__":
@@ -21,6 +21,21 @@ def __getattr__(name):
21
21
  elif name == "ProjectAnalyzer":
22
22
  from .project_analyzer import ProjectAnalyzer
23
23
  return ProjectAnalyzer
24
+ elif name == "AdvancedHealthMonitor":
25
+ try:
26
+ from .health_monitor import AdvancedHealthMonitor
27
+ return AdvancedHealthMonitor
28
+ except ImportError:
29
+ raise AttributeError(f"Health monitoring not available: {name}")
30
+ elif name == "RecoveryManager":
31
+ try:
32
+ from .recovery_manager import RecoveryManager
33
+ return RecoveryManager
34
+ except ImportError:
35
+ raise AttributeError(f"Recovery management not available: {name}")
36
+ elif name == "StandaloneSocketIOServer":
37
+ from .standalone_socketio_server import StandaloneSocketIOServer
38
+ return StandaloneSocketIOServer
24
39
  raise AttributeError(f"module '{__name__}' has no attribute '{name}'")
25
40
 
26
41
  __all__ = [
@@ -30,4 +45,7 @@ __all__ = [
30
45
  "get_memory_manager",
31
46
  "HookService",
32
47
  "ProjectAnalyzer",
48
+ "AdvancedHealthMonitor",
49
+ "RecoveryManager",
50
+ "StandaloneSocketIOServer",
33
51
  ]
@@ -23,6 +23,7 @@ from typing import Dict, List, Optional, Any
23
23
  from datetime import datetime
24
24
  import re
25
25
  import logging
26
+ import os
26
27
 
27
28
  from claude_mpm.core.config import Config
28
29
  from claude_mpm.core.mixins import LoggerMixin
@@ -68,7 +69,7 @@ class AgentMemoryManager:
68
69
 
69
70
  Args:
70
71
  config: Optional Config object. If not provided, will create default Config.
71
- working_directory: Optional working directory. If not provided, uses project root.
72
+ working_directory: Optional working directory. If not provided, uses current working directory.
72
73
  """
73
74
  # Initialize logger using the same pattern as LoggerMixin
74
75
  self._logger_instance = None
@@ -76,8 +77,9 @@ class AgentMemoryManager:
76
77
 
77
78
  self.config = config or Config()
78
79
  self.project_root = PathResolver.get_project_root()
79
- self.working_directory = working_directory or self.project_root
80
- self.memories_dir = self.project_root / ".claude-mpm" / "memories"
80
+ # Use current working directory by default, not project root
81
+ self.working_directory = working_directory or Path(os.getcwd())
82
+ self.memories_dir = self.working_directory / ".claude-mpm" / "memories"
81
83
  self._ensure_memories_directory()
82
84
 
83
85
  # Initialize memory limits from configuration
@@ -945,7 +947,7 @@ class AgentMemoryManager:
945
947
  """
946
948
  try:
947
949
  from claude_mpm.services.memory_optimizer import MemoryOptimizer
948
- optimizer = MemoryOptimizer(self.config)
950
+ optimizer = MemoryOptimizer(self.config, self.working_directory)
949
951
 
950
952
  if agent_id:
951
953
  result = optimizer.optimize_agent_memory(agent_id)
@@ -973,7 +975,7 @@ class AgentMemoryManager:
973
975
  """
974
976
  try:
975
977
  from claude_mpm.services.memory_builder import MemoryBuilder
976
- builder = MemoryBuilder(self.config)
978
+ builder = MemoryBuilder(self.config, self.working_directory)
977
979
 
978
980
  result = builder.build_from_documentation(force_rebuild)
979
981
  self.logger.info("Built memories from documentation")