claude-mpm 4.2.7__py3-none-any.whl → 4.2.11__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.
Files changed (57) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/cli/commands/dashboard.py +62 -120
  3. claude_mpm/cli/commands/monitor.py +71 -212
  4. claude_mpm/cli/commands/run.py +33 -33
  5. claude_mpm/cli/parser.py +79 -2
  6. claude_mpm/cli/parsers/__init__.py +29 -0
  7. claude_mpm/dashboard/static/css/code-tree.css +16 -4
  8. claude_mpm/dashboard/static/css/dashboard.css +15 -1
  9. claude_mpm/dashboard/static/dist/components/code-tree.js +1 -1
  10. claude_mpm/dashboard/static/dist/components/file-viewer.js +2 -0
  11. claude_mpm/dashboard/static/dist/components/module-viewer.js +1 -1
  12. claude_mpm/dashboard/static/dist/components/unified-data-viewer.js +1 -1
  13. claude_mpm/dashboard/static/dist/dashboard.js +1 -1
  14. claude_mpm/dashboard/static/dist/socket-client.js +1 -1
  15. claude_mpm/dashboard/static/js/components/code-tree.js +775 -142
  16. claude_mpm/dashboard/static/js/components/file-viewer.js +538 -0
  17. claude_mpm/dashboard/static/js/components/module-viewer.js +26 -0
  18. claude_mpm/dashboard/static/js/components/unified-data-viewer.js +166 -14
  19. claude_mpm/dashboard/static/js/dashboard.js +108 -91
  20. claude_mpm/dashboard/static/js/socket-client.js +9 -7
  21. claude_mpm/dashboard/templates/index.html +5 -2
  22. claude_mpm/hooks/claude_hooks/hook_handler.py +1 -11
  23. claude_mpm/hooks/claude_hooks/services/connection_manager.py +54 -59
  24. claude_mpm/hooks/claude_hooks/services/connection_manager_http.py +112 -72
  25. claude_mpm/services/agents/deployment/agent_format_converter.py +3 -3
  26. claude_mpm/services/agents/deployment/agent_template_builder.py +3 -5
  27. claude_mpm/services/cli/unified_dashboard_manager.py +354 -0
  28. claude_mpm/services/monitor/__init__.py +20 -0
  29. claude_mpm/services/monitor/daemon.py +256 -0
  30. claude_mpm/services/monitor/event_emitter.py +279 -0
  31. claude_mpm/services/monitor/handlers/__init__.py +20 -0
  32. claude_mpm/services/monitor/handlers/code_analysis.py +334 -0
  33. claude_mpm/services/monitor/handlers/dashboard.py +298 -0
  34. claude_mpm/services/monitor/handlers/hooks.py +491 -0
  35. claude_mpm/services/monitor/management/__init__.py +18 -0
  36. claude_mpm/services/monitor/management/health.py +124 -0
  37. claude_mpm/services/monitor/management/lifecycle.py +298 -0
  38. claude_mpm/services/monitor/server.py +442 -0
  39. claude_mpm/services/socketio/client_proxy.py +20 -12
  40. claude_mpm/services/socketio/dashboard_server.py +4 -4
  41. claude_mpm/services/socketio/monitor_client.py +4 -6
  42. claude_mpm/tools/code_tree_analyzer.py +33 -17
  43. {claude_mpm-4.2.7.dist-info → claude_mpm-4.2.11.dist-info}/METADATA +1 -1
  44. {claude_mpm-4.2.7.dist-info → claude_mpm-4.2.11.dist-info}/RECORD +48 -43
  45. claude_mpm/cli/commands/socketio_monitor.py +0 -233
  46. claude_mpm/scripts/socketio_daemon.py +0 -571
  47. claude_mpm/scripts/socketio_daemon_hardened.py +0 -937
  48. claude_mpm/scripts/socketio_daemon_wrapper.py +0 -78
  49. claude_mpm/scripts/socketio_server_manager.py +0 -349
  50. claude_mpm/services/cli/dashboard_launcher.py +0 -423
  51. claude_mpm/services/cli/socketio_manager.py +0 -595
  52. claude_mpm/services/dashboard/stable_server.py +0 -962
  53. claude_mpm/services/socketio/monitor_server.py +0 -505
  54. {claude_mpm-4.2.7.dist-info → claude_mpm-4.2.11.dist-info}/WHEEL +0 -0
  55. {claude_mpm-4.2.7.dist-info → claude_mpm-4.2.11.dist-info}/entry_points.txt +0 -0
  56. {claude_mpm-4.2.7.dist-info → claude_mpm-4.2.11.dist-info}/licenses/LICENSE +0 -0
  57. {claude_mpm-4.2.7.dist-info → claude_mpm-4.2.11.dist-info}/top_level.txt +0 -0
claude_mpm/VERSION CHANGED
@@ -1 +1 @@
1
- 4.2.7
1
+ 4.2.11
@@ -5,21 +5,20 @@ WHY: This module provides CLI commands for managing the web dashboard interface,
5
5
  allowing users to start, stop, check status, and open the dashboard in a browser.
6
6
 
7
7
  DESIGN DECISIONS:
8
- - Use DashboardLauncher service for consistent dashboard management
8
+ - Use UnifiedMonitorDaemon for integrated dashboard and monitoring
9
9
  - Support both foreground and background operation modes
10
- - Integrate with SocketIO server for real-time event streaming
10
+ - Integrate with EventBus for real-time event streaming
11
11
  - Provide browser auto-opening functionality
12
12
  """
13
13
 
14
14
  import signal
15
15
  import sys
16
- import time
17
16
  from typing import Optional
18
17
 
19
18
  from ...constants import DashboardCommands
20
- from ...services.cli.dashboard_launcher import DashboardLauncher
19
+ from ...services.cli.unified_dashboard_manager import UnifiedDashboardManager
20
+ from ...services.monitor.daemon import UnifiedMonitorDaemon
21
21
  from ...services.port_manager import PortManager
22
- from ...services.socketio.dashboard_server import DashboardServer
23
22
  from ..shared import BaseCommand, CommandResult
24
23
 
25
24
 
@@ -28,7 +27,7 @@ class DashboardCommand(BaseCommand):
28
27
 
29
28
  def __init__(self):
30
29
  super().__init__("dashboard")
31
- self.dashboard_launcher = DashboardLauncher(self.logger)
30
+ self.dashboard_manager = UnifiedDashboardManager(self.logger)
32
31
  self.port_manager = PortManager()
33
32
  self.server = None
34
33
 
@@ -79,20 +78,20 @@ class DashboardCommand(BaseCommand):
79
78
  )
80
79
 
81
80
  # Check if dashboard is already running
82
- if self.dashboard_launcher.is_dashboard_running(port):
83
- dashboard_url = self.dashboard_launcher.get_dashboard_url(port)
81
+ if self.dashboard_manager.is_dashboard_running(port):
82
+ dashboard_url = self.dashboard_manager.get_dashboard_url(port)
84
83
  return CommandResult.success_result(
85
84
  f"Dashboard already running at {dashboard_url}",
86
85
  data={"url": dashboard_url, "port": port},
87
86
  )
88
87
 
89
88
  if background:
90
- # Use the dashboard launcher for background mode
91
- success, browser_opened = self.dashboard_launcher.launch_dashboard(
92
- port=port, monitor_mode=True
89
+ # Use the unified dashboard manager for background mode
90
+ success, browser_opened = self.dashboard_manager.start_dashboard(
91
+ port=port, background=True, open_browser=True
93
92
  )
94
93
  if success:
95
- dashboard_url = self.dashboard_launcher.get_dashboard_url(port)
94
+ dashboard_url = self.dashboard_manager.get_dashboard_url(port)
96
95
  return CommandResult.success_result(
97
96
  f"Dashboard started at {dashboard_url}",
98
97
  data={
@@ -102,102 +101,45 @@ class DashboardCommand(BaseCommand):
102
101
  },
103
102
  )
104
103
  return CommandResult.error_result("Failed to start dashboard in background")
105
-
106
- # Run in foreground mode
107
- server_started = False
108
-
109
- # Try stable server first (or if explicitly requested)
110
- if use_stable:
111
- try:
112
- self.logger.info("Starting stable dashboard server (no monitor dependency)...")
113
- print(f"Starting stable dashboard server on {host}:{port}...")
114
- print("Press Ctrl+C to stop the server")
115
- print("\n✅ Using stable server - works without monitor service\n")
116
-
117
- # Create and run the stable server
118
- from ...services.dashboard.stable_server import StableDashboardServer
119
- stable_server = StableDashboardServer(host=host, port=port, debug=debug)
120
-
121
- # Set up signal handlers for graceful shutdown
122
- def signal_handler(signum, frame):
123
- print("\nShutting down dashboard server...")
124
- sys.exit(0)
125
-
126
- signal.signal(signal.SIGINT, signal_handler)
127
- signal.signal(signal.SIGTERM, signal_handler)
128
-
129
- # Run the server (blocking)
130
- result = stable_server.run()
131
- if result:
132
- # Server ran successfully and stopped normally
133
- server_started = True
134
- return CommandResult.success_result("Dashboard server stopped")
135
- else:
136
- # Server failed to start (e.g., couldn't find templates)
137
- server_started = False
138
- self.logger.warning("Stable server failed to start, trying advanced server...")
139
-
140
- except KeyboardInterrupt:
141
- print("\nDashboard server stopped by user")
142
- return CommandResult.success_result("Dashboard server stopped")
143
- except Exception as e:
144
- self.logger.warning(f"Stable server failed: {e}")
145
- if not getattr(args, "no_fallback", False):
146
- print(f"\n⚠️ Stable server failed: {e}")
147
- print("Attempting fallback to advanced server...")
148
- else:
149
- return CommandResult.error_result(f"Failed to start stable dashboard: {e}")
150
-
151
- # Fallback to advanced DashboardServer if stable server failed or not requested
152
- if not server_started and not getattr(args, "stable_only", False):
153
- try:
154
- self.logger.info("Attempting to start advanced dashboard server with monitor...")
155
- print(f"\nStarting advanced dashboard server on {host}:{port}...")
156
- print("Note: This requires monitor service on port 8766")
157
- print("Press Ctrl+C to stop the server")
158
-
159
- # Create and start the Dashboard server (with monitor client)
160
- self.server = DashboardServer(host=host, port=port)
161
-
162
- # Set up signal handlers for graceful shutdown
163
- def signal_handler(signum, frame):
164
- print("\nShutting down dashboard server...")
165
- if self.server:
166
- self.server.stop_sync()
167
- sys.exit(0)
168
-
169
- signal.signal(signal.SIGINT, signal_handler)
170
- signal.signal(signal.SIGTERM, signal_handler)
171
-
172
- # Start the server (this starts in background thread)
173
- self.server.start_sync()
174
-
175
- # Keep the main thread alive while server is running
176
- # The server runs in a background thread, so we need to block here
177
- try:
178
- while self.server.is_running():
179
- time.sleep(1)
180
- except KeyboardInterrupt:
181
- # Ctrl+C pressed, stop the server
182
- pass
183
-
184
- # Server has stopped or user interrupted
185
- if self.server:
186
- self.server.stop_sync()
187
-
188
- return CommandResult.success_result("Dashboard server stopped")
189
-
190
- except KeyboardInterrupt:
191
- print("\nDashboard server stopped by user")
192
- return CommandResult.success_result("Dashboard server stopped")
193
- except Exception as e:
194
- # If both servers fail, provide helpful error message
195
- error_msg = f"Failed to start dashboard: {e}\n\n"
196
- error_msg += "💡 Troubleshooting tips:\n"
197
- error_msg += f" - Check if port {port} is already in use\n"
198
- error_msg += " - Try running with --stable flag for standalone mode\n"
199
- error_msg += " - Use --debug flag for more details\n"
200
- return CommandResult.error_result(error_msg)
104
+
105
+ # Run in foreground mode using unified monitor daemon
106
+ try:
107
+ self.logger.info("Starting unified monitor daemon with dashboard...")
108
+ print(f"Starting unified dashboard and monitor on {host}:{port}...")
109
+ print("Press Ctrl+C to stop the server")
110
+ print(
111
+ "\n✅ Using unified daemon - includes dashboard, monitoring, and EventBus integration\n"
112
+ )
113
+
114
+ # Create and start the unified monitor daemon
115
+ daemon = UnifiedMonitorDaemon(host=host, port=port, daemon_mode=False)
116
+
117
+ # Set up signal handlers for graceful shutdown
118
+ def signal_handler(signum, frame):
119
+ print("\nShutting down unified monitor daemon...")
120
+ daemon.stop()
121
+ sys.exit(0)
122
+
123
+ signal.signal(signal.SIGINT, signal_handler)
124
+ signal.signal(signal.SIGTERM, signal_handler)
125
+
126
+ # Start the daemon (blocking in foreground mode)
127
+ success = daemon.start()
128
+ if success:
129
+ return CommandResult.success_result("Unified monitor daemon stopped")
130
+ return CommandResult.error_result("Failed to start unified monitor daemon")
131
+
132
+ except KeyboardInterrupt:
133
+ print("\nUnified monitor daemon stopped by user")
134
+ return CommandResult.success_result("Unified monitor daemon stopped")
135
+ except Exception as e:
136
+ self.logger.error(f"Unified monitor daemon failed: {e}")
137
+ error_msg = f"Failed to start unified monitor daemon: {e}\n\n"
138
+ error_msg += "💡 Troubleshooting tips:\n"
139
+ error_msg += f" - Check if port {port} is already in use\n"
140
+ error_msg += " - Use --debug flag for more details\n"
141
+ error_msg += " - Check that all dependencies are installed\n"
142
+ return CommandResult.error_result(error_msg)
201
143
 
202
144
  def _stop_dashboard(self, args) -> CommandResult:
203
145
  """Stop the dashboard server."""
@@ -205,10 +147,10 @@ class DashboardCommand(BaseCommand):
205
147
 
206
148
  self.logger.info(f"Stopping dashboard on port {port}")
207
149
 
208
- if not self.dashboard_launcher.is_dashboard_running(port):
150
+ if not self.dashboard_manager.is_dashboard_running(port):
209
151
  return CommandResult.success_result(f"No dashboard running on port {port}")
210
152
 
211
- if self.dashboard_launcher.stop_dashboard(port):
153
+ if self.dashboard_manager.stop_dashboard(port):
212
154
  return CommandResult.success_result(f"Dashboard stopped on port {port}")
213
155
 
214
156
  return CommandResult.error_result(f"Failed to stop dashboard on port {port}")
@@ -220,7 +162,7 @@ class DashboardCommand(BaseCommand):
220
162
 
221
163
  # Check default port first
222
164
  default_port = 8765
223
- dashboard_running = self.dashboard_launcher.is_dashboard_running(default_port)
165
+ dashboard_running = self.dashboard_manager.is_dashboard_running(default_port)
224
166
 
225
167
  status_data = {
226
168
  "running": dashboard_running,
@@ -228,17 +170,17 @@ class DashboardCommand(BaseCommand):
228
170
  }
229
171
 
230
172
  if dashboard_running:
231
- status_data["url"] = self.dashboard_launcher.get_dashboard_url(default_port)
173
+ status_data["url"] = self.dashboard_manager.get_dashboard_url(default_port)
232
174
 
233
175
  # Check all ports if requested
234
176
  if show_ports:
235
177
  port_status = {}
236
178
  for port in range(8765, 8786):
237
- is_running = self.dashboard_launcher.is_dashboard_running(port)
179
+ is_running = self.dashboard_manager.is_dashboard_running(port)
238
180
  port_status[port] = {
239
181
  "running": is_running,
240
182
  "url": (
241
- self.dashboard_launcher.get_dashboard_url(port)
183
+ self.dashboard_manager.get_dashboard_url(port)
242
184
  if is_running
243
185
  else None
244
186
  ),
@@ -271,14 +213,14 @@ class DashboardCommand(BaseCommand):
271
213
  port = getattr(args, "port", 8765)
272
214
 
273
215
  # Check if dashboard is running
274
- if not self.dashboard_launcher.is_dashboard_running(port):
216
+ if not self.dashboard_manager.is_dashboard_running(port):
275
217
  self.logger.info("Dashboard not running, starting it first...")
276
218
  # Start dashboard in background
277
- success, browser_opened = self.dashboard_launcher.launch_dashboard(
278
- port=port, monitor_mode=True
219
+ success, browser_opened = self.dashboard_manager.start_dashboard(
220
+ port=port, background=True, open_browser=True
279
221
  )
280
222
  if success:
281
- dashboard_url = self.dashboard_launcher.get_dashboard_url(port)
223
+ dashboard_url = self.dashboard_manager.get_dashboard_url(port)
282
224
  return CommandResult.success_result(
283
225
  f"Dashboard started and opened at {dashboard_url}",
284
226
  data={
@@ -289,8 +231,8 @@ class DashboardCommand(BaseCommand):
289
231
  )
290
232
  return CommandResult.error_result("Failed to start and open dashboard")
291
233
  # Dashboard already running, just open browser
292
- dashboard_url = self.dashboard_launcher.get_dashboard_url(port)
293
- if self.dashboard_launcher._open_browser(dashboard_url):
234
+ dashboard_url = self.dashboard_manager.get_dashboard_url(port)
235
+ if self.dashboard_manager.open_browser(dashboard_url):
294
236
  return CommandResult.success_result(
295
237
  f"Opened dashboard at {dashboard_url}",
296
238
  data={"url": dashboard_url, "port": port},
@@ -1,36 +1,31 @@
1
1
  """
2
2
  Monitor command implementation for claude-mpm.
3
3
 
4
- WHY: This module provides CLI commands for managing the lightweight monitoring server,
5
- allowing users to start, stop, restart, and check status of the independent monitoring service.
6
- The monitor service runs as a stable background service on port 8766 (configurable).
4
+ WHY: This module provides CLI commands for managing the unified monitoring daemon,
5
+ providing a single stable way to launch all monitoring functionality including
6
+ HTTP dashboard, Socket.IO events, real AST analysis, and Claude Code hook ingestion.
7
7
 
8
8
  DESIGN DECISIONS:
9
- - Use independent MonitorServer for decoupled architecture
10
- - Monitor runs on port 8766, Dashboard runs on port 8765
11
- - Support background mode by default for stable always-on operation
12
- - Provide status checking and port configuration
13
- - Maintain minimal dependencies and resource usage
9
+ - Use UnifiedMonitorDaemon for single stable monitoring service
10
+ - Single port (8765) for all functionality
11
+ - Support both foreground and daemon modes
12
+ - Real AST analysis using CodeTreeAnalyzer
13
+ - Integrated dashboard and Socket.IO server
14
14
  """
15
15
 
16
- import signal
17
- import sys
18
- import time
19
16
  from typing import Optional
20
17
 
21
18
  from ...constants import MonitorCommands
22
- from ...services.port_manager import PortManager
23
- from ...services.socketio.monitor_server import MonitorServer
19
+ from ...services.monitor.daemon import UnifiedMonitorDaemon
24
20
  from ..shared import BaseCommand, CommandResult
25
21
 
26
22
 
27
23
  class MonitorCommand(BaseCommand):
28
- """Monitor command for managing the independent monitoring server."""
24
+ """Monitor command for managing the unified monitoring daemon."""
29
25
 
30
26
  def __init__(self):
31
27
  super().__init__("monitor")
32
- self.port_manager = PortManager()
33
- self.server = None
28
+ self.daemon = None
34
29
 
35
30
  def validate_args(self, args) -> Optional[str]:
36
31
  """Validate command arguments."""
@@ -43,9 +38,9 @@ class MonitorCommand(BaseCommand):
43
38
  return None
44
39
 
45
40
  def run(self, args) -> CommandResult:
46
- """Execute the monitor command using independent MonitorServer."""
41
+ """Execute the monitor command using unified monitoring daemon."""
47
42
  try:
48
- self.logger.info("Monitor command using independent monitoring server")
43
+ self.logger.info("Monitor command using unified monitoring daemon")
49
44
 
50
45
  # Handle default case (no subcommand) - default to status
51
46
  if not hasattr(args, "monitor_command") or not args.monitor_command:
@@ -60,8 +55,6 @@ class MonitorCommand(BaseCommand):
60
55
  return self._restart_monitor(args)
61
56
  if args.monitor_command == MonitorCommands.STATUS.value:
62
57
  return self._status_monitor(args)
63
- if args.monitor_command == MonitorCommands.PORT.value:
64
- return self._start_monitor_on_port(args)
65
58
 
66
59
  return CommandResult.error_result(
67
60
  f"Unknown monitor command: {args.monitor_command}"
@@ -72,229 +65,95 @@ class MonitorCommand(BaseCommand):
72
65
  return CommandResult.error_result(f"Error executing monitor command: {e}")
73
66
 
74
67
  def _start_monitor(self, args) -> CommandResult:
75
- """Start the monitor server."""
76
- port = getattr(args, "port", 8765) # Default to 8765 for monitor
68
+ """Start the unified monitor daemon."""
69
+ port = getattr(args, "port", None)
70
+ if port is None:
71
+ port = 8765 # Default to 8765 for unified monitor
77
72
  host = getattr(args, "host", "localhost")
78
- background = getattr(
79
- args, "background", True
80
- ) # Default to background for monitor
73
+ daemon_mode = getattr(args, "daemon", False) # Default to foreground
81
74
 
82
75
  self.logger.info(
83
- f"Starting monitor server on {host}:{port} (background: {background})"
76
+ f"Starting unified monitor daemon on {host}:{port} (daemon: {daemon_mode})"
84
77
  )
85
78
 
86
- # Check if monitor is already running
87
- if self._is_monitor_running(port):
79
+ # Create unified monitor daemon
80
+ self.daemon = UnifiedMonitorDaemon(
81
+ host=host, port=port, daemon_mode=daemon_mode
82
+ )
83
+
84
+ # Check if already running
85
+ if self.daemon.lifecycle.is_running():
86
+ existing_pid = self.daemon.lifecycle.get_pid()
88
87
  return CommandResult.success_result(
89
- f"Monitor server already running on {host}:{port}",
90
- data={"url": f"http://{host}:{port}", "port": port},
88
+ f"Unified monitor daemon already running with PID {existing_pid}",
89
+ data={
90
+ "url": f"http://{host}:{port}",
91
+ "port": port,
92
+ "pid": existing_pid,
93
+ },
91
94
  )
92
95
 
93
- if background:
94
- # Start monitor server in background
95
- try:
96
- self.server = MonitorServer(host=host, port=port)
97
- if self.server.start_sync():
98
- return CommandResult.success_result(
99
- f"Monitor server started on {host}:{port}",
100
- data={"url": f"http://{host}:{port}", "port": port},
101
- )
102
- return CommandResult.error_result("Failed to start monitor server")
103
- except Exception as e:
104
- return CommandResult.error_result(
105
- f"Failed to start monitor server: {e}"
106
- )
107
- else:
108
- # Run monitor in foreground mode
109
- try:
110
- print(f"Starting monitor server on {host}:{port}...")
111
- print("Press Ctrl+C to stop the server")
112
-
113
- self.server = MonitorServer(host=host, port=port)
114
-
115
- # Set up signal handlers for graceful shutdown
116
- def signal_handler(signum, frame):
117
- print("\nShutting down monitor server...")
118
- if self.server:
119
- self.server.stop_sync()
120
- sys.exit(0)
121
-
122
- signal.signal(signal.SIGINT, signal_handler)
123
- signal.signal(signal.SIGTERM, signal_handler)
124
-
125
- # Start the server
126
- if not self.server.start_sync():
127
- return CommandResult.error_result("Failed to start monitor server")
128
-
129
- # Keep the main thread alive while server is running
130
- try:
131
- while self.server.is_running():
132
- time.sleep(1)
133
- except KeyboardInterrupt:
134
- pass
135
-
136
- # Stop the server
137
- if self.server:
138
- self.server.stop_sync()
139
-
140
- return CommandResult.success_result("Monitor server stopped")
141
-
142
- except KeyboardInterrupt:
143
- print("\nMonitor server stopped by user")
144
- return CommandResult.success_result("Monitor server stopped")
145
- except Exception as e:
146
- return CommandResult.error_result(
147
- f"Failed to start monitor server: {e}"
148
- )
149
-
150
- def _stop_monitor(self, args) -> CommandResult:
151
- """Stop the monitor server."""
152
- port = getattr(args, "port", 8766)
153
-
154
- self.logger.info(f"Stopping monitor server on port {port}")
155
-
156
- if not self._is_monitor_running(port):
96
+ # Start the daemon
97
+ if self.daemon.start():
157
98
  return CommandResult.success_result(
158
- f"No monitor server running on port {port}"
99
+ f"Unified monitor daemon started on {host}:{port}",
100
+ data={"url": f"http://{host}:{port}", "port": port},
159
101
  )
102
+ return CommandResult.error_result("Failed to start unified monitor daemon")
160
103
 
161
- # Try to stop our server instance if we have one
162
- if self.server and self.server.is_running():
163
- try:
164
- self.server.stop_sync()
165
- return CommandResult.success_result(
166
- f"Monitor server stopped on port {port}"
167
- )
168
- except Exception as e:
169
- return CommandResult.error_result(f"Error stopping monitor server: {e}")
170
-
171
- # If we don't have a server instance, try port manager cleanup
172
- try:
173
- self.port_manager.cleanup_dead_instances()
174
- active_instances = self.port_manager.list_active_instances()
175
-
176
- # Look for instances on the target port
177
- for instance in active_instances:
178
- if (
179
- instance.get("port") == port
180
- and instance.get("service_type") == "monitor"
181
- ):
182
- # Found an instance, but we can't stop it directly
183
- # This would need to be implemented with a proper process manager
184
- return CommandResult.error_result(
185
- f"Monitor server found on port {port} but cannot be stopped "
186
- "(no direct control - you may need to kill the process manually)"
187
- )
104
+ def _stop_monitor(self, args) -> CommandResult:
105
+ """Stop the unified monitor daemon."""
106
+ self.logger.info("Stopping unified monitor daemon")
188
107
 
189
- return CommandResult.success_result(
190
- f"No monitor server found on port {port}"
191
- )
108
+ # Create daemon instance to check status and stop
109
+ daemon = UnifiedMonitorDaemon()
192
110
 
193
- except Exception as e:
194
- return CommandResult.error_result(
195
- f"Error checking monitor server status: {e}"
196
- )
111
+ if not daemon.lifecycle.is_running():
112
+ return CommandResult.success_result("No unified monitor daemon running")
113
+
114
+ # Stop the daemon
115
+ if daemon.stop():
116
+ return CommandResult.success_result("Unified monitor daemon stopped")
117
+ return CommandResult.error_result("Failed to stop unified monitor daemon")
197
118
 
198
119
  def _restart_monitor(self, args) -> CommandResult:
199
- """Restart the monitor server."""
200
- self.logger.info("Restarting monitor server")
201
-
202
- # Stop first
203
- stop_result = self._stop_monitor(args)
204
- if not stop_result.success:
205
- self.logger.warning(
206
- "Failed to stop monitor server for restart, proceeding anyway"
207
- )
120
+ """Restart the unified monitor daemon."""
121
+ self.logger.info("Restarting unified monitor daemon")
208
122
 
209
- # Wait a moment
210
- time.sleep(2)
123
+ # Create daemon instance
124
+ daemon = UnifiedMonitorDaemon()
211
125
 
212
- # Start again
213
- return self._start_monitor(args)
126
+ # Restart the daemon
127
+ if daemon.restart():
128
+ return CommandResult.success_result("Unified monitor daemon restarted")
129
+ return CommandResult.error_result("Failed to restart unified monitor daemon")
214
130
 
215
131
  def _status_monitor(self, args) -> CommandResult:
216
- """Get monitor server status."""
217
- port = getattr(args, "port", 8766)
132
+ """Get unified monitor daemon status."""
218
133
  verbose = getattr(args, "verbose", False)
219
- show_ports = getattr(args, "show_ports", False)
220
-
221
- # Check if monitor is running
222
- monitor_running = self._is_monitor_running(port)
223
-
224
- status_data = {
225
- "running": monitor_running,
226
- "default_port": port,
227
- "service_type": "monitor",
228
- }
229
-
230
- if monitor_running:
231
- status_data["url"] = f"http://localhost:{port}"
232
-
233
- # Check all ports if requested
234
- if show_ports:
235
- port_status = {}
236
- for check_port in range(8766, 8776): # Monitor port range
237
- is_running = self._is_monitor_running(check_port)
238
- port_status[check_port] = {
239
- "running": is_running,
240
- "url": f"http://localhost:{check_port}" if is_running else None,
241
- }
242
- status_data["ports"] = port_status
243
-
244
- # Get active instances from port manager
245
- self.port_manager.cleanup_dead_instances()
246
- active_instances = self.port_manager.list_active_instances()
247
- if active_instances:
248
- monitor_instances = [
249
- inst
250
- for inst in active_instances
251
- if inst.get("service_type") == "monitor"
252
- ]
253
- if monitor_instances:
254
- status_data["active_instances"] = monitor_instances
255
-
256
- if verbose and self.server:
257
- status_data["server_stats"] = self.server.get_stats()
134
+
135
+ # Create daemon instance to check status
136
+ daemon = UnifiedMonitorDaemon()
137
+ status_data = daemon.status()
258
138
 
259
139
  # Format output message
260
- if monitor_running:
261
- message = f"Monitor server is running at {status_data['url']}"
140
+ if status_data["running"]:
141
+ message = f"Unified monitor daemon is running at http://{status_data['host']}:{status_data['port']}"
142
+ if status_data.get("pid"):
143
+ message += f" (PID: {status_data['pid']})"
262
144
  else:
263
- message = "Monitor server is not running"
145
+ message = "Unified monitor daemon is not running"
264
146
 
265
147
  return CommandResult.success_result(message, data=status_data)
266
148
 
267
- def _start_monitor_on_port(self, args) -> CommandResult:
268
- """Start monitor server on specific port."""
269
- port = getattr(args, "port", 8766)
270
- self.logger.info(f"Starting monitor server on port {port}")
271
-
272
- # Ensure background mode for port-specific starts
273
- if not hasattr(args, "background"):
274
- args.background = True
275
-
276
- return self._start_monitor(args)
277
-
278
- def _is_monitor_running(self, port: int) -> bool:
279
- """Check if monitor server is running on given port."""
280
- import socket
281
-
282
- try:
283
- with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
284
- s.settimeout(1)
285
- result = s.connect_ex(("localhost", port))
286
- return result == 0
287
- except Exception:
288
- return False
289
-
290
149
 
291
150
  def manage_monitor(args):
292
151
  """
293
152
  Main entry point for monitor command.
294
153
 
295
- The monitor command manages an independent lightweight monitoring server on port 8766.
296
- This server runs separately from the dashboard (port 8765) and provides stable
297
- event collection and relay services.
154
+ The monitor command manages the unified monitoring daemon that provides
155
+ a single stable way to launch all monitoring functionality including
156
+ HTTP dashboard, Socket.IO events, real AST analysis, and Claude Code hooks.
298
157
  """
299
158
  command = MonitorCommand()
300
159
  error = command.validate_args(args)