claude-mpm 5.6.19__py3-none-any.whl → 5.6.22__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 (55) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/cli/commands/commander.py +1 -1
  3. claude_mpm/cli/parsers/commander_parser.py +2 -2
  4. claude_mpm/commander/chat/cli.py +38 -3
  5. claude_mpm/commander/config.py +5 -3
  6. claude_mpm/commander/daemon.py +9 -0
  7. claude_mpm/commander/frameworks/base.py +4 -1
  8. claude_mpm/commander/instance_manager.py +124 -11
  9. claude_mpm/core/config.py +3 -3
  10. claude_mpm/core/config_constants.py +74 -9
  11. claude_mpm/core/constants.py +56 -12
  12. claude_mpm/core/network_config.py +148 -0
  13. claude_mpm/core/socketio_pool.py +13 -5
  14. claude_mpm/hooks/claude_hooks/auto_pause_handler.py +0 -0
  15. claude_mpm/hooks/claude_hooks/event_handlers.py +0 -0
  16. claude_mpm/hooks/claude_hooks/hook_handler.py +0 -0
  17. claude_mpm/hooks/claude_hooks/memory_integration.py +0 -0
  18. claude_mpm/hooks/claude_hooks/response_tracking.py +0 -0
  19. claude_mpm/hooks/templates/pre_tool_use_template.py +0 -0
  20. claude_mpm/scripts/start_activity_logging.py +0 -0
  21. claude_mpm/services/pm_skills_deployer.py +3 -2
  22. {claude_mpm-5.6.19.dist-info → claude_mpm-5.6.22.dist-info}/METADATA +1 -1
  23. {claude_mpm-5.6.19.dist-info → claude_mpm-5.6.22.dist-info}/RECORD +21 -47
  24. claude_mpm/hooks/claude_hooks/__pycache__/__init__.cpython-312.pyc +0 -0
  25. claude_mpm/hooks/claude_hooks/__pycache__/__init__.cpython-314.pyc +0 -0
  26. claude_mpm/hooks/claude_hooks/__pycache__/auto_pause_handler.cpython-312.pyc +0 -0
  27. claude_mpm/hooks/claude_hooks/__pycache__/auto_pause_handler.cpython-314.pyc +0 -0
  28. claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-312.pyc +0 -0
  29. claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-314.pyc +0 -0
  30. claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-312.pyc +0 -0
  31. claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-314.pyc +0 -0
  32. claude_mpm/hooks/claude_hooks/__pycache__/installer.cpython-311.pyc +0 -0
  33. claude_mpm/hooks/claude_hooks/__pycache__/installer.cpython-314.pyc +0 -0
  34. claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-312.pyc +0 -0
  35. claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-314.pyc +0 -0
  36. claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-312.pyc +0 -0
  37. claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-314.pyc +0 -0
  38. claude_mpm/hooks/claude_hooks/__pycache__/tool_analysis.cpython-312.pyc +0 -0
  39. claude_mpm/hooks/claude_hooks/__pycache__/tool_analysis.cpython-314.pyc +0 -0
  40. claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-312.pyc +0 -0
  41. claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-314.pyc +0 -0
  42. claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager.cpython-311.pyc +0 -0
  43. claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-312.pyc +0 -0
  44. claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-314.pyc +0 -0
  45. claude_mpm/hooks/claude_hooks/services/__pycache__/duplicate_detector.cpython-312.pyc +0 -0
  46. claude_mpm/hooks/claude_hooks/services/__pycache__/duplicate_detector.cpython-314.pyc +0 -0
  47. claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-312.pyc +0 -0
  48. claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-314.pyc +0 -0
  49. claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-312.pyc +0 -0
  50. claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-314.pyc +0 -0
  51. {claude_mpm-5.6.19.dist-info → claude_mpm-5.6.22.dist-info}/WHEEL +0 -0
  52. {claude_mpm-5.6.19.dist-info → claude_mpm-5.6.22.dist-info}/entry_points.txt +0 -0
  53. {claude_mpm-5.6.19.dist-info → claude_mpm-5.6.22.dist-info}/licenses/LICENSE +0 -0
  54. {claude_mpm-5.6.19.dist-info → claude_mpm-5.6.22.dist-info}/licenses/LICENSE-FAQ.md +0 -0
  55. {claude_mpm-5.6.19.dist-info → claude_mpm-5.6.22.dist-info}/top_level.txt +0 -0
claude_mpm/VERSION CHANGED
@@ -1 +1 @@
1
- 5.6.19
1
+ 5.6.22
@@ -142,7 +142,7 @@ def handle_commander_command(args) -> int:
142
142
  print() # Blank line after loading
143
143
 
144
144
  # Get arguments
145
- port = getattr(args, "port", 8765)
145
+ port = getattr(args, "port", 8766) # NetworkPorts.COMMANDER_DEFAULT
146
146
  host = getattr(args, "host", "127.0.0.1")
147
147
  state_dir = getattr(args, "state_dir", None)
148
148
  no_chat = getattr(args, "no_chat", False) or getattr(args, "daemon_only", False)
@@ -77,8 +77,8 @@ Examples:
77
77
  commander_parser.add_argument(
78
78
  "--port",
79
79
  type=int,
80
- default=8765,
81
- help="Port for internal services (default: 8765)",
80
+ default=8766, # NetworkPorts.COMMANDER_DEFAULT
81
+ help="Port for internal services (default: 8766)",
82
82
  )
83
83
 
84
84
  # Optional: State directory
@@ -2,6 +2,7 @@
2
2
 
3
3
  import asyncio
4
4
  import logging
5
+ from dataclasses import dataclass
5
6
  from pathlib import Path
6
7
  from typing import Optional
7
8
 
@@ -26,20 +27,47 @@ load_env()
26
27
  logger = logging.getLogger(__name__)
27
28
 
28
29
 
30
+ @dataclass
31
+ class CommanderCLIConfig:
32
+ """Configuration for Commander CLI mode.
33
+
34
+ Attributes:
35
+ summarize_responses: Whether to use LLM to summarize instance responses
36
+ port: Port for internal services (reserved for future use)
37
+ state_dir: Directory for state persistence (optional)
38
+
39
+ Example:
40
+ >>> config = CommanderCLIConfig(summarize_responses=False)
41
+ """
42
+
43
+ summarize_responses: bool = True
44
+ port: int = 8765
45
+ state_dir: Optional[Path] = None
46
+
47
+
29
48
  async def run_commander(
30
49
  port: int = 8765,
31
50
  state_dir: Optional[Path] = None,
51
+ config: Optional[CommanderCLIConfig] = None,
32
52
  ) -> None:
33
53
  """Run Commander in interactive mode.
34
54
 
35
55
  Args:
36
56
  port: Port for internal services (unused currently).
37
57
  state_dir: Directory for state persistence (optional).
58
+ config: Commander CLI configuration (optional, uses defaults if None).
38
59
 
39
60
  Example:
40
61
  >>> asyncio.run(run_commander())
41
62
  # Starts interactive Commander REPL
63
+ >>> config = CommanderCLIConfig(summarize_responses=False)
64
+ >>> asyncio.run(run_commander(config=config))
65
+ # Starts Commander without response summarization
42
66
  """
67
+ # Use default config if not provided
68
+ if config is None:
69
+ config = CommanderCLIConfig(port=port, state_dir=state_dir)
70
+
43
71
  # Setup logging
44
72
  logging.basicConfig(
45
73
  level=logging.INFO,
@@ -61,8 +89,8 @@ async def run_commander(
61
89
  # Try to initialize LLM client (optional)
62
90
  llm_client: Optional[OpenRouterClient] = None
63
91
  try:
64
- config = OpenRouterConfig()
65
- llm_client = OpenRouterClient(config)
92
+ llm_config = OpenRouterConfig()
93
+ llm_client = OpenRouterClient(llm_config)
66
94
  logger.info("LLM client initialized")
67
95
  except ValueError as e:
68
96
  logger.warning(f"LLM client not available: {e}")
@@ -72,7 +100,14 @@ async def run_commander(
72
100
  output_relay: Optional[OutputRelay] = None
73
101
  if llm_client:
74
102
  try:
75
- summarizer = OutputSummarizer(llm_client)
103
+ # Only create summarizer if summarize_responses is enabled
104
+ summarizer = None
105
+ if config.summarize_responses:
106
+ summarizer = OutputSummarizer(llm_client)
107
+ logger.info("Response summarization enabled")
108
+ else:
109
+ logger.info("Response summarization disabled")
110
+
76
111
  handler = OutputHandler(orchestrator, summarizer)
77
112
  formatter = OutputFormatter()
78
113
  output_relay = OutputRelay(handler, formatter)
@@ -14,28 +14,30 @@ class DaemonConfig:
14
14
 
15
15
  Attributes:
16
16
  host: API server bind address
17
- port: API server port
17
+ port: API server port (default: 8766 from NetworkPorts.COMMANDER_DEFAULT)
18
18
  log_level: Logging level (DEBUG, INFO, WARNING, ERROR)
19
19
  state_dir: Directory for state persistence
20
20
  max_projects: Maximum concurrent projects
21
21
  healthcheck_interval: Healthcheck interval in seconds
22
22
  save_interval: State persistence interval in seconds
23
23
  poll_interval: Event polling interval in seconds
24
+ summarize_responses: Whether to use LLM to summarize instance responses
24
25
 
25
26
  Example:
26
- >>> config = DaemonConfig(port=8765, log_level="DEBUG")
27
+ >>> config = DaemonConfig(port=8766, log_level="DEBUG")
27
28
  >>> config.state_dir
28
29
  PosixPath('/Users/user/.claude-mpm/commander')
29
30
  """
30
31
 
31
32
  host: str = "127.0.0.1"
32
- port: int = 8765
33
+ port: int = 8766 # Default commander port (from network_config.NetworkPorts.COMMANDER_DEFAULT)
33
34
  log_level: str = "INFO"
34
35
  state_dir: Path = Path.home() / ".claude-mpm" / "commander"
35
36
  max_projects: int = 10
36
37
  healthcheck_interval: int = 30
37
38
  save_interval: int = 30
38
39
  poll_interval: float = 2.0
40
+ summarize_responses: bool = True
39
41
 
40
42
  def __post_init__(self):
41
43
  """Ensure state_dir is a Path object and create if needed."""
@@ -307,7 +307,16 @@ class CommanderDaemon:
307
307
 
308
308
  Registers handlers for SIGINT and SIGTERM that trigger
309
309
  daemon shutdown via asyncio event loop.
310
+
311
+ Note: Signal handlers can only be registered from the main thread.
312
+ If called from a background thread, registration is skipped.
310
313
  """
314
+ import threading
315
+
316
+ # Signal handlers can only be registered from the main thread
317
+ if threading.current_thread() is not threading.main_thread():
318
+ logger.info("Running in background thread - signal handlers skipped")
319
+ return
311
320
 
312
321
  def handle_signal(signum: int, frame) -> None:
313
322
  """Handle shutdown signal.
@@ -19,6 +19,7 @@ class InstanceInfo:
19
19
  pane_target: Tmux pane target (e.g., "%1")
20
20
  git_branch: Current git branch if project is a git repo
21
21
  git_status: Git status summary if project is a git repo
22
+ connected: Whether instance has an active adapter connection
22
23
 
23
24
  Example:
24
25
  >>> info = InstanceInfo(
@@ -28,7 +29,8 @@ class InstanceInfo:
28
29
  ... tmux_session="mpm-commander",
29
30
  ... pane_target="%1",
30
31
  ... git_branch="main",
31
- ... git_status="clean"
32
+ ... git_status="clean",
33
+ ... connected=True
32
34
  ... )
33
35
  """
34
36
 
@@ -39,6 +41,7 @@ class InstanceInfo:
39
41
  pane_target: str
40
42
  git_branch: Optional[str] = None
41
43
  git_status: Optional[str] = None
44
+ connected: bool = False
42
45
 
43
46
 
44
47
  class BaseFramework(ABC):
@@ -167,6 +167,20 @@ class InstanceManager:
167
167
  startup_cmd = framework_obj.get_startup_command(project_path)
168
168
  self.orchestrator.send_keys(pane_target, startup_cmd)
169
169
 
170
+ # Create communication adapter for the instance (only for Claude Code for now)
171
+ # Do this BEFORE creating InstanceInfo so we can set connected=True
172
+ has_adapter = False
173
+ if framework == "cc":
174
+ runtime_adapter = ClaudeCodeAdapter()
175
+ comm_adapter = ClaudeCodeCommunicationAdapter(
176
+ orchestrator=self.orchestrator,
177
+ pane_target=pane_target,
178
+ runtime_adapter=runtime_adapter,
179
+ )
180
+ self._adapters[name] = comm_adapter
181
+ has_adapter = True
182
+ logger.debug(f"Created communication adapter for instance '{name}'")
183
+
170
184
  # Create instance info
171
185
  instance = InstanceInfo(
172
186
  name=name,
@@ -176,22 +190,12 @@ class InstanceManager:
176
190
  pane_target=pane_target,
177
191
  git_branch=git_branch,
178
192
  git_status=git_status,
193
+ connected=has_adapter,
179
194
  )
180
195
 
181
196
  # Track instance
182
197
  self._instances[name] = instance
183
198
 
184
- # Create communication adapter for the instance (only for Claude Code for now)
185
- if framework == "cc":
186
- runtime_adapter = ClaudeCodeAdapter()
187
- comm_adapter = ClaudeCodeCommunicationAdapter(
188
- orchestrator=self.orchestrator,
189
- pane_target=pane_target,
190
- runtime_adapter=runtime_adapter,
191
- )
192
- self._adapters[name] = comm_adapter
193
- logger.debug(f"Created communication adapter for instance '{name}'")
194
-
195
199
  logger.info(
196
200
  f"Started instance '{name}' with framework '{framework}' at {project_path}"
197
201
  )
@@ -226,6 +230,7 @@ class InstanceManager:
226
230
  # Remove adapter if exists
227
231
  if name in self._adapters:
228
232
  del self._adapters[name]
233
+ instance.connected = False
229
234
  logger.debug(f"Removed adapter for instance '{name}'")
230
235
 
231
236
  # Remove from tracking
@@ -335,3 +340,111 @@ class InstanceManager:
335
340
  ... print(chunk, end='')
336
341
  """
337
342
  return self._adapters.get(name)
343
+
344
+ async def rename_instance(self, old_name: str, new_name: str) -> bool:
345
+ """Rename an instance.
346
+
347
+ Args:
348
+ old_name: Current instance name
349
+ new_name: New instance name
350
+
351
+ Returns:
352
+ True if renamed successfully
353
+
354
+ Raises:
355
+ InstanceNotFoundError: If old_name doesn't exist
356
+ InstanceAlreadyExistsError: If new_name already exists
357
+
358
+ Example:
359
+ >>> manager = InstanceManager(orchestrator)
360
+ >>> await manager.rename_instance("myapp", "myapp-v2")
361
+ True
362
+ """
363
+ # Validate old_name exists
364
+ if old_name not in self._instances:
365
+ raise InstanceNotFoundError(old_name)
366
+
367
+ # Validate new_name doesn't exist
368
+ if new_name in self._instances:
369
+ raise InstanceAlreadyExistsError(new_name)
370
+
371
+ # Get instance and update name
372
+ instance = self._instances[old_name]
373
+ instance.name = new_name
374
+
375
+ # Update _instances dict (remove old key, add new)
376
+ del self._instances[old_name]
377
+ self._instances[new_name] = instance
378
+
379
+ # Update _adapters dict if exists
380
+ if old_name in self._adapters:
381
+ adapter = self._adapters[old_name]
382
+ del self._adapters[old_name]
383
+ self._adapters[new_name] = adapter
384
+ logger.debug(f"Moved adapter from '{old_name}' to '{new_name}'")
385
+
386
+ logger.info(f"Renamed instance from '{old_name}' to '{new_name}'")
387
+
388
+ return True
389
+
390
+ async def close_instance(self, name: str) -> bool:
391
+ """Close and remove an instance.
392
+
393
+ Alias for stop_instance that provides clearer semantics for closing.
394
+
395
+ Args:
396
+ name: Instance name to close
397
+
398
+ Returns:
399
+ True if closed successfully
400
+
401
+ Raises:
402
+ InstanceNotFoundError: If instance not found
403
+
404
+ Example:
405
+ >>> manager = InstanceManager(orchestrator)
406
+ >>> await manager.close_instance("myapp")
407
+ True
408
+ """
409
+ return await self.stop_instance(name)
410
+
411
+ async def disconnect_instance(self, name: str) -> bool:
412
+ """Disconnect from an instance without closing it.
413
+
414
+ The instance keeps running but we stop communication.
415
+ Removes the adapter while keeping the instance tracked.
416
+
417
+ Args:
418
+ name: Instance name to disconnect from
419
+
420
+ Returns:
421
+ True if disconnected successfully
422
+
423
+ Raises:
424
+ InstanceNotFoundError: If instance not found
425
+
426
+ Example:
427
+ >>> manager = InstanceManager(orchestrator)
428
+ >>> await manager.disconnect_instance("myapp")
429
+ True
430
+ >>> # Instance still running, but no adapter connection
431
+ >>> adapter = manager.get_adapter("myapp")
432
+ >>> print(adapter)
433
+ None
434
+ """
435
+ # Validate instance exists
436
+ if name not in self._instances:
437
+ raise InstanceNotFoundError(name)
438
+
439
+ instance = self._instances[name]
440
+
441
+ # Remove adapter if exists (but keep instance)
442
+ if name in self._adapters:
443
+ # Could add cleanup here if adapter has resources to close
444
+ del self._adapters[name]
445
+ instance.connected = False
446
+ logger.info(f"Disconnected from instance '{name}' (instance still running)")
447
+ else:
448
+ logger.debug(f"No adapter to disconnect for instance '{name}'")
449
+
450
+ return True
claude_mpm/core/config.py CHANGED
@@ -499,7 +499,7 @@ class Config:
499
499
  # Socket.IO server health and recovery configuration
500
500
  "socketio_server": {
501
501
  "host": "localhost",
502
- "port": 8765,
502
+ "port": 8768, # Default SocketIO port (from network_config.NetworkPorts)
503
503
  "enable_health_monitoring": True,
504
504
  "enable_recovery": True,
505
505
  "health_monitoring": {
@@ -540,7 +540,7 @@ class Config:
540
540
  # Monitor server configuration (decoupled from dashboard)
541
541
  "monitor_server": {
542
542
  "host": "localhost",
543
- "port": 8765, # Default monitor port (shared with dashboard)
543
+ "port": 8765, # Default monitor port (from network_config.NetworkPorts.MONITOR_DEFAULT)
544
544
  "enable_health_monitoring": True,
545
545
  "auto_start": False, # Don't auto-start with dashboard by default
546
546
  "event_buffer_size": 2000, # Larger buffer for monitor server
@@ -549,7 +549,7 @@ class Config:
549
549
  # Dashboard server configuration (connects to monitor)
550
550
  "dashboard_server": {
551
551
  "host": "localhost",
552
- "port": 8765, # Dashboard UI port
552
+ "port": 8767, # Dashboard UI port (from network_config.NetworkPorts.DASHBOARD_DEFAULT)
553
553
  "monitor_host": "localhost", # Monitor server host to connect to
554
554
  "monitor_port": 8765, # Monitor server port to connect to
555
555
  "auto_connect_monitor": True, # Automatically connect to monitor
@@ -44,11 +44,14 @@ class ConfigConstants:
44
44
  "startup": 60,
45
45
  "graceful_shutdown": 30,
46
46
  },
47
- # Ports
47
+ # Ports (updated to use network_config.NetworkPorts defaults)
48
48
  "ports": {
49
- "socketio_default": 8765,
50
- "socketio_range_start": 8765,
51
- "socketio_range_end": 8775,
49
+ "monitor_default": 8765, # NetworkPorts.MONITOR_DEFAULT
50
+ "commander_default": 8766, # NetworkPorts.COMMANDER_DEFAULT
51
+ "dashboard_default": 8767, # NetworkPorts.DASHBOARD_DEFAULT
52
+ "socketio_default": 8768, # NetworkPorts.SOCKETIO_DEFAULT
53
+ "socketio_range_start": 8765, # NetworkPorts.PORT_RANGE_START
54
+ "socketio_range_end": 8785, # NetworkPorts.PORT_RANGE_END
52
55
  },
53
56
  # Cache settings
54
57
  "cache": {
@@ -134,23 +137,70 @@ class ConfigConstants:
134
137
  Get port value by type.
135
138
 
136
139
  Args:
137
- port_type: Type of port (e.g., 'socketio_default')
140
+ port_type: Type of port (e.g., 'socketio_default', 'monitor_default')
138
141
 
139
142
  Returns:
140
143
  Port number
141
144
  """
142
145
  try:
146
+ # Try to get from unified config first
143
147
  config = cls._get_config_service().config
144
148
 
149
+ if port_type == "monitor_default":
150
+ return (
151
+ config.network.monitor_port
152
+ if hasattr(config.network, "monitor_port")
153
+ else 8765
154
+ )
155
+ if port_type == "commander_default":
156
+ return (
157
+ config.network.commander_port
158
+ if hasattr(config.network, "commander_port")
159
+ else 8766
160
+ )
161
+ if port_type == "dashboard_default":
162
+ return (
163
+ config.network.dashboard_port
164
+ if hasattr(config.network, "dashboard_port")
165
+ else 8767
166
+ )
145
167
  if port_type == "socketio_default":
146
- return config.network.socketio_port
168
+ return (
169
+ config.network.socketio_port
170
+ if hasattr(config.network, "socketio_port")
171
+ else 8768
172
+ )
147
173
  if port_type == "socketio_range_start":
148
- return config.network.socketio_port_range[0]
174
+ return (
175
+ config.network.socketio_port_range[0]
176
+ if hasattr(config.network, "socketio_port_range")
177
+ else 8765
178
+ )
149
179
  if port_type == "socketio_range_end":
150
- return config.network.socketio_port_range[1]
180
+ return (
181
+ config.network.socketio_port_range[1]
182
+ if hasattr(config.network, "socketio_port_range")
183
+ else 8785
184
+ )
151
185
  return cls.DEFAULT_VALUES["ports"].get(port_type, 8765)
152
186
  except Exception:
153
- return cls.DEFAULT_VALUES["ports"].get(port_type, 8765)
187
+ # Fallback to network_config.NetworkPorts or DEFAULT_VALUES
188
+ try:
189
+ from .network_config import NetworkPorts
190
+
191
+ port_map = {
192
+ "monitor_default": NetworkPorts.MONITOR_DEFAULT,
193
+ "commander_default": NetworkPorts.COMMANDER_DEFAULT,
194
+ "dashboard_default": NetworkPorts.DASHBOARD_DEFAULT,
195
+ "socketio_default": NetworkPorts.SOCKETIO_DEFAULT,
196
+ "socketio_range_start": NetworkPorts.PORT_RANGE_START,
197
+ "socketio_range_end": NetworkPorts.PORT_RANGE_END,
198
+ }
199
+ return port_map.get(
200
+ port_type, cls.DEFAULT_VALUES["ports"].get(port_type, 8765)
201
+ )
202
+ except Exception:
203
+ return cls.DEFAULT_VALUES["ports"].get(port_type, 8765)
154
204
 
155
205
  @classmethod
156
206
  def get_cache_setting(cls, setting_name: str) -> Any:
@@ -304,6 +354,21 @@ def get_socketio_port() -> int:
304
354
  return ConfigConstants.get_port("socketio_default")
305
355
 
306
356
 
357
+ def get_monitor_port() -> int:
358
+ """Get default monitor port."""
359
+ return ConfigConstants.get_port("monitor_default")
360
+
361
+
362
+ def get_commander_port() -> int:
363
+ """Get default commander port."""
364
+ return ConfigConstants.get_port("commander_default")
365
+
366
+
367
+ def get_dashboard_port() -> int:
368
+ """Get default dashboard port."""
369
+ return ConfigConstants.get_port("dashboard_default")
370
+
371
+
307
372
  def get_cache_size() -> float:
308
373
  """Get default cache size in MB."""
309
374
  return ConfigConstants.get_cache_setting("max_size_mb")
@@ -38,12 +38,36 @@ class SystemLimits:
38
38
 
39
39
 
40
40
  class NetworkConfig:
41
- """Network-related configuration constants."""
41
+ """Network-related configuration constants.
42
42
 
43
- # Port ranges
44
- SOCKETIO_PORT_RANGE: Tuple[int, int] = (8765, 8785)
45
- DEFAULT_SOCKETIO_PORT = 8765
46
- DEFAULT_DASHBOARD_PORT = 8765
43
+ NOTE: Port defaults are now centralized in network_config.NetworkPorts.
44
+ This class maintains backward compatibility but delegates to NetworkPorts.
45
+ """
46
+
47
+ # Import from network_config for single source of truth
48
+ # Lazy import to avoid circular dependencies
49
+ @property
50
+ def SOCKETIO_PORT_RANGE(self) -> Tuple[int, int]:
51
+ from .network_config import NetworkPorts
52
+
53
+ return (NetworkPorts.PORT_RANGE_START, NetworkPorts.PORT_RANGE_END)
54
+
55
+ @property
56
+ def DEFAULT_SOCKETIO_PORT(self) -> int:
57
+ from .network_config import NetworkPorts
58
+
59
+ return NetworkPorts.SOCKETIO_DEFAULT
60
+
61
+ @property
62
+ def DEFAULT_DASHBOARD_PORT(self) -> int:
63
+ from .network_config import NetworkPorts
64
+
65
+ return NetworkPorts.DASHBOARD_DEFAULT
66
+
67
+ # Port ranges (module-level for backward compatibility)
68
+ SOCKETIO_PORT_RANGE: Tuple[int, int] = (8765, 8785) # Will be updated at runtime
69
+ DEFAULT_SOCKETIO_PORT = 8768 # Updated to match new default
70
+ DEFAULT_DASHBOARD_PORT = 8767 # Updated to match new default
47
71
 
48
72
  # Connection timeouts (seconds)
49
73
  CONNECTION_TIMEOUT = 5.0
@@ -303,18 +327,38 @@ DEFAULT_TIMEOUT = TimeoutConfig.DEFAULT_TIMEOUT
303
327
 
304
328
 
305
329
  class NetworkPorts:
306
- """Network port configuration."""
330
+ """Network port configuration.
331
+
332
+ DEPRECATED: Use claude_mpm.core.network_config.NetworkPorts instead.
333
+ This class is maintained for backward compatibility.
334
+ """
335
+
336
+ # Import from network_config for single source of truth
337
+ @classmethod
338
+ def _get_config(cls):
339
+ from .network_config import NetworkPorts as NewNetworkPorts
340
+
341
+ return NewNetworkPorts
342
+
343
+ # Delegate to new NetworkPorts
344
+ @property
345
+ def DEFAULT_SOCKETIO(self) -> int:
346
+ return self._get_config().SOCKETIO_DEFAULT
347
+
348
+ @property
349
+ def DEFAULT_DASHBOARD(self) -> int:
350
+ return self._get_config().DASHBOARD_DEFAULT
307
351
 
308
- # Use existing values from NetworkConfig
309
- DEFAULT_SOCKETIO = NetworkConfig.DEFAULT_SOCKETIO_PORT
310
- DEFAULT_DASHBOARD = NetworkConfig.DEFAULT_DASHBOARD_PORT
311
- PORT_RANGE_START = NetworkConfig.SOCKETIO_PORT_RANGE[0]
312
- PORT_RANGE_END = NetworkConfig.SOCKETIO_PORT_RANGE[1]
352
+ # Keep class-level attributes for compatibility
353
+ DEFAULT_SOCKETIO = 8768 # Updated to match network_config
354
+ DEFAULT_DASHBOARD = 8767 # Updated to match network_config
355
+ PORT_RANGE_START = 8765
356
+ PORT_RANGE_END = 8785
313
357
 
314
358
  @classmethod
315
359
  def get_port_range(cls) -> range:
316
360
  """Get the valid port range."""
317
- return range(cls.PORT_RANGE_START, cls.PORT_RANGE_END + 1)
361
+ return cls._get_config().get_port_range()
318
362
 
319
363
 
320
364
  class ProjectPaths:
@@ -0,0 +1,148 @@
1
+ """Centralized network port configuration for Claude MPM.
2
+
3
+ This module provides the single source of truth for all network port defaults
4
+ and environment variable names used throughout the MPM system.
5
+
6
+ WHY: Previously, port defaults were hardcoded in multiple locations (config.py,
7
+ constants.py, commander/config.py, CLI parsers), leading to inconsistencies and
8
+ difficulty maintaining different defaults per service.
9
+
10
+ USAGE:
11
+ from claude_mpm.core.network_config import NetworkPorts
12
+
13
+ # Get default ports
14
+ monitor_port = NetworkPorts.MONITOR_DEFAULT
15
+ commander_port = NetworkPorts.COMMANDER_DEFAULT
16
+
17
+ # Get from environment with fallback
18
+ port = NetworkPorts.get_monitor_port()
19
+ """
20
+
21
+ import os
22
+ from typing import Optional
23
+
24
+
25
+ class NetworkPorts:
26
+ """Network port configuration with different defaults for each service.
27
+
28
+ Service Default Ports:
29
+ - Monitor: 8765 (user's preferred default)
30
+ - Commander: 8766
31
+ - Dashboard: 8767
32
+ - SocketIO: 8768
33
+
34
+ Port Range: 8765-8785 (21 ports available)
35
+
36
+ Environment Variables:
37
+ - CLAUDE_MPM_MONITOR_PORT: Override monitor port
38
+ - CLAUDE_MPM_COMMANDER_PORT: Override commander port
39
+ - CLAUDE_MPM_DASHBOARD_PORT: Override dashboard port
40
+ - CLAUDE_MPM_SOCKETIO_PORT: Override socketio port
41
+ - CLAUDE_MPM_DEFAULT_HOST: Override default host (default: 127.0.0.1)
42
+ """
43
+
44
+ # Default ports for each service
45
+ MONITOR_DEFAULT = 8765
46
+ COMMANDER_DEFAULT = 8766
47
+ DASHBOARD_DEFAULT = 8767
48
+ SOCKETIO_DEFAULT = 8768
49
+
50
+ # Port range configuration
51
+ PORT_RANGE_START = 8765
52
+ PORT_RANGE_END = 8785
53
+
54
+ # Default host
55
+ DEFAULT_HOST = "127.0.0.1"
56
+
57
+ # Environment variable names
58
+ ENV_MONITOR_PORT = "CLAUDE_MPM_MONITOR_PORT"
59
+ ENV_COMMANDER_PORT = "CLAUDE_MPM_COMMANDER_PORT"
60
+ ENV_DASHBOARD_PORT = "CLAUDE_MPM_DASHBOARD_PORT"
61
+ ENV_SOCKETIO_PORT = "CLAUDE_MPM_SOCKETIO_PORT"
62
+ ENV_DEFAULT_HOST = "CLAUDE_MPM_DEFAULT_HOST"
63
+
64
+ @classmethod
65
+ def get_monitor_port(cls, default: Optional[int] = None) -> int:
66
+ """Get monitor port from environment or default.
67
+
68
+ Args:
69
+ default: Optional override default (if not provided, uses MONITOR_DEFAULT)
70
+
71
+ Returns:
72
+ Port number from environment or default
73
+ """
74
+ if default is None:
75
+ default = cls.MONITOR_DEFAULT
76
+ return int(os.getenv(cls.ENV_MONITOR_PORT, default))
77
+
78
+ @classmethod
79
+ def get_commander_port(cls, default: Optional[int] = None) -> int:
80
+ """Get commander port from environment or default.
81
+
82
+ Args:
83
+ default: Optional override default (if not provided, uses COMMANDER_DEFAULT)
84
+
85
+ Returns:
86
+ Port number from environment or default
87
+ """
88
+ if default is None:
89
+ default = cls.COMMANDER_DEFAULT
90
+ return int(os.getenv(cls.ENV_COMMANDER_PORT, default))
91
+
92
+ @classmethod
93
+ def get_dashboard_port(cls, default: Optional[int] = None) -> int:
94
+ """Get dashboard port from environment or default.
95
+
96
+ Args:
97
+ default: Optional override default (if not provided, uses DASHBOARD_DEFAULT)
98
+
99
+ Returns:
100
+ Port number from environment or default
101
+ """
102
+ if default is None:
103
+ default = cls.DASHBOARD_DEFAULT
104
+ return int(os.getenv(cls.ENV_DASHBOARD_PORT, default))
105
+
106
+ @classmethod
107
+ def get_socketio_port(cls, default: Optional[int] = None) -> int:
108
+ """Get socketio port from environment or default.
109
+
110
+ Args:
111
+ default: Optional override default (if not provided, uses SOCKETIO_DEFAULT)
112
+
113
+ Returns:
114
+ Port number from environment or default
115
+ """
116
+ if default is None:
117
+ default = cls.SOCKETIO_DEFAULT
118
+ return int(os.getenv(cls.ENV_SOCKETIO_PORT, default))
119
+
120
+ @classmethod
121
+ def get_default_host(cls) -> str:
122
+ """Get default host from environment or default.
123
+
124
+ Returns:
125
+ Host address from environment or DEFAULT_HOST
126
+ """
127
+ return os.getenv(cls.ENV_DEFAULT_HOST, cls.DEFAULT_HOST)
128
+
129
+ @classmethod
130
+ def get_port_range(cls) -> range:
131
+ """Get the valid port range.
132
+
133
+ Returns:
134
+ Range object from PORT_RANGE_START to PORT_RANGE_END (inclusive)
135
+ """
136
+ return range(cls.PORT_RANGE_START, cls.PORT_RANGE_END + 1)
137
+
138
+ @classmethod
139
+ def is_port_in_range(cls, port: int) -> bool:
140
+ """Check if port is within valid range.
141
+
142
+ Args:
143
+ port: Port number to check
144
+
145
+ Returns:
146
+ True if port is in valid range, False otherwise
147
+ """
148
+ return cls.PORT_RANGE_START <= port <= cls.PORT_RANGE_END
@@ -1,4 +1,3 @@
1
- #!/usr/bin/env python3
2
1
  """Socket.IO connection pool for efficient client connection management.
3
2
 
4
3
  This module provides a connection pool to reuse Socket.IO client connections,
@@ -31,12 +30,21 @@ except ImportError:
31
30
  # Import constants for configuration
32
31
  try:
33
32
  from claude_mpm.core.constants import NetworkConfig
33
+ from claude_mpm.core.network_config import NetworkPorts
34
34
  except ImportError:
35
35
  # Fallback if constants module not available
36
+ class NetworkPorts:
37
+ MONITOR_DEFAULT = 8765
38
+ COMMANDER_DEFAULT = 8766
39
+ DASHBOARD_DEFAULT = 8767
40
+ SOCKETIO_DEFAULT = 8768
41
+ PORT_RANGE_START = 8765
42
+ PORT_RANGE_END = 8785
43
+
36
44
  class NetworkConfig:
37
- DEFAULT_DASHBOARD_PORT = 8765
45
+ DEFAULT_DASHBOARD_PORT = 8767
38
46
  SOCKETIO_PORT_RANGE = (8765, 8785)
39
- DEFAULT_SOCKETIO_PORT = 8765
47
+ DEFAULT_SOCKETIO_PORT = 8768
40
48
 
41
49
  socketio = None
42
50
 
@@ -309,7 +317,7 @@ class SocketIOConnectionPool:
309
317
  self.server_url = f"http://localhost:{port}"
310
318
  self.logger.debug(f"Detected Socket.IO server on port {port}")
311
319
  return
312
- except Exception:
320
+ except Exception: # nosec B112 - intentional: skip ports that fail
313
321
  continue
314
322
 
315
323
  # Fall back to default
@@ -579,7 +587,7 @@ class SocketIOConnectionPool:
579
587
  loop.stop()
580
588
  loop.run_until_complete(loop.shutdown_asyncgens())
581
589
  loop.close()
582
- except Exception:
590
+ except Exception: # nosec B110 - intentional: cleanup best-effort
583
591
  pass
584
592
 
585
593
  async def _connect_client(self, client: socketio.AsyncClient):
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -389,8 +389,9 @@ class PMSkillsDeployerService(LoggerMixin):
389
389
  if not skill_dir.is_dir() or skill_dir.name.startswith("."):
390
390
  continue
391
391
 
392
- # Only process mpm-* skills (framework management)
393
- if not skill_dir.name.startswith("mpm-"):
392
+ # Only process mpm* skills (framework management)
393
+ # Note: Includes both 'mpm' (core skill) and 'mpm-*' (other PM skills)
394
+ if not skill_dir.name.startswith("mpm"):
394
395
  self.logger.debug(f"Skipping non-mpm skill: {skill_dir.name}")
395
396
  continue
396
397
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: claude-mpm
3
- Version: 5.6.19
3
+ Version: 5.6.22
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=9JfxhnDtr-8l3kCP2U5TVXSErptHoga8m7XA8zqgGOc,4
2
- claude_mpm/VERSION,sha256=ie0YMxFiIPcEH1dIjM7FZhHVjQgE8mnza52JVoYQLnM,7
2
+ claude_mpm/VERSION,sha256=0sGdSB_G5lKklYFxAmDOIK9OUQyQNQYgOolvRpM3oEs,7
3
3
  claude_mpm/__init__.py,sha256=AGfh00BHKvLYD-UVFw7qbKtl7NMRIzRXOWw7vEuZ-h4,2214
4
4
  claude_mpm/__main__.py,sha256=Ro5UBWBoQaSAIoSqWAr7zkbLyvi4sSy28WShqAhKJG0,723
5
5
  claude_mpm/constants.py,sha256=pz3lTrZZR5HhV3eZzYtIbtBwWo7iM6pkBHP_ixxmI6Y,6827
@@ -61,7 +61,7 @@ claude_mpm/cli/commands/auto_configure.py,sha256=0Suzil6O0SBNeHUCwHOkt2q7gfuXRTy
61
61
  claude_mpm/cli/commands/autotodos.py,sha256=mm2R6dIfHOjAMCgFkjfjBzdFQ-ou19b_R3Lo0Vi8vCI,19428
62
62
  claude_mpm/cli/commands/cleanup.py,sha256=RQikOGLuLFWXzjeoHArdr5FA4Pf7tSK9w2NXL4vCrok,19769
63
63
  claude_mpm/cli/commands/cleanup_orphaned_agents.py,sha256=JR8crvgrz7Sa6d-SI-gKywok5S9rwc_DzDVk_h85sVs,4467
64
- claude_mpm/cli/commands/commander.py,sha256=HTMC9mOYDCQK40gE7MIibMyL5SqilToqlLFdwDbNcSU,7209
64
+ claude_mpm/cli/commands/commander.py,sha256=jumK9ctW50WL-YPzQYxkBUhmS0phwmxcxcXPYg_P4Xk,7243
65
65
  claude_mpm/cli/commands/config.py,sha256=2M9VUPYcQkBUCIyyB-v1qTL3xYvao9YI2l_JGBUDauA,23374
66
66
  claude_mpm/cli/commands/configure.py,sha256=VUokhUcV90GzEEivV3xNmfQHogM94mdDadW3S4mp83M,136509
67
67
  claude_mpm/cli/commands/configure_agent_display.py,sha256=oSvUhR861o_Pyqmop4ACAQNjwL02-Rf6TMqFvmQNh24,10575
@@ -125,7 +125,7 @@ claude_mpm/cli/parsers/analyze_code_parser.py,sha256=cpJSMFbc3mqB4qrMBIEZiikzPek
125
125
  claude_mpm/cli/parsers/analyze_parser.py,sha256=E00Ao0zwzbJPchs_AJt-aoQ7LQEtJPXRCNQ6Piivb4o,3908
126
126
  claude_mpm/cli/parsers/auto_configure_parser.py,sha256=CZOk_nJZrNtRo8WnYWQKdfXywq6l6ZLMHU7U9hA7A_4,3699
127
127
  claude_mpm/cli/parsers/base_parser.py,sha256=ofGa86qKDdY3DjtF3AGmouGNP9sM2xqzHwSFlvHsEAI,22341
128
- claude_mpm/cli/parsers/commander_parser.py,sha256=bxLxM58wqkyTuccz2Rm_1uve0HjXdxDggW9UWDI16N8,3389
128
+ claude_mpm/cli/parsers/commander_parser.py,sha256=cqVsTm0bLSMeX1_PX1pyBf7Qv7DxC3am65JST-fg7es,3423
129
129
  claude_mpm/cli/parsers/config_parser.py,sha256=i8kv6XWhftzZlEBruAl58Fl6IcyeOb7QVkfPeEayjCk,6255
130
130
  claude_mpm/cli/parsers/configure_parser.py,sha256=t3cwAQX3BfljDDRJH3i0LplpRprw5jdKcI9Uy3M8xtE,4382
131
131
  claude_mpm/cli/parsers/dashboard_parser.py,sha256=JBCM6v_iZhADr_Fwtk_d3up9AOod1avMab-vkNE61gE,3460
@@ -152,11 +152,11 @@ claude_mpm/cli_module/args.py,sha256=50_Y3AgMNeidtPjQ5-WZ1o-5Y7G2GAGQwMmllYjVScE
152
152
  claude_mpm/cli_module/commands.py,sha256=7ZzLm_R0wUkuYV1Hqe1BUDsP8b3BH33i5arkSEugm3U,7013
153
153
  claude_mpm/cli_module/migration_example.py,sha256=DtQ59RyoBD6r8FIfrjKXCQ8-xnUiOqP5McBiS6_W1Qc,5183
154
154
  claude_mpm/commander/__init__.py,sha256=A0RW9NLHzwgMfGP4ByD6f4E1YZmlwNkgycNMMp6zxf0,2458
155
- claude_mpm/commander/config.py,sha256=b9HUNN7LY8tHU4XkLzpuoVdHUZcgC-3by39fRYOg32Q,1583
155
+ claude_mpm/commander/config.py,sha256=ClI9TyiF6Tchiu8sqSUX2aUmiOa6rqwaCPkNk0cD8uc,1831
156
156
  claude_mpm/commander/config_loader.py,sha256=H2ASh19-Nu1Ej4_ojhuIQMU9fR4sMHTsA8fiXocoosE,3736
157
- claude_mpm/commander/daemon.py,sha256=oxwskLLzyNJBiEEwOXXvHkfF-eakA5A8AR5hYtMg4KU,21547
157
+ claude_mpm/commander/daemon.py,sha256=uBoW8xkoJAPmM2L113wZgo_TQq5gGsX4ST760AOTJVY,21959
158
158
  claude_mpm/commander/env_loader.py,sha256=2qf_b4PNsf4TkNzE9xcZotQjTokEdBqjDfPVN6taqY0,1911
159
- claude_mpm/commander/instance_manager.py,sha256=H37wjQkeeIQV5l-0q_ycDk1theU3eT1gg3b-Lbncirw,10790
159
+ claude_mpm/commander/instance_manager.py,sha256=lHkXCeCEyQ_a4qDCZp2z6vM5dbQX38H12k88KEYBufY,14449
160
160
  claude_mpm/commander/project_session.py,sha256=z_vhKcvla8WPmXS1MBl-Iki6oFxNug-YUdHMm15r6H0,9356
161
161
  claude_mpm/commander/registry.py,sha256=vytUc4xpaExA5arRxJnWCw4jb0JhN7t0N_sNJiA49Vs,13203
162
162
  claude_mpm/commander/tmux_orchestrator.py,sha256=uDnMQlzhEt1Ki8DTORpxi7lVtwIvN9n0aTA_5m8EycE,11243
@@ -181,7 +181,7 @@ claude_mpm/commander/api/routes/projects.py,sha256=hvDZW5Bq68Im7zJ6Lwe0RMySHJMoZ
181
181
  claude_mpm/commander/api/routes/sessions.py,sha256=_hIZk2HsYevtesaiuDRmb38UGGVycLFwawT1BjI5fFA,6509
182
182
  claude_mpm/commander/api/routes/work.py,sha256=4rUvWSGvE2V7DT3hYvJNd8aECabTnGC_pcZVo_0gXCk,8853
183
183
  claude_mpm/commander/chat/__init__.py,sha256=5Iiya2YPkF54OvtZgL4NNT0zp5PCsZnnE7D6l19aYnA,243
184
- claude_mpm/commander/chat/cli.py,sha256=neu4B5YLjR-IiVPByVS1YKOOPUytepbEZDyBIqNxixA,3262
184
+ claude_mpm/commander/chat/cli.py,sha256=lCmfSfxrgvyv2XT0JmZE8D4zwTjx6NTq2Jtcw_SUoi8,4531
185
185
  claude_mpm/commander/chat/commands.py,sha256=0Lvc4XT1k-0gpmLxhzgwVNw7IXc40kgZ9YqTVF0vxxk,2440
186
186
  claude_mpm/commander/chat/repl.py,sha256=c7Qi4qBg32b-JQyBKSNGadSWmmUrU7vBpFOkCV94QwU,10999
187
187
  claude_mpm/commander/core/__init__.py,sha256=BVtJoH9hn9LtlmtqPBybPowbPfiKNaNgtotLV82JRQk,357
@@ -190,7 +190,7 @@ claude_mpm/commander/core/response_manager.py,sha256=hOciRaiOmi-MFimaOUyyH0UCKfr
190
190
  claude_mpm/commander/events/__init__.py,sha256=NtUCo8eQfX4D3G9I2U10SRuuU4zMEIySKZGPxyUELzw,478
191
191
  claude_mpm/commander/events/manager.py,sha256=T-gXJ6DWIodxTdfFLv3u4kuks2ilJLjtlBZnmCkRSDA,10352
192
192
  claude_mpm/commander/frameworks/__init__.py,sha256=tOdMc4XNASVDrhpIPA1e7FKtNM7GQW5AEZXXW5NJK9I,280
193
- claude_mpm/commander/frameworks/base.py,sha256=B22tCoctsiQR1n2WVCYRMyBRzbPfUc2Q9NHIUDKT474,4675
193
+ claude_mpm/commander/frameworks/base.py,sha256=sFRNYxZazRaKh1ZBmjdPssdipz7s3SphLGiCnCP4yGw,4804
194
194
  claude_mpm/commander/frameworks/claude_code.py,sha256=6hjy6diDR3ECA4euGlG2wahbXLv5e-9LgS-mDwQGZWM,1724
195
195
  claude_mpm/commander/frameworks/mpm.py,sha256=BLlbFN47sjGTcllYQi7U-sQ_Dd1xNQyc3Bp6spVEs_c,1910
196
196
  claude_mpm/commander/inbox/__init__.py,sha256=eIupl11GtyUTmNPDYTQLbDLpnRE1d857T3l2wZI8EFM,317
@@ -272,10 +272,10 @@ claude_mpm/core/api_validator.py,sha256=zxeUykC2asy1KwHBDu5w3Eoo64E51PJyNe4PT7CT
272
272
  claude_mpm/core/base_service.py,sha256=4Zrt-vQ6g_KVk4hEfvd4QccuItYoOf7wUHCimIW6zJQ,29953
273
273
  claude_mpm/core/cache.py,sha256=orh8B40oYnRRGmXdANR4P4f-KVMIHV0vg7vrsemk0no,17232
274
274
  claude_mpm/core/claude_runner.py,sha256=Wa7dwkLP-Wiu-KiIzRKpW0Is7YY8Bl7hg_XadFnc2gs,39924
275
- claude_mpm/core/config.py,sha256=0R1ZLdwxTM2N3-uIBgcbr37jDiEe-G7h7x4YmYIVit0,43385
275
+ claude_mpm/core/config.py,sha256=LS0wv8MxpLbD625n3iM6kQBWbJnONtCdlpNkVtp0nhc,43525
276
276
  claude_mpm/core/config_aliases.py,sha256=QpNNECkTY4TYYAhVlFZvB-msPnZrui90g19u0-v0HDE,9703
277
- claude_mpm/core/config_constants.py,sha256=MEF35Y2Lj5FMzRdhgSgZrZeTzHfCzistPGZVY8INta4,10073
278
- claude_mpm/core/constants.py,sha256=tm6IkmDxnsEqdEImtdQOeWvvGEHrThWCgAz5FMjckCQ,11557
277
+ claude_mpm/core/config_constants.py,sha256=doCkQG8Gm4a0avlzk8tx53N4bfSXyj8_F6-LRs4n_ME,12807
278
+ claude_mpm/core/constants.py,sha256=KPSOibTnRbH8Pf_5Ys4eyMpIDdbMITvQ8mC-c62m6RA,12988
279
279
  claude_mpm/core/container.py,sha256=oLM91UOJUQ6D-DDVomqkTAozpgBcM2YFzk_JDbjgg8o,32277
280
280
  claude_mpm/core/enums.py,sha256=0wZGz73IXlZQ29I1OaUDbMa9E9vEimvhO6E_8LiRHSc,12279
281
281
  claude_mpm/core/error_handler.py,sha256=fqTstzmWaidfiVm-IvPGBRCjDwqBbQgjofyP31EkVNU,19276
@@ -297,6 +297,7 @@ claude_mpm/core/logging_config.py,sha256=h6beZ1QQTOSM8RM_dq-LC-pnfA3iRxIcTtuGv9z
297
297
  claude_mpm/core/logging_utils.py,sha256=RrHi4mt4h8Aq9igMos3ZyeAB2_Xfmcx_XPLRSGKTtzQ,17333
298
298
  claude_mpm/core/minimal_framework_loader.py,sha256=vmDEjL3MjnV7W4WIR-ymaL8QgsGsgxJJ0KdiQqAtudM,3640
299
299
  claude_mpm/core/mixins.py,sha256=vmZ7Nu2ZOnKjbhN07Ixk4noIej9nsJiknrp-Sclfu0A,5344
300
+ claude_mpm/core/network_config.py,sha256=_29RzxVfwos29oSZL8DIEbwiJewznfAUtUywGY3uYc4,4652
300
301
  claude_mpm/core/oneshot_session.py,sha256=EcBB4P9cR5LIpymuog8_KE7ckaJwbqWR2KnbRHKTyQA,22236
301
302
  claude_mpm/core/optimized_agent_loader.py,sha256=yevEwTZWzVeZYEJhV3czD45OU7ukJIaJos4MGnFP7YQ,15857
302
303
  claude_mpm/core/optimized_startup.py,sha256=U5I4f7PNYXCBOLbCkbWT2V2sv01T8iWP2Bw-f928Q9M,17927
@@ -304,7 +305,7 @@ claude_mpm/core/output_style_manager.py,sha256=QAWxH0fQ6Wr-NlGPs3SY6xT4DUZG5BuMN
304
305
  claude_mpm/core/pm_hook_interceptor.py,sha256=92C8TrpK-XVQD8BiXbqs8lSCX72PU0KZG5oAjhf8GOQ,11197
305
306
  claude_mpm/core/service_registry.py,sha256=QpmAMWCov8XXaxQwE7WiNbgv6u_CRjpKPB64kLYvZKk,11722
306
307
  claude_mpm/core/session_manager.py,sha256=iEDZWKBYHSu001nFX8vFvH33RvQOW0eIgomWhFM53sw,12078
307
- claude_mpm/core/socketio_pool.py,sha256=sW1heGTt0QLcpEgFBge_5-2dKk8PbJHexMW0Rz1Y3YU,31145
308
+ claude_mpm/core/socketio_pool.py,sha256=exJYYR-EOT9be01LXTXsyHaU0dvWU9Dxzp2EKKb94n4,31497
308
309
  claude_mpm/core/system_context.py,sha256=zKSKOQfhmILE8SV31TIGhGPvZ7CkZtfFRQdpdLTQvQU,1565
309
310
  claude_mpm/core/tool_access_control.py,sha256=dpdxxp_77SuxGM2C7SsHUZbtysJmHw1rLDXIeOvS7IA,6428
310
311
  claude_mpm/core/types.py,sha256=Sv62QhMYvfxbt7oIGoAhhN_jxonFTeLRf-BuhxZ4vYw,7719
@@ -436,29 +437,13 @@ claude_mpm/hooks/claude_hooks/memory_integration.py,sha256=fj_d9N7aytLELomdphLLw
436
437
  claude_mpm/hooks/claude_hooks/response_tracking.py,sha256=2dyDOK9u-N4KcqWF7qq2T17bkps0grbs8MeWBRSC8vE,16323
437
438
  claude_mpm/hooks/claude_hooks/tool_analysis.py,sha256=3_o2PP9D7wEMwLriCtIBOw0cj2fSZfepN7lI4P1meSQ,7862
438
439
  claude_mpm/hooks/claude_hooks/__pycache__/__init__.cpython-311.pyc,sha256=EGpgXqhPM0iRRZtCqHaLVQ6wDH42OH_M7Gt5GiFLyro,346
439
- claude_mpm/hooks/claude_hooks/__pycache__/__init__.cpython-312.pyc,sha256=RdPlaoNPDoSlTFV36Ku4vxs7xYqh7t2038D4pyULy-g,322
440
- claude_mpm/hooks/claude_hooks/__pycache__/__init__.cpython-314.pyc,sha256=tBzcQ_3WxkmgPTQAPd7uKgFGtz-Yb6r2hEgF9Y_Flwc,319
441
440
  claude_mpm/hooks/claude_hooks/__pycache__/auto_pause_handler.cpython-311.pyc,sha256=7YbusOM2EaL4NqIuNsLTJRCRJWRxo0DhVLXm9ZVFc7Y,20829
442
- claude_mpm/hooks/claude_hooks/__pycache__/auto_pause_handler.cpython-312.pyc,sha256=aUV7xCwxrzM4iW4Jf0RbUskIs2whf7lXt6HppAEMp1g,18982
443
- claude_mpm/hooks/claude_hooks/__pycache__/auto_pause_handler.cpython-314.pyc,sha256=vo_mPXj0KTtBz2_SAU4NZ7TEWPfgBZtLeXD3CMAvJ3M,21381
444
441
  claude_mpm/hooks/claude_hooks/__pycache__/correlation_manager.cpython-311.pyc,sha256=SQX5iiP9bQZkLL-cj_2tlGH7lpAzarO0mYal7btj3tc,3521
445
442
  claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-311.pyc,sha256=72KlsOUX_pQDIl36j7sQukPRarsLIsjINtzsBIpxCtw,45568
446
- claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-312.pyc,sha256=3ezkq2yC1cejVa-II4cl335SWCJhD8dv_NEDtVsCpUo,39235
447
- claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-314.pyc,sha256=zoDVrDzFnxGtDHqH20seS7KhA-9rmtFqttD-sqINBQI,43516
448
443
  claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-311.pyc,sha256=vhJjvAMTdDWkB_qWaWr_PeDb4I8-h7UymR1wDVHeyYM,30789
449
- claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-312.pyc,sha256=bj168CD_Md4vpYJfS9YvPafP7-nw1hAUnuuEpzSQNKY,27815
450
- claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-314.pyc,sha256=RVz6WvW-sD7kLbh6S5a96WarpPvZv4_qfFMR5SN8WUo,30603
451
- claude_mpm/hooks/claude_hooks/__pycache__/installer.cpython-311.pyc,sha256=tQ1esmuvKPb9PNp-a82X7W-5FGuDSWeQd72Xv4DJ18U,38443
452
- claude_mpm/hooks/claude_hooks/__pycache__/installer.cpython-314.pyc,sha256=Mlynkrh8uwlSOTCEH0LYbDM0gxqJmaCL9Gb5DIt_rJ4,38836
453
444
  claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-311.pyc,sha256=Xu_hwOf9AG0mZthl5gcDcVv68FiSo5KgZ6lDxdZXCfU,11185
454
- claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-312.pyc,sha256=w9SKmvg60p6kvNzuOIddYE-X9LYt-ZQzX3LWiKs4nZY,10055
455
- claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-314.pyc,sha256=oJhMGouqaFI1u3NZryHoRh6ucfPKHPe8GufcsPzi89o,11081
456
445
  claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-311.pyc,sha256=qJ5xY8yYwo8ZO7P-Gem_GK4VZdqyNeYvs8ANvC45jnw,16228
457
- claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-312.pyc,sha256=uyjnluDUFQql1N2oYP6IYP8dxhxnHoSvrmUubEMRNXI,14000
458
- claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-314.pyc,sha256=yu70SIPNsdECY8l_P971JPFuUPXqD4HmjuuGMU-nSAU,15820
459
446
  claude_mpm/hooks/claude_hooks/__pycache__/tool_analysis.cpython-311.pyc,sha256=ZjcNfNY5Ht6FhalPeh7M7OzMffcey5iF4AVjDDg9kak,10694
460
- claude_mpm/hooks/claude_hooks/__pycache__/tool_analysis.cpython-312.pyc,sha256=T98zonvVkllflFNw_pptXXAXbDrIgeBfY6aXvSY8el8,9071
461
- claude_mpm/hooks/claude_hooks/__pycache__/tool_analysis.cpython-314.pyc,sha256=ck8ifVIoVPaMVVRcMgvsM8RfIHnQbwGBUVs25A-yQBw,10771
462
447
  claude_mpm/hooks/claude_hooks/services/__init__.py,sha256=OIYOKsUNw1BHYawOCp-KFK5kmQKuj92cCqCEPO0nwo0,585
463
448
  claude_mpm/hooks/claude_hooks/services/connection_manager.py,sha256=shpoCLJpM9qoCDMUuUhk6L9WEKdzLB_OsPtwtDyJMdk,10197
464
449
  claude_mpm/hooks/claude_hooks/services/connection_manager_http.py,sha256=rdwUhK7DoPqDSRGPHW1uiaE8McsnmggvCQGZ2x_vX-o,7583
@@ -466,21 +451,10 @@ claude_mpm/hooks/claude_hooks/services/duplicate_detector.py,sha256=Fh9LmEMsVmQM
466
451
  claude_mpm/hooks/claude_hooks/services/state_manager.py,sha256=PcEpAzwEnZRjw6JnbtbekDRdhIoaKql5VhhZJCEPNt4,10899
467
452
  claude_mpm/hooks/claude_hooks/services/subagent_processor.py,sha256=unHGeU6PJxc00_3xnLyyZHeLa47x98knxn6cl-i8ALA,15040
468
453
  claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-311.pyc,sha256=xBfLBSqnpcKfcQBWfh7xUm454g1lq1LvbO7SxGvcOPc,644
469
- claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-312.pyc,sha256=75pkmoqE7CeBOc2ZHTYfgSR-mtVjKrcKaOA6PxFGW64,565
470
- claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-314.pyc,sha256=nZDxNz67tIFwbZNRqyCRX8cSi0v3JKCZS5U5FhwwApA,562
471
- claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager.cpython-311.pyc,sha256=IHMkHtVU8Noovuc3W9Eq0-GLPgTnGIL15RKnGD3X49o,10382
472
454
  claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-311.pyc,sha256=SydGEQ9oUWs161Ro4ULHfnPB0wiZgiB7V628B3V6N2U,9613
473
- claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-312.pyc,sha256=qdBU-mDyJxcwjwxfGhqzvo_s7YMO4mva-8hdObDAOcY,8616
474
- claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-314.pyc,sha256=PjdPDUyUQWm0FqYRdG2knPoZDSKS0g1vk-BDAXfzCi8,9627
475
455
  claude_mpm/hooks/claude_hooks/services/__pycache__/duplicate_detector.cpython-311.pyc,sha256=Yy_REAUhJCiFjOhxeDb4v0qyEvEbUtCmXD9PAz40dhw,5321
476
- claude_mpm/hooks/claude_hooks/services/__pycache__/duplicate_detector.cpython-312.pyc,sha256=ef_Tx9Uv1CvS18tC_PXmGVvIoMDsRnEUhKRUZaUdGBw,4751
477
- claude_mpm/hooks/claude_hooks/services/__pycache__/duplicate_detector.cpython-314.pyc,sha256=Xy9iNAtQMfXC_eW9rvumG9rFqK5t-QlLPmUahlqza_I,5341
478
456
  claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-311.pyc,sha256=P-Au2P5H0enEeKm7sP1eQv-SU6AAnCqjjOE5jhXcrgs,12352
479
- claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-312.pyc,sha256=g_Lo7LUYOEgIqS_Rz-6hy_caraHW9nzVJ6f0W4Y14rg,11378
480
- claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-314.pyc,sha256=2QqT8edjLqG8-9q446zpLJ347lzkgvJxPJG-A8u_HAU,13101
481
457
  claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-311.pyc,sha256=4SfbElFMcQK07Frewl9mQ0eB5yCqZUe-CGfGYNUUMh0,14931
482
- claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-312.pyc,sha256=Cky-0tvyWmn9I44WdupIdJ4Qml0k-qlCs5E0x0Y130Q,13301
483
- claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-314.pyc,sha256=o6Dc1-yyZjUdVh_JWZmEIvVt0qG0R1-aLhr7XTt-oX0,15104
484
458
  claude_mpm/hooks/failure_learning/__init__.py,sha256=iJ80AKFHiT8DjIH2a72DQVJvL6nAFrizNA2yTKwZ4rw,1805
485
459
  claude_mpm/hooks/failure_learning/failure_detection_hook.py,sha256=KENoB5N-dBm5hb0SxeIZtCvNKbmG2BKHOJSrSO-3Z_I,7500
486
460
  claude_mpm/hooks/failure_learning/fix_detection_hook.py,sha256=XUk1bnBVLdfhQ9AMQSvGsTSeFQsKsVud2wbWX-Jjor8,7164
@@ -517,7 +491,7 @@ claude_mpm/services/memory_hook_service.py,sha256=u0WyZB2oLm_cEungByPNv7SRa-wqyU
517
491
  claude_mpm/services/monitor_build_service.py,sha256=8gWR9CaqgXdG6-OjOFXGpk28GCcJTlHhojkUYnMCebI,12160
518
492
  claude_mpm/services/native_agent_converter.py,sha256=mG9uYZvjP-lAjlwyfE1aRdJc2vN5x-haMwSry0YqP0E,12247
519
493
  claude_mpm/services/orphan_detection.py,sha256=cEFE96FhC6EJSa53rUjbgjDj_E1_Ej-0KoYDQST22t0,26339
520
- claude_mpm/services/pm_skills_deployer.py,sha256=11MW83rATVoez9d-XRXjkwU4xoBnTxsI48053dCK5f8,32122
494
+ claude_mpm/services/pm_skills_deployer.py,sha256=c4IUJd-AVEasgeKGBIVhSGuoxPyY1LAAiRQfNfHZ-Ng,32203
521
495
  claude_mpm/services/port_manager.py,sha256=tL5oymXrIRzpkGWLy3g2THhA4wy1U_uZ2VtDE6Bbwck,22840
522
496
  claude_mpm/services/profile_manager.py,sha256=L8vuLpFwcHsFUUr8uZwgHBSbqIZPVcUk_hAPrlSWqZU,10991
523
497
  claude_mpm/services/project_port_allocator.py,sha256=L_EPuX_vGdv8vWWRVTpRk7F-v7uimaRTlqZP7LtJk7A,19085
@@ -1128,10 +1102,10 @@ claude_mpm/utils/subprocess_utils.py,sha256=D0izRT8anjiUb_JG72zlJR_JAw1cDkb7kalN
1128
1102
  claude_mpm/validation/__init__.py,sha256=YZhwE3mhit-lslvRLuwfX82xJ_k4haZeKmh4IWaVwtk,156
1129
1103
  claude_mpm/validation/agent_validator.py,sha256=GprtAvu80VyMXcKGsK_VhYiXWA6BjKHv7O6HKx0AB9w,20917
1130
1104
  claude_mpm/validation/frontmatter_validator.py,sha256=YpJlYNNYcV8u6hIOi3_jaRsDnzhbcQpjCBE6eyBKaFY,7076
1131
- claude_mpm-5.6.19.dist-info/licenses/LICENSE,sha256=ca3y_Rk4aPrbF6f62z8Ht5MJM9OAvbGlHvEDcj9vUQ4,3867
1132
- claude_mpm-5.6.19.dist-info/licenses/LICENSE-FAQ.md,sha256=TxfEkXVCK98RzDOer09puc7JVCP_q_bN4dHtZKHCMcM,5104
1133
- claude_mpm-5.6.19.dist-info/METADATA,sha256=-8p7Bgtlyc7iZ7sXV_Ud7UmACVlQ7EKI4Oz-Y6TUc8M,15245
1134
- claude_mpm-5.6.19.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
1135
- claude_mpm-5.6.19.dist-info/entry_points.txt,sha256=n-Uk4vwHPpuvu-g_I7-GHORzTnN_m6iyOsoLveKKD0E,228
1136
- claude_mpm-5.6.19.dist-info/top_level.txt,sha256=1nUg3FEaBySgm8t-s54jK5zoPnu3_eY6EP6IOlekyHA,11
1137
- claude_mpm-5.6.19.dist-info/RECORD,,
1105
+ claude_mpm-5.6.22.dist-info/licenses/LICENSE,sha256=ca3y_Rk4aPrbF6f62z8Ht5MJM9OAvbGlHvEDcj9vUQ4,3867
1106
+ claude_mpm-5.6.22.dist-info/licenses/LICENSE-FAQ.md,sha256=TxfEkXVCK98RzDOer09puc7JVCP_q_bN4dHtZKHCMcM,5104
1107
+ claude_mpm-5.6.22.dist-info/METADATA,sha256=ph1blcWeCPT9r9W3fHOHUxgKvnLCuzwPFdGxeYhjQvE,15245
1108
+ claude_mpm-5.6.22.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
1109
+ claude_mpm-5.6.22.dist-info/entry_points.txt,sha256=n-Uk4vwHPpuvu-g_I7-GHORzTnN_m6iyOsoLveKKD0E,228
1110
+ claude_mpm-5.6.22.dist-info/top_level.txt,sha256=1nUg3FEaBySgm8t-s54jK5zoPnu3_eY6EP6IOlekyHA,11
1111
+ claude_mpm-5.6.22.dist-info/RECORD,,