claude-mpm 4.7.4__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_AGENT_TEMPLATE.md +118 -0
- claude_mpm/agents/BASE_ENGINEER.md +286 -0
- claude_mpm/agents/BASE_PM.md +106 -1
- claude_mpm/agents/OUTPUT_STYLE.md +329 -11
- claude_mpm/agents/PM_INSTRUCTIONS.md +397 -459
- claude_mpm/agents/agent_loader.py +17 -5
- claude_mpm/agents/frontmatter_validator.py +284 -253
- claude_mpm/agents/templates/README.md +465 -0
- claude_mpm/agents/templates/agent-manager.json +4 -1
- claude_mpm/agents/templates/agentic-coder-optimizer.json +13 -3
- claude_mpm/agents/templates/api_qa.json +11 -2
- claude_mpm/agents/templates/circuit_breakers.md +638 -0
- claude_mpm/agents/templates/clerk-ops.json +12 -2
- claude_mpm/agents/templates/code_analyzer.json +8 -2
- claude_mpm/agents/templates/content-agent.json +358 -0
- claude_mpm/agents/templates/dart_engineer.json +15 -2
- claude_mpm/agents/templates/data_engineer.json +15 -2
- claude_mpm/agents/templates/documentation.json +10 -2
- claude_mpm/agents/templates/engineer.json +21 -1
- claude_mpm/agents/templates/gcp_ops_agent.json +12 -2
- claude_mpm/agents/templates/git_file_tracking.md +584 -0
- claude_mpm/agents/templates/golang_engineer.json +270 -0
- claude_mpm/agents/templates/imagemagick.json +4 -1
- claude_mpm/agents/templates/java_engineer.json +346 -0
- claude_mpm/agents/templates/local_ops_agent.json +1227 -6
- claude_mpm/agents/templates/memory_manager.json +4 -1
- claude_mpm/agents/templates/nextjs_engineer.json +141 -133
- claude_mpm/agents/templates/ops.json +12 -2
- claude_mpm/agents/templates/php-engineer.json +270 -174
- claude_mpm/agents/templates/pm_examples.md +474 -0
- claude_mpm/agents/templates/pm_red_flags.md +240 -0
- claude_mpm/agents/templates/product_owner.json +338 -0
- claude_mpm/agents/templates/project_organizer.json +14 -4
- claude_mpm/agents/templates/prompt-engineer.json +13 -2
- claude_mpm/agents/templates/python_engineer.json +174 -81
- claude_mpm/agents/templates/qa.json +11 -2
- claude_mpm/agents/templates/react_engineer.json +16 -3
- claude_mpm/agents/templates/refactoring_engineer.json +12 -2
- claude_mpm/agents/templates/research.json +34 -21
- claude_mpm/agents/templates/response_format.md +583 -0
- claude_mpm/agents/templates/ruby-engineer.json +129 -192
- claude_mpm/agents/templates/rust_engineer.json +270 -0
- claude_mpm/agents/templates/security.json +10 -2
- claude_mpm/agents/templates/svelte-engineer.json +225 -0
- claude_mpm/agents/templates/ticketing.json +10 -2
- claude_mpm/agents/templates/typescript_engineer.json +116 -125
- claude_mpm/agents/templates/validation_templates.md +312 -0
- claude_mpm/agents/templates/vercel_ops_agent.json +12 -2
- claude_mpm/agents/templates/version_control.json +12 -2
- claude_mpm/agents/templates/web_qa.json +11 -2
- claude_mpm/agents/templates/web_ui.json +15 -2
- claude_mpm/cli/__init__.py +34 -614
- 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 +235 -148
- claude_mpm/cli/commands/agents_detect.py +380 -0
- claude_mpm/cli/commands/agents_recommend.py +309 -0
- 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 +570 -0
- claude_mpm/cli/commands/config.py +47 -13
- claude_mpm/cli/commands/configure.py +419 -1571
- 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 +585 -196
- claude_mpm/cli/commands/mpm_init_handler.py +37 -3
- claude_mpm/cli/commands/search.py +170 -4
- claude_mpm/cli/commands/upgrade.py +152 -0
- 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/agents_parser.py +9 -0
- claude_mpm/cli/parsers/auto_configure_parser.py +245 -0
- claude_mpm/cli/parsers/base_parser.py +110 -3
- claude_mpm/cli/parsers/local_deploy_parser.py +227 -0
- claude_mpm/cli/parsers/mpm_init_parser.py +65 -5
- 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-agents-detect.md +168 -0
- claude_mpm/commands/mpm-agents-recommend.md +214 -0
- claude_mpm/commands/mpm-agents.md +75 -1
- claude_mpm/commands/mpm-auto-configure.md +217 -0
- claude_mpm/commands/mpm-help.md +163 -0
- claude_mpm/commands/mpm-init.md +148 -3
- 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/constants.py +1 -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/log_manager.py +2 -0
- 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/__init__.py +20 -0
- claude_mpm/hooks/claude_hooks/event_handlers.py +4 -2
- claude_mpm/hooks/claude_hooks/response_tracking.py +35 -1
- claude_mpm/hooks/claude_hooks/services/connection_manager_http.py +23 -2
- claude_mpm/hooks/failure_learning/__init__.py +60 -0
- claude_mpm/hooks/failure_learning/failure_detection_hook.py +235 -0
- claude_mpm/hooks/failure_learning/fix_detection_hook.py +217 -0
- claude_mpm/hooks/failure_learning/learning_extraction_hook.py +286 -0
- claude_mpm/hooks/instruction_reinforcement.py +7 -2
- claude_mpm/hooks/kuzu_enrichment_hook.py +263 -0
- claude_mpm/hooks/kuzu_memory_hook.py +37 -12
- claude_mpm/hooks/kuzu_response_hook.py +183 -0
- claude_mpm/models/resume_log.py +340 -0
- claude_mpm/services/agents/__init__.py +18 -5
- claude_mpm/services/agents/auto_config_manager.py +796 -0
- 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/observers.py +547 -0
- claude_mpm/services/agents/recommender.py +568 -0
- claude_mpm/services/agents/registry/modification_tracker.py +5 -2
- claude_mpm/services/command_handler_service.py +11 -5
- claude_mpm/services/core/__init__.py +33 -1
- claude_mpm/services/core/interfaces/__init__.py +90 -3
- claude_mpm/services/core/interfaces/agent.py +184 -0
- 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/project.py +121 -0
- claude_mpm/services/core/interfaces/restart.py +307 -0
- claude_mpm/services/core/interfaces/stability.py +260 -0
- claude_mpm/services/core/memory_manager.py +11 -24
- claude_mpm/services/core/models/__init__.py +79 -0
- claude_mpm/services/core/models/agent_config.py +381 -0
- 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/models/toolchain.py +306 -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 +38 -33
- 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/main.py +30 -0
- claude_mpm/services/mcp_gateway/tools/external_mcp_services.py +206 -32
- claude_mpm/services/mcp_gateway/tools/health_check_tool.py +30 -28
- claude_mpm/services/mcp_gateway/tools/kuzu_memory_service.py +25 -5
- claude_mpm/services/mcp_service_verifier.py +1 -1
- claude_mpm/services/memory/failure_tracker.py +563 -0
- claude_mpm/services/memory_hook_service.py +165 -4
- 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/project/__init__.py +23 -0
- claude_mpm/services/project/detection_strategies.py +719 -0
- claude_mpm/services/project/toolchain_analyzer.py +581 -0
- claude_mpm/services/self_upgrade_service.py +342 -0
- 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/storage/state_storage.py +15 -15
- 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 +40 -20
- claude_mpm/utils/display_helper.py +260 -0
- claude_mpm/utils/git_analyzer.py +407 -0
- claude_mpm/utils/robust_installer.py +73 -19
- {claude_mpm-4.7.4.dist-info → claude_mpm-4.18.2.dist-info}/METADATA +129 -12
- {claude_mpm-4.7.4.dist-info → claude_mpm-4.18.2.dist-info}/RECORD +295 -193
- claude_mpm/dashboard/static/css/code-tree.css +0 -1639
- claude_mpm/dashboard/static/index-hub-backup.html +0 -713
- 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.7.4.dist-info → claude_mpm-4.18.2.dist-info}/WHEEL +0 -0
- {claude_mpm-4.7.4.dist-info → claude_mpm-4.18.2.dist-info}/entry_points.txt +0 -0
- {claude_mpm-4.7.4.dist-info → claude_mpm-4.18.2.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-4.7.4.dist-info → claude_mpm-4.18.2.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Stability Monitoring Data Models for Claude MPM Framework
|
|
3
|
+
===========================================================
|
|
4
|
+
|
|
5
|
+
WHY: This module defines data structures for stability monitoring operations,
|
|
6
|
+
including memory leak detection, log pattern matching, and resource usage tracking.
|
|
7
|
+
|
|
8
|
+
DESIGN DECISION: Uses dataclasses for immutability and type safety. Provides
|
|
9
|
+
clear data structures for proactive monitoring and crash prevention.
|
|
10
|
+
|
|
11
|
+
ARCHITECTURE:
|
|
12
|
+
- MemoryTrend: Memory usage trend analysis with leak detection
|
|
13
|
+
- LogPatternMatch: Log pattern match with severity and context
|
|
14
|
+
- ResourceUsage: Comprehensive resource usage snapshot
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from dataclasses import asdict, dataclass, field
|
|
18
|
+
from datetime import datetime
|
|
19
|
+
from typing import Any, Dict, List
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@dataclass
|
|
23
|
+
class MemoryTrend:
|
|
24
|
+
"""
|
|
25
|
+
Memory usage trend analysis result.
|
|
26
|
+
|
|
27
|
+
WHY: Provides structured data for memory leak detection, including
|
|
28
|
+
historical measurements, slope calculation, and leak detection status.
|
|
29
|
+
|
|
30
|
+
Attributes:
|
|
31
|
+
deployment_id: Unique deployment identifier
|
|
32
|
+
timestamps: List of measurement timestamps
|
|
33
|
+
memory_mb: List of memory measurements in megabytes
|
|
34
|
+
slope_mb_per_minute: Calculated memory growth rate (MB/minute)
|
|
35
|
+
is_leaking: Whether a memory leak was detected
|
|
36
|
+
window_size: Number of measurements in the analysis window
|
|
37
|
+
threshold_mb_per_minute: Leak detection threshold used
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
deployment_id: str
|
|
41
|
+
timestamps: List[datetime] = field(default_factory=list)
|
|
42
|
+
memory_mb: List[float] = field(default_factory=list)
|
|
43
|
+
slope_mb_per_minute: float = 0.0
|
|
44
|
+
is_leaking: bool = False
|
|
45
|
+
window_size: int = 0
|
|
46
|
+
threshold_mb_per_minute: float = 10.0
|
|
47
|
+
|
|
48
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
49
|
+
"""
|
|
50
|
+
Convert to dictionary for JSON serialization.
|
|
51
|
+
|
|
52
|
+
Returns:
|
|
53
|
+
Dictionary representation with datetimes converted to ISO format
|
|
54
|
+
"""
|
|
55
|
+
data = asdict(self)
|
|
56
|
+
data["timestamps"] = [ts.isoformat() for ts in self.timestamps]
|
|
57
|
+
return data
|
|
58
|
+
|
|
59
|
+
@classmethod
|
|
60
|
+
def from_dict(cls, data: Dict[str, Any]) -> "MemoryTrend":
|
|
61
|
+
"""
|
|
62
|
+
Create MemoryTrend from dictionary.
|
|
63
|
+
|
|
64
|
+
Args:
|
|
65
|
+
data: Dictionary from JSON deserialization
|
|
66
|
+
|
|
67
|
+
Returns:
|
|
68
|
+
MemoryTrend instance
|
|
69
|
+
"""
|
|
70
|
+
# Convert ISO strings to datetime
|
|
71
|
+
if isinstance(data.get("timestamps"), list):
|
|
72
|
+
data["timestamps"] = [
|
|
73
|
+
datetime.fromisoformat(ts) if isinstance(ts, str) else ts
|
|
74
|
+
for ts in data["timestamps"]
|
|
75
|
+
]
|
|
76
|
+
|
|
77
|
+
return cls(**data)
|
|
78
|
+
|
|
79
|
+
@property
|
|
80
|
+
def latest_memory_mb(self) -> float:
|
|
81
|
+
"""Get the most recent memory measurement."""
|
|
82
|
+
return self.memory_mb[-1] if self.memory_mb else 0.0
|
|
83
|
+
|
|
84
|
+
@property
|
|
85
|
+
def oldest_memory_mb(self) -> float:
|
|
86
|
+
"""Get the oldest memory measurement in the window."""
|
|
87
|
+
return self.memory_mb[0] if self.memory_mb else 0.0
|
|
88
|
+
|
|
89
|
+
@property
|
|
90
|
+
def time_span_minutes(self) -> float:
|
|
91
|
+
"""Get the time span covered by the measurements in minutes."""
|
|
92
|
+
if len(self.timestamps) < 2:
|
|
93
|
+
return 0.0
|
|
94
|
+
delta = self.timestamps[-1] - self.timestamps[0]
|
|
95
|
+
return delta.total_seconds() / 60.0
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
@dataclass
|
|
99
|
+
class LogPatternMatch:
|
|
100
|
+
"""
|
|
101
|
+
Result of a log pattern match.
|
|
102
|
+
|
|
103
|
+
WHY: Contains all information about a detected error pattern in logs,
|
|
104
|
+
enabling analysis, alerting, and debugging of issues before they cause crashes.
|
|
105
|
+
|
|
106
|
+
Attributes:
|
|
107
|
+
deployment_id: Unique deployment identifier
|
|
108
|
+
pattern: Regex pattern that matched
|
|
109
|
+
line: The log line that matched
|
|
110
|
+
timestamp: When the match was detected
|
|
111
|
+
severity: Error severity level (ERROR, CRITICAL, WARNING)
|
|
112
|
+
line_number: Line number in log file (if available)
|
|
113
|
+
context: Additional context lines (before/after)
|
|
114
|
+
"""
|
|
115
|
+
|
|
116
|
+
deployment_id: str
|
|
117
|
+
pattern: str
|
|
118
|
+
line: str
|
|
119
|
+
timestamp: datetime = field(default_factory=datetime.now)
|
|
120
|
+
severity: str = "ERROR"
|
|
121
|
+
line_number: int = 0
|
|
122
|
+
context: List[str] = field(default_factory=list)
|
|
123
|
+
|
|
124
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
125
|
+
"""
|
|
126
|
+
Convert to dictionary for JSON serialization.
|
|
127
|
+
|
|
128
|
+
Returns:
|
|
129
|
+
Dictionary representation with datetime converted to ISO format
|
|
130
|
+
"""
|
|
131
|
+
data = asdict(self)
|
|
132
|
+
data["timestamp"] = self.timestamp.isoformat()
|
|
133
|
+
return data
|
|
134
|
+
|
|
135
|
+
@classmethod
|
|
136
|
+
def from_dict(cls, data: Dict[str, Any]) -> "LogPatternMatch":
|
|
137
|
+
"""
|
|
138
|
+
Create LogPatternMatch from dictionary.
|
|
139
|
+
|
|
140
|
+
Args:
|
|
141
|
+
data: Dictionary from JSON deserialization
|
|
142
|
+
|
|
143
|
+
Returns:
|
|
144
|
+
LogPatternMatch instance
|
|
145
|
+
"""
|
|
146
|
+
# Convert ISO string to datetime
|
|
147
|
+
if isinstance(data.get("timestamp"), str):
|
|
148
|
+
data["timestamp"] = datetime.fromisoformat(data["timestamp"])
|
|
149
|
+
|
|
150
|
+
return cls(**data)
|
|
151
|
+
|
|
152
|
+
@property
|
|
153
|
+
def is_critical(self) -> bool:
|
|
154
|
+
"""Check if this match represents a critical error."""
|
|
155
|
+
return self.severity == "CRITICAL"
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
@dataclass
|
|
159
|
+
class ResourceUsage:
|
|
160
|
+
"""
|
|
161
|
+
Comprehensive resource usage snapshot.
|
|
162
|
+
|
|
163
|
+
WHY: Provides detailed resource consumption metrics across multiple
|
|
164
|
+
resource types to enable preemptive action before exhaustion.
|
|
165
|
+
|
|
166
|
+
Attributes:
|
|
167
|
+
deployment_id: Unique deployment identifier
|
|
168
|
+
file_descriptors: Current file descriptor count
|
|
169
|
+
max_file_descriptors: Maximum file descriptors allowed (ulimit -n)
|
|
170
|
+
threads: Current thread count
|
|
171
|
+
connections: Current network connection count
|
|
172
|
+
disk_free_mb: Free disk space in working directory (MB)
|
|
173
|
+
is_critical: Whether any resource exceeds 80% threshold
|
|
174
|
+
timestamp: When the measurement was taken
|
|
175
|
+
details: Additional resource-specific details
|
|
176
|
+
"""
|
|
177
|
+
|
|
178
|
+
deployment_id: str
|
|
179
|
+
file_descriptors: int = 0
|
|
180
|
+
max_file_descriptors: int = 0
|
|
181
|
+
threads: int = 0
|
|
182
|
+
connections: int = 0
|
|
183
|
+
disk_free_mb: float = 0.0
|
|
184
|
+
is_critical: bool = False
|
|
185
|
+
timestamp: datetime = field(default_factory=datetime.now)
|
|
186
|
+
details: Dict[str, Any] = field(default_factory=dict)
|
|
187
|
+
|
|
188
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
189
|
+
"""
|
|
190
|
+
Convert to dictionary for JSON serialization.
|
|
191
|
+
|
|
192
|
+
Returns:
|
|
193
|
+
Dictionary representation with datetime converted to ISO format
|
|
194
|
+
"""
|
|
195
|
+
data = asdict(self)
|
|
196
|
+
data["timestamp"] = self.timestamp.isoformat()
|
|
197
|
+
return data
|
|
198
|
+
|
|
199
|
+
@classmethod
|
|
200
|
+
def from_dict(cls, data: Dict[str, Any]) -> "ResourceUsage":
|
|
201
|
+
"""
|
|
202
|
+
Create ResourceUsage from dictionary.
|
|
203
|
+
|
|
204
|
+
Args:
|
|
205
|
+
data: Dictionary from JSON deserialization
|
|
206
|
+
|
|
207
|
+
Returns:
|
|
208
|
+
ResourceUsage instance
|
|
209
|
+
"""
|
|
210
|
+
# Convert ISO string to datetime
|
|
211
|
+
if isinstance(data.get("timestamp"), str):
|
|
212
|
+
data["timestamp"] = datetime.fromisoformat(data["timestamp"])
|
|
213
|
+
|
|
214
|
+
return cls(**data)
|
|
215
|
+
|
|
216
|
+
@property
|
|
217
|
+
def fd_usage_percent(self) -> float:
|
|
218
|
+
"""Calculate file descriptor usage percentage."""
|
|
219
|
+
if self.max_file_descriptors == 0:
|
|
220
|
+
return 0.0
|
|
221
|
+
return (self.file_descriptors / self.max_file_descriptors) * 100.0
|
|
222
|
+
|
|
223
|
+
@property
|
|
224
|
+
def is_fd_critical(self) -> bool:
|
|
225
|
+
"""Check if file descriptor usage is critical (>80%)."""
|
|
226
|
+
return self.fd_usage_percent >= 80.0 # >= instead of > for 80% exactly
|
|
227
|
+
|
|
228
|
+
def get_critical_resources(self) -> List[str]:
|
|
229
|
+
"""
|
|
230
|
+
Get list of resources at critical levels.
|
|
231
|
+
|
|
232
|
+
Returns:
|
|
233
|
+
List of resource names exceeding 80% threshold
|
|
234
|
+
"""
|
|
235
|
+
critical = []
|
|
236
|
+
|
|
237
|
+
if self.is_fd_critical:
|
|
238
|
+
critical.append(
|
|
239
|
+
f"file_descriptors ({self.file_descriptors}/{self.max_file_descriptors})"
|
|
240
|
+
)
|
|
241
|
+
|
|
242
|
+
# Check thread count (threshold from details if available)
|
|
243
|
+
thread_threshold = self.details.get("thread_threshold", 1000)
|
|
244
|
+
if self.threads > thread_threshold * 0.8:
|
|
245
|
+
critical.append(f"threads ({self.threads})")
|
|
246
|
+
|
|
247
|
+
# Check connection count (threshold from details if available)
|
|
248
|
+
connection_threshold = self.details.get("connection_threshold", 500)
|
|
249
|
+
if self.connections > connection_threshold * 0.8:
|
|
250
|
+
critical.append(f"connections ({self.connections})")
|
|
251
|
+
|
|
252
|
+
# Check disk space (threshold from details if available)
|
|
253
|
+
disk_threshold_mb = self.details.get("disk_threshold_mb", 100)
|
|
254
|
+
if self.disk_free_mb < disk_threshold_mb:
|
|
255
|
+
critical.append(f"disk_space ({self.disk_free_mb:.1f}MB free)")
|
|
256
|
+
|
|
257
|
+
return critical
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
__all__ = [
|
|
261
|
+
"LogPatternMatch",
|
|
262
|
+
"MemoryTrend",
|
|
263
|
+
"ResourceUsage",
|
|
264
|
+
]
|
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Toolchain Data Models for Claude MPM Framework
|
|
3
|
+
==============================================
|
|
4
|
+
|
|
5
|
+
WHY: These models represent the structure of project toolchain analysis results.
|
|
6
|
+
They provide a standardized way to represent detected languages, frameworks,
|
|
7
|
+
deployment targets, and overall toolchain characteristics.
|
|
8
|
+
|
|
9
|
+
DESIGN DECISION: Uses dataclasses with field validation and default values
|
|
10
|
+
to ensure data consistency. Confidence levels are included to represent
|
|
11
|
+
uncertainty in detection. Immutable where possible to prevent accidental
|
|
12
|
+
modification of analysis results.
|
|
13
|
+
|
|
14
|
+
Part of TSK-0054: Auto-Configuration Feature - Phase 1
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from dataclasses import dataclass, field
|
|
18
|
+
from enum import Enum
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
from typing import Any, Dict, List, Optional
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class ConfidenceLevel(str, Enum):
|
|
24
|
+
"""Confidence level for detection results.
|
|
25
|
+
|
|
26
|
+
WHY: Not all detections are equally certain. This enum provides a
|
|
27
|
+
standardized way to communicate confidence levels to users and
|
|
28
|
+
enable threshold-based decision making.
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
HIGH = "high" # >80% confidence, very strong indicators
|
|
32
|
+
MEDIUM = "medium" # 50-80% confidence, good indicators
|
|
33
|
+
LOW = "low" # 20-50% confidence, weak indicators
|
|
34
|
+
VERY_LOW = "very_low" # <20% confidence, speculative
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@dataclass(frozen=True)
|
|
38
|
+
class ToolchainComponent:
|
|
39
|
+
"""Represents a component in the project's toolchain.
|
|
40
|
+
|
|
41
|
+
WHY: Toolchain components (languages, frameworks, tools) share common
|
|
42
|
+
attributes like name, version, and confidence. This base model enables
|
|
43
|
+
consistent representation across different component types.
|
|
44
|
+
|
|
45
|
+
DESIGN DECISION: Frozen dataclass to prevent modification after creation,
|
|
46
|
+
ensuring analysis results remain consistent. Version is optional as not
|
|
47
|
+
all components have detectable versions.
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
name: str
|
|
51
|
+
version: Optional[str] = None
|
|
52
|
+
confidence: ConfidenceLevel = ConfidenceLevel.MEDIUM
|
|
53
|
+
metadata: Dict[str, Any] = field(default_factory=dict)
|
|
54
|
+
|
|
55
|
+
def __post_init__(self):
|
|
56
|
+
"""Validate component data after initialization."""
|
|
57
|
+
if not self.name or not self.name.strip():
|
|
58
|
+
raise ValueError("Component name cannot be empty")
|
|
59
|
+
if self.version is not None and not self.version.strip():
|
|
60
|
+
raise ValueError("Component version cannot be empty string")
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
@dataclass(frozen=True)
|
|
64
|
+
class LanguageDetection:
|
|
65
|
+
"""Result of language detection analysis.
|
|
66
|
+
|
|
67
|
+
WHY: Projects often use multiple languages. This model captures both
|
|
68
|
+
primary (main codebase) and secondary (scripts, config) languages
|
|
69
|
+
with their relative proportions and confidence levels.
|
|
70
|
+
|
|
71
|
+
DESIGN DECISION: Includes percentage breakdown to help understand
|
|
72
|
+
language distribution. Confidence per language enables threshold-based
|
|
73
|
+
filtering of uncertain detections.
|
|
74
|
+
"""
|
|
75
|
+
|
|
76
|
+
primary_language: str
|
|
77
|
+
primary_version: Optional[str] = None
|
|
78
|
+
primary_confidence: ConfidenceLevel = ConfidenceLevel.MEDIUM
|
|
79
|
+
secondary_languages: List[ToolchainComponent] = field(default_factory=list)
|
|
80
|
+
language_percentages: Dict[str, float] = field(default_factory=dict)
|
|
81
|
+
|
|
82
|
+
def __post_init__(self):
|
|
83
|
+
"""Validate language detection data."""
|
|
84
|
+
if not self.primary_language or not self.primary_language.strip():
|
|
85
|
+
raise ValueError("Primary language cannot be empty")
|
|
86
|
+
|
|
87
|
+
# Validate language percentages sum to ~100% (allow small floating point error)
|
|
88
|
+
if self.language_percentages:
|
|
89
|
+
total = sum(self.language_percentages.values())
|
|
90
|
+
if not (99.0 <= total <= 101.0):
|
|
91
|
+
raise ValueError(f"Language percentages must sum to 100%, got {total}%")
|
|
92
|
+
|
|
93
|
+
@property
|
|
94
|
+
def all_languages(self) -> List[str]:
|
|
95
|
+
"""Get list of all detected languages (primary + secondary)."""
|
|
96
|
+
languages = [self.primary_language]
|
|
97
|
+
languages.extend(comp.name for comp in self.secondary_languages)
|
|
98
|
+
return languages
|
|
99
|
+
|
|
100
|
+
@property
|
|
101
|
+
def high_confidence_languages(self) -> List[str]:
|
|
102
|
+
"""Get languages detected with high confidence."""
|
|
103
|
+
languages = []
|
|
104
|
+
if self.primary_confidence == ConfidenceLevel.HIGH:
|
|
105
|
+
languages.append(self.primary_language)
|
|
106
|
+
languages.extend(
|
|
107
|
+
comp.name
|
|
108
|
+
for comp in self.secondary_languages
|
|
109
|
+
if comp.confidence == ConfidenceLevel.HIGH
|
|
110
|
+
)
|
|
111
|
+
return languages
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
@dataclass(frozen=True)
|
|
115
|
+
class Framework:
|
|
116
|
+
"""Represents a detected framework or library.
|
|
117
|
+
|
|
118
|
+
WHY: Frameworks are critical for agent recommendation as different agents
|
|
119
|
+
specialize in different frameworks. This model captures framework identity,
|
|
120
|
+
version, type, and usage characteristics.
|
|
121
|
+
|
|
122
|
+
DESIGN DECISION: Includes framework type (web, testing, ORM, etc.) to
|
|
123
|
+
enable category-based recommendations. Popularity metric helps prioritize
|
|
124
|
+
agent recommendations for commonly-used frameworks.
|
|
125
|
+
"""
|
|
126
|
+
|
|
127
|
+
name: str
|
|
128
|
+
version: Optional[str] = None
|
|
129
|
+
framework_type: Optional[str] = None # web, testing, orm, cli, etc.
|
|
130
|
+
confidence: ConfidenceLevel = ConfidenceLevel.MEDIUM
|
|
131
|
+
is_dev_dependency: bool = False
|
|
132
|
+
popularity_score: float = 0.0 # 0.0-1.0, higher = more popular/used
|
|
133
|
+
metadata: Dict[str, Any] = field(default_factory=dict)
|
|
134
|
+
|
|
135
|
+
def __post_init__(self):
|
|
136
|
+
"""Validate framework data."""
|
|
137
|
+
if not self.name or not self.name.strip():
|
|
138
|
+
raise ValueError("Framework name cannot be empty")
|
|
139
|
+
if not (0.0 <= self.popularity_score <= 1.0):
|
|
140
|
+
raise ValueError(
|
|
141
|
+
f"Popularity score must be 0.0-1.0, got {self.popularity_score}"
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
@property
|
|
145
|
+
def display_name(self) -> str:
|
|
146
|
+
"""Get formatted display name with version."""
|
|
147
|
+
if self.version:
|
|
148
|
+
return f"{self.name} {self.version}"
|
|
149
|
+
return self.name
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
@dataclass(frozen=True)
|
|
153
|
+
class DeploymentTarget:
|
|
154
|
+
"""Represents the detected deployment target environment.
|
|
155
|
+
|
|
156
|
+
WHY: Deployment target affects agent recommendations (e.g., DevOps agents
|
|
157
|
+
for Kubernetes, serverless agents for Lambda). This model captures the
|
|
158
|
+
deployment platform and configuration details.
|
|
159
|
+
|
|
160
|
+
DESIGN DECISION: Includes target type (cloud, container, serverless, etc.)
|
|
161
|
+
and platform-specific configuration. Confidence level enables fallback
|
|
162
|
+
to generic recommendations when deployment target is unclear.
|
|
163
|
+
"""
|
|
164
|
+
|
|
165
|
+
target_type: str # cloud, container, serverless, on-premise, edge
|
|
166
|
+
platform: Optional[str] = None # aws, gcp, azure, kubernetes, docker, etc.
|
|
167
|
+
configuration: Dict[str, Any] = field(default_factory=dict)
|
|
168
|
+
confidence: ConfidenceLevel = ConfidenceLevel.MEDIUM
|
|
169
|
+
requires_ops_agent: bool = False
|
|
170
|
+
metadata: Dict[str, Any] = field(default_factory=dict)
|
|
171
|
+
|
|
172
|
+
def __post_init__(self):
|
|
173
|
+
"""Validate deployment target data."""
|
|
174
|
+
valid_types = {"cloud", "container", "serverless", "on-premise", "edge"}
|
|
175
|
+
if self.target_type not in valid_types:
|
|
176
|
+
raise ValueError(
|
|
177
|
+
f"Invalid target_type '{self.target_type}'. "
|
|
178
|
+
f"Must be one of: {valid_types}"
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
@property
|
|
182
|
+
def display_name(self) -> str:
|
|
183
|
+
"""Get formatted display name."""
|
|
184
|
+
if self.platform:
|
|
185
|
+
return f"{self.target_type} ({self.platform})"
|
|
186
|
+
return self.target_type
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
@dataclass
|
|
190
|
+
class ToolchainAnalysis:
|
|
191
|
+
"""Complete toolchain analysis result.
|
|
192
|
+
|
|
193
|
+
WHY: This is the primary output of toolchain analysis, aggregating all
|
|
194
|
+
detected components into a single structure. It provides a complete
|
|
195
|
+
picture of the project's technical stack.
|
|
196
|
+
|
|
197
|
+
DESIGN DECISION: Not frozen to allow caching and updating of analysis
|
|
198
|
+
results. Includes project_path for reference and validation. Provides
|
|
199
|
+
convenience methods for common queries (e.g., has framework, get languages).
|
|
200
|
+
"""
|
|
201
|
+
|
|
202
|
+
project_path: Path
|
|
203
|
+
language_detection: LanguageDetection
|
|
204
|
+
frameworks: List[Framework] = field(default_factory=list)
|
|
205
|
+
deployment_target: Optional[DeploymentTarget] = None
|
|
206
|
+
build_tools: List[ToolchainComponent] = field(default_factory=list)
|
|
207
|
+
package_managers: List[ToolchainComponent] = field(default_factory=list)
|
|
208
|
+
development_tools: List[ToolchainComponent] = field(default_factory=list)
|
|
209
|
+
overall_confidence: ConfidenceLevel = ConfidenceLevel.MEDIUM
|
|
210
|
+
analysis_timestamp: Optional[float] = None
|
|
211
|
+
metadata: Dict[str, Any] = field(default_factory=dict)
|
|
212
|
+
|
|
213
|
+
def __post_init__(self):
|
|
214
|
+
"""Validate toolchain analysis data."""
|
|
215
|
+
if not self.project_path.exists():
|
|
216
|
+
raise ValueError(f"Project path does not exist: {self.project_path}")
|
|
217
|
+
if not self.project_path.is_dir():
|
|
218
|
+
raise ValueError(f"Project path is not a directory: {self.project_path}")
|
|
219
|
+
|
|
220
|
+
def has_framework(self, framework_name: str) -> bool:
|
|
221
|
+
"""Check if a specific framework is detected."""
|
|
222
|
+
return any(fw.name.lower() == framework_name.lower() for fw in self.frameworks)
|
|
223
|
+
|
|
224
|
+
def get_framework(self, framework_name: str) -> Optional[Framework]:
|
|
225
|
+
"""Get framework by name (case-insensitive)."""
|
|
226
|
+
for fw in self.frameworks:
|
|
227
|
+
if fw.name.lower() == framework_name.lower():
|
|
228
|
+
return fw
|
|
229
|
+
return None
|
|
230
|
+
|
|
231
|
+
def get_frameworks_by_type(self, framework_type: str) -> List[Framework]:
|
|
232
|
+
"""Get all frameworks of a specific type."""
|
|
233
|
+
return [
|
|
234
|
+
fw
|
|
235
|
+
for fw in self.frameworks
|
|
236
|
+
if fw.framework_type and fw.framework_type.lower() == framework_type.lower()
|
|
237
|
+
]
|
|
238
|
+
|
|
239
|
+
@property
|
|
240
|
+
def primary_language(self) -> str:
|
|
241
|
+
"""Get the primary language detected."""
|
|
242
|
+
return self.language_detection.primary_language
|
|
243
|
+
|
|
244
|
+
@property
|
|
245
|
+
def all_languages(self) -> List[str]:
|
|
246
|
+
"""Get all detected languages."""
|
|
247
|
+
return self.language_detection.all_languages
|
|
248
|
+
|
|
249
|
+
@property
|
|
250
|
+
def web_frameworks(self) -> List[Framework]:
|
|
251
|
+
"""Get all web frameworks."""
|
|
252
|
+
return self.get_frameworks_by_type("web")
|
|
253
|
+
|
|
254
|
+
@property
|
|
255
|
+
def is_web_project(self) -> bool:
|
|
256
|
+
"""Check if this appears to be a web project."""
|
|
257
|
+
return len(self.web_frameworks) > 0
|
|
258
|
+
|
|
259
|
+
@property
|
|
260
|
+
def requires_devops_agent(self) -> bool:
|
|
261
|
+
"""Check if project likely needs DevOps agent."""
|
|
262
|
+
if self.deployment_target and self.deployment_target.requires_ops_agent:
|
|
263
|
+
return True
|
|
264
|
+
# Check for containerization
|
|
265
|
+
return any(
|
|
266
|
+
tool.name.lower() in {"docker", "kubernetes", "terraform"}
|
|
267
|
+
for tool in self.development_tools
|
|
268
|
+
)
|
|
269
|
+
|
|
270
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
271
|
+
"""Convert analysis to dictionary for serialization."""
|
|
272
|
+
return {
|
|
273
|
+
"project_path": str(self.project_path),
|
|
274
|
+
"language_detection": {
|
|
275
|
+
"primary_language": self.language_detection.primary_language,
|
|
276
|
+
"primary_version": self.language_detection.primary_version,
|
|
277
|
+
"primary_confidence": self.language_detection.primary_confidence.value,
|
|
278
|
+
"secondary_languages": [
|
|
279
|
+
{"name": lang.name, "version": lang.version}
|
|
280
|
+
for lang in self.language_detection.secondary_languages
|
|
281
|
+
],
|
|
282
|
+
"language_percentages": self.language_detection.language_percentages,
|
|
283
|
+
},
|
|
284
|
+
"frameworks": [
|
|
285
|
+
{
|
|
286
|
+
"name": fw.name,
|
|
287
|
+
"version": fw.version,
|
|
288
|
+
"type": fw.framework_type,
|
|
289
|
+
"confidence": fw.confidence.value,
|
|
290
|
+
}
|
|
291
|
+
for fw in self.frameworks
|
|
292
|
+
],
|
|
293
|
+
"deployment_target": (
|
|
294
|
+
{
|
|
295
|
+
"type": self.deployment_target.target_type,
|
|
296
|
+
"platform": self.deployment_target.platform,
|
|
297
|
+
"confidence": self.deployment_target.confidence.value,
|
|
298
|
+
}
|
|
299
|
+
if self.deployment_target
|
|
300
|
+
else None
|
|
301
|
+
),
|
|
302
|
+
"build_tools": [tool.name for tool in self.build_tools],
|
|
303
|
+
"package_managers": [pm.name for pm in self.package_managers],
|
|
304
|
+
"overall_confidence": self.overall_confidence.value,
|
|
305
|
+
"metadata": self.metadata,
|
|
306
|
+
}
|
|
@@ -13,6 +13,7 @@ that was previously embedded in FrameworkLoader. It manages:
|
|
|
13
13
|
The service consolidates path management logic while maintaining backward compatibility.
|
|
14
14
|
"""
|
|
15
15
|
|
|
16
|
+
import os
|
|
16
17
|
import subprocess
|
|
17
18
|
from enum import Enum
|
|
18
19
|
from pathlib import Path
|
|
@@ -74,10 +75,25 @@ class PathResolver(IPathResolver):
|
|
|
74
75
|
return path_obj
|
|
75
76
|
|
|
76
77
|
if base_dir is None:
|
|
77
|
-
base_dir =
|
|
78
|
+
base_dir = self._get_working_dir()
|
|
78
79
|
|
|
79
80
|
return (base_dir / path_obj).resolve()
|
|
80
81
|
|
|
82
|
+
def _get_working_dir(self) -> Path:
|
|
83
|
+
"""Get working directory respecting CLAUDE_MPM_USER_PWD.
|
|
84
|
+
|
|
85
|
+
When Claude MPM runs from a global installation, CLAUDE_MPM_USER_PWD
|
|
86
|
+
contains the user's actual working directory. This ensures project-local
|
|
87
|
+
paths are resolved correctly.
|
|
88
|
+
|
|
89
|
+
Returns:
|
|
90
|
+
Path: The user's working directory
|
|
91
|
+
"""
|
|
92
|
+
user_pwd = os.environ.get("CLAUDE_MPM_USER_PWD")
|
|
93
|
+
if user_pwd:
|
|
94
|
+
return Path(user_pwd)
|
|
95
|
+
return Path.cwd()
|
|
96
|
+
|
|
81
97
|
def validate_path(self, path: Path, must_exist: bool = False) -> bool:
|
|
82
98
|
"""
|
|
83
99
|
Validate a path for security and existence.
|
|
@@ -129,7 +145,7 @@ class PathResolver(IPathResolver):
|
|
|
129
145
|
Project root path or None if not found
|
|
130
146
|
"""
|
|
131
147
|
if start_path is None:
|
|
132
|
-
start_path =
|
|
148
|
+
start_path = self._get_working_dir()
|
|
133
149
|
|
|
134
150
|
start_path = start_path.resolve()
|
|
135
151
|
|
|
@@ -265,7 +281,7 @@ class PathResolver(IPathResolver):
|
|
|
265
281
|
|
|
266
282
|
if agents_dir and agents_dir.exists():
|
|
267
283
|
discovered_agents_dir = agents_dir
|
|
268
|
-
self.logger.
|
|
284
|
+
self.logger.debug(f"Using custom agents directory: {discovered_agents_dir}")
|
|
269
285
|
elif framework_path and framework_path != Path("__PACKAGED__"):
|
|
270
286
|
# Prioritize templates directory over main agents directory
|
|
271
287
|
templates_dir = (
|
|
@@ -299,7 +315,7 @@ class PathResolver(IPathResolver):
|
|
|
299
315
|
paths = {"project": None, "user": None, "system": None}
|
|
300
316
|
|
|
301
317
|
# Project-specific instructions
|
|
302
|
-
project_path =
|
|
318
|
+
project_path = self._get_working_dir() / ".claude-mpm" / "INSTRUCTIONS.md"
|
|
303
319
|
if project_path.exists():
|
|
304
320
|
paths["project"] = project_path
|
|
305
321
|
|
|
@@ -423,11 +439,11 @@ class PathResolver(IPathResolver):
|
|
|
423
439
|
"""Check common locations for claude-mpm."""
|
|
424
440
|
candidates = [
|
|
425
441
|
# Current directory (if we're already in claude-mpm)
|
|
426
|
-
|
|
442
|
+
self._get_working_dir(),
|
|
427
443
|
# Development location
|
|
428
444
|
Path.home() / "Projects" / "claude-mpm",
|
|
429
445
|
# Current directory subdirectory
|
|
430
|
-
|
|
446
|
+
self._get_working_dir() / "claude-mpm",
|
|
431
447
|
]
|
|
432
448
|
|
|
433
449
|
for candidate in candidates:
|
|
@@ -487,7 +503,7 @@ class PathResolver(IPathResolver):
|
|
|
487
503
|
pass
|
|
488
504
|
|
|
489
505
|
# Check if we're in development
|
|
490
|
-
if (
|
|
506
|
+
if (self._get_working_dir() / "pyproject.toml").exists():
|
|
491
507
|
return DeploymentContext.DEVELOPMENT
|
|
492
508
|
|
|
493
509
|
return DeploymentContext.UNKNOWN
|
|
@@ -13,6 +13,6 @@ DESIGN DECISIONS:
|
|
|
13
13
|
|
|
14
14
|
from .diagnostic_runner import DiagnosticRunner
|
|
15
15
|
from .doctor_reporter import DoctorReporter
|
|
16
|
-
from .models import DiagnosticResult
|
|
16
|
+
from .models import DiagnosticResult
|
|
17
17
|
|
|
18
|
-
__all__ = ["DiagnosticResult", "DiagnosticRunner", "
|
|
18
|
+
__all__ = ["DiagnosticResult", "DiagnosticRunner", "DoctorReporter"]
|