claude-mpm 4.13.2__py3-none-any.whl → 4.18.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- claude_mpm/VERSION +1 -1
- claude_mpm/agents/BASE_ENGINEER.md +286 -0
- claude_mpm/agents/BASE_PM.md +48 -17
- claude_mpm/agents/OUTPUT_STYLE.md +329 -11
- claude_mpm/agents/PM_INSTRUCTIONS.md +227 -8
- claude_mpm/agents/agent_loader.py +17 -5
- claude_mpm/agents/frontmatter_validator.py +284 -253
- claude_mpm/agents/templates/agentic-coder-optimizer.json +9 -2
- claude_mpm/agents/templates/api_qa.json +7 -1
- claude_mpm/agents/templates/clerk-ops.json +8 -1
- claude_mpm/agents/templates/code_analyzer.json +4 -1
- claude_mpm/agents/templates/dart_engineer.json +11 -1
- claude_mpm/agents/templates/data_engineer.json +11 -1
- claude_mpm/agents/templates/documentation.json +6 -1
- claude_mpm/agents/templates/engineer.json +18 -1
- claude_mpm/agents/templates/gcp_ops_agent.json +8 -1
- claude_mpm/agents/templates/golang_engineer.json +11 -1
- claude_mpm/agents/templates/java_engineer.json +12 -2
- claude_mpm/agents/templates/local_ops_agent.json +1217 -6
- claude_mpm/agents/templates/nextjs_engineer.json +11 -1
- claude_mpm/agents/templates/ops.json +8 -1
- claude_mpm/agents/templates/php-engineer.json +11 -1
- claude_mpm/agents/templates/project_organizer.json +10 -3
- claude_mpm/agents/templates/prompt-engineer.json +5 -1
- claude_mpm/agents/templates/python_engineer.json +11 -1
- claude_mpm/agents/templates/qa.json +7 -1
- claude_mpm/agents/templates/react_engineer.json +11 -1
- claude_mpm/agents/templates/refactoring_engineer.json +8 -1
- claude_mpm/agents/templates/research.json +4 -1
- claude_mpm/agents/templates/ruby-engineer.json +11 -1
- claude_mpm/agents/templates/rust_engineer.json +11 -1
- claude_mpm/agents/templates/security.json +6 -1
- claude_mpm/agents/templates/svelte-engineer.json +225 -0
- claude_mpm/agents/templates/ticketing.json +6 -1
- claude_mpm/agents/templates/typescript_engineer.json +11 -1
- claude_mpm/agents/templates/vercel_ops_agent.json +8 -1
- claude_mpm/agents/templates/version_control.json +8 -1
- claude_mpm/agents/templates/web_qa.json +7 -1
- claude_mpm/agents/templates/web_ui.json +11 -1
- claude_mpm/cli/__init__.py +34 -706
- claude_mpm/cli/commands/agent_manager.py +25 -12
- claude_mpm/cli/commands/agent_state_manager.py +186 -0
- claude_mpm/cli/commands/agents.py +204 -148
- claude_mpm/cli/commands/aggregate.py +7 -3
- claude_mpm/cli/commands/analyze.py +9 -4
- claude_mpm/cli/commands/analyze_code.py +7 -2
- claude_mpm/cli/commands/auto_configure.py +7 -9
- claude_mpm/cli/commands/config.py +47 -13
- claude_mpm/cli/commands/configure.py +294 -1788
- claude_mpm/cli/commands/configure_agent_display.py +261 -0
- claude_mpm/cli/commands/configure_behavior_manager.py +204 -0
- claude_mpm/cli/commands/configure_hook_manager.py +225 -0
- claude_mpm/cli/commands/configure_models.py +18 -0
- claude_mpm/cli/commands/configure_navigation.py +167 -0
- claude_mpm/cli/commands/configure_paths.py +104 -0
- claude_mpm/cli/commands/configure_persistence.py +254 -0
- claude_mpm/cli/commands/configure_startup_manager.py +646 -0
- claude_mpm/cli/commands/configure_template_editor.py +497 -0
- claude_mpm/cli/commands/configure_validators.py +73 -0
- claude_mpm/cli/commands/local_deploy.py +537 -0
- claude_mpm/cli/commands/memory.py +54 -20
- claude_mpm/cli/commands/mpm_init.py +39 -25
- claude_mpm/cli/commands/mpm_init_handler.py +8 -3
- claude_mpm/cli/executor.py +202 -0
- claude_mpm/cli/helpers.py +105 -0
- claude_mpm/cli/interactive/__init__.py +3 -0
- claude_mpm/cli/interactive/skills_wizard.py +491 -0
- claude_mpm/cli/parsers/__init__.py +7 -1
- claude_mpm/cli/parsers/base_parser.py +98 -3
- claude_mpm/cli/parsers/local_deploy_parser.py +227 -0
- claude_mpm/cli/shared/output_formatters.py +28 -19
- claude_mpm/cli/startup.py +481 -0
- claude_mpm/cli/utils.py +52 -1
- claude_mpm/commands/mpm-help.md +3 -0
- claude_mpm/commands/mpm-version.md +113 -0
- claude_mpm/commands/mpm.md +1 -0
- claude_mpm/config/agent_config.py +2 -2
- claude_mpm/config/model_config.py +428 -0
- claude_mpm/core/base_service.py +13 -12
- claude_mpm/core/enums.py +452 -0
- claude_mpm/core/factories.py +1 -1
- claude_mpm/core/instruction_reinforcement_hook.py +2 -1
- claude_mpm/core/interactive_session.py +9 -3
- claude_mpm/core/logging_config.py +6 -2
- claude_mpm/core/oneshot_session.py +8 -4
- claude_mpm/core/optimized_agent_loader.py +3 -3
- claude_mpm/core/output_style_manager.py +12 -192
- claude_mpm/core/service_registry.py +5 -1
- claude_mpm/core/types.py +2 -9
- claude_mpm/core/typing_utils.py +7 -6
- claude_mpm/dashboard/static/js/dashboard.js +0 -14
- claude_mpm/dashboard/templates/index.html +3 -41
- claude_mpm/hooks/claude_hooks/response_tracking.py +35 -1
- claude_mpm/hooks/instruction_reinforcement.py +7 -2
- claude_mpm/models/resume_log.py +340 -0
- claude_mpm/services/agents/auto_config_manager.py +10 -11
- claude_mpm/services/agents/deployment/agent_configuration_manager.py +1 -1
- claude_mpm/services/agents/deployment/agent_record_service.py +1 -1
- claude_mpm/services/agents/deployment/agent_validator.py +17 -1
- claude_mpm/services/agents/deployment/async_agent_deployment.py +1 -1
- claude_mpm/services/agents/deployment/interface_adapter.py +3 -2
- claude_mpm/services/agents/deployment/local_template_deployment.py +1 -1
- claude_mpm/services/agents/deployment/pipeline/steps/agent_processing_step.py +7 -6
- claude_mpm/services/agents/deployment/pipeline/steps/base_step.py +7 -16
- claude_mpm/services/agents/deployment/pipeline/steps/configuration_step.py +4 -3
- claude_mpm/services/agents/deployment/pipeline/steps/target_directory_step.py +5 -3
- claude_mpm/services/agents/deployment/pipeline/steps/validation_step.py +6 -5
- claude_mpm/services/agents/deployment/refactored_agent_deployment_service.py +9 -6
- claude_mpm/services/agents/deployment/validation/__init__.py +3 -1
- claude_mpm/services/agents/deployment/validation/validation_result.py +1 -9
- claude_mpm/services/agents/local_template_manager.py +1 -1
- claude_mpm/services/agents/memory/agent_memory_manager.py +5 -2
- claude_mpm/services/agents/registry/modification_tracker.py +5 -2
- claude_mpm/services/command_handler_service.py +11 -5
- claude_mpm/services/core/interfaces/__init__.py +74 -2
- claude_mpm/services/core/interfaces/health.py +172 -0
- claude_mpm/services/core/interfaces/model.py +281 -0
- claude_mpm/services/core/interfaces/process.py +372 -0
- claude_mpm/services/core/interfaces/restart.py +307 -0
- claude_mpm/services/core/interfaces/stability.py +260 -0
- claude_mpm/services/core/models/__init__.py +33 -0
- claude_mpm/services/core/models/agent_config.py +12 -28
- claude_mpm/services/core/models/health.py +162 -0
- claude_mpm/services/core/models/process.py +235 -0
- claude_mpm/services/core/models/restart.py +302 -0
- claude_mpm/services/core/models/stability.py +264 -0
- claude_mpm/services/core/path_resolver.py +23 -7
- claude_mpm/services/diagnostics/__init__.py +2 -2
- claude_mpm/services/diagnostics/checks/agent_check.py +25 -24
- claude_mpm/services/diagnostics/checks/claude_code_check.py +24 -23
- claude_mpm/services/diagnostics/checks/common_issues_check.py +25 -24
- claude_mpm/services/diagnostics/checks/configuration_check.py +24 -23
- claude_mpm/services/diagnostics/checks/filesystem_check.py +18 -17
- claude_mpm/services/diagnostics/checks/installation_check.py +30 -29
- claude_mpm/services/diagnostics/checks/instructions_check.py +20 -19
- claude_mpm/services/diagnostics/checks/mcp_check.py +50 -36
- claude_mpm/services/diagnostics/checks/mcp_services_check.py +36 -31
- claude_mpm/services/diagnostics/checks/monitor_check.py +23 -22
- claude_mpm/services/diagnostics/checks/startup_log_check.py +9 -8
- claude_mpm/services/diagnostics/diagnostic_runner.py +6 -5
- claude_mpm/services/diagnostics/doctor_reporter.py +28 -25
- claude_mpm/services/diagnostics/models.py +19 -24
- claude_mpm/services/infrastructure/monitoring/__init__.py +1 -1
- claude_mpm/services/infrastructure/monitoring/aggregator.py +12 -12
- claude_mpm/services/infrastructure/monitoring/base.py +5 -13
- claude_mpm/services/infrastructure/monitoring/network.py +7 -6
- claude_mpm/services/infrastructure/monitoring/process.py +13 -12
- claude_mpm/services/infrastructure/monitoring/resources.py +7 -6
- claude_mpm/services/infrastructure/monitoring/service.py +16 -15
- claude_mpm/services/infrastructure/resume_log_generator.py +439 -0
- claude_mpm/services/local_ops/__init__.py +163 -0
- claude_mpm/services/local_ops/crash_detector.py +257 -0
- claude_mpm/services/local_ops/health_checks/__init__.py +28 -0
- claude_mpm/services/local_ops/health_checks/http_check.py +224 -0
- claude_mpm/services/local_ops/health_checks/process_check.py +236 -0
- claude_mpm/services/local_ops/health_checks/resource_check.py +255 -0
- claude_mpm/services/local_ops/health_manager.py +430 -0
- claude_mpm/services/local_ops/log_monitor.py +396 -0
- claude_mpm/services/local_ops/memory_leak_detector.py +294 -0
- claude_mpm/services/local_ops/process_manager.py +595 -0
- claude_mpm/services/local_ops/resource_monitor.py +331 -0
- claude_mpm/services/local_ops/restart_manager.py +401 -0
- claude_mpm/services/local_ops/restart_policy.py +387 -0
- claude_mpm/services/local_ops/state_manager.py +372 -0
- claude_mpm/services/local_ops/unified_manager.py +600 -0
- claude_mpm/services/mcp_config_manager.py +9 -4
- claude_mpm/services/mcp_gateway/core/__init__.py +1 -2
- claude_mpm/services/mcp_gateway/core/base.py +18 -31
- claude_mpm/services/mcp_gateway/tools/external_mcp_services.py +71 -24
- claude_mpm/services/mcp_gateway/tools/health_check_tool.py +30 -28
- claude_mpm/services/memory_hook_service.py +4 -1
- claude_mpm/services/model/__init__.py +147 -0
- claude_mpm/services/model/base_provider.py +365 -0
- claude_mpm/services/model/claude_provider.py +412 -0
- claude_mpm/services/model/model_router.py +453 -0
- claude_mpm/services/model/ollama_provider.py +415 -0
- claude_mpm/services/monitor/daemon_manager.py +3 -2
- claude_mpm/services/monitor/handlers/dashboard.py +2 -1
- claude_mpm/services/monitor/handlers/hooks.py +2 -1
- claude_mpm/services/monitor/management/lifecycle.py +3 -2
- claude_mpm/services/monitor/server.py +2 -1
- claude_mpm/services/session_management_service.py +3 -2
- claude_mpm/services/session_manager.py +205 -1
- claude_mpm/services/shared/async_service_base.py +16 -27
- claude_mpm/services/shared/lifecycle_service_base.py +1 -14
- claude_mpm/services/socketio/handlers/__init__.py +5 -2
- claude_mpm/services/socketio/handlers/hook.py +13 -2
- claude_mpm/services/socketio/handlers/registry.py +4 -2
- claude_mpm/services/socketio/server/main.py +10 -8
- claude_mpm/services/subprocess_launcher_service.py +14 -5
- claude_mpm/services/unified/analyzer_strategies/code_analyzer.py +8 -7
- claude_mpm/services/unified/analyzer_strategies/dependency_analyzer.py +6 -5
- claude_mpm/services/unified/analyzer_strategies/performance_analyzer.py +8 -7
- claude_mpm/services/unified/analyzer_strategies/security_analyzer.py +7 -6
- claude_mpm/services/unified/analyzer_strategies/structure_analyzer.py +5 -4
- claude_mpm/services/unified/config_strategies/validation_strategy.py +13 -9
- claude_mpm/services/unified/deployment_strategies/cloud_strategies.py +10 -3
- claude_mpm/services/unified/deployment_strategies/local.py +6 -5
- claude_mpm/services/unified/deployment_strategies/utils.py +6 -5
- claude_mpm/services/unified/deployment_strategies/vercel.py +7 -6
- claude_mpm/services/unified/interfaces.py +3 -1
- claude_mpm/services/unified/unified_analyzer.py +14 -10
- claude_mpm/services/unified/unified_config.py +2 -1
- claude_mpm/services/unified/unified_deployment.py +9 -4
- claude_mpm/services/version_service.py +104 -1
- claude_mpm/skills/__init__.py +21 -0
- claude_mpm/skills/bundled/__init__.py +6 -0
- claude_mpm/skills/bundled/api-documentation.md +393 -0
- claude_mpm/skills/bundled/async-testing.md +571 -0
- claude_mpm/skills/bundled/code-review.md +143 -0
- claude_mpm/skills/bundled/database-migration.md +199 -0
- claude_mpm/skills/bundled/docker-containerization.md +194 -0
- claude_mpm/skills/bundled/express-local-dev.md +1429 -0
- claude_mpm/skills/bundled/fastapi-local-dev.md +1199 -0
- claude_mpm/skills/bundled/git-workflow.md +414 -0
- claude_mpm/skills/bundled/imagemagick.md +204 -0
- claude_mpm/skills/bundled/json-data-handling.md +223 -0
- claude_mpm/skills/bundled/nextjs-local-dev.md +807 -0
- claude_mpm/skills/bundled/pdf.md +141 -0
- claude_mpm/skills/bundled/performance-profiling.md +567 -0
- claude_mpm/skills/bundled/refactoring-patterns.md +180 -0
- claude_mpm/skills/bundled/security-scanning.md +327 -0
- claude_mpm/skills/bundled/systematic-debugging.md +473 -0
- claude_mpm/skills/bundled/test-driven-development.md +378 -0
- claude_mpm/skills/bundled/vite-local-dev.md +1061 -0
- claude_mpm/skills/bundled/web-performance-optimization.md +2305 -0
- claude_mpm/skills/bundled/xlsx.md +157 -0
- claude_mpm/skills/registry.py +286 -0
- claude_mpm/skills/skill_manager.py +310 -0
- claude_mpm/tools/code_tree_analyzer.py +177 -141
- claude_mpm/tools/code_tree_events.py +4 -2
- claude_mpm/utils/agent_dependency_loader.py +2 -2
- {claude_mpm-4.13.2.dist-info → claude_mpm-4.18.2.dist-info}/METADATA +117 -8
- {claude_mpm-4.13.2.dist-info → claude_mpm-4.18.2.dist-info}/RECORD +238 -174
- claude_mpm/dashboard/static/css/code-tree.css +0 -1639
- claude_mpm/dashboard/static/js/components/code-tree/tree-breadcrumb.js +0 -353
- claude_mpm/dashboard/static/js/components/code-tree/tree-constants.js +0 -235
- claude_mpm/dashboard/static/js/components/code-tree/tree-search.js +0 -409
- claude_mpm/dashboard/static/js/components/code-tree/tree-utils.js +0 -435
- claude_mpm/dashboard/static/js/components/code-tree.js +0 -5869
- claude_mpm/dashboard/static/js/components/code-viewer.js +0 -1386
- claude_mpm/hooks/claude_hooks/hook_handler_eventbus.py +0 -425
- claude_mpm/hooks/claude_hooks/hook_handler_original.py +0 -1041
- claude_mpm/hooks/claude_hooks/hook_handler_refactored.py +0 -347
- claude_mpm/services/agents/deployment/agent_lifecycle_manager_refactored.py +0 -575
- claude_mpm/services/project/analyzer_refactored.py +0 -450
- {claude_mpm-4.13.2.dist-info → claude_mpm-4.18.2.dist-info}/WHEEL +0 -0
- {claude_mpm-4.13.2.dist-info → claude_mpm-4.18.2.dist-info}/entry_points.txt +0 -0
- {claude_mpm-4.13.2.dist-info → claude_mpm-4.18.2.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-4.13.2.dist-info → claude_mpm-4.18.2.dist-info}/top_level.txt +0 -0
claude_mpm/cli/__init__.py
CHANGED
|
@@ -1,290 +1,85 @@
|
|
|
1
|
-
from pathlib import Path
|
|
2
|
-
from typing import Optional
|
|
3
|
-
|
|
4
1
|
"""
|
|
5
2
|
Claude MPM Command-Line Interface.
|
|
6
3
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
4
|
+
Main entry point for CLI. Implementation details extracted to:
|
|
5
|
+
- cli/helpers.py: Configuration checks and prompts
|
|
6
|
+
- cli/startup.py: Initialization (registry, MCP, updates)
|
|
7
|
+
- cli/executor.py: Command execution routing
|
|
10
8
|
|
|
11
|
-
|
|
12
|
-
interface while organizing code into logical modules. The main() function
|
|
13
|
-
remains the primary entry point for both direct execution and package imports.
|
|
9
|
+
Refactored from 803 lines to <130 lines (TSK-0053).
|
|
14
10
|
"""
|
|
15
11
|
|
|
12
|
+
import os
|
|
16
13
|
import sys
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
from typing import Optional
|
|
17
16
|
|
|
18
17
|
from claude_mpm.config.paths import paths
|
|
19
18
|
|
|
20
|
-
from ..constants import CLICommands
|
|
21
|
-
from .
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
manage_config,
|
|
27
|
-
manage_configure,
|
|
28
|
-
manage_debug,
|
|
29
|
-
manage_mcp,
|
|
30
|
-
manage_memory,
|
|
31
|
-
manage_monitor,
|
|
32
|
-
manage_tickets,
|
|
33
|
-
run_doctor,
|
|
34
|
-
run_session,
|
|
35
|
-
show_info,
|
|
19
|
+
from ..constants import CLICommands
|
|
20
|
+
from .executor import ensure_run_attributes, execute_command
|
|
21
|
+
from .helpers import (
|
|
22
|
+
handle_missing_configuration,
|
|
23
|
+
has_configuration_file,
|
|
24
|
+
should_skip_config_check,
|
|
36
25
|
)
|
|
37
|
-
from .commands.analyze_code import manage_analyze_code
|
|
38
|
-
from .commands.dashboard import manage_dashboard
|
|
39
|
-
from .commands.upgrade import upgrade
|
|
40
26
|
from .parser import create_parser, preprocess_args
|
|
27
|
+
from .startup import (
|
|
28
|
+
run_background_services,
|
|
29
|
+
setup_configure_command_environment,
|
|
30
|
+
setup_early_environment,
|
|
31
|
+
setup_mcp_server_logging,
|
|
32
|
+
should_skip_background_services,
|
|
33
|
+
)
|
|
41
34
|
from .utils import ensure_directories, setup_logging
|
|
42
35
|
|
|
43
|
-
#
|
|
44
|
-
# Try package VERSION file first (for installed packages)
|
|
36
|
+
# Version resolution
|
|
45
37
|
package_version_file = Path(__file__).parent.parent / "VERSION"
|
|
46
38
|
if package_version_file.exists():
|
|
47
39
|
__version__ = package_version_file.read_text().strip()
|
|
48
|
-
# Use centralized path management for VERSION file
|
|
49
40
|
elif paths.version_file.exists():
|
|
50
41
|
__version__ = paths.version_file.read_text().strip()
|
|
51
42
|
else:
|
|
52
|
-
# Try to import from package as fallback
|
|
53
43
|
try:
|
|
54
44
|
from .. import __version__
|
|
55
45
|
except ImportError:
|
|
56
|
-
# Default version if all else fails
|
|
57
46
|
__version__ = "0.0.0"
|
|
58
47
|
|
|
59
48
|
|
|
60
|
-
def _has_configuration_file() -> bool:
|
|
61
|
-
"""Check if any configuration file exists in standard locations."""
|
|
62
|
-
config_paths = [
|
|
63
|
-
Path.cwd() / ".claude-mpm" / "configuration.yaml",
|
|
64
|
-
Path.cwd() / ".claude-mpm" / "configuration.yml",
|
|
65
|
-
Path.home() / ".claude-mpm" / "configuration.yaml",
|
|
66
|
-
Path.home() / ".claude-mpm" / "configuration.yml",
|
|
67
|
-
]
|
|
68
|
-
return any(path.exists() for path in config_paths)
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
def _is_interactive_session() -> bool:
|
|
72
|
-
"""Check if running in an interactive terminal."""
|
|
73
|
-
return sys.stdin.isatty() and sys.stdout.isatty()
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
def _should_skip_config_check(command: Optional[str]) -> bool:
|
|
77
|
-
"""Check if command should skip configuration check."""
|
|
78
|
-
skip_commands = ["configure", "doctor", "info", "mcp", "config"]
|
|
79
|
-
return command in skip_commands if command else False
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
def _prompt_for_configuration() -> bool:
|
|
83
|
-
"""Prompt user to run configuration wizard.
|
|
84
|
-
|
|
85
|
-
Returns:
|
|
86
|
-
bool: True if user wants to configure, False otherwise
|
|
87
|
-
"""
|
|
88
|
-
from rich.console import Console as RichConsole
|
|
89
|
-
from rich.prompt import Confirm
|
|
90
|
-
|
|
91
|
-
console = RichConsole()
|
|
92
|
-
|
|
93
|
-
console.print()
|
|
94
|
-
console.print("[yellow]⚙️ Configuration Recommended[/yellow]")
|
|
95
|
-
console.print()
|
|
96
|
-
console.print("Claude MPM works best with proper configuration.")
|
|
97
|
-
console.print("The configurator helps you:")
|
|
98
|
-
console.print(" • Enable/disable MCP services (ticketer, browser, vector-search)")
|
|
99
|
-
console.print(" • Configure hook services (monitor, dashboard)")
|
|
100
|
-
console.print(" • Select system agents to use")
|
|
101
|
-
console.print()
|
|
102
|
-
|
|
103
|
-
return Confirm.ask("Would you like to run the configurator now?", default=True)
|
|
104
|
-
|
|
105
|
-
|
|
106
49
|
def main(argv: Optional[list] = None):
|
|
107
|
-
"""
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
WHY: This function orchestrates the entire CLI flow:
|
|
111
|
-
1. Ensures directories exist
|
|
112
|
-
2. Preprocesses arguments (handling --mpm: prefix)
|
|
113
|
-
3. Parses arguments
|
|
114
|
-
4. Sets up logging
|
|
115
|
-
5. Executes the appropriate command
|
|
116
|
-
|
|
117
|
-
DESIGN DECISION: We keep error handling at this level to provide consistent
|
|
118
|
-
error messages and exit codes across all commands.
|
|
119
|
-
|
|
120
|
-
Args:
|
|
121
|
-
argv: Optional list of command line arguments for testing
|
|
122
|
-
|
|
123
|
-
Returns:
|
|
124
|
-
Exit code (0 for success, non-zero for errors)
|
|
125
|
-
"""
|
|
126
|
-
# Disable telemetry by default (set early in case any imported modules check it)
|
|
127
|
-
import os
|
|
128
|
-
|
|
129
|
-
os.environ.setdefault("DISABLE_TELEMETRY", "1")
|
|
130
|
-
|
|
131
|
-
# Set environment variable BEFORE parsing args to prevent cleanup noise during configure
|
|
132
|
-
# This will be checked later after we know the command
|
|
133
|
-
os.environ.setdefault("CLAUDE_MPM_SKIP_CLEANUP", "0")
|
|
134
|
-
|
|
135
|
-
# EARLY CHECK: If 'configure' is in argv, suppress logging IMMEDIATELY
|
|
136
|
-
# This must happen BEFORE any imports that trigger logging
|
|
137
|
-
if argv is None:
|
|
138
|
-
argv = sys.argv[1:]
|
|
139
|
-
if "configure" in argv or (len(argv) > 0 and argv[0] == "configure"):
|
|
140
|
-
import logging
|
|
141
|
-
|
|
142
|
-
logging.getLogger("claude_mpm").setLevel(logging.WARNING)
|
|
143
|
-
os.environ["CLAUDE_MPM_SKIP_CLEANUP"] = "1"
|
|
50
|
+
"""Main CLI entry point orchestrating argument parsing and command execution."""
|
|
51
|
+
argv = setup_early_environment(argv)
|
|
144
52
|
|
|
145
|
-
# Parse args early to check if we should skip background services
|
|
146
|
-
# (for commands like --version, --help, configure, etc.)
|
|
147
53
|
parser = create_parser(version=__version__)
|
|
148
54
|
processed_argv = preprocess_args(argv)
|
|
149
55
|
args = parser.parse_args(processed_argv)
|
|
150
56
|
|
|
151
|
-
# Check for configuration file on startup
|
|
152
|
-
# Skip for help/version flags or specific commands
|
|
153
57
|
help_version_flags = ["--version", "-v", "--help", "-h"]
|
|
154
58
|
is_help_or_version = any(
|
|
155
59
|
flag in (processed_argv or sys.argv[1:]) for flag in help_version_flags
|
|
156
60
|
)
|
|
157
61
|
|
|
158
|
-
if not
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
if _is_interactive_session():
|
|
162
|
-
# Interactive: Offer to run configurator
|
|
163
|
-
if _prompt_for_configuration():
|
|
164
|
-
# User wants to configure - run configure command
|
|
165
|
-
from rich.console import Console as RichConsole
|
|
166
|
-
|
|
167
|
-
from .commands.configure import ConfigureCommand
|
|
168
|
-
|
|
169
|
-
console = RichConsole()
|
|
170
|
-
config_cmd = ConfigureCommand()
|
|
171
|
-
try:
|
|
172
|
-
config_cmd.execute({})
|
|
173
|
-
console.print(
|
|
174
|
-
"\n[green]✓[/green] Configuration complete! "
|
|
175
|
-
"Continuing with command...\n"
|
|
176
|
-
)
|
|
177
|
-
except Exception as e:
|
|
178
|
-
console.print(f"\n[yellow]⚠[/yellow] Configuration error: {e}")
|
|
179
|
-
console.print(
|
|
180
|
-
"[yellow]Continuing with default "
|
|
181
|
-
"configuration...\n[/yellow]"
|
|
182
|
-
)
|
|
183
|
-
else:
|
|
184
|
-
from rich.console import Console as RichConsole
|
|
185
|
-
|
|
186
|
-
console = RichConsole()
|
|
187
|
-
console.print("\n[dim]Using default configuration.[/dim]")
|
|
188
|
-
console.print(
|
|
189
|
-
"[dim]Run 'claude-mpm configure' anytime to set up your "
|
|
190
|
-
"preferences.\n[/dim]"
|
|
191
|
-
)
|
|
192
|
-
else:
|
|
193
|
-
# Non-interactive: Log message only
|
|
194
|
-
import logging
|
|
195
|
-
|
|
196
|
-
logger = logging.getLogger(__name__)
|
|
197
|
-
logger.info(
|
|
198
|
-
"Configuration file not found, using defaults. "
|
|
199
|
-
"Run 'claude-mpm configure' to customize."
|
|
200
|
-
)
|
|
201
|
-
|
|
202
|
-
# Skip background services for configure command - it needs clean state
|
|
203
|
-
# Also skip for help/version/diagnostic commands
|
|
204
|
-
skip_background_services_commands = ["--version", "-v", "--help", "-h"]
|
|
205
|
-
should_skip_background = any(
|
|
206
|
-
cmd in (processed_argv or sys.argv[1:])
|
|
207
|
-
for cmd in skip_background_services_commands
|
|
208
|
-
) or (
|
|
209
|
-
hasattr(args, "command")
|
|
210
|
-
and args.command in ["info", "doctor", "config", "mcp", "configure"]
|
|
211
|
-
)
|
|
212
|
-
|
|
213
|
-
# Set environment variable to skip cleanup for configure command
|
|
214
|
-
# Also suppress INFO logging to prevent noise during configure
|
|
215
|
-
if hasattr(args, "command") and args.command == "configure":
|
|
216
|
-
os.environ["CLAUDE_MPM_SKIP_CLEANUP"] = "1"
|
|
217
|
-
# Suppress INFO logs for configure command
|
|
218
|
-
import logging
|
|
219
|
-
|
|
220
|
-
logging.getLogger("claude_mpm").setLevel(logging.WARNING)
|
|
62
|
+
if not has_configuration_file() and not is_help_or_version:
|
|
63
|
+
if not should_skip_config_check(getattr(args, "command", None)):
|
|
64
|
+
handle_missing_configuration()
|
|
221
65
|
|
|
222
|
-
|
|
223
|
-
if not should_skip_background:
|
|
224
|
-
# Ensure directories are initialized on first run
|
|
225
|
-
ensure_directories()
|
|
66
|
+
setup_configure_command_environment(args)
|
|
226
67
|
|
|
227
|
-
|
|
228
|
-
|
|
68
|
+
ensure_directories()
|
|
69
|
+
if not should_skip_background_services(args, processed_argv):
|
|
70
|
+
run_background_services()
|
|
71
|
+
logger = setup_mcp_server_logging(args)
|
|
229
72
|
|
|
230
|
-
# Check for MCP auto-configuration (pipx installations)
|
|
231
|
-
_check_mcp_auto_configuration()
|
|
232
|
-
|
|
233
|
-
# Re-enabled: MCP pre-warming is safe with subprocess daemon (v4.2.40)
|
|
234
|
-
# The subprocess approach avoids fork() issues entirely
|
|
235
|
-
_verify_mcp_gateway_startup()
|
|
236
|
-
|
|
237
|
-
# Check for updates (non-blocking, async)
|
|
238
|
-
_check_for_updates_async()
|
|
239
|
-
else:
|
|
240
|
-
# Still need directories for configure command to work
|
|
241
|
-
ensure_directories()
|
|
242
|
-
|
|
243
|
-
# Set up logging
|
|
244
|
-
# Special case: For MCP start command, we need minimal logging to avoid stdout interference
|
|
245
|
-
if (
|
|
246
|
-
args.command == CLICommands.MCP.value
|
|
247
|
-
and getattr(args, "mcp_command", None) == "start"
|
|
248
|
-
):
|
|
249
|
-
# For MCP server, configure minimal stderr-only logging
|
|
250
|
-
import logging
|
|
251
|
-
|
|
252
|
-
# sys is already imported at module level
|
|
253
|
-
# Only log errors to stderr for MCP server
|
|
254
|
-
if not getattr(args, "test", False) and not getattr(
|
|
255
|
-
args, "instructions", False
|
|
256
|
-
):
|
|
257
|
-
# Production MCP mode - minimal logging
|
|
258
|
-
logging.basicConfig(
|
|
259
|
-
level=logging.ERROR, format="%(message)s", stream=sys.stderr, force=True
|
|
260
|
-
)
|
|
261
|
-
logger = logging.getLogger("claude_mpm")
|
|
262
|
-
else:
|
|
263
|
-
# Test or instructions mode - normal logging
|
|
264
|
-
logger = setup_logging(args)
|
|
265
|
-
else:
|
|
266
|
-
# Normal logging for all other commands
|
|
267
|
-
logger = setup_logging(args)
|
|
268
|
-
|
|
269
|
-
# Debug output if requested
|
|
270
73
|
if hasattr(args, "debug") and args.debug:
|
|
271
74
|
logger.debug(f"Command: {args.command}")
|
|
272
75
|
logger.debug(f"Arguments: {args}")
|
|
273
76
|
|
|
274
|
-
# Hook system note: Claude Code hooks are handled externally via the
|
|
275
|
-
# hook_handler.py script installed in ~/.claude/settings.json
|
|
276
|
-
# The --no-hooks flag is kept for backward compatibility but doesn't affect
|
|
277
|
-
# Claude Code hooks which are configured separately.
|
|
278
|
-
|
|
279
|
-
# Default to run command if no command specified
|
|
280
77
|
if not args.command:
|
|
281
78
|
args.command = CLICommands.RUN.value
|
|
282
|
-
|
|
283
|
-
_ensure_run_attributes(args)
|
|
79
|
+
ensure_run_attributes(args)
|
|
284
80
|
|
|
285
|
-
# Execute command
|
|
286
81
|
try:
|
|
287
|
-
return
|
|
82
|
+
return execute_command(args.command, args)
|
|
288
83
|
except KeyboardInterrupt:
|
|
289
84
|
logger.info("Session interrupted by user")
|
|
290
85
|
return 0
|
|
@@ -297,473 +92,6 @@ def main(argv: Optional[list] = None):
|
|
|
297
92
|
return 1
|
|
298
93
|
|
|
299
94
|
|
|
300
|
-
def _initialize_project_registry():
|
|
301
|
-
"""
|
|
302
|
-
Initialize or update the project registry for the current session.
|
|
303
|
-
|
|
304
|
-
WHY: The project registry tracks all claude-mpm projects and their metadata
|
|
305
|
-
across sessions. This function ensures the current project is properly
|
|
306
|
-
registered and updates session information.
|
|
307
|
-
|
|
308
|
-
DESIGN DECISION: Registry failures are logged but don't prevent startup
|
|
309
|
-
to ensure claude-mpm remains functional even if registry operations fail.
|
|
310
|
-
"""
|
|
311
|
-
try:
|
|
312
|
-
from ..services.project.registry import ProjectRegistry
|
|
313
|
-
|
|
314
|
-
registry = ProjectRegistry()
|
|
315
|
-
registry.get_or_create_project_entry()
|
|
316
|
-
except Exception as e:
|
|
317
|
-
# Import logger here to avoid circular imports
|
|
318
|
-
from ..core.logger import get_logger
|
|
319
|
-
|
|
320
|
-
logger = get_logger("cli")
|
|
321
|
-
logger.debug(f"Failed to initialize project registry: {e}")
|
|
322
|
-
# Continue execution - registry failure shouldn't block startup
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
def _check_mcp_auto_configuration():
|
|
326
|
-
"""
|
|
327
|
-
Check and potentially auto-configure MCP for pipx installations.
|
|
328
|
-
|
|
329
|
-
WHY: Users installing via pipx should have MCP work out-of-the-box with
|
|
330
|
-
minimal friction. This function offers one-time auto-configuration with
|
|
331
|
-
user consent.
|
|
332
|
-
|
|
333
|
-
DESIGN DECISION: This is blocking but quick - it only runs once and has
|
|
334
|
-
a 10-second timeout. We want to catch users on first run for the best
|
|
335
|
-
experience.
|
|
336
|
-
"""
|
|
337
|
-
try:
|
|
338
|
-
from ..services.mcp_gateway.auto_configure import check_and_configure_mcp
|
|
339
|
-
|
|
340
|
-
# This function handles all the logic:
|
|
341
|
-
# - Checks if already configured
|
|
342
|
-
# - Checks if pipx installation
|
|
343
|
-
# - Checks if already asked before
|
|
344
|
-
# - Prompts user if needed
|
|
345
|
-
# - Configures if user agrees
|
|
346
|
-
check_and_configure_mcp()
|
|
347
|
-
|
|
348
|
-
except Exception as e:
|
|
349
|
-
# Non-critical - log but don't fail
|
|
350
|
-
from ..core.logger import get_logger
|
|
351
|
-
|
|
352
|
-
logger = get_logger("cli")
|
|
353
|
-
logger.debug(f"MCP auto-configuration check failed: {e}")
|
|
354
|
-
|
|
355
|
-
# Skip MCP service fixes for the doctor and configure commands
|
|
356
|
-
# The doctor command performs its own comprehensive MCP service check
|
|
357
|
-
# The configure command allows users to configure which services to enable
|
|
358
|
-
# Running both would cause duplicate checks and log messages (9 seconds apart)
|
|
359
|
-
import sys
|
|
360
|
-
|
|
361
|
-
if len(sys.argv) > 1 and sys.argv[1] in ("doctor", "configure"):
|
|
362
|
-
return
|
|
363
|
-
|
|
364
|
-
# Also ensure MCP services are properly configured in ~/.claude.json
|
|
365
|
-
# This fixes incorrect paths and adds missing services
|
|
366
|
-
try:
|
|
367
|
-
from ..core.logger import get_logger
|
|
368
|
-
from ..services.mcp_config_manager import MCPConfigManager
|
|
369
|
-
|
|
370
|
-
logger = get_logger("cli")
|
|
371
|
-
mcp_manager = MCPConfigManager()
|
|
372
|
-
|
|
373
|
-
# Fix any corrupted installations first
|
|
374
|
-
fix_success, fix_message = mcp_manager.fix_mcp_service_issues()
|
|
375
|
-
if fix_message and "Fixed:" in fix_message:
|
|
376
|
-
logger.info(f"MCP service fixes applied: {fix_message}")
|
|
377
|
-
|
|
378
|
-
# Ensure all services are configured correctly
|
|
379
|
-
config_success, config_message = mcp_manager.ensure_mcp_services_configured()
|
|
380
|
-
if config_message and "Added MCP services" in config_message:
|
|
381
|
-
logger.info(f"MCP services configured: {config_message}")
|
|
382
|
-
|
|
383
|
-
except Exception as e:
|
|
384
|
-
# Non-critical - log but don't fail
|
|
385
|
-
from ..core.logger import get_logger
|
|
386
|
-
|
|
387
|
-
logger = get_logger("cli")
|
|
388
|
-
logger.debug(f"MCP services configuration update failed: {e}")
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
def _verify_mcp_gateway_startup():
|
|
392
|
-
"""
|
|
393
|
-
Verify MCP Gateway configuration on startup and pre-warm MCP services.
|
|
394
|
-
|
|
395
|
-
WHY: The MCP gateway should be automatically configured and verified on startup
|
|
396
|
-
to provide a seamless experience with diagnostic tools, file summarizer, and
|
|
397
|
-
ticket service. Pre-warming MCP services eliminates the 11.9s delay on first use.
|
|
398
|
-
|
|
399
|
-
DESIGN DECISION: This is non-blocking - failures are logged but don't prevent
|
|
400
|
-
startup to ensure claude-mpm remains functional even if MCP gateway has issues.
|
|
401
|
-
"""
|
|
402
|
-
# Quick verification of MCP services installation
|
|
403
|
-
try:
|
|
404
|
-
from ..core.logger import get_logger
|
|
405
|
-
from ..services.mcp_service_verifier import verify_mcp_services_on_startup
|
|
406
|
-
|
|
407
|
-
logger = get_logger("mcp_verify")
|
|
408
|
-
all_ok, message = verify_mcp_services_on_startup()
|
|
409
|
-
if not all_ok:
|
|
410
|
-
logger.warning(message)
|
|
411
|
-
except Exception:
|
|
412
|
-
# Non-critical - continue with startup
|
|
413
|
-
pass
|
|
414
|
-
|
|
415
|
-
try:
|
|
416
|
-
import asyncio
|
|
417
|
-
import time
|
|
418
|
-
|
|
419
|
-
from ..core.logger import get_logger
|
|
420
|
-
from ..services.mcp_gateway.core.process_pool import pre_warm_mcp_servers
|
|
421
|
-
from ..services.mcp_gateway.core.startup_verification import (
|
|
422
|
-
is_mcp_gateway_configured,
|
|
423
|
-
verify_mcp_gateway_on_startup,
|
|
424
|
-
)
|
|
425
|
-
|
|
426
|
-
logger = get_logger("mcp_prewarm")
|
|
427
|
-
|
|
428
|
-
# Quick check first - if already configured, skip detailed verification
|
|
429
|
-
gateway_configured = is_mcp_gateway_configured()
|
|
430
|
-
|
|
431
|
-
# DISABLED: Pre-warming MCP servers can interfere with Claude Code's MCP management
|
|
432
|
-
# This was causing issues with MCP server initialization and stderr handling
|
|
433
|
-
# def run_pre_warming():
|
|
434
|
-
# loop = None
|
|
435
|
-
# try:
|
|
436
|
-
# start_time = time.time()
|
|
437
|
-
# loop = asyncio.new_event_loop()
|
|
438
|
-
# asyncio.set_event_loop(loop)
|
|
439
|
-
#
|
|
440
|
-
# # Pre-warm MCP servers (especially vector search)
|
|
441
|
-
# logger.info("Pre-warming MCP servers to eliminate startup delay...")
|
|
442
|
-
# loop.run_until_complete(pre_warm_mcp_servers())
|
|
443
|
-
#
|
|
444
|
-
# pre_warm_time = time.time() - start_time
|
|
445
|
-
# if pre_warm_time > 1.0:
|
|
446
|
-
# logger.info(f"MCP servers pre-warmed in {pre_warm_time:.2f}s")
|
|
447
|
-
|
|
448
|
-
# Dummy function to maintain structure
|
|
449
|
-
def run_pre_warming():
|
|
450
|
-
loop = None
|
|
451
|
-
try:
|
|
452
|
-
time.time()
|
|
453
|
-
loop = asyncio.new_event_loop()
|
|
454
|
-
asyncio.set_event_loop(loop)
|
|
455
|
-
|
|
456
|
-
# Also run gateway verification if needed
|
|
457
|
-
if not gateway_configured:
|
|
458
|
-
loop.run_until_complete(verify_mcp_gateway_on_startup())
|
|
459
|
-
|
|
460
|
-
except Exception as e:
|
|
461
|
-
# Non-blocking - log but don't fail
|
|
462
|
-
logger.debug(f"MCP pre-warming error (non-critical): {e}")
|
|
463
|
-
finally:
|
|
464
|
-
# Properly clean up event loop to prevent kqueue warnings
|
|
465
|
-
if loop is not None:
|
|
466
|
-
try:
|
|
467
|
-
# Cancel all running tasks
|
|
468
|
-
pending = asyncio.all_tasks(loop)
|
|
469
|
-
for task in pending:
|
|
470
|
-
task.cancel()
|
|
471
|
-
# Wait for tasks to complete cancellation
|
|
472
|
-
if pending:
|
|
473
|
-
loop.run_until_complete(
|
|
474
|
-
asyncio.gather(*pending, return_exceptions=True)
|
|
475
|
-
)
|
|
476
|
-
except Exception:
|
|
477
|
-
pass # Ignore cleanup errors
|
|
478
|
-
finally:
|
|
479
|
-
loop.close()
|
|
480
|
-
# Clear the event loop reference to help with cleanup
|
|
481
|
-
asyncio.set_event_loop(None)
|
|
482
|
-
|
|
483
|
-
# Run pre-warming in background thread
|
|
484
|
-
import threading
|
|
485
|
-
|
|
486
|
-
pre_warm_thread = threading.Thread(target=run_pre_warming, daemon=True)
|
|
487
|
-
pre_warm_thread.start()
|
|
488
|
-
|
|
489
|
-
return
|
|
490
|
-
|
|
491
|
-
# Run detailed verification in background if not configured
|
|
492
|
-
if not gateway_configured:
|
|
493
|
-
# Note: We don't await this to avoid blocking startup
|
|
494
|
-
def run_verification():
|
|
495
|
-
loop = None
|
|
496
|
-
try:
|
|
497
|
-
loop = asyncio.new_event_loop()
|
|
498
|
-
asyncio.set_event_loop(loop)
|
|
499
|
-
results = loop.run_until_complete(verify_mcp_gateway_on_startup())
|
|
500
|
-
|
|
501
|
-
# Log results but don't block
|
|
502
|
-
from ..core.logger import get_logger
|
|
503
|
-
|
|
504
|
-
logger = get_logger("cli")
|
|
505
|
-
|
|
506
|
-
if results.get("gateway_configured"):
|
|
507
|
-
logger.debug("MCP Gateway verification completed successfully")
|
|
508
|
-
else:
|
|
509
|
-
logger.debug("MCP Gateway verification completed with warnings")
|
|
510
|
-
|
|
511
|
-
except Exception as e:
|
|
512
|
-
from ..core.logger import get_logger
|
|
513
|
-
|
|
514
|
-
logger = get_logger("cli")
|
|
515
|
-
logger.debug(f"MCP Gateway verification failed: {e}")
|
|
516
|
-
finally:
|
|
517
|
-
# Properly clean up event loop to prevent kqueue warnings
|
|
518
|
-
if loop is not None:
|
|
519
|
-
try:
|
|
520
|
-
# Cancel all running tasks
|
|
521
|
-
pending = asyncio.all_tasks(loop)
|
|
522
|
-
for task in pending:
|
|
523
|
-
task.cancel()
|
|
524
|
-
# Wait for tasks to complete cancellation
|
|
525
|
-
if pending:
|
|
526
|
-
loop.run_until_complete(
|
|
527
|
-
asyncio.gather(*pending, return_exceptions=True)
|
|
528
|
-
)
|
|
529
|
-
except Exception:
|
|
530
|
-
pass # Ignore cleanup errors
|
|
531
|
-
finally:
|
|
532
|
-
loop.close()
|
|
533
|
-
# Clear the event loop reference to help with cleanup
|
|
534
|
-
asyncio.set_event_loop(None)
|
|
535
|
-
|
|
536
|
-
# Run in background thread to avoid blocking startup
|
|
537
|
-
import threading
|
|
538
|
-
|
|
539
|
-
verification_thread = threading.Thread(target=run_verification, daemon=True)
|
|
540
|
-
verification_thread.start()
|
|
541
|
-
|
|
542
|
-
except Exception as e:
|
|
543
|
-
# Import logger here to avoid circular imports
|
|
544
|
-
from ..core.logger import get_logger
|
|
545
|
-
|
|
546
|
-
logger = get_logger("cli")
|
|
547
|
-
logger.debug(f"Failed to start MCP Gateway verification: {e}")
|
|
548
|
-
# Continue execution - MCP gateway issues shouldn't block startup
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
def _check_for_updates_async():
|
|
552
|
-
"""
|
|
553
|
-
Check for updates in background thread (non-blocking).
|
|
554
|
-
|
|
555
|
-
WHY: Users should be notified of new versions and have an easy way to upgrade
|
|
556
|
-
without manually checking PyPI/npm. This runs asynchronously on startup to avoid
|
|
557
|
-
blocking the CLI.
|
|
558
|
-
|
|
559
|
-
DESIGN DECISION: This is non-blocking and non-critical - failures are logged
|
|
560
|
-
but don't prevent startup. Only runs for pip/pipx/npm installations, skips
|
|
561
|
-
editable/development installations.
|
|
562
|
-
"""
|
|
563
|
-
|
|
564
|
-
def run_update_check():
|
|
565
|
-
"""Inner function to run in background thread."""
|
|
566
|
-
loop = None
|
|
567
|
-
try:
|
|
568
|
-
import asyncio
|
|
569
|
-
|
|
570
|
-
from ..core.logger import get_logger
|
|
571
|
-
from ..services.self_upgrade_service import SelfUpgradeService
|
|
572
|
-
|
|
573
|
-
logger = get_logger("upgrade_check")
|
|
574
|
-
|
|
575
|
-
# Create new event loop for this thread
|
|
576
|
-
loop = asyncio.new_event_loop()
|
|
577
|
-
asyncio.set_event_loop(loop)
|
|
578
|
-
|
|
579
|
-
# Create upgrade service and check for updates
|
|
580
|
-
upgrade_service = SelfUpgradeService()
|
|
581
|
-
|
|
582
|
-
# Skip for editable installs (development mode)
|
|
583
|
-
from ..services.self_upgrade_service import InstallationMethod
|
|
584
|
-
|
|
585
|
-
if upgrade_service.installation_method == InstallationMethod.EDITABLE:
|
|
586
|
-
logger.debug("Skipping version check for editable installation")
|
|
587
|
-
return
|
|
588
|
-
|
|
589
|
-
# Check and prompt for upgrade if available (non-blocking)
|
|
590
|
-
loop.run_until_complete(upgrade_service.check_and_prompt_on_startup())
|
|
591
|
-
|
|
592
|
-
except Exception as e:
|
|
593
|
-
# Non-critical - log but don't fail startup
|
|
594
|
-
try:
|
|
595
|
-
from ..core.logger import get_logger
|
|
596
|
-
|
|
597
|
-
logger = get_logger("upgrade_check")
|
|
598
|
-
logger.debug(f"Update check failed (non-critical): {e}")
|
|
599
|
-
except Exception:
|
|
600
|
-
pass # Avoid any errors in error handling
|
|
601
|
-
finally:
|
|
602
|
-
# Properly clean up event loop
|
|
603
|
-
if loop is not None:
|
|
604
|
-
try:
|
|
605
|
-
# Cancel all running tasks
|
|
606
|
-
pending = asyncio.all_tasks(loop)
|
|
607
|
-
for task in pending:
|
|
608
|
-
task.cancel()
|
|
609
|
-
# Wait for tasks to complete cancellation
|
|
610
|
-
if pending:
|
|
611
|
-
loop.run_until_complete(
|
|
612
|
-
asyncio.gather(*pending, return_exceptions=True)
|
|
613
|
-
)
|
|
614
|
-
except Exception:
|
|
615
|
-
pass # Ignore cleanup errors
|
|
616
|
-
finally:
|
|
617
|
-
loop.close()
|
|
618
|
-
# Clear the event loop reference to help with cleanup
|
|
619
|
-
asyncio.set_event_loop(None)
|
|
620
|
-
|
|
621
|
-
# Run update check in background thread to avoid blocking startup
|
|
622
|
-
import threading
|
|
623
|
-
|
|
624
|
-
update_check_thread = threading.Thread(target=run_update_check, daemon=True)
|
|
625
|
-
update_check_thread.start()
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
def _ensure_run_attributes(args):
|
|
629
|
-
"""
|
|
630
|
-
Ensure run command attributes exist when defaulting to run.
|
|
631
|
-
|
|
632
|
-
WHY: When no command is specified, we default to 'run' but the args object
|
|
633
|
-
won't have run-specific attributes from the subparser. This function ensures
|
|
634
|
-
they exist with sensible defaults.
|
|
635
|
-
|
|
636
|
-
Args:
|
|
637
|
-
args: Parsed arguments object to update
|
|
638
|
-
"""
|
|
639
|
-
# Set defaults for run command attributes
|
|
640
|
-
args.no_tickets = getattr(args, "no_tickets", False)
|
|
641
|
-
args.no_hooks = getattr(args, "no_hooks", False)
|
|
642
|
-
args.intercept_commands = getattr(args, "intercept_commands", False)
|
|
643
|
-
args.input = getattr(args, "input", None)
|
|
644
|
-
args.non_interactive = getattr(args, "non_interactive", False)
|
|
645
|
-
args.no_native_agents = getattr(args, "no_native_agents", False)
|
|
646
|
-
|
|
647
|
-
# Handle claude_args - if --resume flag is set, add it to claude_args
|
|
648
|
-
claude_args = getattr(args, "claude_args", [])
|
|
649
|
-
if getattr(args, "resume", False):
|
|
650
|
-
# Add --resume to claude_args if not already present
|
|
651
|
-
if "--resume" not in claude_args:
|
|
652
|
-
claude_args = ["--resume", *claude_args]
|
|
653
|
-
args.claude_args = claude_args
|
|
654
|
-
|
|
655
|
-
args.launch_method = getattr(args, "launch_method", "exec")
|
|
656
|
-
args.websocket = getattr(args, "websocket", False)
|
|
657
|
-
args.websocket_port = getattr(args, "websocket_port", 8765)
|
|
658
|
-
# CRITICAL: Include mpm_resume attribute for session resumption
|
|
659
|
-
args.mpm_resume = getattr(args, "mpm_resume", None)
|
|
660
|
-
# Also include monitor and force attributes
|
|
661
|
-
args.monitor = getattr(args, "monitor", False)
|
|
662
|
-
args.force = getattr(args, "force", False)
|
|
663
|
-
args.reload_agents = getattr(args, "reload_agents", False)
|
|
664
|
-
# Include dependency checking attributes
|
|
665
|
-
args.check_dependencies = getattr(args, "check_dependencies", True)
|
|
666
|
-
args.force_check_dependencies = getattr(args, "force_check_dependencies", False)
|
|
667
|
-
args.no_prompt = getattr(args, "no_prompt", False)
|
|
668
|
-
args.force_prompt = getattr(args, "force_prompt", False)
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
def _execute_command(command: str, args) -> int:
|
|
672
|
-
"""
|
|
673
|
-
Execute the specified command.
|
|
674
|
-
|
|
675
|
-
WHY: This function maps command names to their implementations, providing
|
|
676
|
-
a single place to manage command routing. Experimental commands are imported
|
|
677
|
-
lazily to avoid loading unnecessary code.
|
|
678
|
-
|
|
679
|
-
DESIGN DECISION: run_guarded is imported only when needed to maintain
|
|
680
|
-
separation between stable and experimental features.
|
|
681
|
-
|
|
682
|
-
Args:
|
|
683
|
-
command: The command name to execute
|
|
684
|
-
args: Parsed command line arguments
|
|
685
|
-
|
|
686
|
-
Returns:
|
|
687
|
-
Exit code from the command
|
|
688
|
-
"""
|
|
689
|
-
# Handle experimental run-guarded command separately with lazy import
|
|
690
|
-
if command == "run-guarded":
|
|
691
|
-
# Lazy import to avoid loading experimental code unless needed
|
|
692
|
-
from .commands.run_guarded import execute_run_guarded
|
|
693
|
-
|
|
694
|
-
result = execute_run_guarded(args)
|
|
695
|
-
return result if result is not None else 0
|
|
696
|
-
|
|
697
|
-
# Handle mpm-init command with lazy import
|
|
698
|
-
if command == "mpm-init":
|
|
699
|
-
# Lazy import to avoid loading unless needed
|
|
700
|
-
from .commands.mpm_init_handler import manage_mpm_init
|
|
701
|
-
|
|
702
|
-
result = manage_mpm_init(args)
|
|
703
|
-
return result if result is not None else 0
|
|
704
|
-
|
|
705
|
-
# Handle uninstall command with lazy import
|
|
706
|
-
if command == "uninstall":
|
|
707
|
-
# Lazy import to avoid loading unless needed
|
|
708
|
-
from .commands.uninstall import UninstallCommand
|
|
709
|
-
|
|
710
|
-
cmd = UninstallCommand()
|
|
711
|
-
result = cmd.execute(args)
|
|
712
|
-
# Convert CommandResult to exit code
|
|
713
|
-
return result.exit_code if result else 0
|
|
714
|
-
|
|
715
|
-
# Handle verify command with lazy import
|
|
716
|
-
if command == "verify":
|
|
717
|
-
# Lazy import to avoid loading unless needed
|
|
718
|
-
from .commands.verify import handle_verify
|
|
719
|
-
|
|
720
|
-
result = handle_verify(args)
|
|
721
|
-
return result if result is not None else 0
|
|
722
|
-
|
|
723
|
-
# Handle auto-configure command with lazy import
|
|
724
|
-
if command == "auto-configure":
|
|
725
|
-
# Lazy import to avoid loading unless needed
|
|
726
|
-
from .commands.auto_configure import AutoConfigureCommand
|
|
727
|
-
|
|
728
|
-
cmd = AutoConfigureCommand()
|
|
729
|
-
result = cmd.run(args)
|
|
730
|
-
# Convert CommandResult to exit code
|
|
731
|
-
return result.exit_code if result else 0
|
|
732
|
-
|
|
733
|
-
# Map stable commands to their implementations
|
|
734
|
-
command_map = {
|
|
735
|
-
CLICommands.RUN.value: run_session,
|
|
736
|
-
# CLICommands.RUN_GUARDED.value is handled above
|
|
737
|
-
CLICommands.TICKETS.value: manage_tickets,
|
|
738
|
-
CLICommands.INFO.value: show_info,
|
|
739
|
-
CLICommands.AGENTS.value: manage_agents,
|
|
740
|
-
CLICommands.AGENT_MANAGER.value: manage_agent_manager,
|
|
741
|
-
CLICommands.MEMORY.value: manage_memory,
|
|
742
|
-
CLICommands.MONITOR.value: manage_monitor,
|
|
743
|
-
CLICommands.DASHBOARD.value: manage_dashboard,
|
|
744
|
-
CLICommands.CONFIG.value: manage_config,
|
|
745
|
-
CLICommands.CONFIGURE.value: manage_configure,
|
|
746
|
-
CLICommands.AGGREGATE.value: aggregate_command,
|
|
747
|
-
CLICommands.ANALYZE_CODE.value: manage_analyze_code,
|
|
748
|
-
CLICommands.CLEANUP.value: cleanup_memory,
|
|
749
|
-
CLICommands.MCP.value: manage_mcp,
|
|
750
|
-
CLICommands.DOCTOR.value: run_doctor,
|
|
751
|
-
CLICommands.UPGRADE.value: upgrade,
|
|
752
|
-
"debug": manage_debug, # Add debug command
|
|
753
|
-
"mpm-init": None, # Will be handled separately with lazy import
|
|
754
|
-
}
|
|
755
|
-
|
|
756
|
-
# Execute command if found
|
|
757
|
-
if command in command_map:
|
|
758
|
-
result = command_map[command](args)
|
|
759
|
-
# Commands may return None (success) or an exit code
|
|
760
|
-
return result if result is not None else 0
|
|
761
|
-
# Unknown command - this shouldn't happen with argparse
|
|
762
|
-
# but we handle it for completeness
|
|
763
|
-
print(f"Unknown command: {command}")
|
|
764
|
-
return 1
|
|
765
|
-
|
|
766
|
-
|
|
767
95
|
# For backward compatibility - export main
|
|
768
96
|
if __name__ == "__main__":
|
|
769
97
|
sys.exit(main())
|