claude-mpm 4.5.8__py3-none-any.whl → 4.5.12__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 (226) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/__init__.py +20 -5
  3. claude_mpm/agents/agent_loader.py +19 -2
  4. claude_mpm/agents/base_agent_loader.py +5 -5
  5. claude_mpm/agents/frontmatter_validator.py +4 -4
  6. claude_mpm/agents/templates/agent-manager.json +3 -3
  7. claude_mpm/agents/templates/agentic-coder-optimizer.json +3 -3
  8. claude_mpm/agents/templates/api_qa.json +1 -1
  9. claude_mpm/agents/templates/clerk-ops.json +3 -3
  10. claude_mpm/agents/templates/code_analyzer.json +3 -3
  11. claude_mpm/agents/templates/dart_engineer.json +294 -0
  12. claude_mpm/agents/templates/data_engineer.json +3 -3
  13. claude_mpm/agents/templates/documentation.json +2 -2
  14. claude_mpm/agents/templates/engineer.json +2 -2
  15. claude_mpm/agents/templates/gcp_ops_agent.json +2 -2
  16. claude_mpm/agents/templates/imagemagick.json +1 -1
  17. claude_mpm/agents/templates/local_ops_agent.json +319 -41
  18. claude_mpm/agents/templates/memory_manager.json +2 -2
  19. claude_mpm/agents/templates/nextjs_engineer.json +2 -2
  20. claude_mpm/agents/templates/ops.json +2 -2
  21. claude_mpm/agents/templates/php-engineer.json +1 -1
  22. claude_mpm/agents/templates/project_organizer.json +1 -1
  23. claude_mpm/agents/templates/prompt-engineer.json +6 -4
  24. claude_mpm/agents/templates/python_engineer.json +2 -2
  25. claude_mpm/agents/templates/qa.json +1 -1
  26. claude_mpm/agents/templates/react_engineer.json +3 -3
  27. claude_mpm/agents/templates/refactoring_engineer.json +3 -3
  28. claude_mpm/agents/templates/research.json +2 -2
  29. claude_mpm/agents/templates/security.json +2 -2
  30. claude_mpm/agents/templates/ticketing.json +2 -2
  31. claude_mpm/agents/templates/typescript_engineer.json +2 -2
  32. claude_mpm/agents/templates/vercel_ops_agent.json +2 -2
  33. claude_mpm/agents/templates/version_control.json +2 -2
  34. claude_mpm/agents/templates/web_qa.json +6 -6
  35. claude_mpm/agents/templates/web_ui.json +3 -3
  36. claude_mpm/cli/__init__.py +49 -19
  37. claude_mpm/cli/commands/agent_manager.py +3 -3
  38. claude_mpm/cli/commands/agents.py +6 -6
  39. claude_mpm/cli/commands/aggregate.py +4 -4
  40. claude_mpm/cli/commands/analyze.py +2 -2
  41. claude_mpm/cli/commands/analyze_code.py +1 -1
  42. claude_mpm/cli/commands/cleanup.py +3 -3
  43. claude_mpm/cli/commands/config.py +2 -2
  44. claude_mpm/cli/commands/configure.py +605 -21
  45. claude_mpm/cli/commands/dashboard.py +1 -1
  46. claude_mpm/cli/commands/debug.py +3 -3
  47. claude_mpm/cli/commands/doctor.py +1 -1
  48. claude_mpm/cli/commands/mcp.py +7 -7
  49. claude_mpm/cli/commands/mcp_command_router.py +1 -1
  50. claude_mpm/cli/commands/mcp_config.py +2 -2
  51. claude_mpm/cli/commands/mcp_external_commands.py +2 -2
  52. claude_mpm/cli/commands/mcp_install_commands.py +3 -3
  53. claude_mpm/cli/commands/mcp_pipx_config.py +2 -2
  54. claude_mpm/cli/commands/mcp_setup_external.py +3 -3
  55. claude_mpm/cli/commands/monitor.py +1 -1
  56. claude_mpm/cli/commands/mpm_init_handler.py +1 -1
  57. claude_mpm/cli/interactive/agent_wizard.py +1 -1
  58. claude_mpm/cli/parsers/configure_parser.py +5 -0
  59. claude_mpm/cli/parsers/search_parser.py +1 -1
  60. claude_mpm/cli/shared/argument_patterns.py +2 -2
  61. claude_mpm/cli/shared/base_command.py +1 -1
  62. claude_mpm/cli/startup_logging.py +4 -4
  63. claude_mpm/config/experimental_features.py +4 -4
  64. claude_mpm/config/socketio_config.py +2 -2
  65. claude_mpm/core/__init__.py +53 -17
  66. claude_mpm/core/agent_session_manager.py +2 -2
  67. claude_mpm/core/api_validator.py +3 -3
  68. claude_mpm/core/base_service.py +10 -1
  69. claude_mpm/core/cache.py +2 -2
  70. claude_mpm/core/config.py +5 -5
  71. claude_mpm/core/config_aliases.py +4 -4
  72. claude_mpm/core/config_constants.py +1 -1
  73. claude_mpm/core/error_handler.py +1 -1
  74. claude_mpm/core/file_utils.py +5 -5
  75. claude_mpm/core/framework/formatters/capability_generator.py +5 -5
  76. claude_mpm/core/framework/loaders/agent_loader.py +1 -1
  77. claude_mpm/core/framework/processors/metadata_processor.py +1 -1
  78. claude_mpm/core/framework/processors/template_processor.py +3 -3
  79. claude_mpm/core/framework_loader.py +2 -2
  80. claude_mpm/core/log_manager.py +11 -4
  81. claude_mpm/core/logger.py +2 -2
  82. claude_mpm/core/optimized_startup.py +1 -1
  83. claude_mpm/core/output_style_manager.py +1 -1
  84. claude_mpm/core/service_registry.py +2 -2
  85. claude_mpm/core/session_manager.py +3 -3
  86. claude_mpm/core/shared/config_loader.py +1 -1
  87. claude_mpm/core/socketio_pool.py +2 -2
  88. claude_mpm/core/unified_agent_registry.py +2 -2
  89. claude_mpm/core/unified_config.py +6 -6
  90. claude_mpm/core/unified_paths.py +2 -2
  91. claude_mpm/dashboard/api/simple_directory.py +1 -1
  92. claude_mpm/generators/agent_profile_generator.py +1 -1
  93. claude_mpm/hooks/claude_hooks/event_handlers.py +2 -2
  94. claude_mpm/hooks/claude_hooks/installer.py +9 -9
  95. claude_mpm/hooks/claude_hooks/response_tracking.py +16 -11
  96. claude_mpm/hooks/claude_hooks/services/connection_manager_http.py +16 -13
  97. claude_mpm/hooks/claude_hooks/tool_analysis.py +2 -2
  98. claude_mpm/hooks/memory_integration_hook.py +1 -1
  99. claude_mpm/hooks/validation_hooks.py +1 -1
  100. claude_mpm/init.py +4 -4
  101. claude_mpm/models/agent_session.py +1 -1
  102. claude_mpm/scripts/socketio_daemon.py +5 -5
  103. claude_mpm/services/__init__.py +145 -161
  104. claude_mpm/services/agent_capabilities_service.py +1 -1
  105. claude_mpm/services/agents/agent_builder.py +4 -4
  106. claude_mpm/services/agents/deployment/agent_lifecycle_manager.py +1 -1
  107. claude_mpm/services/agents/deployment/agent_metrics_collector.py +1 -1
  108. claude_mpm/services/agents/deployment/agent_record_service.py +3 -3
  109. claude_mpm/services/agents/deployment/deployment_config_loader.py +21 -0
  110. claude_mpm/services/agents/deployment/deployment_wrapper.py +1 -1
  111. claude_mpm/services/agents/deployment/pipeline/steps/target_directory_step.py +2 -2
  112. claude_mpm/services/agents/loading/agent_profile_loader.py +2 -2
  113. claude_mpm/services/agents/loading/base_agent_manager.py +12 -2
  114. claude_mpm/services/agents/local_template_manager.py +5 -5
  115. claude_mpm/services/agents/registry/deployed_agent_discovery.py +1 -1
  116. claude_mpm/services/agents/registry/modification_tracker.py +19 -11
  117. claude_mpm/services/async_session_logger.py +3 -3
  118. claude_mpm/services/claude_session_logger.py +4 -4
  119. claude_mpm/services/cli/agent_listing_service.py +3 -3
  120. claude_mpm/services/cli/agent_validation_service.py +1 -1
  121. claude_mpm/services/cli/session_manager.py +2 -2
  122. claude_mpm/services/core/path_resolver.py +1 -1
  123. claude_mpm/services/diagnostics/checks/agent_check.py +1 -1
  124. claude_mpm/services/diagnostics/checks/claude_code_check.py +2 -2
  125. claude_mpm/services/diagnostics/checks/common_issues_check.py +3 -3
  126. claude_mpm/services/diagnostics/checks/configuration_check.py +2 -2
  127. claude_mpm/services/diagnostics/checks/installation_check.py +1 -1
  128. claude_mpm/services/diagnostics/checks/mcp_check.py +1 -1
  129. claude_mpm/services/diagnostics/checks/mcp_services_check.py +9 -9
  130. claude_mpm/services/diagnostics/checks/monitor_check.py +1 -1
  131. claude_mpm/services/diagnostics/doctor_reporter.py +1 -1
  132. claude_mpm/services/event_aggregator.py +1 -1
  133. claude_mpm/services/event_bus/event_bus.py +7 -2
  134. claude_mpm/services/events/consumers/dead_letter.py +2 -2
  135. claude_mpm/services/framework_claude_md_generator/__init__.py +1 -1
  136. claude_mpm/services/framework_claude_md_generator/deployment_manager.py +3 -3
  137. claude_mpm/services/framework_claude_md_generator/version_manager.py +1 -1
  138. claude_mpm/services/hook_installer_service.py +7 -7
  139. claude_mpm/services/infrastructure/context_preservation.py +7 -7
  140. claude_mpm/services/infrastructure/daemon_manager.py +5 -5
  141. claude_mpm/services/mcp_config_manager.py +169 -48
  142. claude_mpm/services/mcp_gateway/__init__.py +98 -94
  143. claude_mpm/services/mcp_gateway/auto_configure.py +5 -5
  144. claude_mpm/services/mcp_gateway/config/config_loader.py +2 -2
  145. claude_mpm/services/mcp_gateway/config/configuration.py +3 -3
  146. claude_mpm/services/mcp_gateway/core/process_pool.py +3 -3
  147. claude_mpm/services/mcp_gateway/core/singleton_manager.py +2 -2
  148. claude_mpm/services/mcp_gateway/core/startup_verification.py +1 -1
  149. claude_mpm/services/mcp_gateway/main.py +1 -1
  150. claude_mpm/services/mcp_gateway/registry/service_registry.py +4 -2
  151. claude_mpm/services/mcp_gateway/registry/tool_registry.py +2 -1
  152. claude_mpm/services/mcp_gateway/server/stdio_handler.py +1 -1
  153. claude_mpm/services/mcp_gateway/tools/document_summarizer.py +1 -1
  154. claude_mpm/services/mcp_gateway/tools/health_check_tool.py +1 -1
  155. claude_mpm/services/mcp_gateway/tools/hello_world.py +1 -1
  156. claude_mpm/services/mcp_gateway/utils/package_version_checker.py +5 -5
  157. claude_mpm/services/mcp_gateway/utils/update_preferences.py +2 -2
  158. claude_mpm/services/mcp_service_verifier.py +1 -1
  159. claude_mpm/services/memory/builder.py +1 -1
  160. claude_mpm/services/memory/cache/shared_prompt_cache.py +2 -1
  161. claude_mpm/services/memory/indexed_memory.py +3 -3
  162. claude_mpm/services/monitor/daemon.py +1 -1
  163. claude_mpm/services/monitor/daemon_manager.py +9 -9
  164. claude_mpm/services/monitor/event_emitter.py +1 -1
  165. claude_mpm/services/monitor/handlers/file.py +1 -1
  166. claude_mpm/services/monitor/handlers/hooks.py +3 -3
  167. claude_mpm/services/monitor/management/lifecycle.py +7 -7
  168. claude_mpm/services/monitor/server.py +2 -2
  169. claude_mpm/services/orphan_detection.py +788 -0
  170. claude_mpm/services/port_manager.py +2 -2
  171. claude_mpm/services/project/analyzer.py +3 -3
  172. claude_mpm/services/project/archive_manager.py +13 -13
  173. claude_mpm/services/project/dependency_analyzer.py +4 -4
  174. claude_mpm/services/project/documentation_manager.py +4 -4
  175. claude_mpm/services/project/enhanced_analyzer.py +8 -8
  176. claude_mpm/services/project/registry.py +4 -4
  177. claude_mpm/services/project_port_allocator.py +597 -0
  178. claude_mpm/services/response_tracker.py +1 -1
  179. claude_mpm/services/session_management_service.py +1 -1
  180. claude_mpm/services/session_manager.py +6 -4
  181. claude_mpm/services/socketio/event_normalizer.py +1 -1
  182. claude_mpm/services/socketio/handlers/code_analysis.py +14 -12
  183. claude_mpm/services/socketio/handlers/file.py +1 -1
  184. claude_mpm/services/socketio/migration_utils.py +1 -1
  185. claude_mpm/services/socketio/server/core.py +1 -1
  186. claude_mpm/services/unified/analyzer_strategies/code_analyzer.py +1 -1
  187. claude_mpm/services/unified/analyzer_strategies/dependency_analyzer.py +4 -4
  188. claude_mpm/services/unified/analyzer_strategies/structure_analyzer.py +1 -1
  189. claude_mpm/services/unified/config_strategies/config_schema.py +4 -4
  190. claude_mpm/services/unified/config_strategies/context_strategy.py +6 -6
  191. claude_mpm/services/unified/config_strategies/error_handling_strategy.py +10 -10
  192. claude_mpm/services/unified/config_strategies/file_loader_strategy.py +5 -5
  193. claude_mpm/services/unified/config_strategies/unified_config_service.py +8 -8
  194. claude_mpm/services/unified/config_strategies/validation_strategy.py +15 -15
  195. claude_mpm/services/unified/deployment_strategies/base.py +4 -4
  196. claude_mpm/services/unified/deployment_strategies/cloud_strategies.py +15 -15
  197. claude_mpm/services/unified/deployment_strategies/local.py +9 -9
  198. claude_mpm/services/unified/deployment_strategies/utils.py +9 -9
  199. claude_mpm/services/unified/deployment_strategies/vercel.py +7 -7
  200. claude_mpm/services/unified/unified_config.py +5 -5
  201. claude_mpm/services/unified/unified_deployment.py +2 -2
  202. claude_mpm/services/utility_service.py +1 -1
  203. claude_mpm/services/version_control/conflict_resolution.py +2 -2
  204. claude_mpm/services/version_control/git_operations.py +3 -3
  205. claude_mpm/services/version_control/semantic_versioning.py +13 -13
  206. claude_mpm/services/version_control/version_parser.py +1 -1
  207. claude_mpm/storage/state_storage.py +12 -13
  208. claude_mpm/tools/code_tree_analyzer.py +5 -5
  209. claude_mpm/tools/code_tree_builder.py +4 -4
  210. claude_mpm/tools/socketio_debug.py +1 -1
  211. claude_mpm/utils/agent_dependency_loader.py +4 -4
  212. claude_mpm/utils/common.py +2 -2
  213. claude_mpm/utils/config_manager.py +3 -3
  214. claude_mpm/utils/dependency_cache.py +2 -2
  215. claude_mpm/utils/dependency_strategies.py +6 -6
  216. claude_mpm/utils/file_utils.py +11 -11
  217. claude_mpm/utils/log_cleanup.py +1 -1
  218. claude_mpm/utils/path_operations.py +1 -1
  219. claude_mpm/validation/agent_validator.py +2 -2
  220. claude_mpm/validation/frontmatter_validator.py +1 -1
  221. {claude_mpm-4.5.8.dist-info → claude_mpm-4.5.12.dist-info}/METADATA +1 -1
  222. {claude_mpm-4.5.8.dist-info → claude_mpm-4.5.12.dist-info}/RECORD +226 -223
  223. {claude_mpm-4.5.8.dist-info → claude_mpm-4.5.12.dist-info}/WHEEL +0 -0
  224. {claude_mpm-4.5.8.dist-info → claude_mpm-4.5.12.dist-info}/entry_points.txt +0 -0
  225. {claude_mpm-4.5.8.dist-info → claude_mpm-4.5.12.dist-info}/licenses/LICENSE +0 -0
  226. {claude_mpm-4.5.8.dist-info → claude_mpm-4.5.12.dist-info}/top_level.txt +0 -0
@@ -26,6 +26,8 @@ from rich.syntax import Syntax
26
26
  from rich.table import Table
27
27
  from rich.text import Text
28
28
 
29
+ from ...core.config import Config
30
+ from ...services.mcp_config_manager import MCPConfigManager
29
31
  from ...services.version_service import VersionService
30
32
  from ...utils.console import console as default_console
31
33
  from ..shared import BaseCommand, CommandResult
@@ -54,18 +56,22 @@ class SimpleAgentManager:
54
56
  self.templates_dir = (
55
57
  Path(__file__).parent.parent.parent / "agents" / "templates"
56
58
  )
59
+ # Add logger for error reporting
60
+ import logging
61
+
62
+ self.logger = logging.getLogger(__name__)
57
63
 
58
64
  def _load_states(self):
59
65
  """Load agent states from file."""
60
66
  if self.config_file.exists():
61
- with open(self.config_file) as f:
67
+ with self.config_file.open() as f:
62
68
  self.states = json.load(f)
63
69
  else:
64
70
  self.states = {}
65
71
 
66
72
  def _save_states(self):
67
73
  """Save agent states to file."""
68
- with open(self.config_file, "w") as f:
74
+ with self.config_file.open("w") as f:
69
75
  json.dump(self.states, f, indent=2)
70
76
 
71
77
  def is_agent_enabled(self, agent_name: str) -> bool:
@@ -99,7 +105,7 @@ class SimpleAgentManager:
99
105
  continue
100
106
 
101
107
  try:
102
- with open(template_file) as f:
108
+ with template_file.open() as f:
103
109
  template_data = json.load(f)
104
110
 
105
111
  # Extract agent information from template
@@ -115,6 +121,9 @@ class SimpleAgentManager:
115
121
  # Extract capabilities/tools as dependencies for display
116
122
  capabilities = template_data.get("capabilities", {})
117
123
  tools = capabilities.get("tools", [])
124
+ # Ensure tools is a list before slicing
125
+ if not isinstance(tools, list):
126
+ tools = []
118
127
  # Show first few tools as "dependencies" for UI purposes
119
128
  display_tools = tools[:3] if len(tools) > 3 else tools
120
129
 
@@ -133,14 +142,24 @@ class SimpleAgentManager:
133
142
  )
134
143
  )
135
144
 
136
- except (json.JSONDecodeError, KeyError):
137
- # Skip malformed templates
145
+ except (json.JSONDecodeError, KeyError) as e:
146
+ # Log malformed templates but continue
147
+ self.logger.debug(
148
+ f"Skipping malformed template {template_file.name}: {e}"
149
+ )
150
+ continue
151
+ except Exception as e:
152
+ # Log unexpected errors but continue processing other templates
153
+ self.logger.debug(
154
+ f"Error processing template {template_file.name}: {e}"
155
+ )
138
156
  continue
139
157
 
140
158
  except Exception as e:
141
- # If there's an error reading templates, return a minimal set
159
+ # If there's a catastrophic error reading templates directory
160
+ self.logger.error(f"Failed to read templates directory: {e}")
142
161
  return [
143
- AgentConfig("engineer", f"Error loading templates: {e!s}", []),
162
+ AgentConfig("engineer", f"Error accessing templates: {e!s}", []),
144
163
  AgentConfig("research", "Research agent", []),
145
164
  ]
146
165
 
@@ -174,6 +193,7 @@ class ConfigureCommand(BaseCommand):
174
193
  getattr(args, "agents", False),
175
194
  getattr(args, "templates", False),
176
195
  getattr(args, "behaviors", False),
196
+ getattr(args, "startup", False),
177
197
  getattr(args, "version_info", False),
178
198
  ]
179
199
  if sum(nav_options) > 1:
@@ -185,7 +205,7 @@ class ConfigureCommand(BaseCommand):
185
205
 
186
206
  return None
187
207
 
188
- def run(self, args) -> CommandResult: # noqa: PLR0911
208
+ def run(self, args) -> CommandResult:
189
209
  """Execute the configure command."""
190
210
  # Set configuration scope
191
211
  self.current_scope = getattr(args, "scope", "project")
@@ -242,6 +262,9 @@ class ConfigureCommand(BaseCommand):
242
262
  if getattr(args, "behaviors", False):
243
263
  return self._run_behavior_management()
244
264
 
265
+ if getattr(args, "startup", False):
266
+ return self._run_startup_configuration()
267
+
245
268
  # Launch interactive TUI
246
269
  return self._run_interactive_tui(args)
247
270
 
@@ -263,8 +286,15 @@ class ConfigureCommand(BaseCommand):
263
286
  elif choice == "3":
264
287
  self._manage_behaviors()
265
288
  elif choice == "4":
266
- self._switch_scope()
289
+ # If user saves and wants to proceed to startup, exit the configurator
290
+ if self._manage_startup_configuration():
291
+ self.console.print(
292
+ "\n[green]Configuration saved. Exiting configurator...[/green]"
293
+ )
294
+ break
267
295
  elif choice == "5":
296
+ self._switch_scope()
297
+ elif choice == "6":
268
298
  self._show_version_info_interactive()
269
299
  elif choice == "q":
270
300
  self.console.print(
@@ -287,10 +317,14 @@ class ConfigureCommand(BaseCommand):
287
317
  """Display the TUI header."""
288
318
  self.console.clear()
289
319
 
320
+ # Get version for display
321
+ from claude_mpm import __version__
322
+
290
323
  # Create header panel
291
324
  header_text = Text()
292
325
  header_text.append("Claude MPM ", style="bold cyan")
293
326
  header_text.append("Configuration Interface", style="bold white")
327
+ header_text.append(f"\nv{__version__}", style="dim cyan")
294
328
 
295
329
  scope_text = Text(f"Scope: {self.current_scope.upper()}", style="yellow")
296
330
  dir_text = Text(f"Directory: {self.project_dir}", style="dim")
@@ -315,8 +349,13 @@ class ConfigureCommand(BaseCommand):
315
349
  ("1", "Agent Management", "Enable/disable agents and customize settings"),
316
350
  ("2", "Template Editing", "Edit agent JSON templates"),
317
351
  ("3", "Behavior Files", "Manage identity and workflow configurations"),
318
- ("4", "Switch Scope", f"Current: {self.current_scope}"),
319
- ("5", "Version Info", "Display MPM and Claude versions"),
352
+ (
353
+ "4",
354
+ "Startup Configuration",
355
+ "Configure MCP services and agents to start",
356
+ ),
357
+ ("5", "Switch Scope", f"Current: {self.current_scope}"),
358
+ ("6", "Version Info", "Display MPM and Claude versions"),
320
359
  ("q", "Quit", "Exit configuration interface"),
321
360
  ]
322
361
 
@@ -408,7 +447,7 @@ class ConfigureCommand(BaseCommand):
408
447
  try:
409
448
  template_path = self._get_agent_template_path(agent.name)
410
449
  if template_path.exists():
411
- with open(template_path) as f:
450
+ with template_path.open() as f:
412
451
  template = json.load(f)
413
452
  model = template.get("capabilities", {}).get("model", "default")
414
453
  tools_display = f"Model: {model}"
@@ -503,7 +542,7 @@ class ConfigureCommand(BaseCommand):
503
542
  template_path = self._get_agent_template_path(agent.name)
504
543
 
505
544
  if template_path.exists():
506
- with open(template_path) as f:
545
+ with template_path.open() as f:
507
546
  template = json.load(f)
508
547
  is_system = str(template_path).startswith(
509
548
  str(self.agent_manager.templates_dir)
@@ -654,11 +693,11 @@ class ConfigureCommand(BaseCommand):
654
693
  subprocess.call([editor, temp_path])
655
694
 
656
695
  # Read back the edited content
657
- with open(temp_path) as f:
696
+ with temp_path.open() as f:
658
697
  new_template = json.load(f)
659
698
 
660
699
  # Save to actual template path
661
- with open(template_path, "w") as f:
700
+ with template_path.open("w") as f:
662
701
  json.dump(new_template, f, indent=2)
663
702
 
664
703
  self.console.print("[green]Template updated successfully![/green]")
@@ -693,7 +732,7 @@ class ConfigureCommand(BaseCommand):
693
732
  current[parts[-1]] = value
694
733
 
695
734
  # Save the template
696
- with open(template_path, "w") as f:
735
+ with template_path.open("w") as f:
697
736
  json.dump(template, f, indent=2)
698
737
 
699
738
  self.console.print(
@@ -726,7 +765,7 @@ class ConfigureCommand(BaseCommand):
726
765
  del current[parts[-1]]
727
766
 
728
767
  # Save the template
729
- with open(template_path, "w") as f:
768
+ with template_path.open("w") as f:
730
769
  json.dump(template, f, indent=2)
731
770
 
732
771
  self.console.print(
@@ -763,7 +802,7 @@ class ConfigureCommand(BaseCommand):
763
802
  return
764
803
 
765
804
  # Save the template copy
766
- with open(custom_path, "w") as f:
805
+ with custom_path.open("w") as f:
767
806
  json.dump(template, f, indent=2)
768
807
 
769
808
  self.console.print(f"[green]Created custom template at: {custom_path}[/green]")
@@ -800,7 +839,7 @@ class ConfigureCommand(BaseCommand):
800
839
 
801
840
  if template_path.exists():
802
841
  try:
803
- with open(template_path) as f:
842
+ with template_path.open() as f:
804
843
  template = json.load(f)
805
844
 
806
845
  # Extract additional information
@@ -979,6 +1018,537 @@ class ConfigureCommand(BaseCommand):
979
1018
  self.console.print("[yellow]Behavior file export - Coming soon![/yellow]")
980
1019
  Prompt.ask("Press Enter to continue")
981
1020
 
1021
+ def _manage_startup_configuration(self) -> bool:
1022
+ """Manage startup configuration for MCP services and agents.
1023
+
1024
+ Returns:
1025
+ bool: True if user saved and wants to proceed to startup, False otherwise
1026
+ """
1027
+ # Temporarily suppress INFO logging during Config initialization
1028
+ import logging
1029
+
1030
+ root_logger = logging.getLogger("claude_mpm")
1031
+ original_level = root_logger.level
1032
+ root_logger.setLevel(logging.WARNING)
1033
+
1034
+ try:
1035
+ # Load current configuration ONCE at the start
1036
+ config = Config()
1037
+ startup_config = self._load_startup_configuration(config)
1038
+ finally:
1039
+ # Restore original logging level
1040
+ root_logger.setLevel(original_level)
1041
+
1042
+ proceed_to_startup = False
1043
+ while True:
1044
+ self.console.clear()
1045
+ self._display_header()
1046
+
1047
+ self.console.print("[bold]Startup Configuration Management[/bold]\n")
1048
+ self.console.print(
1049
+ "[dim]Configure which MCP services, hook services, and system agents "
1050
+ "are enabled when Claude MPM starts.[/dim]\n"
1051
+ )
1052
+
1053
+ # Display current configuration (using in-memory state)
1054
+ self._display_startup_configuration(startup_config)
1055
+
1056
+ # Show menu options
1057
+ self.console.print("\n[bold]Options:[/bold]")
1058
+ self.console.print(" [cyan]1[/cyan] - Configure MCP Services")
1059
+ self.console.print(" [cyan]2[/cyan] - Configure Hook Services")
1060
+ self.console.print(" [cyan]3[/cyan] - Configure System Agents")
1061
+ self.console.print(" [cyan]4[/cyan] - Enable All")
1062
+ self.console.print(" [cyan]5[/cyan] - Disable All")
1063
+ self.console.print(" [cyan]6[/cyan] - Reset to Defaults")
1064
+ self.console.print(
1065
+ " [cyan]s[/cyan] - Save configuration and start claude-mpm"
1066
+ )
1067
+ self.console.print(" [cyan]b[/cyan] - Cancel and return without saving")
1068
+ self.console.print()
1069
+
1070
+ choice = Prompt.ask("[bold cyan]Select an option[/bold cyan]", default="s")
1071
+
1072
+ if choice == "b":
1073
+ break
1074
+ if choice == "1":
1075
+ self._configure_mcp_services(startup_config, config)
1076
+ elif choice == "2":
1077
+ self._configure_hook_services(startup_config, config)
1078
+ elif choice == "3":
1079
+ self._configure_system_agents(startup_config, config)
1080
+ elif choice == "4":
1081
+ self._enable_all_services(startup_config, config)
1082
+ elif choice == "5":
1083
+ self._disable_all_services(startup_config, config)
1084
+ elif choice == "6":
1085
+ self._reset_to_defaults(startup_config, config)
1086
+ elif choice == "s":
1087
+ # Save and exit if successful
1088
+ if self._save_startup_configuration(startup_config, config):
1089
+ proceed_to_startup = True
1090
+ break
1091
+ else:
1092
+ self.console.print("[red]Invalid choice.[/red]")
1093
+ Prompt.ask("Press Enter to continue")
1094
+
1095
+ return proceed_to_startup
1096
+
1097
+ def _load_startup_configuration(self, config: Config) -> Dict:
1098
+ """Load current startup configuration from config."""
1099
+ startup_config = config.get("startup", {})
1100
+
1101
+ # Ensure all required sections exist
1102
+ if "enabled_mcp_services" not in startup_config:
1103
+ # Get available MCP services from MCPConfigManager
1104
+ mcp_manager = MCPConfigManager()
1105
+ available_services = list(mcp_manager.STATIC_MCP_CONFIGS.keys())
1106
+ startup_config["enabled_mcp_services"] = available_services.copy()
1107
+
1108
+ if "enabled_hook_services" not in startup_config:
1109
+ # Default hook services (health-monitor enabled by default)
1110
+ startup_config["enabled_hook_services"] = [
1111
+ "monitor",
1112
+ "dashboard",
1113
+ "response-logger",
1114
+ "health-monitor",
1115
+ ]
1116
+
1117
+ if "disabled_agents" not in startup_config:
1118
+ # NEW LOGIC: Track DISABLED agents instead of enabled
1119
+ # By default, NO agents are disabled (all agents enabled)
1120
+ startup_config["disabled_agents"] = []
1121
+
1122
+ return startup_config
1123
+
1124
+ def _display_startup_configuration(self, startup_config: Dict) -> None:
1125
+ """Display current startup configuration in a table."""
1126
+ table = Table(
1127
+ title="Current Startup Configuration", box=ROUNDED, show_lines=True
1128
+ )
1129
+
1130
+ table.add_column("Category", style="cyan", width=20)
1131
+ table.add_column("Enabled Services", style="white", width=50)
1132
+ table.add_column("Count", style="dim", width=10)
1133
+
1134
+ # MCP Services
1135
+ mcp_services = startup_config.get("enabled_mcp_services", [])
1136
+ mcp_display = ", ".join(mcp_services[:3]) + (
1137
+ "..." if len(mcp_services) > 3 else ""
1138
+ )
1139
+ table.add_row(
1140
+ "MCP Services",
1141
+ mcp_display if mcp_services else "[dim]None[/dim]",
1142
+ str(len(mcp_services)),
1143
+ )
1144
+
1145
+ # Hook Services
1146
+ hook_services = startup_config.get("enabled_hook_services", [])
1147
+ hook_display = ", ".join(hook_services[:3]) + (
1148
+ "..." if len(hook_services) > 3 else ""
1149
+ )
1150
+ table.add_row(
1151
+ "Hook Services",
1152
+ hook_display if hook_services else "[dim]None[/dim]",
1153
+ str(len(hook_services)),
1154
+ )
1155
+
1156
+ # System Agents - show count of ENABLED agents (total - disabled)
1157
+ all_agents = self.agent_manager.discover_agents() if self.agent_manager else []
1158
+ disabled_agents = startup_config.get("disabled_agents", [])
1159
+ enabled_count = len(all_agents) - len(disabled_agents)
1160
+
1161
+ # Show first few enabled agent names
1162
+ enabled_names = [a.name for a in all_agents if a.name not in disabled_agents]
1163
+ agent_display = ", ".join(enabled_names[:3]) + (
1164
+ "..." if len(enabled_names) > 3 else ""
1165
+ )
1166
+ table.add_row(
1167
+ "System Agents",
1168
+ agent_display if enabled_names else "[dim]All Disabled[/dim]",
1169
+ f"{enabled_count}/{len(all_agents)}",
1170
+ )
1171
+
1172
+ self.console.print(table)
1173
+
1174
+ def _configure_mcp_services(self, startup_config: Dict, config: Config) -> None:
1175
+ """Configure which MCP services to enable at startup."""
1176
+ self.console.clear()
1177
+ self._display_header()
1178
+ self.console.print("[bold]Configure MCP Services[/bold]\n")
1179
+
1180
+ # Get available MCP services
1181
+ mcp_manager = MCPConfigManager()
1182
+ available_services = list(mcp_manager.STATIC_MCP_CONFIGS.keys())
1183
+ enabled_services = set(startup_config.get("enabled_mcp_services", []))
1184
+
1185
+ # Display services with checkboxes
1186
+ table = Table(box=ROUNDED, show_lines=True)
1187
+ table.add_column("ID", style="dim", width=5)
1188
+ table.add_column("Service", style="cyan", width=25)
1189
+ table.add_column("Status", width=15)
1190
+ table.add_column("Description", style="white", width=45)
1191
+
1192
+ service_descriptions = {
1193
+ "kuzu-memory": "Graph-based memory system for agents",
1194
+ "mcp-ticketer": "Ticket and issue tracking integration",
1195
+ "mcp-browser": "Browser automation and web scraping",
1196
+ "mcp-vector-search": "Semantic code search capabilities",
1197
+ }
1198
+
1199
+ for idx, service in enumerate(available_services, 1):
1200
+ status = (
1201
+ "[green]✓ Enabled[/green]"
1202
+ if service in enabled_services
1203
+ else "[red]✗ Disabled[/red]"
1204
+ )
1205
+ description = service_descriptions.get(service, "MCP service")
1206
+ table.add_row(str(idx), service, status, description)
1207
+
1208
+ self.console.print(table)
1209
+ self.console.print("\n[bold]Commands:[/bold]")
1210
+ self.console.print(" Enter service IDs to toggle (e.g., '1,3' or '1-4')")
1211
+ self.console.print(" [cyan][a][/cyan] Enable all")
1212
+ self.console.print(" [cyan][n][/cyan] Disable all")
1213
+ self.console.print(" [cyan][b][/cyan] Back to previous menu")
1214
+ self.console.print()
1215
+
1216
+ choice = Prompt.ask("[bold cyan]Toggle services[/bold cyan]", default="b")
1217
+
1218
+ if choice == "b":
1219
+ return
1220
+ if choice == "a":
1221
+ startup_config["enabled_mcp_services"] = available_services.copy()
1222
+ self.console.print("[green]All MCP services enabled![/green]")
1223
+ elif choice == "n":
1224
+ startup_config["enabled_mcp_services"] = []
1225
+ self.console.print("[green]All MCP services disabled![/green]")
1226
+ else:
1227
+ # Parse service IDs
1228
+ try:
1229
+ selected_ids = self._parse_id_selection(choice, len(available_services))
1230
+ for idx in selected_ids:
1231
+ service = available_services[idx - 1]
1232
+ if service in enabled_services:
1233
+ enabled_services.remove(service)
1234
+ self.console.print(f"[red]Disabled {service}[/red]")
1235
+ else:
1236
+ enabled_services.add(service)
1237
+ self.console.print(f"[green]Enabled {service}[/green]")
1238
+ startup_config["enabled_mcp_services"] = list(enabled_services)
1239
+ except (ValueError, IndexError) as e:
1240
+ self.console.print(f"[red]Invalid selection: {e}[/red]")
1241
+
1242
+ Prompt.ask("Press Enter to continue")
1243
+
1244
+ def _configure_hook_services(self, startup_config: Dict, config: Config) -> None:
1245
+ """Configure which hook services to enable at startup."""
1246
+ self.console.clear()
1247
+ self._display_header()
1248
+ self.console.print("[bold]Configure Hook Services[/bold]\n")
1249
+
1250
+ # Available hook services
1251
+ available_services = [
1252
+ ("monitor", "Real-time event monitoring server (SocketIO)"),
1253
+ ("dashboard", "Web-based dashboard interface"),
1254
+ ("response-logger", "Agent response logging"),
1255
+ ("health-monitor", "Service health and recovery monitoring"),
1256
+ ]
1257
+
1258
+ enabled_services = set(startup_config.get("enabled_hook_services", []))
1259
+
1260
+ # Display services with checkboxes
1261
+ table = Table(box=ROUNDED, show_lines=True)
1262
+ table.add_column("ID", style="dim", width=5)
1263
+ table.add_column("Service", style="cyan", width=25)
1264
+ table.add_column("Status", width=15)
1265
+ table.add_column("Description", style="white", width=45)
1266
+
1267
+ for idx, (service, description) in enumerate(available_services, 1):
1268
+ status = (
1269
+ "[green]✓ Enabled[/green]"
1270
+ if service in enabled_services
1271
+ else "[red]✗ Disabled[/red]"
1272
+ )
1273
+ table.add_row(str(idx), service, status, description)
1274
+
1275
+ self.console.print(table)
1276
+ self.console.print("\n[bold]Commands:[/bold]")
1277
+ self.console.print(" Enter service IDs to toggle (e.g., '1,3' or '1-4')")
1278
+ self.console.print(" [cyan][a][/cyan] Enable all")
1279
+ self.console.print(" [cyan][n][/cyan] Disable all")
1280
+ self.console.print(" [cyan][b][/cyan] Back to previous menu")
1281
+ self.console.print()
1282
+
1283
+ choice = Prompt.ask("[bold cyan]Toggle services[/bold cyan]", default="b")
1284
+
1285
+ if choice == "b":
1286
+ return
1287
+ if choice == "a":
1288
+ startup_config["enabled_hook_services"] = [s[0] for s in available_services]
1289
+ self.console.print("[green]All hook services enabled![/green]")
1290
+ elif choice == "n":
1291
+ startup_config["enabled_hook_services"] = []
1292
+ self.console.print("[green]All hook services disabled![/green]")
1293
+ else:
1294
+ # Parse service IDs
1295
+ try:
1296
+ selected_ids = self._parse_id_selection(choice, len(available_services))
1297
+ for idx in selected_ids:
1298
+ service = available_services[idx - 1][0]
1299
+ if service in enabled_services:
1300
+ enabled_services.remove(service)
1301
+ self.console.print(f"[red]Disabled {service}[/red]")
1302
+ else:
1303
+ enabled_services.add(service)
1304
+ self.console.print(f"[green]Enabled {service}[/green]")
1305
+ startup_config["enabled_hook_services"] = list(enabled_services)
1306
+ except (ValueError, IndexError) as e:
1307
+ self.console.print(f"[red]Invalid selection: {e}[/red]")
1308
+
1309
+ Prompt.ask("Press Enter to continue")
1310
+
1311
+ def _configure_system_agents(self, startup_config: Dict, config: Config) -> None:
1312
+ """Configure which system agents to deploy at startup.
1313
+
1314
+ NEW LOGIC: Uses disabled_agents list. All agents from templates are enabled by default.
1315
+ """
1316
+ while True:
1317
+ self.console.clear()
1318
+ self._display_header()
1319
+ self.console.print("[bold]Configure System Agents[/bold]\n")
1320
+ self.console.print(
1321
+ "[dim]All agents discovered from templates are enabled by default. "
1322
+ "Mark agents as disabled to prevent deployment.[/dim]\n"
1323
+ )
1324
+
1325
+ # Discover available agents from template files
1326
+ agents = self.agent_manager.discover_agents()
1327
+ disabled_agents = set(startup_config.get("disabled_agents", []))
1328
+
1329
+ # Display agents with checkboxes
1330
+ table = Table(box=ROUNDED, show_lines=True)
1331
+ table.add_column("ID", style="dim", width=5)
1332
+ table.add_column("Agent", style="cyan", width=25)
1333
+ table.add_column("Status", width=15)
1334
+ table.add_column("Description", style="white", width=45)
1335
+
1336
+ for idx, agent in enumerate(agents, 1):
1337
+ # Agent is ENABLED if NOT in disabled list
1338
+ is_enabled = agent.name not in disabled_agents
1339
+ status = (
1340
+ "[green]✓ Enabled[/green]"
1341
+ if is_enabled
1342
+ else "[red]✗ Disabled[/red]"
1343
+ )
1344
+ desc_display = (
1345
+ agent.description[:42] + "..."
1346
+ if len(agent.description) > 42
1347
+ else agent.description
1348
+ )
1349
+ table.add_row(str(idx), agent.name, status, desc_display)
1350
+
1351
+ self.console.print(table)
1352
+ self.console.print("\n[bold]Commands:[/bold]")
1353
+ self.console.print(" Enter agent IDs to toggle (e.g., '1,3' or '1-4')")
1354
+ self.console.print(" [cyan]a[/cyan] - Enable all (clear disabled list)")
1355
+ self.console.print(" [cyan]n[/cyan] - Disable all")
1356
+ self.console.print(" [cyan]b[/cyan] - Back to previous menu")
1357
+ self.console.print()
1358
+
1359
+ choice = Prompt.ask("[bold cyan]Select option[/bold cyan]", default="b")
1360
+
1361
+ if choice == "b":
1362
+ return
1363
+ if choice == "a":
1364
+ # Enable all = empty disabled list
1365
+ startup_config["disabled_agents"] = []
1366
+ self.console.print("[green]All agents enabled![/green]")
1367
+ Prompt.ask("Press Enter to continue")
1368
+ elif choice == "n":
1369
+ # Disable all = all agents in disabled list
1370
+ startup_config["disabled_agents"] = [agent.name for agent in agents]
1371
+ self.console.print("[green]All agents disabled![/green]")
1372
+ Prompt.ask("Press Enter to continue")
1373
+ else:
1374
+ # Parse agent IDs
1375
+ try:
1376
+ selected_ids = self._parse_id_selection(choice, len(agents))
1377
+ for idx in selected_ids:
1378
+ agent = agents[idx - 1]
1379
+ if agent.name in disabled_agents:
1380
+ # Currently disabled, enable it (remove from disabled list)
1381
+ disabled_agents.remove(agent.name)
1382
+ self.console.print(f"[green]Enabled {agent.name}[/green]")
1383
+ else:
1384
+ # Currently enabled, disable it (add to disabled list)
1385
+ disabled_agents.add(agent.name)
1386
+ self.console.print(f"[red]Disabled {agent.name}[/red]")
1387
+ startup_config["disabled_agents"] = list(disabled_agents)
1388
+ # Refresh the display to show updated status immediately
1389
+ except (ValueError, IndexError) as e:
1390
+ self.console.print(f"[red]Invalid selection: {e}[/red]")
1391
+ Prompt.ask("Press Enter to continue")
1392
+
1393
+ def _parse_id_selection(self, selection: str, max_id: int) -> List[int]:
1394
+ """Parse ID selection string (e.g., '1,3,5' or '1-4')."""
1395
+ ids = set()
1396
+ parts = selection.split(",")
1397
+
1398
+ for part in parts:
1399
+ part = part.strip()
1400
+ if "-" in part:
1401
+ # Range selection
1402
+ start, end = part.split("-")
1403
+ start_id = int(start.strip())
1404
+ end_id = int(end.strip())
1405
+ if start_id < 1 or end_id > max_id or start_id > end_id:
1406
+ raise ValueError(f"Invalid range: {part}")
1407
+ ids.update(range(start_id, end_id + 1))
1408
+ else:
1409
+ # Single ID
1410
+ id_num = int(part)
1411
+ if id_num < 1 or id_num > max_id:
1412
+ raise ValueError(f"Invalid ID: {id_num}")
1413
+ ids.add(id_num)
1414
+
1415
+ return sorted(ids)
1416
+
1417
+ def _enable_all_services(self, startup_config: Dict, config: Config) -> None:
1418
+ """Enable all services and agents."""
1419
+ if Confirm.ask("[yellow]Enable ALL services and agents?[/yellow]"):
1420
+ # Enable all MCP services
1421
+ mcp_manager = MCPConfigManager()
1422
+ startup_config["enabled_mcp_services"] = list(
1423
+ mcp_manager.STATIC_MCP_CONFIGS.keys()
1424
+ )
1425
+
1426
+ # Enable all hook services
1427
+ startup_config["enabled_hook_services"] = [
1428
+ "monitor",
1429
+ "dashboard",
1430
+ "response-logger",
1431
+ "health-monitor",
1432
+ ]
1433
+
1434
+ # Enable all agents (empty disabled list)
1435
+ startup_config["disabled_agents"] = []
1436
+
1437
+ self.console.print("[green]All services and agents enabled![/green]")
1438
+ Prompt.ask("Press Enter to continue")
1439
+
1440
+ def _disable_all_services(self, startup_config: Dict, config: Config) -> None:
1441
+ """Disable all services and agents."""
1442
+ if Confirm.ask("[yellow]Disable ALL services and agents?[/yellow]"):
1443
+ startup_config["enabled_mcp_services"] = []
1444
+ startup_config["enabled_hook_services"] = []
1445
+ # Disable all agents = add all to disabled list
1446
+ agents = self.agent_manager.discover_agents()
1447
+ startup_config["disabled_agents"] = [agent.name for agent in agents]
1448
+
1449
+ self.console.print("[green]All services and agents disabled![/green]")
1450
+ self.console.print(
1451
+ "[yellow]Note: You may need to enable at least some services for Claude MPM to function properly.[/yellow]"
1452
+ )
1453
+ Prompt.ask("Press Enter to continue")
1454
+
1455
+ def _reset_to_defaults(self, startup_config: Dict, config: Config) -> None:
1456
+ """Reset startup configuration to defaults."""
1457
+ if Confirm.ask("[yellow]Reset startup configuration to defaults?[/yellow]"):
1458
+ # Reset to default values
1459
+ mcp_manager = MCPConfigManager()
1460
+ startup_config["enabled_mcp_services"] = list(
1461
+ mcp_manager.STATIC_MCP_CONFIGS.keys()
1462
+ )
1463
+ startup_config["enabled_hook_services"] = [
1464
+ "monitor",
1465
+ "dashboard",
1466
+ "response-logger",
1467
+ "health-monitor",
1468
+ ]
1469
+ # Default: All agents enabled (empty disabled list)
1470
+ startup_config["disabled_agents"] = []
1471
+
1472
+ self.console.print(
1473
+ "[green]Startup configuration reset to defaults![/green]"
1474
+ )
1475
+ Prompt.ask("Press Enter to continue")
1476
+
1477
+ def _save_startup_configuration(self, startup_config: Dict, config: Config) -> bool:
1478
+ """Save startup configuration to config file and return whether to proceed to startup.
1479
+
1480
+ Returns:
1481
+ bool: True if should proceed to startup, False to continue in menu
1482
+ """
1483
+ try:
1484
+ # Update the startup configuration
1485
+ config.set("startup", startup_config)
1486
+
1487
+ # IMPORTANT: Also update agent_deployment.disabled_agents so the deployment
1488
+ # system actually uses the configured disabled agents list
1489
+ config.set(
1490
+ "agent_deployment.disabled_agents",
1491
+ startup_config.get("disabled_agents", []),
1492
+ )
1493
+
1494
+ # Determine config file path
1495
+ if self.current_scope == "project":
1496
+ config_file = self.project_dir / ".claude-mpm" / "configuration.yaml"
1497
+ else:
1498
+ config_file = Path.home() / ".claude-mpm" / "configuration.yaml"
1499
+
1500
+ # Ensure directory exists
1501
+ config_file.parent.mkdir(parents=True, exist_ok=True)
1502
+
1503
+ # Temporarily suppress INFO logging to avoid duplicate save messages
1504
+ import logging
1505
+
1506
+ root_logger = logging.getLogger("claude_mpm")
1507
+ original_level = root_logger.level
1508
+ root_logger.setLevel(logging.WARNING)
1509
+
1510
+ try:
1511
+ # Save configuration (this will log at INFO level which we've suppressed)
1512
+ config.save(config_file, format="yaml")
1513
+ finally:
1514
+ # Restore original logging level
1515
+ root_logger.setLevel(original_level)
1516
+
1517
+ self.console.print(
1518
+ f"[green]✓ Startup configuration saved to {config_file}[/green]"
1519
+ )
1520
+ self.console.print(
1521
+ "\n[cyan]Applying configuration and launching Claude MPM...[/cyan]\n"
1522
+ )
1523
+
1524
+ # Launch claude-mpm run command to get full startup cycle
1525
+ # This ensures:
1526
+ # 1. Configuration is loaded
1527
+ # 2. Enabled agents are deployed
1528
+ # 3. Disabled agents are removed from .claude/agents/
1529
+ # 4. MCP services and hooks are started
1530
+ try:
1531
+ # Use execvp to replace the current process with claude-mpm run
1532
+ # This ensures a clean transition from configurator to Claude MPM
1533
+ os.execvp("claude-mpm", ["claude-mpm", "run"])
1534
+ except Exception as e:
1535
+ self.console.print(
1536
+ f"[yellow]Could not launch Claude MPM automatically: {e}[/yellow]"
1537
+ )
1538
+ self.console.print(
1539
+ "[cyan]Please run 'claude-mpm' manually to start.[/cyan]"
1540
+ )
1541
+ Prompt.ask("Press Enter to continue")
1542
+ return True
1543
+
1544
+ # This line will never be reached if execvp succeeds
1545
+ return True
1546
+
1547
+ except Exception as e:
1548
+ self.console.print(f"[red]Error saving configuration: {e}[/red]")
1549
+ Prompt.ask("Press Enter to continue")
1550
+ return False
1551
+
982
1552
  def _switch_scope(self) -> None:
983
1553
  """Switch between project and user scope."""
984
1554
  self.current_scope = "user" if self.current_scope == "project" else "project"
@@ -1105,7 +1675,7 @@ Directory: {self.project_dir}
1105
1675
 
1106
1676
  # Write to file
1107
1677
  output_path = Path(file_path)
1108
- with open(output_path, "w") as f:
1678
+ with output_path.open("w") as f:
1109
1679
  json.dump(config_data, f, indent=2)
1110
1680
 
1111
1681
  return CommandResult.success_result(
@@ -1122,7 +1692,7 @@ Directory: {self.project_dir}
1122
1692
  if not input_path.exists():
1123
1693
  return CommandResult.error_result(f"File not found: {file_path}")
1124
1694
 
1125
- with open(input_path) as f:
1695
+ with input_path.open() as f:
1126
1696
  config_data = json.load(f)
1127
1697
 
1128
1698
  # Apply agent states
@@ -1377,6 +1947,20 @@ Directory: {self.project_dir}
1377
1947
  except Exception as e:
1378
1948
  return CommandResult.error_result(f"Behavior management failed: {e}")
1379
1949
 
1950
+ def _run_startup_configuration(self) -> CommandResult:
1951
+ """Jump directly to startup configuration."""
1952
+ try:
1953
+ proceed = self._manage_startup_configuration()
1954
+ if proceed:
1955
+ return CommandResult.success_result(
1956
+ "Startup configuration saved, proceeding to startup"
1957
+ )
1958
+ return CommandResult.success_result("Startup configuration completed")
1959
+ except KeyboardInterrupt:
1960
+ return CommandResult.success_result("Startup configuration cancelled")
1961
+ except Exception as e:
1962
+ return CommandResult.error_result(f"Startup configuration failed: {e}")
1963
+
1380
1964
 
1381
1965
  def manage_configure(args) -> int:
1382
1966
  """Main entry point for configuration management command.